diff --git a/.cursor/rules/rust-best-practices.mdc b/.cursor/rules/rust-best-practices.mdc new file mode 100644 index 0000000000..8ab8a35c13 --- /dev/null +++ b/.cursor/rules/rust-best-practices.mdc @@ -0,0 +1,113 @@ +--- +description: +globs: backend/**/*.rs +alwaysApply: false +--- +--- +description: Rust best practices for the Windmill backend, covering code organization, error handling, performance optimizations, and common patterns to follow when adding new code. +globs: **/*.rs +--- +# Windmill Backend - Rust Best Practices + +## Project Structure + +Windmill uses a workspace-based architecture with multiple crates: + +- **windmill-api**: API server functionality +- **windmill-worker**: Job execution +- **windmill-common**: Shared code used by all crates +- **windmill-queue**: Job & flow queuing +- **windmill-audit**: Audit logging +- Other specialized crates (git-sync, autoscaling, etc.) + +## Adding New Code + +### Module Organization + +- Place new code in the appropriate crate based on functionality +- For API endpoints, create or modify files in `windmill-api/src/` organized by domain +- For shared functionality, use `windmill-common/src/` +- Use the `_ee.rs` suffix for enterprise-only modules +- Follow existing patterns for file structure and organization + +### Error Handling + +- Use the custom `Error` enum from `windmill-common::error` +- Return `Result` or `JsonResult` for functions that can fail +- Use the `?` operator for error propagation +- Add location tracking to errors using `#[track_caller]` + +### Database Operations + +- Use `sqlx` for database operations with prepared statements +- Leverage existing database helper functions in `db.rs` modules +- Use transactions for multi-step operations +- Handle database errors properly + +### API Endpoints + +- Follow existing patterns in the `windmill-api` crate +- Use axum's routing system and extractors +- Group related routes together +- Use consistent response formats (JSON) +- Follow proper authentication and authorization patterns + +## Performance Optimizations + +When generating code, especially involving `serde`, `sqlx`, and `tokio`, prioritize performance by applying the following principles: + +### Serde Optimizations (Serialization & Deserialization) + +- **Specify Structure Explicitly:** When defining structs for Serde (`#[derive(Serialize, Deserialize)]`), use `#[serde(...` attributes extensively. This includes: + * `#[serde(rename = "...")]` or `#[serde(alias = "...")]` to map external names precisely, avoiding dynamic lookups. + * `#[serde(default)]` for optional fields with default values, reducing parsing complexity. + * `#[serde(skip_serializing_if = "...")]` to avoid writing fields that meet a certain condition (e.g., `Option::is_none()`, `Vec::is_empty()`, or a custom function), reducing output size and serialization work. + * `#[serde(skip_serializing)]` or `#[serde(skip_deserializing)]` for fields that should *not* be included. +- **Prefer Borrowing:** Where possible and safe (data lifetime allows), use `Cow<'a, str>` or `&'a str` (with `#[serde(borrow)]`) instead of `String` for string fields during deserialization. This avoids allocating new strings, enabling zero-copy reading from the input buffer. Apply this principle to byte slices (`&'a [u8]` / `Cow<'a, [u8]>`) and potentially borrowed vectors as well. +- **Avoid Intermediate `Value`:** Unless the data structure is truly dynamic or unknown at compile time, deserialize directly into a well-defined struct or enum rather than into `serde_json::Value` (or equivalent for other formats). This avoids unnecessary heap allocations and type switching. + +### SQLx Optimizations (Database Interaction) + +- **Select Only Necessary Columns:** In `SELECT` queries, list specific column names rather than using `SELECT *`. This reduces data transferred from the database and the work needed for hydration/deserialization. +- **Batch Operations:** For multiple `INSERT`, `UPDATE`, or `DELETE` statements, prefer executing them in a single query if the database and driver support it efficiently (e.g., `INSERT INTO ... VALUES (...), (...), ...`). This minimizes round trips to the database. +- **Avoid N+1 Queries:** Do not loop through results of one query and execute a separate query for each item (e.g., fetching users, then querying for each user's profile in a loop). Instead, use JOINs or a single query with an `IN` clause to fetch related data efficiently. +- **Deserialize Directly:** Use `#[derive(FromRow)]` on structs and ensure the struct fields match the selected columns in the query. This allows SQLx to hydrate objects directly, avoiding intermediate data structures. +- **Parameterize Queries:** Always use SQLx's query methods (`.bind(...)`) to pass values as parameters rather than string formatting. This prevents SQL injection and allows the database to cache query plans, improving performance on repeated executions. + +### Tokio Optimizations (Asynchronous Runtime) + +- **Avoid Blocking Operations:** **Crucially**, never perform blocking operations (synchronous file I/O, `std::thread::sleep`, CPU-bound loops, `std::sync::Mutex::lock`, blocking network calls without `tokio::net`) directly within an `async fn` or a standard `tokio::spawn` task. Blocking pauses the entire worker thread, potentially starving other tasks. Use `tokio::task::spawn_blocking` for CPU-intensive work or blocking I/O. +- **Use Tokio's Async Primitives:** Prefer `tokio::sync` (channels, mutexes, semaphores), `tokio::io`, `tokio::net`, and `tokio::time` over their `std` counterparts in asynchronous contexts. These are designed to yield control back to the scheduler. +- **Manage Concurrency:** Be mindful of how many tasks are spawned. Creating a new task for every tiny piece of work can introduce overhead. Group related asynchronous operations where appropriate. +- **Handle Shared State Efficiently:** Use `Arc` for shared ownership in concurrent tasks. When shared state needs mutation, prefer `tokio::sync::Mutex` over `std::sync::Mutex` in `async` code. Consider `tokio::sync::RwLock` if reads significantly outnumber writes. Minimize the duration for which locks are held. +- **Understand `.await`:** Place `.await` strategically to allow the runtime to switch to other ready tasks. Ensure that `.await` points to genuinely asynchronous operations. +- **Backpressure:** If dealing with data streams or queues between tasks, implement backpressure mechanisms (e.g., bounded channels like `tokio::sync::mpsc::channel`) to prevent one component from overwhelming another or critical resources like the database. + +## Enterprise Features + +- Use feature flags for enterprise functionality +- Conditionally compile with `#[cfg(feature = "enterprise")]` +- Isolate enterprise code in separate modules + +## Code Style + +- Group imports by external and internal crates +- Place struct/enum definitions before implementations +- Group similar functionality together +- Use descriptive naming consistent with the codebase +- Follow existing patterns for async code using tokio + +## Testing + +- Write unit tests for core functionality +- Use the `#[cfg(test)]` module for test code +- For database tests, use the existing test utilities + +## Common Crates Used + +- **tokio**: For async runtime +- **axum**: For web server and routing +- **sqlx**: For database operations +- **serde**: For serialization/deserialization +- **tracing**: For logging and diagnostics +- **reqwest**: For HTTP client functionality \ No newline at end of file diff --git a/.cursor/rules/svelte5-best-practices.mdc b/.cursor/rules/svelte5-best-practices.mdc new file mode 100644 index 0000000000..c2fa3baae8 --- /dev/null +++ b/.cursor/rules/svelte5-best-practices.mdc @@ -0,0 +1,229 @@ +--- +description: +globs: frontend/src/**/*.svelte +alwaysApply: false +--- +# Svelte 5 Best Practices + +This guide outlines best practices for developing with Svelte 5, incorporating the new Runes API and other modern Svelte features. They should be applied on every new files created, but not on existing svelte 4 files unless specifically asked to. + +## Reactivity with Runes + +Svelte 5 introduces Runes for more explicit and flexible reactivity. + +1. **Embrace Runes for State Management**: + * Use `$state` for reactive local component state. + ```svelte + + + + ``` + * Use `$derived` for computed values based on other reactive state. + ```svelte + + +

{count} * 2 = {doubled}

+ ``` + * Use `$effect` for side effects that need to run when reactive values change (e.g., logging, manual DOM manipulation, data fetching). Remember `$effect` does not run on the server. + ```svelte + + ``` + +2. **Props with `$props`**: + * Declare component props using `$props()`. This offers better clarity and flexibility compared to `export let`. + ```svelte + + +

Name: {name}

+

Age: {age}

+ ``` + * For bindable props, use `$bindable`. + ```svelte + + + + ``` + +## Event Handling + +* **Use direct event attributes**: Svelte 5 moves away from `on:` directives for DOM events. + * **Do**: `` + * **Don't**: `` +* **For component events, prefer callback props**: Instead of `createEventDispatcher`, pass functions as props. + ```svelte + + + +

Message from child: {message}

+ + + + + ``` + +## Snippets for Content Projection + +* **Use `{#snippet ...}` and `{@render ...}` instead of slots**: Snippets are more powerful and flexible. + ```svelte + + + + + {#snippet title()} + My Awesome Title + {/snippet} + {#snippet content()} +

Some interesting content here.

+ {/snippet} +
+ + + + +
+
{@render title()}
+
{@render content()}
+
+ ``` +* Default content is passed via the `children` prop (which is a snippet). + ```svelte + + +
+ {@render children?.()} +
+ ``` + +## Component Design + +1. **Create Small, Reusable Components**: Break down complex UIs into smaller, focused components. Each component should have a single responsibility. This also aids performance by limiting the scope of reactivity updates. +2. **Descriptive Naming**: Use clear and descriptive names for variables, functions, and components. +3. **Minimize Logic in Components**: Move complex business logic to utility functions or services. Keep components focused on presentation and interaction. + +## State Management (Stores) + +1. **Segment Stores**: Avoid a single global store. Create multiple stores, each responsible for a specific piece of global state (e.g., `userStore.js`, `themeStore.js`). This can help limit reactivity updates to only the parts of the UI that depend on specific state segments. +2. **Use Custom Stores for Complex Logic**: For stores with related methods, create custom stores. + ```javascript + // counterStore.js + import { writable } from 'svelte/store'; + + function createCounter() { + const { subscribe, set, update } = writable(0); + + return { + subscribe, + increment: () => update(n => n + 1), + decrement: () => update(n => n - 1), + reset: () => set(0) + }; + } + export const counter = createCounter(); + ``` +3. **Use Context API for Localized State**: For state shared within a component subtree, consider Svelte's context API (`setContext`, `getContext`) instead of global stores when the state doesn't need to be truly global. + +## Performance Optimizations (Svelte 5) + +When generating Svelte 5 code, prioritize frontend performance by applying the following principles: + +### General Svelte 5 Principles + +- **Leverage the Compiler:** Trust Svelte's compiler to generate optimized JavaScript. Avoid manual DOM manipulation (`document.querySelector`, etc.) unless absolutely necessary for integrating third-party libraries that lack Svelte adapters. +- **Keep Components Small and Focused:** Reinforcing from Component Design, smaller components lead to less complex reactivity graphs and more targeted, efficient updates. + +### Reactivity & State Management + +- **Optimize Computations with `$derived`:** Always use `$derived` for computed values that depend on other state. This ensures the computation only runs when its specific dependencies change, avoiding unnecessary work compared to recomputing derived values in `$effect` or less efficient methods. +- **Minimize `$effect` Usage:** Use `$effect` sparingly and only for true side effects that interact with the outside world or non-Svelte state. Avoid putting complex logic or state updates *within* an `$effect` unless those updates are explicitly intended as a reaction to external changes or non-Svelte state. Excessive or complex effects can impact rendering performance. +- **Structure State for Fine-Grained Updates:** Design your `$state` objects or variables such that updates affect only the necessary parts of the UI. Avoid putting too much unrelated state into a single large object that gets frequently updated, as this can potentially trigger broader updates than necessary. Consider normalizing complex, nested state. + +### List Rendering (`{#each}`) + +- **Mandate `key` Attribute:** Always use a `key` attribute (`{#each items as item (item.id)}`) that refers to a unique, stable identifier for each item in a list. This is critical for allowing Svelte to efficiently update, reorder, add, or remove list items without destroying and re-creating unnecessary DOM elements and component instances. + +### Component Loading & Bundling + +- **Implement Lazy Loading/Code Splitting:** For routes, components, or modules that are not immediately needed on page load, use dynamic imports (`import(...)`) to split the code bundle. SvelteKit handles this automatically for routes, but it can be applied manually to components using helper patterns if needed. +- **Be Mindful of Third-Party Libraries:** When incorporating external libraries, import only the necessary functions or components to minimize the final bundle size. Prefer libraries designed to be tree-shakeable. + +### Rendering & DOM + +- **Use CSS for Animations/Transitions:** Prefer CSS animations or transitions where possible for performance. Svelte's built-in `transition:` directive is also highly optimized and should be used for complex state-driven transitions, but simple cases can often use plain CSS. +- **Optimize Image Loading:** Implement best practices for images: use optimized formats (WebP, AVIF), lazy loading (`loading="lazy"`), and responsive images (``, `srcset`) to avoid loading unnecessarily large images. + +### Server-Side Rendering (SSR) & Hydration + +- **Ensure SSR Compatibility:** Write components that can be rendered on the server for faster initial page loads. Avoid relying on browser-specific APIs (like `window` or `document`) in the main ` + +
+ Hello +
+ ``` +5. **Stay Updated**: Keep Svelte and its related packages up to date to benefit from the latest features, performance improvements, and security fixes. \ No newline at end of file diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 4de33c741c..98a82077cf 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -7,7 +7,6 @@ services: # image: mcr.microsoft.com/vscode/devcontainers/rust:bullseye environment: - DENO_PATH=/usr/local/cargo/bin/deno - - PYTHON_PATH=/usr/bin/python3 - NSJAIL_PATH=/bin/nsjail volumes: - .:/workspace:cached diff --git a/.env b/.env index d4a48661cd..ad887513cd 100644 --- a/.env +++ b/.env @@ -7,3 +7,7 @@ WM_IMAGE=ghcr.io/windmill-labs/windmill:main # To use another port than :80, setup the Caddyfile and the caddy section of the docker-compose to your needs: https://caddyserver.com/docs/getting-started # To have caddy take care of automatic TLS + +# To rotate logs, set the following variables: +#LOG_MAX_SIZE=10m +#LOG_MAX_FILE=3 \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index aba00bfed5..282cba946a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ -* @rubenfiszel +* @rubenfiszel @HugoCasa @alpetric -/community/ @fatonramadani @rubenfiszel -/frontend/ @fatonramadani @rubenfiszel +/community/ @rubenfiszel @HugoCasa @alpetric +/frontend/ @rubenfiszel @HugoCasa @alpetric diff --git a/.github/DockerfileBackendTests b/.github/DockerfileBackendTests index 979d2112dc..7fda0e025f 100644 --- a/.github/DockerfileBackendTests +++ b/.github/DockerfileBackendTests @@ -27,31 +27,38 @@ RUN wget https://golang.org/dl/go1.21.5.linux-amd64.tar.gz && tar -C /usr/local ENV PATH="${PATH}:/usr/local/go/bin" ENV GO_PATH=/usr/local/go/bin/go -# Install UV +# UV RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.4.18/uv-installer.sh | sh && mv /usr/local/cargo/bin/uv /usr/local/bin/uv ENV TZ=Etc/UTC ENV PYTHON_VERSION 3.11.4 +# Python RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz \ && tar -xf Python-${PYTHON_VERSION}.tgz && cd Python-${PYTHON_VERSION}/ && ./configure --enable-optimizations \ && make -j 4 && make install RUN /usr/local/bin/python3 -m pip install pip-tools -COPY --from=oven/bun:1.1.31 /usr/local/bin/bun /usr/bin/bun +# Bun +COPY --from=oven/bun:1.2.4 /usr/local/bin/bun /usr/bin/bun ARG TARGETPLATFORM +# Deno RUN curl -Lsf https://github.com/denoland/deno/releases/download/v2.0.2/deno-x86_64-unknown-linux-gnu.zip -o deno.zip # RUN [ "$TARGETPLATFORM" == "linux/arm64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v2.0.0/deno-aarch64-unknown-linux-gnu.zip -o deno.zip || true - RUN unzip deno.zip && rm deno.zip && mv deno /usr/bin/deno RUN apt-get update \ && apt-get install -y postgresql-client --allow-unauthenticated RUN rustup component add rustfmt + +# C# COPY --from=bitnami/dotnet-sdk:9.0.101-debian-12-r0 /opt/bitnami/dotnet-sdk /opt/dotnet-sdk RUN ln -s /opt/dotnet-sdk/bin/dotnet /usr/bin/dotnet + +# Nushell +COPY --from=ghcr.io/nushell/nushell:0.101.0-bookworm /usr/bin/nu /usr/bin/nu diff --git a/.github/change-versions.sh b/.github/change-versions.sh index 699b87b0d7..454f373038 100755 --- a/.github/change-versions.sh +++ b/.github/change-versions.sh @@ -24,4 +24,4 @@ sed -i -e "/^wmill_pg =/s/= .*/= \">=$VERSION\"/" ${root_dirpath}/lsp/Pipfile sed -i -zE "s/name = \"windmill\"\nversion = \"[^\"]*\"\\n(.*)/name = \"windmill\"\nversion = \"$VERSION\"\\n\\1/" ${root_dirpath}/backend/Cargo.lock -cd ${root_dirpath}/frontend && npm i --package-lock-only +cd ${root_dirpath}/frontend && npm i --package-lock-only --ignore-scripts diff --git a/.github/workflows/backend-check.yml b/.github/workflows/backend-check.yml index 58a1926d6c..901a30cca6 100644 --- a/.github/workflows/backend-check.yml +++ b/.github/workflows/backend-check.yml @@ -1,5 +1,9 @@ name: Backend check on: + workflow_run: + workflows: ["Change versions"] + types: + - completed push: paths: - "backend/**" @@ -16,7 +20,7 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: cache-workspaces: backend - toolchain: 1.83.0 + toolchain: 1.85.0 - uses: Swatinem/rust-cache@v2 with: workspaces: backend @@ -40,7 +44,7 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: cache-workspaces: backend - toolchain: 1.83.0 + toolchain: 1.85.0 - uses: Swatinem/rust-cache@v2 with: workspaces: backend @@ -77,7 +81,7 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: cache-workspaces: backend - toolchain: 1.83.0 + toolchain: 1.85.0 - uses: Swatinem/rust-cache@v2 with: workspaces: backend @@ -117,7 +121,7 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: cache-workspaces: backend - toolchain: 1.83.0 + toolchain: 1.85.0 - uses: Swatinem/rust-cache@v2 with: workspaces: backend diff --git a/.github/workflows/backend-test.yml b/.github/workflows/backend-test.yml index 04668e96e0..6d12e4f69c 100644 --- a/.github/workflows/backend-test.yml +++ b/.github/workflows/backend-test.yml @@ -42,9 +42,6 @@ jobs: - uses: actions/setup-go@v2 with: go-version: 1.21.5 - - uses: actions/setup-python@v2 - with: - python-version: 3.11 - uses: oven-sh/setup-bun@v2 with: bun-version: 1.1.43 @@ -54,7 +51,7 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: cache-workspaces: backend - toolchain: 1.83.0 + toolchain: 1.85.0 - uses: Swatinem/rust-cache@v2 with: workspaces: backend @@ -64,7 +61,7 @@ jobs: deno --version && bun -v && go version && python3 --version && SQLX_OFFLINE=true DATABASE_URL=postgres://postgres:changeme@localhost:5432/windmill - DISABLE_EMBEDDING=true RUST_LOG=info PYTHON_PATH=$(which python) + DISABLE_EMBEDDING=true RUST_LOG=info DENO_PATH=$(which deno) BUN_PATH=$(which bun) GO_PATH=$(which go) UV_PATH=$(which uv) cargo test --features enterprise,deno_core,license,python,rust,scoped_cache --all -- diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 14735dc246..195821b2dd 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -14,9 +14,13 @@ jobs: env: POSTGRES_DB: windmill POSTGRES_PASSWORD: changeme + POSTGRES_INITDB_ARGS: "-c shared_buffers=2GB -c work_mem=32MB -c effective_cache_size=4GB" options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + --shm-size=2g + + windmill: image: ghcr.io/windmill-labs/windmill-ee:main env: @@ -33,10 +37,10 @@ jobs: steps: - uses: denoland/setup-deno@v2 with: - deno-version: v1.x + deno-version: v2.x - name: benchmark timeout-minutes: 30 - run: deno run --unstable -A -r + run: deno run -A -r https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts -c https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json @@ -55,6 +59,7 @@ jobs: env: POSTGRES_DB: windmill POSTGRES_PASSWORD: changeme + POSTGRES_INITDB_ARGS: "-c shared_buffers=2GB -c work_mem=32MB -c effective_cache_size=4GB" options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 @@ -74,10 +79,10 @@ jobs: steps: - uses: denoland/setup-deno@v2 with: - deno-version: v1.x + deno-version: v2.x - name: benchmark timeout-minutes: 20 - run: deno run --unstable -A -r + run: deno run -A -r https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts --no-warm-up -c https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_dedicated.json @@ -96,6 +101,7 @@ jobs: env: POSTGRES_DB: windmill POSTGRES_PASSWORD: changeme + POSTGRES_INITDB_ARGS: "-c shared_buffers=2GB -c work_mem=32MB -c effective_cache_size=4GB" options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 @@ -148,14 +154,15 @@ jobs: steps: - uses: denoland/setup-deno@v2 with: - deno-version: v1.x + deno-version: v2.x - name: benchmark timeout-minutes: 20 - run: deno run --unstable -A -r + run: deno run -A -r https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts -c https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json --workers 4 + --factor 3 - name: Save benchmark results uses: actions/upload-artifact@v4 with: @@ -171,6 +178,7 @@ jobs: env: POSTGRES_DB: windmill POSTGRES_PASSWORD: changeme + POSTGRES_INITDB_ARGS: "-c shared_buffers=2GB -c work_mem=32MB -c effective_cache_size=4GB" options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 @@ -266,14 +274,15 @@ jobs: steps: - uses: denoland/setup-deno@v2 with: - deno-version: v1.x + deno-version: v2.x - name: benchmark timeout-minutes: 20 - run: deno run --unstable -A -r + run: deno run -A -r https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts -c https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json --workers 8 + --factor 3 - name: Save benchmark results uses: actions/upload-artifact@v4 with: @@ -291,7 +300,7 @@ jobs: steps: - uses: denoland/setup-deno@v2 with: - deno-version: v1.x + deno-version: v2.x - uses: actions/checkout@v4 with: ref: benchmarks @@ -300,7 +309,7 @@ jobs: with: merge-multiple: true - name: graphs - run: deno run --unstable -A -r + run: deno run -A -r https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_graphs.ts -c https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/graphs_config.json diff --git a/.github/workflows/build-publish-rh-image.yml b/.github/workflows/build-publish-rh-image.yml index 060f572fe8..b919de6e18 100644 --- a/.github/workflows/build-publish-rh-image.yml +++ b/.github/workflows/build-publish-rh-image.yml @@ -64,7 +64,7 @@ jobs: platforms: linux/amd64 push: true build-args: | - features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core,license,http_trigger,zip,oauth2,kafka,nats,php,mysql,mssql,bigquery,oracledb,postgres_trigger,websocket,python,smtp,csharp,static_frontend,rust + features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp secrets: | rh_username=${{ secrets.RH_USERNAME }} rh_password=${{ secrets.RH_PASSWORD }} @@ -81,7 +81,7 @@ jobs: platforms: linux/arm64 push: true build-args: | - features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core,license,http_trigger,zip,oauth2,kafka,nats,php,mysql,mssql,bigquery,oracledb,postgres_trigger,websocket,python,smtp,csharp,static_frontend,rust + features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp secrets: | rh_username=${{ secrets.RH_USERNAME }} rh_password=${{ secrets.RH_PASSWORD }} @@ -111,8 +111,7 @@ jobs: - uses: actions/upload-artifact@v4 with: name: RHEL9-amd64 build - path: - ${{ steps.extract-ee-amd64.outputs.destination + path: ${{ steps.extract-ee-amd64.outputs.destination }}/windmill-ee-amd64-rhel9 # - uses: actions/upload-artifact@v4 diff --git a/.github/workflows/build_windows_worker_.yml b/.github/workflows/build_windows_worker_.yml index 6082a47e9b..ac1476d76e 100644 --- a/.github/workflows/build_windows_worker_.yml +++ b/.github/workflows/build_windows_worker_.yml @@ -30,6 +30,12 @@ jobs: token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }} fetch-depth: 0 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: 1.85.0 + override: true + - name: Substitute EE code shell: bash run: | @@ -45,8 +51,7 @@ jobs: $env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static" mkdir frontend/build && cd backend New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force - cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,nats,php,mysql,mssql,bigquery,oracledb,postgres_trigger,websocket,python,smtp,csharp,static_frontend,rust - + cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,nats,sqs_trigger,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,mcp - name: Rename binary with corresponding architecture run: | Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe" diff --git a/.github/workflows/docker-image-rpi4.yml b/.github/workflows/docker-image-rpi4.yml index c450723835..bb270aac8a 100644 --- a/.github/workflows/docker-image-rpi4.yml +++ b/.github/workflows/docker-image-rpi4.yml @@ -67,7 +67,7 @@ jobs: platforms: linux/amd64,linux/arm64 push: true build-args: | - features=embedding,parquet,openidconnect,deno_core,license,http_trigger,zip,oauth2,php,mysql,mssql,bigquery,oracledb,postgres_trigger,websocket,python,smtp,csharp,static_frontend,rust + features=embedding,parquet,openidconnect,license,http_trigger,zip,oauth2,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp tags: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev ${{ steps.meta-public.outputs.tags }} diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 18145886db..216d7cefea 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,10 +1,8 @@ env: REGISTRY: ghcr.io - IMAGE_NAME: - ${{ github.event_name != 'pull_request' && github.event_name != + IMAGE_NAME: ${{ github.event_name != 'pull_request' && github.event_name != 'workflow_dispatch' && github.repository || 'windmill-labs/windmill-test' }} - DEV_SHA: - ${{ github.event_name != 'pull_request' && github.event_name != + DEV_SHA: ${{ github.event_name != 'pull_request' && github.event_name != 'workflow_dispatch' && 'dev' || github.event.inputs.tag || github.sha }} name: Build windmill:main on: @@ -33,15 +31,14 @@ on: type: boolean concurrency: group: ${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: false permissions: write-all jobs: build: runs-on: ubicloud - if: - (github.event_name != 'workflow_dispatch') || (github.event.inputs && + if: (github.event_name != 'workflow_dispatch') || (github.event.inputs && !github.event.inputs.ee) steps: - uses: actions/checkout@v4 @@ -95,13 +92,12 @@ jobs: platforms: linux/amd64,linux/arm64 push: true build-args: | - features=embedding,parquet,openidconnect,jemalloc,deno_core,license,http_trigger,zip,oauth2,dind,php,mysql,mssql,bigquery,oracledb,postgres_trigger,websocket,python,smtp,csharp,static_frontend,rust + features=embedding,parquet,openidconnect,jemalloc,license,http_trigger,zip,oauth2,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,agent_worker_server,all_languages,deno_core,mcp tags: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }} ${{ steps.meta-public.outputs.tags }} labels: | ${{ steps.meta-public.outputs.labels }} - org.opencontainers.image.licenses=AGPLv3 build_ee: runs-on: ubicloud @@ -158,7 +154,7 @@ jobs: platforms: linux/amd64,linux/arm64 push: true build-args: | - features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,nats,otel,dind,php,mysql,mssql,bigquery,oracledb,postgres_trigger,websocket,python,smtp,csharp,static_frontend,rust + features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,agent_worker_server,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp tags: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }} ${{ steps.meta-ee-public.outputs.tags }} @@ -316,7 +312,7 @@ jobs: needs: [run_integration_test, build] if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || - startsWith(github.ref, 'refs/tags/v')) && (github.event_name != 'workflow_dispatch') + startsWith(github.ref, 'refs/tags/v')) && (github.event_name != 'workflow_dispatch') steps: - uses: actions/checkout@v4 with: @@ -356,7 +352,7 @@ jobs: verify_ee_image_vulnerabilities: runs-on: ubicloud needs: [tag_latest_ee] - if: startsWith(github.ref, 'refs/tags/v') && (github.event_name != 'workflow_dispatch') + if: startsWith(github.ref, 'refs/tags/v') && (github.event_name != 'workflow_dispatch') steps: - name: Checkout code uses: actions/checkout@v4 @@ -398,8 +394,7 @@ jobs: build_ee_nsjail: needs: [build_ee] runs-on: ubicloud - if: - (github.event_name != 'pull_request') && ((github.event_name != 'workflow_dispatch') || (github.event.inputs.ee || github.event.inputs.nsjail)) + if: (github.event_name != 'pull_request') && ((github.event_name != 'workflow_dispatch') || (github.event.inputs.ee || github.event.inputs.nsjail)) steps: - uses: actions/checkout@v4 @@ -438,7 +433,7 @@ jobs: run: | sed -i 's|FROM ghcr.io/windmill-labs/windmill-ee:dev|FROM ghcr.io/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}|' ./docker/DockerfileNsjail cat ./docker/DockerfileNsjail | grep "FROM" - + - name: Build and push publicly ee uses: depot/build-push-action@v1 with: @@ -452,55 +447,10 @@ jobs: ${{ steps.meta-ee-public.outputs.labels }} org.opencontainers.image.licenses=Windmill-Enterprise-License - build_ee_reports_privately: - needs: [build_ee_nsjail] - runs-on: ubicloud - if: github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch') - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - # - name: Set up Docker Buildx - # uses: docker/setup-buildx-action@v2 - - - uses: depot/setup-action@v1 - - - name: Login to registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Docker meta - id: meta-ee-public - uses: docker/metadata-action@v5 - with: - images: | - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-reports - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=sha,enable=true,priority=100,prefix=,suffix=,format=short - - - name: Build and push publicly ee reports - uses: depot/build-push-action@v1 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - file: "./docker/DockerfileReports" - tags: | - ${{ steps.meta-ee-public.outputs.tags }} - labels: | - ${{ steps.meta-ee-public.outputs.labels }} - org.opencontainers.image.licenses=Windmill-Enterprise-License - publish_ecr_s3: needs: [build_ee_nsjail] runs-on: ubicloud-standard-2-arm - if: - (github.event_name != 'pull_request') && (github.event_name != + if: (github.event_name != 'pull_request') && (github.event_name != 'workflow_dispatch') env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} diff --git a/.github/workflows/frontend-check.yml b/.github/workflows/frontend-check.yml index 64883cb303..a838ab3627 100644 --- a/.github/workflows/frontend-check.yml +++ b/.github/workflows/frontend-check.yml @@ -1,10 +1,15 @@ name: check frontend build on: - pull_request: - types: [opened, synchronize, reopened, closed] + workflow_run: + workflows: ["Change versions"] + types: + - completed + + merge_group: + push: paths: - "frontend/**" - merge_group: + - ".github/workflows/frontend-check.yml" jobs: npm_check: @@ -16,5 +21,6 @@ jobs: node-version: 18 - name: "npm check" timeout-minutes: 5 - run: cd frontend && npm ci && npm run generate-backend-client && npm run + run: + cd frontend && npm ci && npm run generate-backend-client && npm run check diff --git a/.github/workflows/npm_on_release.yml b/.github/workflows/npm_on_release.yml index 352109a0da..18c52cb38d 100644 --- a/.github/workflows/npm_on_release.yml +++ b/.github/workflows/npm_on_release.yml @@ -27,7 +27,7 @@ jobs: registry-url: "https://registry.npmjs.org" - uses: denoland/setup-deno@v2 with: - deno-version: v1.x + deno-version: v2.x - run: cd cli && ./build.sh && cd npm && npm publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/publish_windows_worker.yml b/.github/workflows/publish_windows_worker.yml index b97d3b5560..057a858881 100644 --- a/.github/workflows/publish_windows_worker.yml +++ b/.github/workflows/publish_windows_worker.yml @@ -32,6 +32,12 @@ jobs: token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }} fetch-depth: 0 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: 1.85.0 + override: true + - name: Substitute EE code shell: bash run: | @@ -47,8 +53,7 @@ jobs: $env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static" mkdir frontend/build && cd backend New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force - cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,nats,php,mysql,mssql,bigquery,oracledb,postgres_trigger,websocket,python,smtp,csharp,static_frontend,rust - + cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages,mcp - name: Rename binary with corresponding architecture run: | Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe" diff --git a/.gitignore b/.gitignore index 3e6c2f3a75..b157a1679a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ CaddyfileRemoteMalo **/.idea/ .direnv .vscode +.dev-docker-wrapper* +backend/.minio-data diff --git a/CHANGELOG.md b/CHANGELOG.md index 58de9ee921..6d39fb7dae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,794 @@ # Changelog +## [1.488.0](https://github.com/windmill-labs/windmill/compare/v1.487.0...v1.488.0) (2025-05-07) + + +### Features + +* handle . in interpolated args ([0ac8e47](https://github.com/windmill-labs/windmill/commit/0ac8e477d6fb7c5a7699a198fce9d18a08aff68c)) + + +### Bug Fixes + +* fix azure object storage regression due to object_store regression ([df9f827](https://github.com/windmill-labs/windmill/commit/df9f827d103def27166a767044373bd0754285e2)) +* performance and stability improvement to fetch last deployed script ([75d9924](https://github.com/windmill-labs/windmill/commit/75d992449c845fd11c9a317d401c405e7d78e1ec)) + +## [1.487.0](https://github.com/windmill-labs/windmill/compare/v1.486.1...v1.487.0) (2025-05-06) + + +### Features + +* critical alert if disk near full ([#5549](https://github.com/windmill-labs/windmill/issues/5549)) ([4fd0561](https://github.com/windmill-labs/windmill/commit/4fd056123907337efb5f5669975b337973a124cc)) + + +### Bug Fixes + +* ansible in agent mode can use inventory.ini ([9bdd301](https://github.com/windmill-labs/windmill/commit/9bdd301f5296fbfb631df9ff9100e92e0984ff64)) + +## [1.486.1](https://github.com/windmill-labs/windmill/compare/v1.486.0...v1.486.1) (2025-05-04) + + +### Bug Fixes + +* improve MultiSelectWrapper behavior ([36da8ae](https://github.com/windmill-labs/windmill/commit/36da8aec080742e13f23e1dee12b3954947f53dd)) + +## [1.486.0](https://github.com/windmill-labs/windmill/compare/v1.485.3...v1.486.0) (2025-05-01) + + +### Features + +* add run now directly on schedule drawer and duplicate schedule option ([#5674](https://github.com/windmill-labs/windmill/issues/5674)) ([dfb947f](https://github.com/windmill-labs/windmill/commit/dfb947ff37c688f54a32de5aa3c5c3d142cb80f4)) +* Database Manager ([#5586](https://github.com/windmill-labs/windmill/issues/5586)) ([41c15fc](https://github.com/windmill-labs/windmill/commit/41c15fc78aaf844c559d3d6c772e04ecce436e9d)) +* Integrate MCP with hub ([#5685](https://github.com/windmill-labs/windmill/issues/5685)) ([ec701a9](https://github.com/windmill-labs/windmill/commit/ec701a9ee74c9d890b54234362392deca63a77c7)) + + +### Bug Fixes + +* Ai Chat: do not send tools if empty + respond even if tool fails ([#5692](https://github.com/windmill-labs/windmill/issues/5692)) ([9c55040](https://github.com/windmill-labs/windmill/commit/9c55040e47e76af8b7e2864b82fa30505545dcb5)) +* do not track relative deps for scripts with raw defined deps from CLI ([#5696](https://github.com/windmill-labs/windmill/issues/5696)) ([7eb9d7d](https://github.com/windmill-labs/windmill/commit/7eb9d7d46cb48ae69a3fd3ff852a57abae450a3b)) +* improve CLI file scanning performances ([0916978](https://github.com/windmill-labs/windmill/commit/09169784bd2d0ab7acf5f40dc86f36f1cae967b7)) + +## [1.485.3](https://github.com/windmill-labs/windmill/compare/v1.485.2...v1.485.3) (2025-04-29) + + +### Bug Fixes + +* improve performance of background cleanup monitoring operations ([18dced3](https://github.com/windmill-labs/windmill/commit/18dced3c748cd5305f0934b26e50d69899563723)) + +## [1.485.2](https://github.com/windmill-labs/windmill/compare/v1.485.1...v1.485.2) (2025-04-29) + + +### Bug Fixes + +* improve agent workers for deployed scripts ([60018aa](https://github.com/windmill-labs/windmill/commit/60018aadf62cecadf111e019d3600513a89810f1)) +* make `#(extra_)requirements:` work better with pins ([#5680](https://github.com/windmill-labs/windmill/issues/5680)) ([1ab4160](https://github.com/windmill-labs/windmill/commit/1ab41603f4fd1526d0c944396ef250b184aed1f4)) +* **python:** handle better relative imports with requirements or extra_requirements ([f662cf5](https://github.com/windmill-labs/windmill/commit/f662cf5d75beed8fd114ba171cbe0fa8e4b2773f)) + +## [1.485.1](https://github.com/windmill-labs/windmill/compare/v1.485.0...v1.485.1) (2025-04-28) + + +### Bug Fixes + +* improve mcp mode api ([cf77ff0](https://github.com/windmill-labs/windmill/commit/cf77ff088b8382b861113120589de58f7cf241d0)) +* MCP handle long names + invalid char in prop key + fix for not found resource type ([#5668](https://github.com/windmill-labs/windmill/issues/5668)) ([eadae95](https://github.com/windmill-labs/windmill/commit/eadae95a42d679bf8792bdefd8b9d19dbcbc4b57)) +* skip_flow_update for dependency tracking table ([#5670](https://github.com/windmill-labs/windmill/issues/5670)) ([35b69da](https://github.com/windmill-labs/windmill/commit/35b69da25c5bd17deff5a54b635e9150cb865cc0)) + +## [1.485.0](https://github.com/windmill-labs/windmill/compare/v1.484.0...v1.485.0) (2025-04-28) + + +### Features + +* add universal search to object viewer ([7254743](https://github.com/windmill-labs/windmill/commit/72547437fead0a071fceac27dae8628cdcae6a3e)) + + +### Bug Fixes + +* add svelte 5 boundaries to app components to contain errors ([1b16918](https://github.com/windmill-labs/windmill/commit/1b1691837a7e6b88afbacf7d88c14ca5e475b493)) +* Fix object handling on some MCP clients + better frontend for MCP ([#5663](https://github.com/windmill-labs/windmill/issues/5663)) ([12c3202](https://github.com/windmill-labs/windmill/commit/12c32026e5879a65fc0f1cc9f2481087c4b95111)) + +## [1.484.0](https://github.com/windmill-labs/windmill/compare/v1.483.2...v1.484.0) (2025-04-26) + + +### Features + +* Add MCP endpoints ([#5639](https://github.com/windmill-labs/windmill/issues/5639)) ([a34ac4f](https://github.com/windmill-labs/windmill/commit/a34ac4fa24c2a5482e45724e76316d57f64f7040)) +* Add MCP only mode ([#5661](https://github.com/windmill-labs/windmill/issues/5661)) ([1625524](https://github.com/windmill-labs/windmill/commit/162552431138d68002c7060cad4ae31f1ec4c69c)) +* Ansible improvements (vault, roles and git repos) ([#5655](https://github.com/windmill-labs/windmill/issues/5655)) ([fdd1642](https://github.com/windmill-labs/windmill/commit/fdd1642ce10866da1d8d373bda44f050e2e0f403)) + + +### Bug Fixes + +* check for valid teams_channel config when saving critical alerts settings ([#5660](https://github.com/windmill-labs/windmill/issues/5660)) ([dc5c8d8](https://github.com/windmill-labs/windmill/commit/dc5c8d8c5f8577b7ded3da1d684cdb735fa7a936)) +* Fix CI for MCP + optimization ([#5657](https://github.com/windmill-labs/windmill/issues/5657)) ([b199a77](https://github.com/windmill-labs/windmill/commit/b199a77d486c5bfd086ca73a58a14bb747e386b5)) +* fix token creation after mcp mode change to make it non workspace specific ([2b5dfcf](https://github.com/windmill-labs/windmill/commit/2b5dfcfb251471dcc39b04c54e25008d617cc34f)) +* improve full-scaleout of autoscaling event logging ([8435eb3](https://github.com/windmill-labs/windmill/commit/8435eb3adff8429a73db88b12204f7cf8f14d3d2)) +* improve skip failure on parallel branchall ([a7b2b51](https://github.com/windmill-labs/windmill/commit/a7b2b51444d757964560de3a89024b1c9b0fefe9)) + +## [1.483.2](https://github.com/windmill-labs/windmill/compare/v1.483.1...v1.483.2) (2025-04-23) + + +### Bug Fixes + +* batch reruns query missing workspace_id check in subquery ([#5652](https://github.com/windmill-labs/windmill/issues/5652)) ([444a6ab](https://github.com/windmill-labs/windmill/commit/444a6abad670114c52e44f3606bf6fefc5d3fd98)) +* **frontend:** fix validity check ([#5654](https://github.com/windmill-labs/windmill/issues/5654)) ([c41c1eb](https://github.com/windmill-labs/windmill/commit/c41c1eb587bf22364f1202310a0c64b6040ab968)) +* improve MySQL datetime parser timezone handling (WIN-1155) ([#5645](https://github.com/windmill-labs/windmill/issues/5645)) ([5bca8f6](https://github.com/windmill-labs/windmill/commit/5bca8f60e970cc67839edb5dc491685f36cf0499)) +* track relative imports in python and ts even if lockfile is provided ([e316dbd](https://github.com/windmill-labs/windmill/commit/e316dbd9bdd5c59e9aaba6a4472bb7d832834e84)) + +## [1.483.1](https://github.com/windmill-labs/windmill/compare/v1.483.0...v1.483.1) (2025-04-19) + + +### Bug Fixes + +* pin libxml to 0.3.3 ([e5595e4](https://github.com/windmill-labs/windmill/commit/e5595e41b5c87704d814eff95bc00b82195728ba)) + +## [1.483.0](https://github.com/windmill-labs/windmill/compare/v1.482.1...v1.483.0) (2025-04-19) + + +### Features + +* handle different aws auth resource type ([#5637](https://github.com/windmill-labs/windmill/issues/5637)) ([5b123b0](https://github.com/windmill-labs/windmill/commit/5b123b01a1318208450789b5bcade447a0b331c7)) +* oidc support for sqs trigger ([#5614](https://github.com/windmill-labs/windmill/issues/5614)) ([34b307b](https://github.com/windmill-labs/windmill/commit/34b307b2be1f6cf92a81694325f4c333bdd7b055)) + + +### Bug Fixes + +* fix click outside popover fullscreen ([#5631](https://github.com/windmill-labs/windmill/issues/5631)) ([0811457](https://github.com/windmill-labs/windmill/commit/081145726a5d4ab81510a7637126a036823a1565)) +* improve flow editor step switch performance ([58fa4c8](https://github.com/windmill-labs/windmill/commit/58fa4c80062a5704bbd13ddda1b2f00c7c9e40dd)) +* linter in early stop doesn't include flow_input ([#5638](https://github.com/windmill-labs/windmill/issues/5638)) ([6a9bdfd](https://github.com/windmill-labs/windmill/commit/6a9bdfd3bd52ff802b6a71c3ae9504bfe7d0421f)) +* output picker output opening doesn't change id ([#5641](https://github.com/windmill-labs/windmill/issues/5641)) ([64c72b6](https://github.com/windmill-labs/windmill/commit/64c72b6fce669e47f04dc620750840857cbe66cf)) + +## [1.482.1](https://github.com/windmill-labs/windmill/compare/v1.482.0...v1.482.1) (2025-04-16) + + +### Bug Fixes + +* flow editor workspace script test use actual workspace script hash ([24e893b](https://github.com/windmill-labs/windmill/commit/24e893b8c50fafdb41f4b6e1777cb34aceafc466)) +* **frontend:** postgres remove selectedTable ([#5386](https://github.com/windmill-labs/windmill/issues/5386)) ([bd7c6a2](https://github.com/windmill-labs/windmill/commit/bd7c6a2a46047de5fe89753decdfdf1f4851ee3f)) +* **openapi:** fix openapi def of batch re-run jobs ([cb8731e](https://github.com/windmill-labs/windmill/commit/cb8731e7e37fb6cd052f5dae6fdce46e6ca2409c)) +* show workspace color if superadmin and not in workspace + change workspace name when switching workspace ([#5625](https://github.com/windmill-labs/windmill/issues/5625)) ([cc4384f](https://github.com/windmill-labs/windmill/commit/cc4384f48cc89f883237a2082d854d69a7b5dc56)) + +## [1.482.0](https://github.com/windmill-labs/windmill/compare/v1.481.0...v1.482.0) (2025-04-15) + + +### Features + +* add diff toggle to flow inline scripts ([#5550](https://github.com/windmill-labs/windmill/issues/5550)) ([b3ecde3](https://github.com/windmill-labs/windmill/commit/b3ecde3316252bcd7323de98149786349019ba7e)) +* add gcp trigger ([#5501](https://github.com/windmill-labs/windmill/issues/5501)) ([6339775](https://github.com/windmill-labs/windmill/commit/63397754046eed41d32e28d4698db37b4c9b9710)) +* add wildcards filter for worker/label/tags ([62f14d1](https://github.com/windmill-labs/windmill/commit/62f14d1cb95e3f1c7de85c46e1c6bb092247656c)) +* add windmill context to autocomplete ([#5548](https://github.com/windmill-labs/windmill/issues/5548)) ([b47c151](https://github.com/windmill-labs/windmill/commit/b47c15165f93ca68a58f81cf2b86fc9467155482)) +* agent workers v2 using http ([#5588](https://github.com/windmill-labs/windmill/issues/5588)) ([63fa499](https://github.com/windmill-labs/windmill/commit/63fa4990153f33434b49269922f7803d04e407cd)) +* Batch re-run ([#5553](https://github.com/windmill-labs/windmill/issues/5553)) ([26b5ea5](https://github.com/windmill-labs/windmill/commit/26b5ea5023a100c57d077910c99a5e5703edf1c1)) +* **frontend:** app editor code input component (monaco) ([#5566](https://github.com/windmill-labs/windmill/issues/5566)) ([177e16b](https://github.com/windmill-labs/windmill/commit/177e16bb18eed0d1c454b967aaa59547f61e8d26)) +* handle sending selected lines to ai context ([#5527](https://github.com/windmill-labs/windmill/issues/5527)) ([5abdc3e](https://github.com/windmill-labs/windmill/commit/5abdc3e4403b5c604309bd99a24d7a2847a17b9b)) +* Implement sending diff to ai ([#5510](https://github.com/windmill-labs/windmill/issues/5510)) ([e118d2c](https://github.com/windmill-labs/windmill/commit/e118d2cd5f9c641884a76229802a5228ef41f1a5)) +* make azure a standalone AI provider ([#5558](https://github.com/windmill-labs/windmill/issues/5558)) ([2c5e58c](https://github.com/windmill-labs/windmill/commit/2c5e58cf1ab9225d516540b38d9e4dde482a3a7f)) +* migrate to svelte5 + vite6 ([#4813](https://github.com/windmill-labs/windmill/issues/4813)) ([3c99b3f](https://github.com/windmill-labs/windmill/commit/3c99b3fdc7b78b1cdc7d8fb21d999296695f7889)) +* **postgres-trigger:** postgres trigger fix circular dependencies and add remove associate resource ([#5606](https://github.com/windmill-labs/windmill/issues/5606)) ([1daeb2f](https://github.com/windmill-labs/windmill/commit/1daeb2f48f3026621b3ffc58e10f048d5911906c)) +* **python:** per import requirement pin ([#5520](https://github.com/windmill-labs/windmill/issues/5520)) ([0b6d017](https://github.com/windmill-labs/windmill/commit/0b6d017fedc31e790a76cf29a1adaaf2a72acc61)) +* signed s3 objects ([#5593](https://github.com/windmill-labs/windmill/issues/5593)) ([b9e8796](https://github.com/windmill-labs/windmill/commit/b9e879618bc223ce17effde8bb4c5d1df2ad6df5)) + + +### Bug Fixes + +* add support for ${} syntax without default in bash ([#5594](https://github.com/windmill-labs/windmill/issues/5594)) ([3950cfd](https://github.com/windmill-labs/windmill/commit/3950cfd7e3297d7f8ec56430d6462f6b67ecd3c2)) +* app editor svelte 5 fixes ([#5570](https://github.com/windmill-labs/windmill/issues/5570)) ([b926076](https://github.com/windmill-labs/windmill/commit/b9260769883348ecd5aeb5684f527a8bf0073928)) +* binding not working in nested array script arg ([#5585](https://github.com/windmill-labs/windmill/issues/5585)) ([f5d46d5](https://github.com/windmill-labs/windmill/commit/f5d46d5751bc875b7f4da1db06be40571ac55ab8)) +* **cli:** properly handle enabled/disabled updates of schedules ([2629458](https://github.com/windmill-labs/windmill/commit/26294584d6c2ca02bbc4fc5f28cb8df6a5fb3790)) +* **cli:** wmill-locks improvement ([8d062c4](https://github.com/windmill-labs/windmill/commit/8d062c47ecd9e84a81140d5c59814da9217dd434)) +* Dynamic select does not work with tag //native ([#5576](https://github.com/windmill-labs/windmill/issues/5576)) ([1f3e7d9](https://github.com/windmill-labs/windmill/commit/1f3e7d9029051832db6ab1755b3cad38176a9e96)), closes [#5490](https://github.com/windmill-labs/windmill/issues/5490) +* fix list jobs by tag ([0c3cb37](https://github.com/windmill-labs/windmill/commit/0c3cb3700a3fb9b69e396487bd7491dbbd8861c0)) +* flow editor svelte 5 issues ([#5567](https://github.com/windmill-labs/windmill/issues/5567)) ([4f6be6e](https://github.com/windmill-labs/windmill/commit/4f6be6ed340e26bf1ed95398a9dc9f1eb41b33dd)) +* freeze when clicking script history diff button ([#5581](https://github.com/windmill-labs/windmill/issues/5581)) ([07094b6](https://github.com/windmill-labs/windmill/commit/07094b6aa21f10688b138d2a81d4fd5833f003fc)) +* **frontend:** app builder - force json configuration in rich result ([#5565](https://github.com/windmill-labs/windmill/issues/5565)) ([6fae3a5](https://github.com/windmill-labs/windmill/commit/6fae3a566be06dae88ece8ec23f5723cd8f3f2b9)) +* **frontend:** load all step jobs ([#5617](https://github.com/windmill-labs/windmill/issues/5617)) ([16bed59](https://github.com/windmill-labs/windmill/commit/16bed593dfd0b735a92d0928df5091547b98ae79)) +* **frontend:** prevent deploy popover to show if deploy dropdown is open ([#5542](https://github.com/windmill-labs/windmill/issues/5542)) ([c2180c6](https://github.com/windmill-labs/windmill/commit/c2180c6eb34e14fe2292ff40aa6a99c627698d5e)) +* **frontend:** proper each block binding + better app settings reactivity ([#5568](https://github.com/windmill-labs/windmill/issues/5568)) ([4c71af8](https://github.com/windmill-labs/windmill/commit/4c71af8a74627d0ba76917e0dac0ac9e5e984cca)) +* improve app image picker UX ([#5589](https://github.com/windmill-labs/windmill/issues/5589)) ([f497a4b](https://github.com/windmill-labs/windmill/commit/f497a4bfae8d1bff097e0c2c9df8381a531dfeb9)) +* legacy script gen model selection ([#5574](https://github.com/windmill-labs/windmill/issues/5574)) ([3507925](https://github.com/windmill-labs/windmill/commit/3507925624a43804a3be463b6f7913cea5821384)) +* mssql ca_cert deserializing ([#5587](https://github.com/windmill-labs/windmill/issues/5587)) ([b4f8c88](https://github.com/windmill-labs/windmill/commit/b4f8c88c19bd4f844c3ecb53ececc340ee326b0e)) +* number input in app multiselect yields NOT_NUMBER ([#5616](https://github.com/windmill-labs/windmill/issues/5616)) ([4aae6ab](https://github.com/windmill-labs/windmill/commit/4aae6ab634280adc1de9abd890100b7c12c89158)) +* prevent invalid returned ai completion object errors ([#5564](https://github.com/windmill-labs/windmill/issues/5564)) ([9276c71](https://github.com/windmill-labs/windmill/commit/9276c717a21aaee3241845a9cc00d3fb6bce9eb9)) +* Remaining svelte 5 bugs ([#5563](https://github.com/windmill-labs/windmill/issues/5563)) ([6e9ec63](https://github.com/windmill-labs/windmill/commit/6e9ec6323c265a747ef8696865297e6d47abb016)) +* tenant id to never be undefined on teams ([#5572](https://github.com/windmill-labs/windmill/issues/5572)) ([102b58a](https://github.com/windmill-labs/windmill/commit/102b58a5f40dde22f15700d4b6c11eb7f3fbf4bb)) +* validate saved module before passing to flow module editor ([#5580](https://github.com/windmill-labs/windmill/issues/5580)) ([2eb1a16](https://github.com/windmill-labs/windmill/commit/2eb1a161d15627b440195b65eec54998561f4ef6)) + +## [1.481.0](https://github.com/windmill-labs/windmill/compare/v1.480.1...v1.481.0) (2025-04-02) + + +### Features + +* mssql support cert configuration ([#5559](https://github.com/windmill-labs/windmill/issues/5559)) ([e5519f7](https://github.com/windmill-labs/windmill/commit/e5519f79aaa83f04014364c7d1ec11157044011d)) + +## [1.480.1](https://github.com/windmill-labs/windmill/compare/v1.480.0...v1.480.1) (2025-04-02) + + +### Bug Fixes + +* aad_token can be empty string ([#5557](https://github.com/windmill-labs/windmill/issues/5557)) ([3fd7a5c](https://github.com/windmill-labs/windmill/commit/3fd7a5ce9c02332be40c34c0b6da57894b0b3d55)) +* improve workspace selection for default tag settings ([7083efd](https://github.com/windmill-labs/windmill/commit/7083efd051aeb7f653cccc97db099f4d9b2591a0)) +* mssql aad_token can be empty string ([#5556](https://github.com/windmill-labs/windmill/issues/5556)) ([dd30692](https://github.com/windmill-labs/windmill/commit/dd30692617e3cbc852239c4b1c50f975ff247c33)) + +## [1.480.0](https://github.com/windmill-labs/windmill/compare/v1.479.3...v1.480.0) (2025-03-31) + + +### Features + +* ms sql aad authentication support ([#5539](https://github.com/windmill-labs/windmill/issues/5539)) ([c230e2a](https://github.com/windmill-labs/windmill/commit/c230e2aed9b7fafb86548a4f4151939d5aca5127)) +* put db resources in ai context ([#5507](https://github.com/windmill-labs/windmill/issues/5507)) ([f7c8654](https://github.com/windmill-labs/windmill/commit/f7c86549879582c7f9dc72d52524f3a394f493f3)) + + +### Bug Fixes + +* correctly run empty flow with preprocessor from UI ([#5537](https://github.com/windmill-labs/windmill/issues/5537)) ([3d32501](https://github.com/windmill-labs/windmill/commit/3d3250194d43aee1a640a57505bc7a6afee62c84)) +* **frontend:** use custom caret position function ([#5544](https://github.com/windmill-labs/windmill/issues/5544)) ([ca0cda3](https://github.com/windmill-labs/windmill/commit/ca0cda3ecf5bd449f9c371cf5102c11d880c9822)) +* ignore invalid chunks in completion stream: empty choices when using azure ([#5545](https://github.com/windmill-labs/windmill/issues/5545)) ([b31090c](https://github.com/windmill-labs/windmill/commit/b31090cb544632680947492dc28f7b7c1a9c7287)) +* only format valid resource types ([#5541](https://github.com/windmill-labs/windmill/issues/5541)) ([113f038](https://github.com/windmill-labs/windmill/commit/113f038fc0e53e37c3bc319f85b3f7fa780c6fe5)) + +## [1.479.3](https://github.com/windmill-labs/windmill/compare/v1.479.2...v1.479.3) (2025-03-28) + + +### Bug Fixes + +* **cli:** pin encodeHex to 1.0.4 to work with dnt ([4703e3c](https://github.com/windmill-labs/windmill/commit/4703e3c848c9b06603b83885267023ccf84316c3)) + + +### Performance Improvements + +* improve hub resource type pulling when using the cli ([#5535](https://github.com/windmill-labs/windmill/issues/5535)) ([dd488a2](https://github.com/windmill-labs/windmill/commit/dd488a2bdbc0c9c7311c06dc25504a1336661cde)) + +## [1.479.2](https://github.com/windmill-labs/windmill/compare/v1.479.1...v1.479.2) (2025-03-28) + + +### Bug Fixes + +* fetch correct resource for interactive slack when multiple workspaces connected ([#5532](https://github.com/windmill-labs/windmill/issues/5532)) ([08e8283](https://github.com/windmill-labs/windmill/commit/08e8283c58c94f773936bac09d56bc6430382bbb)) + +## [1.479.1](https://github.com/windmill-labs/windmill/compare/v1.479.0...v1.479.1) (2025-03-27) + + +### Bug Fixes + +* pin backend deps half to 2.4.1 ([6cd2dc7](https://github.com/windmill-labs/windmill/commit/6cd2dc7178c62530f893d69f6e76b6cbc465e419)) + +## [1.479.0](https://github.com/windmill-labs/windmill/compare/v1.478.1...v1.479.0) (2025-03-27) + + +### Features + +* add description option to schedule page ([#5500](https://github.com/windmill-labs/windmill/issues/5500)) ([4c6f600](https://github.com/windmill-labs/windmill/commit/4c6f60010fec7d82181867e0082079e446797ce2)) +* add java support ([#5458](https://github.com/windmill-labs/windmill/issues/5458)) ([59740c0](https://github.com/windmill-labs/windmill/commit/59740c047816ad90d7383b15c846302db1a2e354)) +* add nu-lang support ([#5217](https://github.com/windmill-labs/windmill/issues/5217)) ([a3faea1](https://github.com/windmill-labs/windmill/commit/a3faea16e77796a1b989db4285b3fef722ac55b2)) +* api key/basic/hmac auth for http triggers ([#5476](https://github.com/windmill-labs/windmill/issues/5476)) ([e920101](https://github.com/windmill-labs/windmill/commit/e920101107256589bb5aee09fa8f04f5bd9707e4)) +* autocomplete v2 + AI chat ([#5323](https://github.com/windmill-labs/windmill/issues/5323)) ([234b20f](https://github.com/windmill-labs/windmill/commit/234b20f8bd55ea19b17b80f08d9ff1e0e00ba739)) +* github app token instead of pat for git sync ([#5279](https://github.com/windmill-labs/windmill/issues/5279)) ([b822c66](https://github.com/windmill-labs/windmill/commit/b822c66262f7c4c01ea4baad9383a12d138b0815)) +* list references upon renaming a script or a flow ([#5487](https://github.com/windmill-labs/windmill/issues/5487)) ([e868fe2](https://github.com/windmill-labs/windmill/commit/e868fe2bf5695b968151e27826854def3e847eb1)) +* make custom ai CE + add together AI provider ([#5522](https://github.com/windmill-labs/windmill/issues/5522)) ([a28c78d](https://github.com/windmill-labs/windmill/commit/a28c78dd920c695c3dfac05bc48c82f1477b022d)) +* **python:** fully qualified imports mapping ([#5511](https://github.com/windmill-labs/windmill/issues/5511)) ([1a5566b](https://github.com/windmill-labs/windmill/commit/1a5566b8c29773d94a681c86676d4cdb0b7c7777)) +* remove stripe dep ([#5508](https://github.com/windmill-labs/windmill/issues/5508)) ([7a62527](https://github.com/windmill-labs/windmill/commit/7a625275752ba69e26d7e3b41416e335496eff84)) +* unsafe parameters for sql queries (table names, column names) ([#5488](https://github.com/windmill-labs/windmill/issues/5488)) ([38ee018](https://github.com/windmill-labs/windmill/commit/38ee0183aaa014c740da7b54d66928ec851fb522)) + + +### Bug Fixes + +* add missing privileged hub script for app slack reports ([#5515](https://github.com/windmill-labs/windmill/issues/5515)) ([63fe9c1](https://github.com/windmill-labs/windmill/commit/63fe9c1852c1f87901f42eff8904c3482f7ceb43)) +* clean job dirs between flow locks ([8129672](https://github.com/windmill-labs/windmill/commit/8129672d9e8c6b591c1a46c30060a9d4f207e499)) +* **cli:** add --dry-run option ([4667507](https://github.com/windmill-labs/windmill/commit/466750752f6ffcb098cecd4ef6d6f33fb42d39ba)) +* correct private hub url in CLI for resource types sync ([#5513](https://github.com/windmill-labs/windmill/issues/5513)) ([9fd224c](https://github.com/windmill-labs/windmill/commit/9fd224cc469ae6f47c3ba9839ed43c85ff4d2181)) +* **frontend:** use stable path for capture tables + nits ([#5495](https://github.com/windmill-labs/windmill/issues/5495)) ([e16d629](https://github.com/windmill-labs/windmill/commit/e16d6299f52564def484e78fb2f48e9bf39cbd3d)) +* improve cancel for flows with many substeps ([ec11d57](https://github.com/windmill-labs/windmill/commit/ec11d577c6089df0b6019cd05064f5ea63fb317c)) + + +### Performance Improvements + +* cache workspace env variables to avoid one query ([#5499](https://github.com/windmill-labs/windmill/issues/5499)) ([a3f6db7](https://github.com/windmill-labs/windmill/commit/a3f6db7dca983a4dfd62b30423340f899c4d1da6)) +* cache workspace premium check ([5573d88](https://github.com/windmill-labs/windmill/commit/5573d886954182efcac71b3baa54d455f5086b30)) +* optimize number of queries needed for job run ([#5504](https://github.com/windmill-labs/windmill/issues/5504)) ([3edca4b](https://github.com/windmill-labs/windmill/commit/3edca4bc91ee9a1f1c0a98d39bc673dc56f899b6)) + +## [1.478.1](https://github.com/windmill-labs/windmill/compare/v1.478.0...v1.478.1) (2025-03-20) + + +### Bug Fixes + +* update deps versions ([0463c10](https://github.com/windmill-labs/windmill/commit/0463c10a84ab09f66b99c331d3860fa750606f51)) + +## [1.478.0](https://github.com/windmill-labs/windmill/compare/v1.477.1...v1.478.0) (2025-03-20) + + +### Features + +* add raw string option and wrap option for http trigger ([#5467](https://github.com/windmill-labs/windmill/issues/5467)) ([9dba57d](https://github.com/windmill-labs/windmill/commit/9dba57d546c984ff8cfb26c73d2ccdda4c18aaf3)) +* add support for python list[x] ([#5486](https://github.com/windmill-labs/windmill/issues/5486)) ([90ccc3a](https://github.com/windmill-labs/windmill/commit/90ccc3aae5f79e701e2c9241ce2cf009674ff356)) +* backend arg schema validation ([#5455](https://github.com/windmill-labs/windmill/issues/5455)) ([6634c82](https://github.com/windmill-labs/windmill/commit/6634c82e209a36021e5b0c392de433f48f3d8b80)) +* eager app mode ([fe20e33](https://github.com/windmill-labs/windmill/commit/fe20e3374f24fc644036a6d19e34421aeb839a73)) +* filter by worker + backend perf opt ([#5489](https://github.com/windmill-labs/windmill/issues/5489)) ([880db31](https://github.com/windmill-labs/windmill/commit/880db319e8e2479fdf12abcac526f7fd5064a00f)) +* keep captures across drafts and deploys ([#5482](https://github.com/windmill-labs/windmill/issues/5482)) ([4f43b19](https://github.com/windmill-labs/windmill/commit/4f43b1984f4ea9a87b0489d4b40c1ecdcfdbdecd)) + + +### Bug Fixes + +* avoid lock contention for native workers on cached connection ([#5481](https://github.com/windmill-labs/windmill/issues/5481)) ([8e95bc3](https://github.com/windmill-labs/windmill/commit/8e95bc397284607188f861f27288bcc0ab368023)) +* fix delete completed job ([ead1592](https://github.com/windmill-labs/windmill/commit/ead1592399d832039e3e866c554529dfd25a7af9)) +* fix empty schema on flow page error ([86121ed](https://github.com/windmill-labs/windmill/commit/86121ed4ab68ec17b9481b8d74bb2ccaae8c3b60)) +* improve concurrency limit check performances ([eee7d33](https://github.com/windmill-labs/windmill/commit/eee7d33bd8811be75d319956133bd8d6292aea90)) +* improve memory metrics graph ([a6cf327](https://github.com/windmill-labs/windmill/commit/a6cf327f74ae84d58181280995f8d8e2d909ee05)) +* improve row lock contention on concurrency counter ([e8bb307](https://github.com/windmill-labs/windmill/commit/e8bb3075020ca44978f503a81a7997ba1bcd671b)) +* label not part of default variant arg ([4bc5c04](https://github.com/windmill-labs/windmill/commit/4bc5c04cd40e23ef9d13ba48d28612d5a865796e)) +* set proper slot for MobileFitlers popover ([#5491](https://github.com/windmill-labs/windmill/issues/5491)) ([6b4c25d](https://github.com/windmill-labs/windmill/commit/6b4c25d0d808a841dbfeaf91d29437071128266a)) + + +### Performance Improvements + +* improve perf of get completed flow node ([#5418](https://github.com/windmill-labs/windmill/issues/5418)) ([551c0ec](https://github.com/windmill-labs/windmill/commit/551c0ecd6a83671d60ede5a81f656c27ddbdbe4c)) + +## [1.477.1](https://github.com/windmill-labs/windmill/compare/v1.477.0...v1.477.1) (2025-03-13) + + +### Bug Fixes + +* fix rusttls panic ([6a6b760](https://github.com/windmill-labs/windmill/commit/6a6b760e321fae949a02c1b0e0c32b0beaa8693b)) + +## [1.477.0](https://github.com/windmill-labs/windmill/compare/v1.476.0...v1.477.0) (2025-03-12) + + +### Features + +* add search by args on input history directly ([593dc30](https://github.com/windmill-labs/windmill/commit/593dc30bc81ab407bd119963a6befaa4fbc16eae)) + + +### Bug Fixes + +* add setValue support for tables ([ec52476](https://github.com/windmill-labs/windmill/commit/ec5247645d425a35b5adf0aaed40713d08439b11)) +* improve oneOf arg input reactivity to value changes ([a695621](https://github.com/windmill-labs/windmill/commit/a6956215eca8d1180b3c999519f9fa2ef43b5ab0)) +* pg_listeners have no timeout ([52f55ff](https://github.com/windmill-labs/windmill/commit/52f55ff1f11adf9157ca0f0fe356fa17d65ea20a)) +* prevent monitoring task to die without sending killpill ([#5472](https://github.com/windmill-labs/windmill/issues/5472)) ([d58ca9b](https://github.com/windmill-labs/windmill/commit/d58ca9b395cb151b05c43d910b0081988b3291ae)) +* tutorial's step 6 not working (button.click is not a function) ([#5474](https://github.com/windmill-labs/windmill/issues/5474)) ([00e1841](https://github.com/windmill-labs/windmill/commit/00e18419f5db8ef19ad92c6ac290812084cd1ecd)) +* update bun to 1.2.4 ([8e0963e](https://github.com/windmill-labs/windmill/commit/8e0963eec8a86b6d8593995c803dfbdd2c96bfc1)) + +## [1.476.0](https://github.com/windmill-labs/windmill/compare/v1.475.1...v1.476.0) (2025-03-11) + + +### Features + +* option to prefix http route with workspace id ([#5461](https://github.com/windmill-labs/windmill/issues/5461)) ([61a5cea](https://github.com/windmill-labs/windmill/commit/61a5ceaba38787dc146a36b443bbd3f78e26102b)) + + +### Bug Fixes + +* cache for querying scripts correclty handles ScriptMetadata ([#5466](https://github.com/windmill-labs/windmill/issues/5466)) ([6dd2502](https://github.com/windmill-labs/windmill/commit/6dd2502d70dffcadee4427164db02607cd109c61)) +* codebases compatible with git sync ([#5470](https://github.com/windmill-labs/windmill/issues/5470)) ([bd7586a](https://github.com/windmill-labs/windmill/commit/bd7586a5eec5516fe291070303fa6516d8adc8de)) + +## [1.475.1](https://github.com/windmill-labs/windmill/compare/v1.475.0...v1.475.1) (2025-03-11) + + +### Bug Fixes + +* improve arginput sql and object viewer args change ([2a8a756](https://github.com/windmill-labs/windmill/commit/2a8a756b3f0a0e69145421eee87251956d85403b)) +* improve flow status viewer iteration picker behavior with very large forloops ([78d9664](https://github.com/windmill-labs/windmill/commit/78d9664ad89212196ef32c0a02114092331bfe63)) + +## [1.475.0](https://github.com/windmill-labs/windmill/compare/v1.474.0...v1.475.0) (2025-03-06) + + +### Features + +* **backend:** option to invalidate all sessions on logout ([#5419](https://github.com/windmill-labs/windmill/issues/5419)) ([e9044f0](https://github.com/windmill-labs/windmill/commit/e9044f0b9b1647e0fc74e5e3cce39a4fc2718672)) +* deploy triggers to prod/staging workspace ([#5429](https://github.com/windmill-labs/windmill/issues/5429)) ([b210ae3](https://github.com/windmill-labs/windmill/commit/b210ae36f7c1fd8860241aa5908cefc0ac956b7b)) +* **frontend:** improve flow suspend status display ([#5425](https://github.com/windmill-labs/windmill/issues/5425)) ([a845733](https://github.com/windmill-labs/windmill/commit/a8457337cec870e9e3d6a053f4fbe89f58229401)) +* **frontend:** pick image from workspace storage bucket ([#5382](https://github.com/windmill-labs/windmill/issues/5382)) ([8dbe0fa](https://github.com/windmill-labs/windmill/commit/8dbe0fa6446a34bd60484f1b5ac828ff9f892735)) +* kafka mTLS ([#5449](https://github.com/windmill-labs/windmill/issues/5449)) ([371c892](https://github.com/windmill-labs/windmill/commit/371c892f9aa43fe2757b4d23ee076781b237f11f)) +* MQTT triggers ([#5277](https://github.com/windmill-labs/windmill/issues/5277)) ([5c39037](https://github.com/windmill-labs/windmill/commit/5c39037aea35f9bdf780f1946abbb384533ee547)) + + +### Bug Fixes + +* **cli:** fix wmill user create-token with email and password ([a16cab0](https://github.com/windmill-labs/windmill/commit/a16cab0923f3be46f181dffc209a10c711873f86)) +* **frontend:** fix many s3 file picker bugs ([#5428](https://github.com/windmill-labs/windmill/issues/5428)) ([4fabc2a](https://github.com/windmill-labs/windmill/commit/4fabc2a8256b5088b1af61baf81355cd556d23e2)) +* **frontend:** improve capture payload preview ([#5417](https://github.com/windmill-labs/windmill/issues/5417)) ([fd56a63](https://github.com/windmill-labs/windmill/commit/fd56a639d21366bea5129409d805feefb22659c1)) +* improve objectviewer performance ([2444f4f](https://github.com/windmill-labs/windmill/commit/2444f4f23e5212fa4d2ff8542d864118d4b4feb9)) +* s3 file picker delete + better s3 path handling ([#5454](https://github.com/windmill-labs/windmill/issues/5454)) ([ae618c7](https://github.com/windmill-labs/windmill/commit/ae618c79dff08e8e9fe1f4ce69665ad7faeac006)) + +## [1.474.0](https://github.com/windmill-labs/windmill/compare/v1.473.1...v1.474.0) (2025-03-04) + + +### Features + +* add template script for all triggers ([#5424](https://github.com/windmill-labs/windmill/issues/5424)) ([0a9d8c6](https://github.com/windmill-labs/windmill/commit/0a9d8c6b8b95c0e1093aef89b2a1956dbe59bbee)) +* **frontend:** global recompute helper function ([#5408](https://github.com/windmill-labs/windmill/issues/5408)) ([b961efa](https://github.com/windmill-labs/windmill/commit/b961efa8691f806d437ba2cb303ad1a50fa618c4)) +* more controls on setting token duration ([#5421](https://github.com/windmill-labs/windmill/issues/5421)) ([534a824](https://github.com/windmill-labs/windmill/commit/534a8249d60f7de6b542c3b5b5ee0b5eda360f22)) + + +### Bug Fixes + +* do not depend on public schema anymore ([90b00f5](https://github.com/windmill-labs/windmill/commit/90b00f55011288115e00b7a30b3e8e91bc0b7f4b)) +* **python:** windows worker fails to install 3.10 ([#5409](https://github.com/windmill-labs/windmill/issues/5409)) ([ebb58e0](https://github.com/windmill-labs/windmill/commit/ebb58e0dc7cc104e0bbfd90cf7f37e40ffd0bbf5)) + +## [1.473.1](https://github.com/windmill-labs/windmill/compare/v1.473.0...v1.473.1) (2025-03-03) + + +### Bug Fixes + +* **backend:** copilot info exists_ai_resource ([#5415](https://github.com/windmill-labs/windmill/issues/5415)) ([844edd1](https://github.com/windmill-labs/windmill/commit/844edd1117bc5fdcc108adf53aec3974a7c66384)) +* improve cancel performance ([fba9e7e](https://github.com/windmill-labs/windmill/commit/fba9e7ef03b91d3e2c78ff833ddcc5207b7436d2)) + +## [1.473.0](https://github.com/windmill-labs/windmill/compare/v1.472.1...v1.473.0) (2025-03-03) + + +### Features + +* app s3 input anonymous delete ([#5401](https://github.com/windmill-labs/windmill/issues/5401)) ([46c7845](https://github.com/windmill-labs/windmill/commit/46c784574add176cb75d3627c9d3f55b6fb945f8)) +* track workspace runnables used in flows ([#5369](https://github.com/windmill-labs/windmill/issues/5369)) ([7bf9e25](https://github.com/windmill-labs/windmill/commit/7bf9e25ede82486115eae71865202f85aa931a8d)) + + +### Bug Fixes + +* improve db loads by adding index on audit ([e1ff001](https://github.com/windmill-labs/windmill/commit/e1ff00117ca5b66dd0b9365e63b9ff7dc277bc2c)) +* migrations do not refer to public schema anymore ([#5400](https://github.com/windmill-labs/windmill/issues/5400)) ([3063001](https://github.com/windmill-labs/windmill/commit/3063001491b49a4b6d0cd5985818b32aa4d3f16f)) +* remove typings_extensions from python sdk ([04ffbf8](https://github.com/windmill-labs/windmill/commit/04ffbf8c266a06c3efcebcbbaee767f0ab0771e2)) + +## [1.472.1](https://github.com/windmill-labs/windmill/compare/v1.472.0...v1.472.1) (2025-02-26) + + +### Bug Fixes + +* disable bundling using env var ([#5396](https://github.com/windmill-labs/windmill/issues/5396)) ([cb559d6](https://github.com/windmill-labs/windmill/commit/cb559d6083553c400e18e6077002c4891289a8a2)) + +## [1.472.0](https://github.com/windmill-labs/windmill/compare/v1.471.1...v1.472.0) (2025-02-26) + + + +### Bug Fixes + +* downgrade v8 to fix some rare panics ([5569e4d](https://github.com/windmill-labs/windmill/commit/5569e4d4953a01f2ad03ea8b71e695e833964bea)) +* **frontend:** markdown shows single backtick in single line code block ([#5391](https://github.com/windmill-labs/windmill/issues/5391)) ([7f290bb](https://github.com/windmill-labs/windmill/commit/7f290bbf6a33e2811dbe2bd8ee905c0fa8e8db3b)) +* migrate toggle to melt (4/4) ([#5329](https://github.com/windmill-labs/windmill/issues/5329)) ([69fc8a9](https://github.com/windmill-labs/windmill/commit/69fc8a98ae78bc01dc3d97f9732ee28864b323dd)) + +## [1.471.1](https://github.com/windmill-labs/windmill/compare/v1.471.0...v1.471.1) (2025-02-26) + + +### Bug Fixes + +* update to rust 1.86.0 ([3ada264](https://github.com/windmill-labs/windmill/commit/3ada264c4ad49f666c3a053eb48c7df294bf085b)) + +## [1.471.0](https://github.com/windmill-labs/windmill/compare/v1.470.1...v1.471.0) (2025-02-26) + + +### Features + +* add support for claude sonnet 3.7 thinking ([#5387](https://github.com/windmill-labs/windmill/issues/5387)) ([487d84b](https://github.com/windmill-labs/windmill/commit/487d84bd7fdc39a2401df4108fca6183189cf38a)) + + +### Bug Fixes + +* **frontend:** improve pagination handling and filter refreshes ([#5378](https://github.com/windmill-labs/windmill/issues/5378)) ([a85ebfb](https://github.com/windmill-labs/windmill/commit/a85ebfbbf48590812c0931ad93179c322f819849)) + +## [1.470.1](https://github.com/windmill-labs/windmill/compare/v1.470.0...v1.470.1) (2025-02-26) + + +### Bug Fixes + +* multiple app initializations fixes ([630e54f](https://github.com/windmill-labs/windmill/commit/630e54f65c950ec0073b3cdac9974cb666c1ab3f)) + +## [1.470.0](https://github.com/windmill-labs/windmill/compare/v1.469.0...v1.470.0) (2025-02-26) + + +### Features + +* **frontend:** set default app refesh interval ([#5380](https://github.com/windmill-labs/windmill/issues/5380)) ([478d3fb](https://github.com/windmill-labs/windmill/commit/478d3fbf4a7e52d19fcb5cf8d601b2eeb3487716)) + + +### Bug Fixes + +* multiple app initializations fixes ([24b6003](https://github.com/windmill-labs/windmill/commit/24b600378025632aecb2ca898b63d6032e08eb2e)) + +## [1.469.0](https://github.com/windmill-labs/windmill/compare/v1.468.0...v1.469.0) (2025-02-25) + + +### Features + +* limit the number of times a job can be restarted (3) after loss of pings ([c8a9596](https://github.com/windmill-labs/windmill/commit/c8a959691c37350def37fe3eb9f24c6f7789960d)) +* python-client now support mocked api via `WM_MOCKED_API_FILE` env ([#5372](https://github.com/windmill-labs/windmill/issues/5372)) ([50607c7](https://github.com/windmill-labs/windmill/commit/50607c7625e4a48fb397cff167b41bb6602716c0)) + + +### Bug Fixes + +* improve flow editor for vscode extension ([44b26d2](https://github.com/windmill-labs/windmill/commit/44b26d2ccec0c9dd65d1f53b057d031f841d7dba)) +* improve infinite grid behavior ([56d1da7](https://github.com/windmill-labs/windmill/commit/56d1da78fd3424ae5b4abbb009c7437ea98765ef)) + +## [1.468.0](https://github.com/windmill-labs/windmill/compare/v1.467.1...v1.468.0) (2025-02-24) + + +### Features + +* add audit logs scope filter in admins workspace ([#5352](https://github.com/windmill-labs/windmill/issues/5352)) ([b3e00b7](https://github.com/windmill-labs/windmill/commit/b3e00b7fdc3ad4c689fc30216accbed05822794c)) +* add support for | None and Optional in python ([#5361](https://github.com/windmill-labs/windmill/issues/5361)) ([9736355](https://github.com/windmill-labs/windmill/commit/9736355d5f82615100212698c5537997e5a0de39)) +* make flow lock deployment error visible in UI ([b8e6d0d](https://github.com/windmill-labs/windmill/commit/b8e6d0da79ca57b115e7cb0ccff9f5623b23f3f3)) + + +### Bug Fixes + +* add LOCALAPPDATA env variable to python execution on windows ([8806870](https://github.com/windmill-labs/windmill/commit/8806870b1bf67c2f77beaf04d986cf172c7b4bf4)) +* fix confirmation modal check on deploy ([3028325](https://github.com/windmill-labs/windmill/commit/3028325615e2f7e5ee3d1b6278580121880db14f)) +* **frontend:** make html app component content selectable ([#5359](https://github.com/windmill-labs/windmill/issues/5359)) ([f1c5b77](https://github.com/windmill-labs/windmill/commit/f1c5b77d7af8433905937d274b97c2d5cd6c1316)) +* handle better forced value propagation in apps ([7c842c8](https://github.com/windmill-labs/windmill/commit/7c842c88bf5225b6bc39857109b1b1ba5f99d708)) +* handle better optional chaining operator ([d45c1f6](https://github.com/windmill-labs/windmill/commit/d45c1f69d48a5ad93f4399ce0150bbff6fd4fa6b)) +* improve app markdown rendering ([96597d3](https://github.com/windmill-labs/windmill/commit/96597d3d6b3d31298e5582a55e11e1d48edbf175)) +* improve cancel/back behavior on editors ([0565981](https://github.com/windmill-labs/windmill/commit/05659816e722effcba27e71f855e819c606f8756)) +* improve custom component rendering ([4ee4ff7](https://github.com/windmill-labs/windmill/commit/4ee4ff78d389d61c63952c84dee967113c783c45)) +* improve webhook settings cache invalidation ([0456272](https://github.com/windmill-labs/windmill/commit/0456272e3f36996c5f223fc332b150c7a64c2f05)) +* update bun t.1.43->1.2.3 ([4e477d1](https://github.com/windmill-labs/windmill/commit/4e477d1f589343980d7bd2953909ff6a6be30739)) +* update deno 2.1.2->2.2.1 ([b102ff4](https://github.com/windmill-labs/windmill/commit/b102ff4a4643e2f06d44d493f2f776b44ae721cc)) + +## [1.467.1](https://github.com/windmill-labs/windmill/compare/v1.467.0...v1.467.1) (2025-02-22) + + +### Bug Fixes + +* add uv bin path to PATH ([85993cc](https://github.com/windmill-labs/windmill/commit/85993ccac2abc2295e0f1b21544a6674fcf43411)) +* app markdown is selectable in preview mode ([0aa6a39](https://github.com/windmill-labs/windmill/commit/0aa6a39cad16bff74adf3326d47ba0ba9851ccf6)) +* init_script do not need to use nsjail even in nsjail mode ([e92a46b](https://github.com/windmill-labs/windmill/commit/e92a46b088088148d13a8e625a828657bcf44fe3)) + +## [1.467.0](https://github.com/windmill-labs/windmill/compare/v1.466.3...v1.467.0) (2025-02-21) + + +### Features + +* enable rust AI gen/fix/edit ([#5349](https://github.com/windmill-labs/windmill/issues/5349)) ([d9844fd](https://github.com/windmill-labs/windmill/commit/d9844fd7f7cf89a0914176944d4af0b485ed3f3c)) +* provision from SSO preferred_username ([#5347](https://github.com/windmill-labs/windmill/issues/5347)) ([19d33bd](https://github.com/windmill-labs/windmill/commit/19d33bdc7c4633f0c338c77de1d316f733e4304a)) + + +### Bug Fixes + +* disable toggle is more consistently applied on arg inputs ([3188bee](https://github.com/windmill-labs/windmill/commit/3188bee46e3dc46a699096bd3c2668df0cbdb9a1)) +* do not pin python patch version in docker preinstalled python ([f058782](https://github.com/windmill-labs/windmill/commit/f05878271becb28f83678c5b0ae498d0192b2458)) +* fix app component header buttons ([ab1c15d](https://github.com/windmill-labs/windmill/commit/ab1c15d92f3f86f4bd8d782fa6a806a59f30fdf1)) +* fix schedule run now args ([3430f9c](https://github.com/windmill-labs/windmill/commit/3430f9c4390b6c630086394ddfaf1a1b2030c78f)) +* **frontend:** improve rename workspace id UX ([#5353](https://github.com/windmill-labs/windmill/issues/5353)) ([521b6ba](https://github.com/windmill-labs/windmill/commit/521b6ba92c86a55b9977463ae05ecd4fca400ce4)) +* **frontend:** invalid username for superadmin in some workspaces ([#5350](https://github.com/windmill-labs/windmill/issues/5350)) ([7d73dec](https://github.com/windmill-labs/windmill/commit/7d73decd8dc7039ef84915994074c07dc51280c9)) +* **frontend:** missing config for Custom AI ([#5351](https://github.com/windmill-labs/windmill/issues/5351)) ([8a7730e](https://github.com/windmill-labs/windmill/commit/8a7730efa06283e72292d894584b279c908a7604)) +* handle better forced value propagation in apps ([3ac912f](https://github.com/windmill-labs/windmill/commit/3ac912fa308fbbf6cf41562cfdbe8eea7c1cc372)) +* **image:** use debian image instead of python image as base ([676b78b](https://github.com/windmill-labs/windmill/commit/676b78b15db8e1c749107fa41c4c98ab3a37154e)) +* initialize s3 file input if value already present ([c6601da](https://github.com/windmill-labs/windmill/commit/c6601da3d8557af9d32b0202bf50c40b89d481a9)) +* schedules do not accept 5 units cron syntax on update/create anymore ([c90fe38](https://github.com/windmill-labs/windmill/commit/c90fe387e882f7767c3b3621e5e230fc8acd80b0)) + +## [1.466.3](https://github.com/windmill-labs/windmill/compare/v1.466.2...v1.466.3) (2025-02-20) + + +### Bug Fixes + +* **frontend:** add warning when integer number if too big for frontend ([#5340](https://github.com/windmill-labs/windmill/issues/5340)) ([03f8834](https://github.com/windmill-labs/windmill/commit/03f88349c8730bfbb4613105c35482b4f3fadd64)) +* remove db streaming to avoid backpressure on db ([#5342](https://github.com/windmill-labs/windmill/issues/5342)) ([9ba66ea](https://github.com/windmill-labs/windmill/commit/9ba66eacd28175607900a7d2294584662b4c26a2)) + +## [1.466.2](https://github.com/windmill-labs/windmill/compare/v1.466.1...v1.466.2) (2025-02-20) + + +### Bug Fixes + +* add proxy envs (http_proxy) to uv install ([affb0b4](https://github.com/windmill-labs/windmill/commit/affb0b4c720551f7f1c7fa5315e3b39e5580b732)) + +## [1.466.1](https://github.com/windmill-labs/windmill/compare/v1.466.0...v1.466.1) (2025-02-20) + + +### Bug Fixes + +* **cli:** improve cli dependency error clarity ([dcc0d35](https://github.com/windmill-labs/windmill/commit/dcc0d35e971ab3df6a0122dc881b968e8221f40f)) +* **cli:** improve dependency job error message (logs in result) ([2c67e84](https://github.com/windmill-labs/windmill/commit/2c67e84abe98a3c43972cf5555536104119c6527)) +* **cli:** improve flow cli dependency error clarity ([d5b3a04](https://github.com/windmill-labs/windmill/commit/d5b3a04b0ab5f003c4c512cc9ba74eb620a3afc1)) +* **python:** PYTHON_PATH overrides python from uv ([39c0dd3](https://github.com/windmill-labs/windmill/commit/39c0dd3736da0722c7e18d84183c0e9b06cf2839)) + +## [1.466.0](https://github.com/windmill-labs/windmill/compare/v1.465.0...v1.466.0) (2025-02-19) + + +### Features + +* add support for gemini ([#5235](https://github.com/windmill-labs/windmill/issues/5235)) ([35d5293](https://github.com/windmill-labs/windmill/commit/35d5293fba47d368e503e9781719e6e9ccc96713)) +* remove `pip` fallback option for python and ansible ([#5186](https://github.com/windmill-labs/windmill/issues/5186)) ([4ad654f](https://github.com/windmill-labs/windmill/commit/4ad654fcf0c603aefc5a9b5c41da1ffa24b99d2d)) + + +### Bug Fixes + +* **apps:** font-size of title text not screen dependent ([44a6a62](https://github.com/windmill-labs/windmill/commit/44a6a62fbe3a9cae79e2d7ab7efd119f559aa374)) +* improve app db explorer handling of always identity columns ([74c0a10](https://github.com/windmill-labs/windmill/commit/74c0a10c3a8a4848341456635f36c0c2061b7943)) + +## [1.465.0](https://github.com/windmill-labs/windmill/compare/v1.464.0...v1.465.0) (2025-02-18) + + +### Features + +* SQS triggers ([#5182](https://github.com/windmill-labs/windmill/issues/5182)) ([58a67a3](https://github.com/windmill-labs/windmill/commit/58a67a3ac0c57b9504a90a6e454f738cf0810e21)) + + +### Bug Fixes + +* fix rendering of app components without component inputs ([0e72991](https://github.com/windmill-labs/windmill/commit/0e72991476ba932a526e1b4cf42bad157be2cfdb)) + +## [1.464.0](https://github.com/windmill-labs/windmill/compare/v1.463.6...v1.464.0) (2025-02-18) + + +### Features + +* add ready endpoints for workers to enterprise ([1ef482e](https://github.com/windmill-labs/windmill/commit/1ef482e8aee9433c518ce3cbc5bc38174e27c34f)) + + +### Bug Fixes + +* **bash:** allow process substitution on nsjail ([d4f61f1](https://github.com/windmill-labs/windmill/commit/d4f61f13fd6a9c2e5707738fba960b7fd926230c)) +* **bash:** improve bash last line as result reliability using bash process substitution ([#5321](https://github.com/windmill-labs/windmill/issues/5321)) ([138cedf](https://github.com/windmill-labs/windmill/commit/138cedf1da91290f97c19513daf0c1981488a94a)) + +## [1.463.6](https://github.com/windmill-labs/windmill/compare/v1.463.5...v1.463.6) (2025-02-18) + + +### Bug Fixes + +* fix reactivity issue on loading live flow on runs page ([52e12d1](https://github.com/windmill-labs/windmill/commit/52e12d1021831adc2ce9b7b0946a93562038017e)) +* improve v2 migration finalizer to avoid deadlocks ([1069ad3](https://github.com/windmill-labs/windmill/commit/1069ad39992940e32e5d8566ef2283970525be1a)) + +## [1.463.5](https://github.com/windmill-labs/windmill/compare/v1.463.4...v1.463.5) (2025-02-18) + + +### Bug Fixes + +* fix teams cleanup preventing start ([1b46e0f](https://github.com/windmill-labs/windmill/commit/1b46e0f08426497d549cf5007c93981df9ab41e5)) + + +## [1.463.4](https://github.com/windmill-labs/windmill/compare/v1.463.3...v1.463.4) (2025-02-17) + + +### Bug Fixes + +* improve queue job indices for faster performances ([9530826](https://github.com/windmill-labs/windmill/commit/953082681e2c4fd71d5ac1acf372265ccc72297b)) +* improve teams settings in workspace settings ([#5316](https://github.com/windmill-labs/windmill/issues/5316)) ([935b5b7](https://github.com/windmill-labs/windmill/commit/935b5b799636c0f02597315837268d4a76f6709a)) + +## [1.463.3](https://github.com/windmill-labs/windmill/compare/v1.463.2...v1.463.3) (2025-02-17) + + +### Bug Fixes + +* windmill_admin has implicit bypass rls on v2_job even if role not set ([0208f53](https://github.com/windmill-labs/windmill/commit/0208f53541473aa51bed0e15d938def3d4530e3f)) + +## [1.463.2](https://github.com/windmill-labs/windmill/compare/v1.463.1...v1.463.2) (2025-02-16) + + +### Bug Fixes + +* show skipped flows as success ([#5304](https://github.com/windmill-labs/windmill/issues/5304)) ([062e6bc](https://github.com/windmill-labs/windmill/commit/062e6bc161b56215cb081209d37ad8e0cbd1dd99)) + +## [1.463.1](https://github.com/windmill-labs/windmill/compare/v1.463.0...v1.463.1) (2025-02-15) + + +### Bug Fixes + +* not able to filter runs by schedule ([#5302](https://github.com/windmill-labs/windmill/issues/5302)) ([53f47bc](https://github.com/windmill-labs/windmill/commit/53f47bcfc84ed747b55d3a7d84ccf13ff1c43c97)) + +## [1.463.0](https://github.com/windmill-labs/windmill/compare/v1.462.1...v1.463.0) (2025-02-14) + + +### Features + +* adding docker log rotation by default in docker compose ([#5295](https://github.com/windmill-labs/windmill/issues/5295)) ([dad829a](https://github.com/windmill-labs/windmill/commit/dad829adf4bff97e998f7d18e0bbafb8497d4198)) +* parse script for preprocessor/no_main_func on deploy ([#5292](https://github.com/windmill-labs/windmill/issues/5292)) ([28558e6](https://github.com/windmill-labs/windmill/commit/28558e674f60fef1b165a79c039b1b450759d500)) + + +### Bug Fixes + +* display branch chosen even if emoty branch ([77a8eed](https://github.com/windmill-labs/windmill/commit/77a8eedc96171e9f84463407bdc5aec9b7b10d62)) +* improve handling of empty branches and loops ([e7d4582](https://github.com/windmill-labs/windmill/commit/e7d458278969897aa7312dcd20a8091aaad772d7)) +* improve runs page load time ([266f820](https://github.com/windmill-labs/windmill/commit/266f82046ad287163d24910902393cd63156ca1d)) +* static website serving ([#5298](https://github.com/windmill-labs/windmill/issues/5298)) ([41eecc1](https://github.com/windmill-labs/windmill/commit/41eecc1437301bea557fb467cc48b502162de419)) +* users should be able to see their own jobs ([9ccadb6](https://github.com/windmill-labs/windmill/commit/9ccadb6085498119bdfcc172d52c7fce1eb3336e)) + +## [1.462.3](https://github.com/windmill-labs/windmill/compare/v1.462.1...v1.462.2) (2025-02-14) + + +### Bug Fixes + +* users should be able to see their own jobs ([9ccadb6](https://github.com/windmill-labs/windmill/commit/9ccadb6085498119bdfcc172d52c7fce1eb3336e)) + +## [1.462.2](https://github.com/windmill-labs/windmill/compare/v1.462.1...v1.462.2) (2025-02-14) + + +### Bug Fixes + +* display branch chosen even if emoty branch ([77a8eed](https://github.com/windmill-labs/windmill/commit/77a8eedc96171e9f84463407bdc5aec9b7b10d62)) +* improve handling of empty branches and loops ([e7d4582](https://github.com/windmill-labs/windmill/commit/e7d458278969897aa7312dcd20a8091aaad772d7)) + +## [1.462.1](https://github.com/windmill-labs/windmill/compare/v1.462.0...v1.462.1) (2025-02-14) + + +### Bug Fixes + +* ai_models in workspace_settings is now optional ([470d80e](https://github.com/windmill-labs/windmill/commit/470d80e219f3b8a3fc3f56802d0eaeffbb1d415f)) + +## [1.462.0](https://github.com/windmill-labs/windmill/compare/v1.461.1...v1.462.0) (2025-02-13) + + +### Features + +* teams workspace scripts ([#5238](https://github.com/windmill-labs/windmill/issues/5238)) ([149d5fb](https://github.com/windmill-labs/windmill/commit/149d5fb3e1d7c89a6005aa34ef34fa57657f507b)) + + +### Bug Fixes + +* **bun:** remove unecessary buntar in a bun bundle world ([1be335f](https://github.com/windmill-labs/windmill/commit/1be335f042727bbb33b5f515433b65c54bf841fe)) +* **bun:** remove unecessary buntar in a bun bundle world ([fe92211](https://github.com/windmill-labs/windmill/commit/fe922114a74b1757c37f7f7b76adb3aed1ffccc4)) +* **cli:** support lock in wmill dev ([dd695b4](https://github.com/windmill-labs/windmill/commit/dd695b40f41decdf9f2f3d6918d860249661fb36)) +* populate teams channel on initial load ([#5284](https://github.com/windmill-labs/windmill/issues/5284)) ([2ea3bde](https://github.com/windmill-labs/windmill/commit/2ea3bdec2d7a65f8ceeea84f9e677fb4d2c5e0f3)) + +## [1.461.1](https://github.com/windmill-labs/windmill/compare/v1.461.0...v1.461.1) (2025-02-13) + + +### Bug Fixes + +* **cli:** fix nits preventing release ([6fb8f7b](https://github.com/windmill-labs/windmill/commit/6fb8f7b45dd85fdf5edc5ca3948f767eb0a39629)) + +## [1.461.0](https://github.com/windmill-labs/windmill/compare/v1.460.1...v1.461.0) (2025-02-13) + + +### Features + +* **cli:** wmill dev works with flows ([956a5ac](https://github.com/windmill-labs/windmill/commit/956a5ac68236df1c1f9ea4facd7ad237457427cf)) + + +### Bug Fixes + +* **backend:** improve schedule queries plan to leverage indices better for performance ([#5273](https://github.com/windmill-labs/windmill/issues/5273)) ([bf20651](https://github.com/windmill-labs/windmill/commit/bf206515e8653bbe431e106277b72082e0c9e388)) +* better handling of null pre-processor return values ([2015e79](https://github.com/windmill-labs/windmill/commit/2015e79ff09293cafb799f4049de35f786059831)) +* remove variable pickers in app forms ([055c336](https://github.com/windmill-labs/windmill/commit/055c3367b7afd06a9c789d17fb29bf1d195055bc)) + +## [1.460.1](https://github.com/windmill-labs/windmill/compare/v1.460.0...v1.460.1) (2025-02-12) + + +### Bug Fixes + +* pin opentelemetry to 0.27.1 ([e92a909](https://github.com/windmill-labs/windmill/commit/e92a90907f41568e4e04c932e1fbef64ab4c48a9)) + +## [1.460.0](https://github.com/windmill-labs/windmill/compare/v1.459.0...v1.460.0) (2025-02-11) + + +### Features + +* add postgres trigger captures ([#5165](https://github.com/windmill-labs/windmill/issues/5165)) ([57cfa40](https://github.com/windmill-labs/windmill/commit/57cfa4045bf9aa7c2ef625cf3b24067567466aff)) +* improve large apps performances ([#5265](https://github.com/windmill-labs/windmill/issues/5265)) ([aae3683](https://github.com/windmill-labs/windmill/commit/aae3683fe90adc0eea055238f7776b96140706bd)) +* lazy mode ([7c4b8a7](https://github.com/windmill-labs/windmill/commit/7c4b8a7e1dca870b51b60f33a352d344ef34218f)) + + +### Bug Fixes + +* Remove cache dir mount and mount only the cache executable (Rust, C#) ([#5270](https://github.com/windmill-labs/windmill/issues/5270)) ([6357ed3](https://github.com/windmill-labs/windmill/commit/6357ed3d5e1188bb92ccaf4710e526ab2ec7e874)) + +## [1.459.0](https://github.com/windmill-labs/windmill/compare/v1.458.4...v1.459.0) (2025-02-10) + + +### Features + +* triggers cli sync ([#5243](https://github.com/windmill-labs/windmill/issues/5243)) ([df62925](https://github.com/windmill-labs/windmill/commit/df6292589479766acfe642d757f3736dfc369e33)) + + +### Bug Fixes + +* if user is authed, no need to use anonymous path for display result in apps ([deb1861](https://github.com/windmill-labs/windmill/commit/deb18615c20c4650e1bf765350f7abf4d2320a0a)) + +## [1.458.4](https://github.com/windmill-labs/windmill/compare/v1.458.3...v1.458.4) (2025-02-10) + + +### Bug Fixes + +* fix concurrent limit jobs non restarting ([4828a77](https://github.com/windmill-labs/windmill/commit/4828a77f21fe62f36632490f811fb01b39977662)) + +## [1.458.3](https://github.com/windmill-labs/windmill/compare/v1.458.2...v1.458.3) (2025-02-10) + + +### Bug Fixes + +* Support authentication with auth0 ([#5249](https://github.com/windmill-labs/windmill/issues/5249)) ([3d8dee9](https://github.com/windmill-labs/windmill/commit/3d8dee9e6ac10a59caf8e1ef9eff077fd78d2e20)) + +## [1.458.2](https://github.com/windmill-labs/windmill/compare/v1.458.1...v1.458.2) (2025-02-09) + + +### Bug Fixes + +* **frontend:** accordion list header on eval / background function ([#5244](https://github.com/windmill-labs/windmill/issues/5244)) ([32298e5](https://github.com/windmill-labs/windmill/commit/32298e5bfcd9ad1ca2954642d789e0f5d03b1680)) +* worker name in job + better timeout handling for same_worker jobs ([#5248](https://github.com/windmill-labs/windmill/issues/5248)) ([403826f](https://github.com/windmill-labs/windmill/commit/403826fca994535e59cc3c042f41bb47448dd951)) +* workflow as code status ([#5246](https://github.com/windmill-labs/windmill/issues/5246)) ([61ac7e9](https://github.com/windmill-labs/windmill/commit/61ac7e91de7da54bd405d721fe6e47ed8e5a5e9e)) + ## [1.458.1](https://github.com/windmill-labs/windmill/compare/v1.458.0...v1.458.1) (2025-02-07) diff --git a/Caddyfile b/Caddyfile index 67925e6492..933407b98d 100644 --- a/Caddyfile +++ b/Caddyfile @@ -12,7 +12,7 @@ bind {$ADDRESS} reverse_proxy /ws/* http://lsp:3001 # reverse_proxy /ws_mp/* http://multiplayer:3002 - # reverse_proxy /api/srch/* http://windmill_indexer:8001 + # reverse_proxy /api/srch/* http://windmill_indexer:8002 reverse_proxy /* http://windmill_server:8000 # tls /certs/cert.pem /certs/key.pem } diff --git a/Dockerfile b/Dockerfile index 8f5c90a8aa..0c824dbb83 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,5 @@ ARG DEBIAN_IMAGE=debian:bookworm-slim -ARG RUST_IMAGE=rust:1.83-slim-bookworm -ARG PYTHON_IMAGE=python:3.11.10-slim-bookworm +ARG RUST_IMAGE=rust:1.85-slim-bookworm FROM ${RUST_IMAGE} AS rust_base @@ -26,6 +25,7 @@ FROM node:20-alpine as frontend # install dependencies WORKDIR /frontend COPY ./frontend/package.json ./frontend/package-lock.json ./ +COPY ./frontend/scripts/ ./scripts/ RUN npm ci # Copy all local files into the image. @@ -42,6 +42,8 @@ COPY /typescript-client/docs/ /frontend/static/tsdocs/ RUN npm run generate-backend-client ENV NODE_OPTIONS "--max-old-space-size=8192" ARG VITE_BASE_URL "" +# Read more about macro in docker/dev.nu +# -- MACRO-SPREAD-WASM-PARSER-DEV-ONLY -- # RUN npm run build @@ -81,11 +83,11 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \ CARGO_NET_GIT_FETCH_WITH_CLI=true cargo build --release --features "$features" -FROM ${PYTHON_IMAGE} +FROM ${DEBIAN_IMAGE} ARG TARGETPLATFORM -ARG POWERSHELL_VERSION=7.3.5 -ARG POWERSHELL_DEB_VERSION=7.3.5-1 +ARG POWERSHELL_VERSION=7.5.0 +ARG POWERSHELL_DEB_VERSION=7.5.0-1 ARG KUBECTL_VERSION=1.28.7 ARG HELM_VERSION=3.14.3 ARG GO_VERSION=1.22.5 @@ -102,11 +104,13 @@ ARG WITH_GIT=true ARG LATEST_STABLE_PY=3.11.10 ENV UV_PYTHON_INSTALL_DIR=/tmp/windmill/cache/py_runtime ENV UV_PYTHON_PREFERENCE=only-managed +ENV UV_TOOL_BIN_DIR=/usr/local/bin + +ENV PATH /usr/local/bin:/root/.local/bin:$PATH -RUN pip install --upgrade pip==24.2 RUN apt-get update \ - && apt-get install -y ca-certificates wget curl jq unzip build-essential unixodbc xmlsec1 software-properties-common \ + && apt-get install -y --no-install-recommends netbase tzdata ca-certificates wget curl jq unzip build-essential unixodbc xmlsec1 software-properties-common \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* @@ -167,12 +171,15 @@ ENV PATH="${PATH}:/usr/local/go/bin" ENV GO_PATH=/usr/local/go/bin/go # Install UV -RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.5.15/uv-installer.sh | sh && mv /root/.local/bin/uv /usr/local/bin/uv +RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.6.2/uv-installer.sh | sh && mv /root/.local/bin/uv /usr/local/bin/uv # Preinstall python runtimes -RUN uv python install 3.11.10 +RUN uv python install 3.11 RUN uv python install $LATEST_STABLE_PY +RUN uv venv + + RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - RUN apt-get -y update && apt-get install -y curl procps nodejs awscli && apt-get clean \ && rm -rf /var/lib/apt/lists/* @@ -182,14 +189,12 @@ RUN mkdir -p /tmp/gobuildwarm && cd /tmp/gobuildwarm && go mod init gobuildwarm ENV TZ=Etc/UTC -RUN /usr/local/bin/python3 -m pip install pip-tools - COPY --from=builder /frontend/build /static_frontend COPY --from=builder /windmill/target/release/windmill ${APP}/windmill -COPY --from=denoland/deno:2.1.2 --chmod=755 /usr/bin/deno /usr/bin/deno +COPY --from=denoland/deno:2.2.1 --chmod=755 /usr/bin/deno /usr/bin/deno -COPY --from=oven/bun:1.1.43 /usr/local/bin/bun /usr/bin/bun +COPY --from=oven/bun:1.2.4 /usr/local/bin/bun /usr/bin/bun COPY --from=php:8.3.7-cli /usr/local/bin/php /usr/bin/php COPY --from=composer:2.7.6 /usr/bin/composer /usr/bin/composer @@ -217,9 +222,10 @@ RUN cp -r /root/.cache /home/windmill/.cache RUN mkdir -p /tmp/windmill/logs && \ mkdir -p /tmp/windmill/search -RUN chown -R windmill:windmill ${APP} && \ - chown -R windmill:windmill /tmp/windmill && \ - chown -R windmill:windmill /home/windmill/.cache +# Make directories world-readable and writable +RUN chmod -R 777 ${APP} && \ + chmod -R 777 /tmp/windmill && \ + chmod -R 777 /home/windmill/.cache USER root diff --git a/LICENSE b/LICENSE index efd4abf0fe..aea6c58c6f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ Source code in this repository is variously licensed under the Apache License Version 2.0 (see file ./LICENSE-APACHE), or the AGPLv3 License (see file -./LICENSE-AGPL) +./LICENSE-AGPL) and a proprietary license for certain enterprise features. Every file is under copyright (c) Windmill Labs, Inc 2022 unless otherwise specified. Every file is under License AGPL unless otherwise specified or @@ -12,11 +12,23 @@ and commercial license. The files under frontend/ are AGPLv3 Licensed, except any snippets of code that require a positive license check to be activated. Those snippets and files are under a proprietary and commercial license. Private and public forks MUST not include any of the above proprietary and commercial -code. Windmill Labs, Inc. provide tools to clean the codebase from those -snippets upon demand. The files under python-client/ deno-client/ go-client/ powershell-client/ are +code. The files under python-client/ deno-client/ go-client/ powershell-client/ +are Apache 2.0 Licensed. The openapi files, including the OpenFlow spec is Apache 2.0 Licensed. -The openapi files, including the OpenFlow spec is Apache 2.0 Licensed. +The binary compilable from source code in this repository without the +"enterprise" feature flag is open-source under the AGPLv3 License terms and +conditions. + +The "Community Edition" of Windmill available in the docker images hosted under +ghcr.io/windmill-labs/windmill and the github binary releases contains the files +under the AGPLv3 and Apache 2 sources but also includes proprietary and +non-public code and features which are not open source and under the following +terms: Windmill Labs, Inc. grants a right to use all the features of the +"Community Edition" for free without restrictions other than the limits and +quotas set in the software and a right to distribute the community edition as is +but not to sell, resell, serve as a managed service, modify or wrap under any +form without an explicit agreement. All third party components incorporated into the Windmill Software are licensed under the original license provided by the owner of the applicable component. diff --git a/README.md b/README.md index 3f0fef9ff6..27a5aaf296 100644 --- a/README.md +++ b/README.md @@ -110,8 +110,8 @@ You can build your entire infra on top of Windmill! ```typescript //import any dependency from npm -import * as wmill from "windmill-client" -import * as cowsay from 'cowsay@1.5.0'; +import * as wmill from "windmill-client"; +import * as cowsay from "cowsay@1.5.0"; // fill the type, or use the +Resource type to get a type-safe reference to a resource type Postgresql = { @@ -146,7 +146,9 @@ export async function main( ## CLI -We have a powerful CLI to interact with the windmill platform and sync your scripts from local files, GitHub repos and to run scripts and flows on the instance from local commands. See +We have a powerful CLI to interact with the windmill platform and sync your +scripts from local files, GitHub repos and to run scripts and flows on the +instance from local commands. See [more details](https://www.windmill.dev/docs/advanced/cli). ![CLI Screencast](./cli/vhs/output/setup.gif) @@ -168,7 +170,8 @@ Code extension: . Architecture: - Stateless API backend. - Workers that pull jobs from a queue in Postgres (and later, Kafka or Redis. - Upvote [#173](#https://github.com/windmill-labs/windmill/issues/173) if interested). + Upvote [#173](#https://github.com/windmill-labs/windmill/issues/173) if + interested). - Frontend in Svelte. - Scripts executions are sandboxed using Google's [nsjail](https://github.com/google/nsjail). @@ -284,22 +287,37 @@ edition. ### Commercial license -To self-host Windmill, you must respect the terms of the -[AGPLv3 license](https://www.gnu.org/licenses/agpl-3.0.en.html) which you do not -need to worry about for personal uses. For business uses, you should be fine if -you do not re-expose Windmill in any way to your users and are comfortable with -AGPLv3. +See the [LICENSE](https://github.com/windmill-labs/windmill/blob/main/LICENSE) +file for the full license text. + +The "Community Edition" of Windmill available in the docker images hosted under +ghcr.io/windmill-labs/windmill and the github binary releases contains the files +under the AGPLv3 and Apache 2 sources but also includes proprietary and +non-public code and features which are not open source and under the following +terms: Windmill Labs, Inc. grants a right to use all the features of the +"Community Edition" for free without restrictions other than the limits and +quotas set in the software and a right to distribute the community edition as is +but not to sell, resell, serve Windmill as a managed service, modify or wrap +under any form without an explicit agreement. + +The binary compilable from source code in this repository without the +"enterprise" feature flag is open-source under the +[LICENSE-AGPLv3](https://github.com/windmill-labs/windmill/blob/main/LICENSE-AGPL) +License terms and conditions. To -[re-expose any Windmill parts to your users](https://www.windmill.dev/docs/misc/white_labelling) -as a feature of your product, or to build a feature on top of Windmill, to -comply with AGPLv3 your product must be AGPLv3 or you must get a commercial -license. Contact us at if you have any doubts. +[re-expose directly any Windmill parts to your users](https://www.windmill.dev/docs/misc/white_labelling) +as a feature of your product, with the exception of iframed public Windmill +"apps", or to build a feature on top of "Windmill Community Edition" that you +sell commercially or embed in a distributable product or binary, you must get a +commercial license. Contact us at if you have any +questions. To do the same from the binary compiled from the source code in this +repository without the "enterprise" feature flag, you must comply with the +AGPLv3 license terms and conditions or get a commercial license from Windmill +Labs, Inc. -In addition, a commercial license grants you a dedicated engineer to transition -your current infrastructure to Windmill, support with tight SLA, and our global -cache sync for high-performance/no dependency cache miss of cluster from 10+ -nodes to 200+ nodes. +To use Windmill "Community Edition" as is internally in your organization, or to +use its APIs as is, you do NOT need a commercial license. ### Integrations @@ -314,70 +332,77 @@ you to have it being synced automatically everyday. ## Environment Variables -| Environment Variable name | Default | Description | Api Server/Worker/All | -| ------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | -| DATABASE_URL | | The Postgres database url. | All | -| WORKER_GROUP | default | The worker group the worker belongs to and get its configuration pulled from | Worker | -| MODE | standalone | The mode if the binary. Possible values: standalone, worker, server, agent | All | -| METRICS_ADDR | None | (ee only) The socket addr at which to expose Prometheus metrics at the /metrics path. Set to "true" to expose it on port 8001 | All | -| JSON_FMT | false | Output the logs in json format instead of logfmt | All | -| BASE_URL | http://localhost:8000 | The base url that is exposed publicly to access your instance. Is overriden by the instance settings if any. | Server | -| ZOMBIE_JOB_TIMEOUT | 30 | The timeout after which a job is considered to be zombie if the worker did not send pings about processing the job (every server check for zombie jobs every 30s) | Server | -| RESTART_ZOMBIE_JOBS | true | If true then a zombie job is restarted (in-place with the same uuid and some logs), if false the zombie job is failed | Server | -| SLEEP_QUEUE | 50 | The number of ms to sleep in between the last check for new jobs in the DB. It is multiplied by NUM_WORKERS such that in average, for one worker instance, there is one pull every SLEEP_QUEUE ms. | Worker | -| KEEP_JOB_DIR | false | Keep the job directory after the job is done. Useful for debugging. | Worker | -| LICENSE_KEY (EE only) | None | License key checked at startup for the Enterprise Edition of Windmill | Worker | -| SLACK_SIGNING_SECRET | None | The signing secret of your Slack app. See [Slack documentation](https://api.slack.com/authentication/verifying-requests-from-slack) | Server | -| COOKIE_DOMAIN | None | The domain of the cookie. If not set, the cookie will be set by the browser based on the full origin | Server | -| DENO_PATH | /usr/bin/deno | The path to the deno binary. | Worker | -| PYTHON_PATH | /usr/local/bin/python3 | The path to the python binary. | Worker | -| GO_PATH | /usr/bin/go | The path to the go binary. | Worker | -| GOPRIVATE | | The GOPRIVATE env variable to use private go modules | Worker | -| GOPROXY | | The GOPROXY env variable to use | Worker | -| NETRC | | The netrc content to use a private go registry | Worker | | Worker | -| PY_CONCURRENT_DOWNLOADS | 20 | Sets the maximum number of in-flight concurrent python downloads that windmill will perform at any given time. | Worker | -| PATH | None | The path environment variable, usually inherited | Worker | -| HOME | None | The home directory to use for Go and Bash , usually inherited | Worker | -| DATABASE_CONNECTIONS | 50 (Server)/3 (Worker) | The max number of connections in the database connection pool | All | -| SUPERADMIN_SECRET | None | A token that would let the caller act as a virtual superadmin superadmin@windmill.dev | Server | -| TIMEOUT_WAIT_RESULT | 20 | The number of seconds to wait before timeout on the 'run_wait_result' endpoint | Worker | -| QUEUE_LIMIT_WAIT_RESULT | None | The number of max jobs in the queue before rejecting immediately the request in 'run_wait_result' endpoint. Takes precedence on the query arg. If none is specified, there are no limit. | Worker | -| DENO_AUTH_TOKENS | None | Custom DENO_AUTH_TOKENS to pass to worker to allow the use of private modules | Worker | -| DISABLE_RESPONSE_LOGS | false | Disable response logs | Server | -| CREATE_WORKSPACE_REQUIRE_SUPERADMIN | true | If true, only superadmins can create new workspaces | Server | +| Environment Variable name | Default | Description | Api Server/Worker/All | +| ----------------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | +| DATABASE_URL | | The Postgres database url. | All | +| WORKER_GROUP | default | The worker group the worker belongs to and get its configuration pulled from | Worker | +| MODE | standalone | The mode if the binary. Possible values: standalone, worker, server, agent | All | +| METRICS_ADDR | None | (ee only) The socket addr at which to expose Prometheus metrics at the /metrics path. Set to "true" to expose it on port 8001 | All | +| JSON_FMT | false | Output the logs in json format instead of logfmt | All | +| BASE_URL | http://localhost:8000 | The base url that is exposed publicly to access your instance. Is overriden by the instance settings if any. | Server | +| ZOMBIE_JOB_TIMEOUT | 30 | The timeout after which a job is considered to be zombie if the worker did not send pings about processing the job (every server check for zombie jobs every 30s) | Server | +| RESTART_ZOMBIE_JOBS | true | If true then a zombie job is restarted (in-place with the same uuid and some logs), if false the zombie job is failed | Server | +| SLEEP_QUEUE | 50 | The number of ms to sleep in between the last check for new jobs in the DB. It is multiplied by NUM_WORKERS such that in average, for one worker instance, there is one pull every SLEEP_QUEUE ms. | Worker | +| KEEP_JOB_DIR | false | Keep the job directory after the job is done. Useful for debugging. | Worker | +| LICENSE_KEY (EE only) | None | License key checked at startup for the Enterprise Edition of Windmill | Worker | +| SLACK_SIGNING_SECRET | None | The signing secret of your Slack app. See [Slack documentation](https://api.slack.com/authentication/verifying-requests-from-slack) | Server | +| COOKIE_DOMAIN | None | The domain of the cookie. If not set, the cookie will be set by the browser based on the full origin | Server | +| DENO_PATH | /usr/bin/deno | The path to the deno binary. | Worker | +| PYTHON_PATH | | The path to the python binary if wanting to not have it managed by uv. | Worker | +| GO_PATH | /usr/bin/go | The path to the go binary. | Worker | +| GOPRIVATE | | The GOPRIVATE env variable to use private go modules | Worker | +| GOPROXY | | The GOPROXY env variable to use | Worker | +| NETRC | | The netrc content to use a private go registry | Worker | +| PY_CONCURRENT_DOWNLOADS | 20 | Sets the maximum number of in-flight concurrent python downloads that windmill will perform at any given time. | Worker | +| PATH | None | The path environment variable, usually inherited | Worker | +| HOME | None | The home directory to use for Go and Bash , usually inherited | Worker | +| DATABASE_CONNECTIONS | 50 (Server)/3 (Worker) | The max number of connections in the database connection pool | All | +| SUPERADMIN_SECRET | None | A token that would let the caller act as a virtual superadmin superadmin@windmill.dev | Server | +| TIMEOUT_WAIT_RESULT | 20 | The number of seconds to wait before timeout on the 'run_wait_result' endpoint | Worker | +| QUEUE_LIMIT_WAIT_RESULT | None | The number of max jobs in the queue before rejecting immediately the request in 'run_wait_result' endpoint. Takes precedence on the query arg. If none is specified, there are no limit. | Worker | +| DENO_AUTH_TOKENS | None | Custom DENO_AUTH_TOKENS to pass to worker to allow the use of private modules | Worker | +| DISABLE_RESPONSE_LOGS | false | Disable response logs | Server | +| CREATE_WORKSPACE_REQUIRE_SUPERADMIN | true | If true, only superadmins can create new workspaces | Server | +| MIN_FREE_DISK_SPACE_MB | 15000 | Minimum amount of free space on worker. Sends critical alert if worker has less free space. | Worker | ## Run a local dev setup + See the [./frontend/README_DEV.md](./frontend/README_DEV.md) file for all running options. Using [Nix](./frontend/README_DEV.md#nix). ### only Frontend + This will use the backend of but your own frontend -with hot-code reloading. Note that you will need to use a username / password login due to CSRF checks using a different auth provider. +with hot-code reloading. Note that you will need to use a username / password +login due to CSRF checks using a different auth provider. In the `frontend/` directory: 1. install the dependencies with `npm install` (or `pnpm install` or `yarn`) 2. generate the windmill client: - ``` - npm run generate-backend-client - ## on mac use - npm run generate-backend-client-mac - ``` + +``` +npm run generate-backend-client +## on mac use +npm run generate-backend-client-mac +``` + 3. Run your dev server with `npm run dev` 4. Et voilà, windmill should be available at `http://localhost/` ### Backend + Frontend + See the [./frontend/README_DEV.md](./frontend/README_DEV.md) file for all running options. + 1. Create a Postgres Database for Windmill and create an admin role inside your - Postgres setup. - The easiest way to get a working db is to run - ``` + Postgres setup. The easiest way to get a working db is to run + ``` cargo install sqlx-cli env DATABASE_URL= sqlx migrate run - ``` + ``` This will also avoid compile time issue with sqlx's `query!` macro 2. Install [nsjail](https://github.com/google/nsjail) and have it accessible in your PATH @@ -387,15 +412,15 @@ running options. 5. Install the [lld linker](https://lld.llvm.org/) 6. Go to `frontend/`: 1. `npm install`, `npm run generate-backend-client` then `npm run dev` - 2. You might need to set some extra heap space for the node runtime `export NODE_OPTIONS="--max-old-space-size=4096"` - 3. In another shell `npm run build` otherwise the backend will not find the `frontend/build` folder and will not compile. + 2. You might need to set some extra heap space for the node runtime + `export NODE_OPTIONS="--max-old-space-size=4096"` + 3. In another shell `npm run build` otherwise the backend will not find the + `frontend/build` folder and will not compile. 4. In another shell `sudo caddy run --config Caddyfile` 7. Go to `backend/`: `env DATABASE_URL= RUST_LOG=info cargo run` 8. Et voilà, windmill should be available at `http://localhost/` - - ## Contributors @@ -404,4 +429,4 @@ running options. ## Copyright -Windmill Labs, Inc 2023 \ No newline at end of file +Windmill Labs, Inc 2023 diff --git a/backend/.sqlx/query-00e63eab76d26e148b77e932848de74e8b0943d30481465da453942e299a128f.json b/backend/.sqlx/query-00e63eab76d26e148b77e932848de74e8b0943d30481465da453942e299a128f.json deleted file mode 100644 index 9c1f8d00a7..0000000000 --- a/backend/.sqlx/query-00e63eab76d26e148b77e932848de74e8b0943d30481465da453942e299a128f.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO metrics (id, value)\n VALUES ($1, to_jsonb((\n SELECT EXTRACT(EPOCH FROM now() - scheduled_for)\n FROM v2_job_queue\n WHERE tag = $2 AND running = false AND scheduled_for <= now() - ('3 seconds')::interval\n ORDER BY priority DESC NULLS LAST, scheduled_for LIMIT 1\n )))", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Text" - ] - }, - "nullable": [] - }, - "hash": "00e63eab76d26e148b77e932848de74e8b0943d30481465da453942e299a128f" -} diff --git a/backend/.sqlx/query-011c7638eeeda710deb86a216a9e10df9c3e9458e85bcdde466b01011a1f2ac2.json b/backend/.sqlx/query-011c7638eeeda710deb86a216a9e10df9c3e9458e85bcdde466b01011a1f2ac2.json new file mode 100644 index 0000000000..749b684fd9 --- /dev/null +++ b/backend/.sqlx/query-011c7638eeeda710deb86a216a9e10df9c3e9458e85bcdde466b01011a1f2ac2.json @@ -0,0 +1,50 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n path,\n is_flow,\n workspace_id,\n owner,\n email,\n trigger_config as \"trigger_config!: _\"\n FROM\n capture_config\n WHERE\n trigger_kind = 'postgres' AND\n last_client_ping > NOW() - INTERVAL '10 seconds' AND\n trigger_config IS NOT NULL AND\n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds')\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "owner", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "trigger_config!: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + false, + true + ] + }, + "hash": "011c7638eeeda710deb86a216a9e10df9c3e9458e85bcdde466b01011a1f2ac2" +} diff --git a/backend/.sqlx/query-0128194fb539809e15bee670864fff3e22de8332d0c6d8ca98d22d62137fe701.json b/backend/.sqlx/query-0128194fb539809e15bee670864fff3e22de8332d0c6d8ca98d22d62137fe701.json deleted file mode 100644 index 045993ce2e..0000000000 --- a/backend/.sqlx/query-0128194fb539809e15bee670864fff3e22de8332d0c6d8ca98d22d62137fe701.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n c.id IS NOT NULL AS completed,\n q.id IS NOT NULL AND q.running AS running,\n SUBSTR(logs, GREATEST($1 - log_offset, 0)) AS logs,\n COALESCE(r.memory_peak, c.memory_peak) AS mem_peak,\n CASE\n -- flow step:\n WHEN flow_step_id IS NOT NULL THEN NULL\n -- completed:\n WHEN c.id IS NOT NULL THEN COALESCE(\n c.workflow_as_code_status || c.flow_status,\n c.workflow_as_code_status,\n c.flow_status\n )\n -- not completed:\n ELSE COALESCE(\n f.workflow_as_code_status || f.flow_status,\n f.workflow_as_code_status,\n f.flow_status\n )\n END AS \"flow_status: sqlx::types::Json>\",\n job_logs.log_offset + CHAR_LENGTH(job_logs.logs) + 1 AS log_offset,\n created_by AS \"created_by!\",\n CASE WHEN $4::BOOLEAN THEN (\n SELECT scalar_int FROM job_stats WHERE job_id = $3 AND metric_id = 'progress_perc'\n ) END AS progress\n FROM v2_job j\n LEFT JOIN v2_job_queue q USING (id)\n LEFT JOIN v2_job_runtime r USING (id)\n LEFT JOIN v2_job_status f USING (id)\n LEFT JOIN v2_job_completed c USING (id)\n LEFT JOIN job_logs ON job_logs.job_id = $3\n WHERE j.workspace_id = $2 AND j.id = $3", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "completed", - "type_info": "Bool" - }, - { - "ordinal": 1, - "name": "running", - "type_info": "Bool" - }, - { - "ordinal": 2, - "name": "logs", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "mem_peak", - "type_info": "Int4" - }, - { - "ordinal": 4, - "name": "flow_status: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 5, - "name": "log_offset", - "type_info": "Int4" - }, - { - "ordinal": 6, - "name": "created_by!", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "progress", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int4", - "Text", - "Uuid", - "Bool" - ] - }, - "nullable": [ - null, - null, - null, - null, - null, - null, - false, - null - ] - }, - "hash": "0128194fb539809e15bee670864fff3e22de8332d0c6d8ca98d22d62137fe701" -} diff --git a/backend/.sqlx/query-016bf078cdea0aae4a05ae7e004fad573d5c7cbdca975edc34f36890c824c44b.json b/backend/.sqlx/query-016bf078cdea0aae4a05ae7e004fad573d5c7cbdca975edc34f36890c824c44b.json deleted file mode 100644 index 28a9e33022..0000000000 --- a/backend/.sqlx/query-016bf078cdea0aae4a05ae7e004fad573d5c7cbdca975edc34f36890c824c44b.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n email AS \"email!\",\n created_by AS \"created_by!\",\n parent_job, permissioned_as AS \"permissioned_as!\",\n script_path, schedule_path, flow_step_id, root_job,\n scheduled_for AS \"scheduled_for!: chrono::DateTime\"\n FROM queue WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "email!", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "created_by!", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent_job", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "permissioned_as!", - "type_info": "Varchar" - }, - { - "ordinal": 4, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "schedule_path", - "type_info": "Varchar" - }, - { - "ordinal": 6, - "name": "flow_step_id", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "root_job", - "type_info": "Uuid" - }, - { - "ordinal": 8, - "name": "scheduled_for!: chrono::DateTime", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true, - true, - true, - true, - true, - true, - true, - true - ] - }, - "hash": "016bf078cdea0aae4a05ae7e004fad573d5c7cbdca975edc34f36890c824c44b" -} diff --git a/backend/.sqlx/query-01755585cd3f6e100a66da331720286cbc09d4abf2926146b24a8c95cf21e5c8.json b/backend/.sqlx/query-01755585cd3f6e100a66da331720286cbc09d4abf2926146b24a8c95cf21e5c8.json new file mode 100644 index 0000000000..8c1ba3782c --- /dev/null +++ b/backend/.sqlx/query-01755585cd3f6e100a66da331720286cbc09d4abf2926146b24a8c95cf21e5c8.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO workspace_runnable_dependencies (app_path, runnable_path, runnable_is_flow, workspace_id) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Bool", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "01755585cd3f6e100a66da331720286cbc09d4abf2926146b24a8c95cf21e5c8" +} diff --git a/backend/.sqlx/query-01e92a4ba3074f1dce6ec98bc6c3fad4878f48db8c17c6d58590bd5df2e3350a.json b/backend/.sqlx/query-01e92a4ba3074f1dce6ec98bc6c3fad4878f48db8c17c6d58590bd5df2e3350a.json deleted file mode 100644 index a6f202a16f..0000000000 --- a/backend/.sqlx/query-01e92a4ba3074f1dce6ec98bc6c3fad4878f48db8c17c6d58590bd5df2e3350a.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE postgres_trigger \n SET \n server_id = $1, \n last_server_ping = now(),\n error = 'Connecting...'\n WHERE \n enabled IS TRUE \n AND workspace_id = $2 \n AND path = $3 \n AND (last_server_ping IS NULL \n OR last_server_ping < now() - INTERVAL '15 seconds'\n ) \n RETURNING true\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "01e92a4ba3074f1dce6ec98bc6c3fad4878f48db8c17c6d58590bd5df2e3350a" -} diff --git a/backend/.sqlx/query-029ed3dcba207c58aa6936e44bd825b2166f1846b1bb684522607d5ca31a0df3.json b/backend/.sqlx/query-029ed3dcba207c58aa6936e44bd825b2166f1846b1bb684522607d5ca31a0df3.json deleted file mode 100644 index d9cf2bd091..0000000000 --- a/backend/.sqlx/query-029ed3dcba207c58aa6936e44bd825b2166f1846b1bb684522607d5ca31a0df3.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET flow_status = jsonb_set(COALESCE(flow_status, '{}'::jsonb), array[$1], jsonb_set(jsonb_set('{}'::jsonb, '{scheduled_for}', to_jsonb(now()::text)), '{name}', to_jsonb($4::text))) WHERE id = $2 AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Uuid", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "029ed3dcba207c58aa6936e44bd825b2166f1846b1bb684522607d5ca31a0df3" -} diff --git a/backend/.sqlx/query-02bb4ea17e83c79f870e2655d6d9c035af6d763b7ee9577280785ccf0220a123.json b/backend/.sqlx/query-02bb4ea17e83c79f870e2655d6d9c035af6d763b7ee9577280785ccf0220a123.json deleted file mode 100644 index e8df1339ff..0000000000 --- a/backend/.sqlx/query-02bb4ea17e83c79f870e2655d6d9c035af6d763b7ee9577280785ccf0220a123.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT running FROM queue WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "running", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "02bb4ea17e83c79f870e2655d6d9c035af6d763b7ee9577280785ccf0220a123" -} diff --git a/backend/.sqlx/query-036af7b1cf6d731647fd718458944b9a9759bdb034e73f3065cde6a2f88c8dce.json b/backend/.sqlx/query-036af7b1cf6d731647fd718458944b9a9759bdb034e73f3065cde6a2f88c8dce.json deleted file mode 100644 index 6f17ee0e9d..0000000000 --- a/backend/.sqlx/query-036af7b1cf6d731647fd718458944b9a9759bdb034e73f3065cde6a2f88c8dce.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET permissioned_as = ('u/' || $1) WHERE permissioned_as = ('u/' || $2) AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "036af7b1cf6d731647fd718458944b9a9759bdb034e73f3065cde6a2f88c8dce" -} diff --git a/backend/.sqlx/query-2cef109784efc04999e4537e0d1d3fb3221e04f3a7c1abe91dd763f366d06618.json b/backend/.sqlx/query-05c65ba8a56b3b5f8bd37c30c0c6707522e01c4a05104969889b7bb41d6aa509.json similarity index 56% rename from backend/.sqlx/query-2cef109784efc04999e4537e0d1d3fb3221e04f3a7c1abe91dd763f366d06618.json rename to backend/.sqlx/query-05c65ba8a56b3b5f8bd37c30c0c6707522e01c4a05104969889b7bb41d6aa509.json index 3b7793dd05..3a4f5e1b82 100644 --- a/backend/.sqlx/query-2cef109784efc04999e4537e0d1d3fb3221e04f3a7c1abe91dd763f366d06618.json +++ b/backend/.sqlx/query-05c65ba8a56b3b5f8bd37c30c0c6707522e01c4a05104969889b7bb41d6aa509.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT success AS \"success!\" FROM completed_job WHERE id = ANY($1)", + "query": "SELECT status = 'success' OR status = 'skipped' AS \"success!\" FROM v2_job_completed WHERE id = ANY($1)", "describe": { "columns": [ { @@ -15,8 +15,8 @@ ] }, "nullable": [ - true + null ] }, - "hash": "2cef109784efc04999e4537e0d1d3fb3221e04f3a7c1abe91dd763f366d06618" + "hash": "05c65ba8a56b3b5f8bd37c30c0c6707522e01c4a05104969889b7bb41d6aa509" } diff --git a/backend/.sqlx/query-05cb171b610bfb45f6228128a385cde8a5b86d7ca377a028004cc382e12faf41.json b/backend/.sqlx/query-05cb171b610bfb45f6228128a385cde8a5b86d7ca377a028004cc382e12faf41.json new file mode 100644 index 0000000000..5290fab4c4 --- /dev/null +++ b/backend/.sqlx/query-05cb171b610bfb45f6228128a385cde8a5b86d7ca377a028004cc382e12faf41.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO concurrency_counter(concurrency_id, job_uuids) \n VALUES ($1, '{}'::jsonb)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "05cb171b610bfb45f6228128a385cde8a5b86d7ca377a028004cc382e12faf41" +} diff --git a/backend/.sqlx/query-05d6405b2cc6aabf564a10f05402878e9f2a13e0ce0dad42723f95ac7fb15d4b.json b/backend/.sqlx/query-05d6405b2cc6aabf564a10f05402878e9f2a13e0ce0dad42723f95ac7fb15d4b.json deleted file mode 100644 index 195c9bfe92..0000000000 --- a/backend/.sqlx/query-05d6405b2cc6aabf564a10f05402878e9f2a13e0ce0dad42723f95ac7fb15d4b.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT success AS \"success!\"\n FROM v2_as_completed_job WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "success!", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "05d6405b2cc6aabf564a10f05402878e9f2a13e0ce0dad42723f95ac7fb15d4b" -} diff --git a/backend/.sqlx/query-06731936fb073169b3a1a8a9817f1e669b60edccd260625a95094b7244f5fb83.json b/backend/.sqlx/query-06731936fb073169b3a1a8a9817f1e669b60edccd260625a95094b7244f5fb83.json deleted file mode 100644 index c509986552..0000000000 --- a/backend/.sqlx/query-06731936fb073169b3a1a8a9817f1e669b60edccd260625a95094b7244f5fb83.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['retry'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "06731936fb073169b3a1a8a9817f1e669b60edccd260625a95094b7244f5fb83" -} diff --git a/backend/.sqlx/query-ceda377fa534656ac12a7f41db77db1f054ec751855c8db7352b0e9c20b63ef8.json b/backend/.sqlx/query-0689cdc6c7676f5e1984792a0e0b172ea9a70835bfba6cef56142556197e9767.json similarity index 56% rename from backend/.sqlx/query-ceda377fa534656ac12a7f41db77db1f054ec751855c8db7352b0e9c20b63ef8.json rename to backend/.sqlx/query-0689cdc6c7676f5e1984792a0e0b172ea9a70835bfba6cef56142556197e9767.json index a309a03762..27c1d1d2d8 100644 --- a/backend/.sqlx/query-ceda377fa534656ac12a7f41db77db1f054ec751855c8db7352b0e9c20b63ef8.json +++ b/backend/.sqlx/query-0689cdc6c7676f5e1984792a0e0b172ea9a70835bfba6cef56142556197e9767.json @@ -1,11 +1,11 @@ { "db_name": "PostgreSQL", - "query": "SELECT ai_resource FROM workspace_settings WHERE workspace_id = $1", + "query": "SELECT ai_config FROM workspace_settings WHERE workspace_id = $1", "describe": { "columns": [ { "ordinal": 0, - "name": "ai_resource", + "name": "ai_config", "type_info": "Jsonb" } ], @@ -18,5 +18,5 @@ true ] }, - "hash": "ceda377fa534656ac12a7f41db77db1f054ec751855c8db7352b0e9c20b63ef8" + "hash": "0689cdc6c7676f5e1984792a0e0b172ea9a70835bfba6cef56142556197e9767" } diff --git a/backend/.sqlx/query-06db0e720dd59a7c52c0a98ea7b316237eb1a547678858c1a1e45985035b3468.json b/backend/.sqlx/query-06db0e720dd59a7c52c0a98ea7b316237eb1a547678858c1a1e45985035b3468.json new file mode 100644 index 0000000000..aea8b8302b --- /dev/null +++ b/backend/.sqlx/query-06db0e720dd59a7c52c0a98ea7b316237eb1a547678858c1a1e45985035b3468.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = flow_status - 'retry'\n WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "06db0e720dd59a7c52c0a98ea7b316237eb1a547678858c1a1e45985035b3468" +} diff --git a/backend/.sqlx/query-070b8ad0b59f485fa5bf68082b060f5c3561c37e9c6f2834d234a862a475a6eb.json b/backend/.sqlx/query-070b8ad0b59f485fa5bf68082b060f5c3561c37e9c6f2834d234a862a475a6eb.json new file mode 100644 index 0000000000..280db517dd --- /dev/null +++ b/backend/.sqlx/query-070b8ad0b59f485fa5bf68082b060f5c3561c37e9c6f2834d234a862a475a6eb.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n gcp_trigger \n SET \n enabled = $1, \n email = $2, \n edited_by = $3, \n edited_at = now(), \n server_id = NULL, \n error = NULL\n WHERE \n path = $4 AND \n workspace_id = $5 \n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Bool", + "Varchar", + "Varchar", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "070b8ad0b59f485fa5bf68082b060f5c3561c37e9c6f2834d234a862a475a6eb" +} diff --git a/backend/.sqlx/query-0721acae4f627df4687bb43b830a47faeee5c0a152cda8d62794c14dd200fac1.json b/backend/.sqlx/query-0721acae4f627df4687bb43b830a47faeee5c0a152cda8d62794c14dd200fac1.json deleted file mode 100644 index fde6a8d881..0000000000 --- a/backend/.sqlx/query-0721acae4f627df4687bb43b830a47faeee5c0a152cda8d62794c14dd200fac1.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE completed_job SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "0721acae4f627df4687bb43b830a47faeee5c0a152cda8d62794c14dd200fac1" -} diff --git a/backend/.sqlx/query-07a7f1da7ee77324a73eb5b3743e4a801e0c446c55fc9fd8bc75e36d58073bee.json b/backend/.sqlx/query-07a7f1da7ee77324a73eb5b3743e4a801e0c446c55fc9fd8bc75e36d58073bee.json deleted file mode 100644 index 15f9729ee1..0000000000 --- a/backend/.sqlx/query-07a7f1da7ee77324a73eb5b3743e4a801e0c446c55fc9fd8bc75e36d58073bee.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n substr(concat(coalesce(completed_job.logs, ''), job_logs.logs), greatest($1 - job_logs.log_offset, 0)) AS logs,\n mem_peak,\n CASE WHEN is_flow_step is true then NULL else flow_status END AS \"flow_status: sqlx::types::Json>\",\n job_logs.log_offset + char_length(job_logs.logs) + 1 AS log_offset,\n created_by AS \"created_by!\"\n FROM completed_job\n LEFT JOIN job_logs ON job_logs.job_id = completed_job.id \n WHERE completed_job.workspace_id = $2 AND completed_job.id = $3", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "logs", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "mem_peak", - "type_info": "Int4" - }, - { - "ordinal": 2, - "name": "flow_status: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 3, - "name": "log_offset", - "type_info": "Int4" - }, - { - "ordinal": 4, - "name": "created_by!", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Int4", - "Text", - "Uuid" - ] - }, - "nullable": [ - null, - true, - null, - null, - true - ] - }, - "hash": "07a7f1da7ee77324a73eb5b3743e4a801e0c446c55fc9fd8bc75e36d58073bee" -} diff --git a/backend/.sqlx/query-08f288d2781d823e109a9e5b8848234ca7d1efeee9661f3901f298da375e73f7.json b/backend/.sqlx/query-08f288d2781d823e109a9e5b8848234ca7d1efeee9661f3901f298da375e73f7.json index 2be39fce26..388fd55418 100644 --- a/backend/.sqlx/query-08f288d2781d823e109a9e5b8848234ca7d1efeee9661f3901f298da375e73f7.json +++ b/backend/.sqlx/query-08f288d2781d823e109a9e5b8848234ca7d1efeee9661f3901f298da375e73f7.json @@ -65,7 +65,7 @@ }, { "ordinal": 12, - "name": "ai_resource", + "name": "ai_config", "type_info": "Jsonb" }, { @@ -100,58 +100,48 @@ }, { "ordinal": 19, - "name": "automatic_billing", - "type_info": "Bool" - }, - { - "ordinal": 20, "name": "default_scripts", "type_info": "Jsonb" }, { - "ordinal": 21, + "ordinal": 20, "name": "deploy_ui", "type_info": "Jsonb" }, { - "ordinal": 22, + "ordinal": 21, "name": "mute_critical_alerts", "type_info": "Bool" }, { - "ordinal": 23, + "ordinal": 22, "name": "color", "type_info": "Varchar" }, { - "ordinal": 24, + "ordinal": 23, "name": "operator_settings", "type_info": "Jsonb" }, { - "ordinal": 25, + "ordinal": 24, "name": "teams_command_script", "type_info": "Text" }, { - "ordinal": 26, + "ordinal": 25, "name": "teams_team_id", "type_info": "Text" }, { - "ordinal": 27, + "ordinal": 26, "name": "teams_team_name", "type_info": "Text" }, { - "ordinal": 28, - "name": "ai_models", - "type_info": "VarcharArray" - }, - { - "ordinal": 29, - "name": "code_completion_model", - "type_info": "Varchar" + "ordinal": 27, + "name": "git_app_installations", + "type_info": "Jsonb" } ], "parameters": { @@ -179,7 +169,6 @@ true, true, true, - false, true, true, true, @@ -188,8 +177,7 @@ true, true, true, - false, - true + false ] }, "hash": "08f288d2781d823e109a9e5b8848234ca7d1efeee9661f3901f298da375e73f7" diff --git a/backend/.sqlx/query-099e7c7a66968575f896e0c11ecd9cfe9a2ec315d6589e940be157a0563f81af.json b/backend/.sqlx/query-099e7c7a66968575f896e0c11ecd9cfe9a2ec315d6589e940be157a0563f81af.json deleted file mode 100644 index 59fb4a5dba..0000000000 --- a/backend/.sqlx/query-099e7c7a66968575f896e0c11ecd9cfe9a2ec315d6589e940be157a0563f81af.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET last_ping = now() WHERE id = $1 AND workspace_id = $2 AND canceled = false", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [] - }, - "hash": "099e7c7a66968575f896e0c11ecd9cfe9a2ec315d6589e940be157a0563f81af" -} diff --git a/backend/.sqlx/query-09efbd7177c6172569dc29b7d9ede70315eeb4e0ef9ed3165365f257e27f5e68.json b/backend/.sqlx/query-09efbd7177c6172569dc29b7d9ede70315eeb4e0ef9ed3165365f257e27f5e68.json new file mode 100644 index 0000000000..4500681506 --- /dev/null +++ b/backend/.sqlx/query-09efbd7177c6172569dc29b7d9ede70315eeb4e0ef9ed3165365f257e27f5e68.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO workspace_runnable_dependencies (flow_path, runnable_path, runnable_is_flow, workspace_id) VALUES ($1, $2, TRUE, $3) ON CONFLICT DO NOTHING", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "09efbd7177c6172569dc29b7d9ede70315eeb4e0ef9ed3165365f257e27f5e68" +} diff --git a/backend/.sqlx/query-0a6a89e6ab3037f02c3c4c84ee02138d5fded1e6360bb992046fe9711b5ea213.json b/backend/.sqlx/query-0a6a89e6ab3037f02c3c4c84ee02138d5fded1e6360bb992046fe9711b5ea213.json deleted file mode 100644 index 460ce2bf8d..0000000000 --- a/backend/.sqlx/query-0a6a89e6ab3037f02c3c4c84ee02138d5fded1e6360bb992046fe9711b5ea213.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n job_kind AS \"job_kind: JobKind\",\n script_hash AS \"script_hash: ScriptHash\",\n flow_status AS \"flow_status!: Json>\",\n raw_flow AS \"raw_flow: Json>\"\n FROM queue WHERE id = $1 AND workspace_id = $2 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "job_kind: JobKind", - "type_info": { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - } - }, - { - "ordinal": 1, - "name": "script_hash: ScriptHash", - "type_info": "Int8" - }, - { - "ordinal": 2, - "name": "flow_status!: Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 3, - "name": "raw_flow: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - false, - true, - true, - true - ] - }, - "hash": "0a6a89e6ab3037f02c3c4c84ee02138d5fded1e6360bb992046fe9711b5ea213" -} diff --git a/backend/.sqlx/query-0aaec91ab06753e46c595d82469924a98f28b0dead245df7248a9ccb8a5f20c3.json b/backend/.sqlx/query-0aaec91ab06753e46c595d82469924a98f28b0dead245df7248a9ccb8a5f20c3.json new file mode 100644 index 0000000000..04f5ba5b84 --- /dev/null +++ b/backend/.sqlx/query-0aaec91ab06753e46c595d82469924a98f28b0dead245df7248a9ccb8a5f20c3.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2)\n WHERE id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "0aaec91ab06753e46c595d82469924a98f28b0dead245df7248a9ccb8a5f20c3" +} diff --git a/backend/.sqlx/query-0ad36c1598ff4ece0c325eaeb9a9177a87e1accd192402e21db5ae09c3498ab0.json b/backend/.sqlx/query-0ad36c1598ff4ece0c325eaeb9a9177a87e1accd192402e21db5ae09c3498ab0.json deleted file mode 100644 index 2d06bac0c9..0000000000 --- a/backend/.sqlx/query-0ad36c1598ff4ece0c325eaeb9a9177a87e1accd192402e21db5ae09c3498ab0.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO queue\n (workspace_id, id, running, parent_job, created_by, permissioned_as, scheduled_for, \n script_hash, script_path, raw_code, raw_lock, args, job_kind, schedule_path, raw_flow, flow_status, is_flow_step, language, started_at, same_worker, pre_run_error, email, visible_to_owner, root_job, tag, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id, cache_ttl, priority, last_ping)\n VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, now()), $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, CASE WHEN $3 THEN now() END, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, NULL) RETURNING id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Uuid", - "Bool", - "Uuid", - "Varchar", - "Varchar", - "Timestamptz", - "Int8", - "Varchar", - "Text", - "Text", - "Jsonb", - { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - }, - "Varchar", - "Jsonb", - "Jsonb", - "Bool", - { - "Custom": { - "name": "script_lang", - "kind": { - "Enum": [ - "python3", - "deno", - "go", - "bash", - "postgresql", - "nativets", - "bun", - "mysql", - "bigquery", - "snowflake", - "graphql", - "powershell", - "mssql", - "php", - "bunnative", - "rust", - "ansible", - "csharp", - "oracledb" - ] - } - } - }, - "Bool", - "Text", - "Varchar", - "Bool", - "Uuid", - "Varchar", - "Int4", - "Int4", - "Int4", - "Varchar", - "Int4", - "Int2" - ] - }, - "nullable": [ - false - ] - }, - "hash": "0ad36c1598ff4ece0c325eaeb9a9177a87e1accd192402e21db5ae09c3498ab0" -} diff --git a/backend/.sqlx/query-0c0b5d5d1e6ab2fed7532f94b50be3210e3845b61551691bbef81c2b6fb01121.json b/backend/.sqlx/query-0c0b5d5d1e6ab2fed7532f94b50be3210e3845b61551691bbef81c2b6fb01121.json deleted file mode 100644 index 9b37241475..0000000000 --- a/backend/.sqlx/query-0c0b5d5d1e6ab2fed7532f94b50be3210e3845b61551691bbef81c2b6fb01121.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['failure_module'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "0c0b5d5d1e6ab2fed7532f94b50be3210e3845b61551691bbef81c2b6fb01121" -} diff --git a/backend/.sqlx/query-19cc8499f682ec34d54bc4f694cb281a9bd7f5431c646c6268513751fff95395.json b/backend/.sqlx/query-0cb0e912bc942af2b1ef784455f3f073a79e300f3dd48f14122d1782eee663cd.json similarity index 72% rename from backend/.sqlx/query-19cc8499f682ec34d54bc4f694cb281a9bd7f5431c646c6268513751fff95395.json rename to backend/.sqlx/query-0cb0e912bc942af2b1ef784455f3f073a79e300f3dd48f14122d1782eee663cd.json index 0c379a7bdb..0a9e91b206 100644 --- a/backend/.sqlx/query-19cc8499f682ec34d54bc4f694cb281a9bd7f5431c646c6268513751fff95395.json +++ b/backend/.sqlx/query-0cb0e912bc942af2b1ef784455f3f073a79e300f3dd48f14122d1782eee663cd.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT coalesce(COUNT(*) FILTER(WHERE suspend = 0 AND running = false), 0) as \"database_length!\", coalesce(COUNT(*) FILTER(WHERE suspend > 0), 0) as \"suspended!\" FROM v2_as_queue WHERE (workspace_id = $1 OR $2) AND scheduled_for <= now()", + "query": "SELECT coalesce(COUNT(*) FILTER(WHERE suspend = 0 AND running = false), 0) as \"database_length!\", coalesce(COUNT(*) FILTER(WHERE suspend > 0), 0) as \"suspended!\" FROM v2_as_queue WHERE (workspace_id = $1 OR $2) AND scheduled_for <= now() AND ($3::text[] IS NULL OR tag = ANY($3))", "describe": { "columns": [ { @@ -17,7 +17,8 @@ "parameters": { "Left": [ "Text", - "Bool" + "Bool", + "TextArray" ] }, "nullable": [ @@ -25,5 +26,5 @@ null ] }, - "hash": "19cc8499f682ec34d54bc4f694cb281a9bd7f5431c646c6268513751fff95395" + "hash": "0cb0e912bc942af2b1ef784455f3f073a79e300f3dd48f14122d1782eee663cd" } diff --git a/backend/.sqlx/query-63b5f03741be97d0e8763dd070649ebb6ec02aa083d7e175c1a02a38935a4024.json b/backend/.sqlx/query-0d8153986cea6166820f601f80d8e67156408b08360d628300b28221ea995a58.json similarity index 52% rename from backend/.sqlx/query-63b5f03741be97d0e8763dd070649ebb6ec02aa083d7e175c1a02a38935a4024.json rename to backend/.sqlx/query-0d8153986cea6166820f601f80d8e67156408b08360d628300b28221ea995a58.json index 4d914355db..1d2f993f5d 100644 --- a/backend/.sqlx/query-63b5f03741be97d0e8763dd070649ebb6ec02aa083d7e175c1a02a38935a4024.json +++ b/backend/.sqlx/query-0d8153986cea6166820f601f80d8e67156408b08360d628300b28221ea995a58.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE route_path_key = $1 AND http_method = $2 AND ($3::TEXT IS NULL OR path != $3))", + "query": "\n SELECT EXISTS(\n SELECT 1 \n FROM http_trigger \n WHERE \n ((workspaced_route IS TRUE AND workspace_id || '/' || route_path_key = $1) \n OR (workspaced_route IS FALSE AND route_path_key = $1))\n AND http_method = $2 \n AND ($3::TEXT IS NULL OR path != $3)\n )\n ", "describe": { "columns": [ { @@ -33,5 +33,5 @@ null ] }, - "hash": "63b5f03741be97d0e8763dd070649ebb6ec02aa083d7e175c1a02a38935a4024" + "hash": "0d8153986cea6166820f601f80d8e67156408b08360d628300b28221ea995a58" } diff --git a/backend/.sqlx/query-0df84fc35f2780ceb7c473b0165ebab93a4bc1bcab166aae68244ab1f3d4df9f.json b/backend/.sqlx/query-0df84fc35f2780ceb7c473b0165ebab93a4bc1bcab166aae68244ab1f3d4df9f.json deleted file mode 100644 index d34383496a..0000000000 --- a/backend/.sqlx/query-0df84fc35f2780ceb7c473b0165ebab93a4bc1bcab166aae68244ab1f3d4df9f.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO completed_job AS cj\n ( workspace_id\n , id\n , parent_job\n , created_by\n , created_at\n , started_at\n , duration_ms\n , success\n , script_hash\n , script_path\n , args\n , result\n , raw_code\n , raw_lock\n , canceled\n , canceled_by\n , canceled_reason\n , job_kind\n , schedule_path\n , permissioned_as\n , flow_status\n , raw_flow\n , is_flow_step\n , is_skipped\n , language\n , email\n , visible_to_owner\n , mem_peak\n , tag\n , priority\n )\n VALUES ($1, $2, $3, $4, $5, COALESCE($6, now()), COALESCE($30::bigint, (EXTRACT('epoch' FROM (now())) - EXTRACT('epoch' FROM (COALESCE($6, now()))))*1000), $7, $8, $9,$10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29)\n ON CONFLICT (id) DO UPDATE SET success = $7, result = $11 RETURNING duration_ms AS \"duration_ms!\"", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "duration_ms!", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Uuid", - "Uuid", - "Varchar", - "Timestamptz", - "Timestamptz", - "Bool", - "Int8", - "Varchar", - "Jsonb", - "Jsonb", - "Text", - "Text", - "Bool", - "Varchar", - "Text", - { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - }, - "Varchar", - "Varchar", - "Jsonb", - "Jsonb", - "Bool", - "Bool", - { - "Custom": { - "name": "script_lang", - "kind": { - "Enum": [ - "python3", - "deno", - "go", - "bash", - "postgresql", - "nativets", - "bun", - "mysql", - "bigquery", - "snowflake", - "graphql", - "powershell", - "mssql", - "php", - "bunnative", - "rust", - "ansible", - "csharp", - "oracledb" - ] - } - } - }, - "Varchar", - "Bool", - "Int4", - "Varchar", - "Int2", - "Int8" - ] - }, - "nullable": [ - true - ] - }, - "hash": "0df84fc35f2780ceb7c473b0165ebab93a4bc1bcab166aae68244ab1f3d4df9f" -} diff --git a/backend/.sqlx/query-0e52a588f3edeb8fb58d6d62247b8590e51171e2811c62737bdb81fb0ac8f182.json b/backend/.sqlx/query-0e52a588f3edeb8fb58d6d62247b8590e51171e2811c62737bdb81fb0ac8f182.json deleted file mode 100644 index f23cf3f710..0000000000 --- a/backend/.sqlx/query-0e52a588f3edeb8fb58d6d62247b8590e51171e2811c62737bdb81fb0ac8f182.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE worker_ping SET \n ping_at = now(), \n jobs_executed = 1, \n current_job_id = $1, \n current_job_workspace_id = 'admins' \n WHERE worker = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [] - }, - "hash": "0e52a588f3edeb8fb58d6d62247b8590e51171e2811c62737bdb81fb0ac8f182" -} diff --git a/backend/.sqlx/query-0ee14619dd81df460b2b8cc6df2b89646279f77469c35deffca8e17a11d7f6c8.json b/backend/.sqlx/query-0ee14619dd81df460b2b8cc6df2b89646279f77469c35deffca8e17a11d7f6c8.json new file mode 100644 index 0000000000..ac882aec64 --- /dev/null +++ b/backend/.sqlx/query-0ee14619dd81df460b2b8cc6df2b89646279f77469c35deffca8e17a11d7f6c8.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n (elem->>'installation_id')::bigint as installation_id,\n elem->>'account_id' as account_id\n FROM workspace_settings,\n LATERAL jsonb_array_elements(git_app_installations) AS elem\n WHERE workspace_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "installation_id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "account_id", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null, + null + ] + }, + "hash": "0ee14619dd81df460b2b8cc6df2b89646279f77469c35deffca8e17a11d7f6c8" +} diff --git a/backend/.sqlx/query-0ef638eb62cb8b285cb20855679486b78eae82901a0128b9c9c837c9e9e91212.json b/backend/.sqlx/query-0ef638eb62cb8b285cb20855679486b78eae82901a0128b9c9c837c9e9e91212.json deleted file mode 100644 index 85523ee5c3..0000000000 --- a/backend/.sqlx/query-0ef638eb62cb8b285cb20855679486b78eae82901a0128b9c9c837c9e9e91212.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n success AS \"success!\",\n result AS \"result: Json>\",\n started_at AS \"started_at!\"FROM completed_job WHERE workspace_id = $1 AND schedule_path = $2 AND script_path = $3 AND id != $4\n ORDER BY created_at DESC\n LIMIT $5", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "success!", - "type_info": "Bool" - }, - { - "ordinal": 1, - "name": "result: Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 2, - "name": "started_at!", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Text", - "Uuid", - "Int8" - ] - }, - "nullable": [ - true, - true, - true - ] - }, - "hash": "0ef638eb62cb8b285cb20855679486b78eae82901a0128b9c9c837c9e9e91212" -} diff --git a/backend/.sqlx/query-0efb16cbf130ec6e9922ecc82a95b252449bd569df374e40ce8820fc3d75a0f0.json b/backend/.sqlx/query-0efb16cbf130ec6e9922ecc82a95b252449bd569df374e40ce8820fc3d75a0f0.json new file mode 100644 index 0000000000..2f17b5e8db --- /dev/null +++ b/backend/.sqlx/query-0efb16cbf130ec6e9922ecc82a95b252449bd569df374e40ce8820fc3d75a0f0.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DROP INDEX CONCURRENTLY IF EXISTS queue_sort", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "0efb16cbf130ec6e9922ecc82a95b252449bd569df374e40ce8820fc3d75a0f0" +} diff --git a/backend/.sqlx/query-0f9c4c2fda3beeafc940e14a6c2c44d61782aaae68ffa413f37b13dc6cf4d83d.json b/backend/.sqlx/query-0f9c4c2fda3beeafc940e14a6c2c44d61782aaae68ffa413f37b13dc6cf4d83d.json deleted file mode 100644 index fc68dc313a..0000000000 --- a/backend/.sqlx/query-0f9c4c2fda3beeafc940e14a6c2c44d61782aaae68ffa413f37b13dc6cf4d83d.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = jsonb_set(\n jsonb_set(\n COALESCE(flow_status, '{}'::jsonb),\n array[$1],\n COALESCE(flow_status->$1, '{}'::jsonb)\n ),\n array[$1, 'started_at'],\n to_jsonb(now()::text)\n )\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "0f9c4c2fda3beeafc940e14a6c2c44d61782aaae68ffa413f37b13dc6cf4d83d" -} diff --git a/backend/.sqlx/query-103ef3cf5cf4d25d780e4aefd5b290d810a5e8ea6458d9f9fd484ced549ea82e.json b/backend/.sqlx/query-103ef3cf5cf4d25d780e4aefd5b290d810a5e8ea6458d9f9fd484ced549ea82e.json deleted file mode 100644 index 15d772ab16..0000000000 --- a/backend/.sqlx/query-103ef3cf5cf4d25d780e4aefd5b290d810a5e8ea6458d9f9fd484ced549ea82e.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT value, resource_type FROM resource WHERE path = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "value", - "type_info": "Jsonb" - }, - { - "ordinal": 1, - "name": "resource_type", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - true, - false - ] - }, - "hash": "103ef3cf5cf4d25d780e4aefd5b290d810a5e8ea6458d9f9fd484ced549ea82e" -} diff --git a/backend/.sqlx/query-1060c503cf8d4bb5cef9720c162b8192924b4a938d249fae92624cd55e44f488.json b/backend/.sqlx/query-1060c503cf8d4bb5cef9720c162b8192924b4a938d249fae92624cd55e44f488.json new file mode 100644 index 0000000000..39b7179e5c --- /dev/null +++ b/backend/.sqlx/query-1060c503cf8d4bb5cef9720c162b8192924b4a938d249fae92624cd55e44f488.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT],\n $4\n )\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Uuid", + "Text", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "1060c503cf8d4bb5cef9720c162b8192924b4a938d249fae92624cd55e44f488" +} diff --git a/backend/.sqlx/query-115544a96173f9cb1d27757e7b931fb27912cfd05ba768a42cf9b3dfd7205e9a.json b/backend/.sqlx/query-115544a96173f9cb1d27757e7b931fb27912cfd05ba768a42cf9b3dfd7205e9a.json new file mode 100644 index 0000000000..66a2152ae9 --- /dev/null +++ b/backend/.sqlx/query-115544a96173f9cb1d27757e7b931fb27912cfd05ba768a42cf9b3dfd7205e9a.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n mqtt_trigger \n SET \n server_id = $1, \n last_server_ping = now(),\n error = 'Connecting...'\n WHERE \n enabled IS TRUE \n AND workspace_id = $2 \n AND path = $3 \n AND (last_server_ping IS NULL \n OR last_server_ping < now() - INTERVAL '15 seconds'\n ) \n RETURNING true\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "115544a96173f9cb1d27757e7b931fb27912cfd05ba768a42cf9b3dfd7205e9a" +} diff --git a/backend/.sqlx/query-1182fe055306d7ea435d76b74d781e066915c8397e6bbc9e408ff3dda9fec27f.json b/backend/.sqlx/query-1182fe055306d7ea435d76b74d781e066915c8397e6bbc9e408ff3dda9fec27f.json deleted file mode 100644 index 767f5cba57..0000000000 --- a/backend/.sqlx/query-1182fe055306d7ea435d76b74d781e066915c8397e6bbc9e408ff3dda9fec27f.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE script SET ws_error_handler_muted = $3 WHERE workspace_id = $2 AND path = $1 AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Bool" - ] - }, - "nullable": [] - }, - "hash": "1182fe055306d7ea435d76b74d781e066915c8397e6bbc9e408ff3dda9fec27f" -} diff --git a/backend/.sqlx/query-119469ebfe8572c78ed3ee5ab5b1a6a1cb1b0f31e357b5370f9bb7eab1e20a7b.json b/backend/.sqlx/query-119469ebfe8572c78ed3ee5ab5b1a6a1cb1b0f31e357b5370f9bb7eab1e20a7b.json deleted file mode 100644 index bb701df30d..0000000000 --- a/backend/.sqlx/query-119469ebfe8572c78ed3ee5ab5b1a6a1cb1b0f31e357b5370f9bb7eab1e20a7b.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH uuid_table as (\n select gen_random_uuid() as uuid from generate_series(1, $6)\n )\n INSERT INTO job\n (id, workspace_id, raw_code, raw_lock, raw_flow, tag)\n (SELECT uuid, $1, $2, $3, $4, $5 FROM uuid_table)\n RETURNING id AS \"id!\"", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Text", - "Jsonb", - "Varchar", - "Int4" - ] - }, - "nullable": [ - true - ] - }, - "hash": "119469ebfe8572c78ed3ee5ab5b1a6a1cb1b0f31e357b5370f9bb7eab1e20a7b" -} diff --git a/backend/.sqlx/query-11d59fb24aeb40f82e6fd11b697f26e14a0ae955fabeecc4a936b95937bf04d1.json b/backend/.sqlx/query-11d59fb24aeb40f82e6fd11b697f26e14a0ae955fabeecc4a936b95937bf04d1.json deleted file mode 100644 index 3375abd07a..0000000000 --- a/backend/.sqlx/query-11d59fb24aeb40f82e6fd11b697f26e14a0ae955fabeecc4a936b95937bf04d1.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = jsonb_set(\n jsonb_set(flow_status, ARRAY['modules', $4::INTEGER::TEXT, 'job'], to_jsonb($1::UUID::TEXT)),\n ARRAY['modules', $4::INTEGER::TEXT, 'type'],\n to_jsonb('InProgress'::text)\n )\n WHERE id = $2 AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Text", - "Int4" - ] - }, - "nullable": [] - }, - "hash": "11d59fb24aeb40f82e6fd11b697f26e14a0ae955fabeecc4a936b95937bf04d1" -} diff --git a/backend/.sqlx/query-122090a0f89e5248a0a0f199ebd24582fdb302883aebd2da187ac0084e767ea3.json b/backend/.sqlx/query-122090a0f89e5248a0a0f199ebd24582fdb302883aebd2da187ac0084e767ea3.json deleted file mode 100644 index a329998c95..0000000000 --- a/backend/.sqlx/query-122090a0f89e5248a0a0f199ebd24582fdb302883aebd2da187ac0084e767ea3.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT set_config('session.pgroups', $1, true)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "set_config", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "122090a0f89e5248a0a0f199ebd24582fdb302883aebd2da187ac0084e767ea3" -} diff --git a/backend/.sqlx/query-1252ef3a652ffb99529c2ce84928197fa15efb9c78d68e3a191c01a04efe153f.json b/backend/.sqlx/query-1252ef3a652ffb99529c2ce84928197fa15efb9c78d68e3a191c01a04efe153f.json deleted file mode 100644 index d3d6dd84cf..0000000000 --- a/backend/.sqlx/query-1252ef3a652ffb99529c2ce84928197fa15efb9c78d68e3a191c01a04efe153f.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4),\n ARRAY['modules', $1::TEXT, 'iterator', 'index'],\n ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "int4", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int4", - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [ - null - ] - }, - "hash": "1252ef3a652ffb99529c2ce84928197fa15efb9c78d68e3a191c01a04efe153f" -} diff --git a/backend/.sqlx/query-126be8832776644e0d2c5d004acb26f6d4820a6a1fc8e028c3550c438248a82b.json b/backend/.sqlx/query-126be8832776644e0d2c5d004acb26f6d4820a6a1fc8e028c3550c438248a82b.json deleted file mode 100644 index ecc75e957a..0000000000 --- a/backend/.sqlx/query-126be8832776644e0d2c5d004acb26f6d4820a6a1fc8e028c3550c438248a82b.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET running = false\n , started_at = null\n , scheduled_for = $1\n , last_ping = null\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Timestamptz", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "126be8832776644e0d2c5d004acb26f6d4820a6a1fc8e028c3550c438248a82b" -} diff --git a/backend/.sqlx/query-12828c9b2964f2b484a68de1e01b65cdcd277257192ee0a6d18a00f41bce49d4.json b/backend/.sqlx/query-12828c9b2964f2b484a68de1e01b65cdcd277257192ee0a6d18a00f41bce49d4.json deleted file mode 100644 index 84c0804c95..0000000000 --- a/backend/.sqlx/query-12828c9b2964f2b484a68de1e01b65cdcd277257192ee0a6d18a00f41bce49d4.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT suspend > 0 AS \"r!\" FROM v2_job_queue WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "r!", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "12828c9b2964f2b484a68de1e01b65cdcd277257192ee0a6d18a00f41bce49d4" -} diff --git a/backend/.sqlx/query-12a0fd7d8d99fb73b01bc24774fe9a8da57b5204bb6b1207aed47143c17a20bc.json b/backend/.sqlx/query-12a0fd7d8d99fb73b01bc24774fe9a8da57b5204bb6b1207aed47143c17a20bc.json deleted file mode 100644 index e60e67da63..0000000000 --- a/backend/.sqlx/query-12a0fd7d8d99fb73b01bc24774fe9a8da57b5204bb6b1207aed47143c17a20bc.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT COUNT(id) FROM queue WHERE running = true AND workspace_id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "12a0fd7d8d99fb73b01bc24774fe9a8da57b5204bb6b1207aed47143c17a20bc" -} diff --git a/backend/.sqlx/query-12e868b63a7c622c76713db5a5577a927efca4ae49a15c2b999e2410f2a312ff.json b/backend/.sqlx/query-12e868b63a7c622c76713db5a5577a927efca4ae49a15c2b999e2410f2a312ff.json new file mode 100644 index 0000000000..49ad10ac07 --- /dev/null +++ b/backend/.sqlx/query-12e868b63a7c622c76713db5a5577a927efca4ae49a15c2b999e2410f2a312ff.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE \n capture_config \n SET \n last_server_ping = NULL \n WHERE \n workspace_id = $1 AND \n path = $2 AND \n is_flow = $3 AND \n trigger_kind = 'postgres' AND \n server_id IS NULL\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "12e868b63a7c622c76713db5a5577a927efca4ae49a15c2b999e2410f2a312ff" +} diff --git a/backend/.sqlx/query-1312b7fd622cc814a406c85dbbff61f003c29185c267642cfd898075ebda855d.json b/backend/.sqlx/query-1312b7fd622cc814a406c85dbbff61f003c29185c267642cfd898075ebda855d.json new file mode 100644 index 0000000000..582896cc65 --- /dev/null +++ b/backend/.sqlx/query-1312b7fd622cc814a406c85dbbff61f003c29185c267642cfd898075ebda855d.json @@ -0,0 +1,145 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n subscription_mode AS \"subscription_mode: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "gcp_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "subscription_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "topic_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "delivery_type: _", + "type_info": { + "Custom": { + "name": "delivery_mode", + "kind": { + "Enum": [ + "push", + "pull" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "delivery_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 6, + "name": "subscription_mode: _", + "type_info": { + "Custom": { + "name": "gcp_subscription_mode", + "kind": { + "Enum": [ + "create_update", + "existing" + ] + } + } + } + }, + { + "ordinal": 7, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "enabled", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false + ] + }, + "hash": "1312b7fd622cc814a406c85dbbff61f003c29185c267642cfd898075ebda855d" +} diff --git a/backend/.sqlx/query-1438e8dc5738fc69bc6601eb11729610f671b7df0ab25da058e16c6654279d61.json b/backend/.sqlx/query-1438e8dc5738fc69bc6601eb11729610f671b7df0ab25da058e16c6654279d61.json deleted file mode 100644 index 452510a97f..0000000000 --- a/backend/.sqlx/query-1438e8dc5738fc69bc6601eb11729610f671b7df0ab25da058e16c6654279d61.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT flow_status->'user_states'->$1\n FROM queue\n WHERE id = $2 AND workspace_id = $3\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Text", - "Uuid", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "1438e8dc5738fc69bc6601eb11729610f671b7df0ab25da058e16c6654279d61" -} diff --git a/backend/.sqlx/query-144e4eccfd1c1e729e3c864bd5dc3316248719dfa8a6c9e1d15a7931638e86db.json b/backend/.sqlx/query-144e4eccfd1c1e729e3c864bd5dc3316248719dfa8a6c9e1d15a7931638e86db.json new file mode 100644 index 0000000000..e573bfdab7 --- /dev/null +++ b/backend/.sqlx/query-144e4eccfd1c1e729e3c864bd5dc3316248719dfa8a6c9e1d15a7931638e86db.json @@ -0,0 +1,158 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n workspace_id, \n path, \n route_path, \n route_path_key,\n workspaced_route,\n script_path, \n is_flow, \n http_method as \"http_method: _\", \n edited_by, \n email, \n edited_at, \n extra_perms, \n is_async, \n authentication_method as \"authentication_method: _\", \n static_asset_config as \"static_asset_config: _\", \n is_static_website,\n authentication_resource_path,\n wrap_body,\n raw_string\n FROM \n http_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "route_path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "route_path_key", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "workspaced_route", + "type_info": "Bool" + }, + { + "ordinal": 5, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 7, + "name": "http_method: _", + "type_info": { + "Custom": { + "name": "http_method", + "kind": { + "Enum": [ + "get", + "post", + "put", + "delete", + "patch" + ] + } + } + } + }, + { + "ordinal": 8, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 11, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 12, + "name": "is_async", + "type_info": "Bool" + }, + { + "ordinal": 13, + "name": "authentication_method: _", + "type_info": { + "Custom": { + "name": "authentication_method", + "kind": { + "Enum": [ + "none", + "windmill", + "api_key", + "basic_http", + "custom_script", + "signature" + ] + } + } + } + }, + { + "ordinal": 14, + "name": "static_asset_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 15, + "name": "is_static_website", + "type_info": "Bool" + }, + { + "ordinal": 16, + "name": "authentication_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 17, + "name": "wrap_body", + "type_info": "Bool" + }, + { + "ordinal": 18, + "name": "raw_string", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + true, + false, + false + ] + }, + "hash": "144e4eccfd1c1e729e3c864bd5dc3316248719dfa8a6c9e1d15a7931638e86db" +} diff --git a/backend/.sqlx/query-14540eef4594d9282cee3df4f92a7ed2e67243e5c1522850045b2da42fa914bc.json b/backend/.sqlx/query-14540eef4594d9282cee3df4f92a7ed2e67243e5c1522850045b2da42fa914bc.json deleted file mode 100644 index b10496550f..0000000000 --- a/backend/.sqlx/query-14540eef4594d9282cee3df4f92a7ed2e67243e5c1522850045b2da42fa914bc.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n queue.job_kind AS \"job_kind!: JobKind\",\n queue.script_hash AS \"script_hash: ScriptHash\",\n queue.raw_flow AS \"raw_flow: sqlx::types::Json>\",\n completed_job.parent_job AS \"parent_job: Uuid\",\n completed_job.created_at AS \"created_at!: chrono::NaiveDateTime\",\n completed_job.created_by AS \"created_by!\",\n queue.script_path,\n queue.args AS \"args: sqlx::types::Json>\"\n FROM queue\n JOIN completed_job ON completed_job.parent_job = queue.id\n WHERE completed_job.id = $1 AND completed_job.workspace_id = $2\n LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "job_kind!: JobKind", - "type_info": { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - } - }, - { - "ordinal": 1, - "name": "script_hash: ScriptHash", - "type_info": "Int8" - }, - { - "ordinal": 2, - "name": "raw_flow: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 3, - "name": "parent_job: Uuid", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "created_at!: chrono::NaiveDateTime", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "created_by!", - "type_info": "Varchar" - }, - { - "ordinal": 6, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "args: sqlx::types::Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true, - true, - true, - true, - true, - true, - true - ] - }, - "hash": "14540eef4594d9282cee3df4f92a7ed2e67243e5c1522850045b2da42fa914bc" -} diff --git a/backend/.sqlx/query-1488e1b5007752e1ebae4235ad04c398fe6398745e16fd119008b8ea67662416.json b/backend/.sqlx/query-1488e1b5007752e1ebae4235ad04c398fe6398745e16fd119008b8ea67662416.json new file mode 100644 index 0000000000..197298f788 --- /dev/null +++ b/backend/.sqlx/query-1488e1b5007752e1ebae4235ad04c398fe6398745e16fd119008b8ea67662416.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE postgres_trigger \n SET \n server_id = $1, \n last_server_ping = now(),\n error = 'Connecting...'\n WHERE \n enabled IS TRUE \n AND workspace_id = $2 \n AND path = $3 \n AND (last_server_ping IS NULL \n OR last_server_ping < now() - INTERVAL '15 seconds'\n ) \n RETURNING true\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "1488e1b5007752e1ebae4235ad04c398fe6398745e16fd119008b8ea67662416" +} diff --git a/backend/.sqlx/query-15697f3b63f88b9cfa33ab0aa64b441961aad80bf9fd0125bcf55a729e556d1e.json b/backend/.sqlx/query-15697f3b63f88b9cfa33ab0aa64b441961aad80bf9fd0125bcf55a729e556d1e.json deleted file mode 100644 index 4199d677a7..0000000000 --- a/backend/.sqlx/query-15697f3b63f88b9cfa33ab0aa64b441961aad80bf9fd0125bcf55a729e556d1e.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n DELETE\n FROM parallel_monitor_lock\n WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval \n RETURNING parent_flow_id, job_id, last_ping, (SELECT workspace_id FROM queue q\n WHERE q.id = parent_flow_id AND q.running = true AND q.canceled = false) AS workspace_id\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "parent_flow_id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "job_id", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "last_ping", - "type_info": "Timestamptz" - }, - { - "ordinal": 3, - "name": "workspace_id", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - true, - null - ] - }, - "hash": "15697f3b63f88b9cfa33ab0aa64b441961aad80bf9fd0125bcf55a729e556d1e" -} diff --git a/backend/.sqlx/query-1625a84fbcf8c5f77eb0519f60d9418f85d4d3d599f4177403fad5ad99380715.json b/backend/.sqlx/query-1625a84fbcf8c5f77eb0519f60d9418f85d4d3d599f4177403fad5ad99380715.json new file mode 100644 index 0000000000..f0ee89dbc9 --- /dev/null +++ b/backend/.sqlx/query-1625a84fbcf8c5f77eb0519f60d9418f85d4d3d599f4177403fad5ad99380715.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n sqs_trigger \n SET \n enabled = $1, \n email = $2, \n edited_by = $3, \n edited_at = now(), \n server_id = NULL, \n error = NULL\n WHERE \n path = $4 AND \n workspace_id = $5 \n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Bool", + "Varchar", + "Varchar", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "1625a84fbcf8c5f77eb0519f60d9418f85d4d3d599f4177403fad5ad99380715" +} diff --git a/backend/.sqlx/query-170f620fbd99269d194d14d56f6a3863d9db5fe736a0a34325b824d9cec9b1a0.json b/backend/.sqlx/query-170f620fbd99269d194d14d56f6a3863d9db5fe736a0a34325b824d9cec9b1a0.json deleted file mode 100644 index 61fd7ed3ef..0000000000 --- a/backend/.sqlx/query-170f620fbd99269d194d14d56f6a3863d9db5fe736a0a34325b824d9cec9b1a0.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO completed_job AS cj\n ( workspace_id\n , id\n , parent_job\n , created_by\n , created_at\n , started_at\n , duration_ms\n , success\n , script_hash\n , script_path\n , args\n , result\n , raw_code\n , raw_lock\n , canceled\n , canceled_by\n , canceled_reason\n , job_kind\n , schedule_path\n , permissioned_as\n , flow_status\n , raw_flow\n , is_flow_step\n , is_skipped\n , language\n , email\n , visible_to_owner\n , mem_peak\n , tag\n , priority\n )\n SELECT workspace_id\n , id\n , parent_job\n , created_by\n , created_at\n , now()\n , 0\n , false\n , script_hash\n , script_path\n , args\n , $4\n , raw_code\n , raw_lock\n , true\n , $1\n , canceled_reason\n , job_kind\n , schedule_path\n , permissioned_as\n , flow_status\n , raw_flow\n , is_flow_step\n , false\n , language\n , email\n , visible_to_owner\n , mem_peak\n , tag\n , priority FROM queue \n WHERE id = any($2) AND running = false AND parent_job IS NULL AND workspace_id = $3 AND schedule_path IS NULL FOR UPDATE SKIP LOCKED\n ON CONFLICT (id) DO NOTHING RETURNING id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Varchar", - "UuidArray", - "Text", - "Jsonb" - ] - }, - "nullable": [ - false - ] - }, - "hash": "170f620fbd99269d194d14d56f6a3863d9db5fe736a0a34325b824d9cec9b1a0" -} diff --git a/backend/.sqlx/query-171b11d66b9ec6cb7b0dd74929e233389683f8d510850487453052a317391f0f.json b/backend/.sqlx/query-171b11d66b9ec6cb7b0dd74929e233389683f8d510850487453052a317391f0f.json new file mode 100644 index 0000000000..b8f2ccca5f --- /dev/null +++ b/backend/.sqlx/query-171b11d66b9ec6cb7b0dd74929e233389683f8d510850487453052a317391f0f.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT a.path\n FROM workspace_runnable_dependencies wru \n JOIN app a\n ON wru.app_path = a.path AND wru.workspace_id = a.workspace_id\n WHERE wru.runnable_path = $1 AND wru.runnable_is_flow = $2 AND wru.workspace_id = $3", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text", + "Bool", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "171b11d66b9ec6cb7b0dd74929e233389683f8d510850487453052a317391f0f" +} diff --git a/backend/.sqlx/query-173fbfd3ca2344fd08f73af75524c917d27fdb6273a35a563292b1f0701dc6ed.json b/backend/.sqlx/query-173fbfd3ca2344fd08f73af75524c917d27fdb6273a35a563292b1f0701dc6ed.json deleted file mode 100644 index 214bf50c6e..0000000000 --- a/backend/.sqlx/query-173fbfd3ca2344fd08f73af75524c917d27fdb6273a35a563292b1f0701dc6ed.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['failure_module'], $1),\n ARRAY['step'],\n $2\n )\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "173fbfd3ca2344fd08f73af75524c917d27fdb6273a35a563292b1f0701dc6ed" -} diff --git a/backend/.sqlx/query-17851a0710b80ffd6bebe42012a354665dff01554549ea7bbbb9953c68231296.json b/backend/.sqlx/query-17851a0710b80ffd6bebe42012a354665dff01554549ea7bbbb9953c68231296.json deleted file mode 100644 index eefc009b5e..0000000000 --- a/backend/.sqlx/query-17851a0710b80ffd6bebe42012a354665dff01554549ea7bbbb9953c68231296.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', $1::TEXT, 'iterator', 'index'],\n ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "int4", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int4", - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "17851a0710b80ffd6bebe42012a354665dff01554549ea7bbbb9953c68231296" -} diff --git a/backend/.sqlx/query-17e3e2a3232865c33fa535b5d99455942e30f932c5b97f1e5b508128f39a288f.json b/backend/.sqlx/query-17e3e2a3232865c33fa535b5d99455942e30f932c5b97f1e5b508128f39a288f.json deleted file mode 100644 index 4000bbb962..0000000000 --- a/backend/.sqlx/query-17e3e2a3232865c33fa535b5d99455942e30f932c5b97f1e5b508128f39a288f.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1), ARRAY['step'], $2)\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "17e3e2a3232865c33fa535b5d99455942e30f932c5b97f1e5b508128f39a288f" -} diff --git a/backend/.sqlx/query-17f04341c5c52173a776b71672f9e4d932d2f072b6c08ce419e4b95a2fd83e96.json b/backend/.sqlx/query-17f04341c5c52173a776b71672f9e4d932d2f072b6c08ce419e4b95a2fd83e96.json deleted file mode 100644 index 65ae96f587..0000000000 --- a/backend/.sqlx/query-17f04341c5c52173a776b71672f9e4d932d2f072b6c08ce419e4b95a2fd83e96.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH zombie_jobs AS (\n UPDATE queue SET running = false, started_at = null\n WHERE last_ping < now() - ($1 || ' seconds')::interval\n AND running = true AND job_kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow') AND same_worker = false\n RETURNING id, workspace_id, last_ping\n ),\n update_concurrency AS (\n UPDATE concurrency_counter cc\n SET job_uuids = job_uuids - zj.id::text\n FROM zombie_jobs zj\n INNER JOIN concurrency_key ck ON ck.job_id = zj.id\n WHERE cc.concurrency_id = ck.key\n )\n SELECT id AS \"id!\", workspace_id AS \"workspace_id!\", last_ping FROM zombie_jobs", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "workspace_id!", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "last_ping", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - true, - true, - true - ] - }, - "hash": "17f04341c5c52173a776b71672f9e4d932d2f072b6c08ce419e4b95a2fd83e96" -} diff --git a/backend/.sqlx/query-1850552883e67da181d68ff5c4e1babaa2fe072900b57e78e461590a6dafb682.json b/backend/.sqlx/query-1850552883e67da181d68ff5c4e1babaa2fe072900b57e78e461590a6dafb682.json deleted file mode 100644 index 037e63ae49..0000000000 --- a/backend/.sqlx/query-1850552883e67da181d68ff5c4e1babaa2fe072900b57e78e461590a6dafb682.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "1850552883e67da181d68ff5c4e1babaa2fe072900b57e78e461590a6dafb682" -} diff --git a/backend/.sqlx/query-186aef850c2eeb89c186ac6b2934dd3a703e2b9428096801e1d2d61fdbb99c9e.json b/backend/.sqlx/query-186aef850c2eeb89c186ac6b2934dd3a703e2b9428096801e1d2d61fdbb99c9e.json new file mode 100644 index 0000000000..648c5dda19 --- /dev/null +++ b/backend/.sqlx/query-186aef850c2eeb89c186ac6b2934dd3a703e2b9428096801e1d2d61fdbb99c9e.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n mqtt_trigger \n SET\n last_server_ping = NULL \n WHERE \n workspace_id = $1 \n AND path = $2 \n AND server_id IS NULL", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "186aef850c2eeb89c186ac6b2934dd3a703e2b9428096801e1d2d61fdbb99c9e" +} diff --git a/backend/.sqlx/query-187e8f85a71dea958e89fdfdf96c913a19eef8678dc7890c2f0e1ef8758ec43b.json b/backend/.sqlx/query-187e8f85a71dea958e89fdfdf96c913a19eef8678dc7890c2f0e1ef8758ec43b.json new file mode 100644 index 0000000000..db7d3ab2ec --- /dev/null +++ b/backend/.sqlx/query-187e8f85a71dea958e89fdfdf96c913a19eef8678dc7890c2f0e1ef8758ec43b.json @@ -0,0 +1,58 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n http_trigger \n SET \n route_path = $1, \n route_path_key = $2, \n workspaced_route = $3,\n wrap_body = $4,\n raw_string = $5,\n authentication_resource_path = $6,\n script_path = $7, \n path = $8, \n is_flow = $9, \n http_method = $10, \n static_asset_config = $11, \n edited_by = $12, \n email = $13, \n is_async = $14, \n authentication_method = $15, \n edited_at = now(), \n is_static_website = $16\n WHERE \n workspace_id = $17 AND \n path = $18\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Bool", + "Bool", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Bool", + { + "Custom": { + "name": "http_method", + "kind": { + "Enum": [ + "get", + "post", + "put", + "delete", + "patch" + ] + } + } + }, + "Jsonb", + "Varchar", + "Varchar", + "Bool", + { + "Custom": { + "name": "authentication_method", + "kind": { + "Enum": [ + "none", + "windmill", + "api_key", + "basic_http", + "custom_script", + "signature" + ] + } + } + }, + "Bool", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "187e8f85a71dea958e89fdfdf96c913a19eef8678dc7890c2f0e1ef8758ec43b" +} diff --git a/backend/.sqlx/query-18ddbb9eb4c0ec03e6170f8c70634546cddaf1029618ed14015fd7b0a7017441.json b/backend/.sqlx/query-18ddbb9eb4c0ec03e6170f8c70634546cddaf1029618ed14015fd7b0a7017441.json new file mode 100644 index 0000000000..c1258697c9 --- /dev/null +++ b/backend/.sqlx/query-18ddbb9eb4c0ec03e6170f8c70634546cddaf1029618ed14015fd7b0a7017441.json @@ -0,0 +1,27 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT set_session_context($1, $2, $3, $4, $5, $6)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "set_session_context", + "type_info": "Void" + } + ], + "parameters": { + "Left": [ + "Bool", + "Text", + "Text", + "Text", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "18ddbb9eb4c0ec03e6170f8c70634546cddaf1029618ed14015fd7b0a7017441" +} diff --git a/backend/.sqlx/query-1974bd65bbf40024773aad4dee1c50b12e110e76bb58e6de25bec094e758a71c.json b/backend/.sqlx/query-1974bd65bbf40024773aad4dee1c50b12e110e76bb58e6de25bec094e758a71c.json new file mode 100644 index 0000000000..f19670704d --- /dev/null +++ b/backend/.sqlx/query-1974bd65bbf40024773aad4dee1c50b12e110e76bb58e6de25bec094e758a71c.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n last_server_ping = now(), \n error = $1 \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'postgres' AND \n server_id = $5 AND \n last_client_ping > NOW() - INTERVAL '10 seconds' \n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Bool", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "1974bd65bbf40024773aad4dee1c50b12e110e76bb58e6de25bec094e758a71c" +} diff --git a/backend/.sqlx/query-197590e7ab74f64bbf374f23128850bed8f435ea5de16ba796f346fba51d9437.json b/backend/.sqlx/query-197590e7ab74f64bbf374f23128850bed8f435ea5de16ba796f346fba51d9437.json new file mode 100644 index 0000000000..28d629c587 --- /dev/null +++ b/backend/.sqlx/query-197590e7ab74f64bbf374f23128850bed8f435ea5de16ba796f346fba51d9437.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT ai_config as \"ai_config: sqlx::types::Json\" FROM workspace_settings WHERE workspace_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "ai_config: sqlx::types::Json", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + true + ] + }, + "hash": "197590e7ab74f64bbf374f23128850bed8f435ea5de16ba796f346fba51d9437" +} diff --git a/backend/.sqlx/query-199a76c04e3f0891ad09af27b9534bbabdd8703bfdf4d43df2c65e50d4ca2c85.json b/backend/.sqlx/query-199a76c04e3f0891ad09af27b9534bbabdd8703bfdf4d43df2c65e50d4ca2c85.json deleted file mode 100644 index 7dfa56ad40..0000000000 --- a/backend/.sqlx/query-199a76c04e3f0891ad09af27b9534bbabdd8703bfdf4d43df2c65e50d4ca2c85.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n schemaname AS schema_name,\n tablename AS table_name,\n attnames AS columns,\n rowfilter AS where_clause\n FROM\n pg_publication_tables\n WHERE\n pubname = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "schema_name", - "type_info": "Name" - }, - { - "ordinal": 1, - "name": "table_name", - "type_info": "Name" - }, - { - "ordinal": 2, - "name": "columns", - "type_info": "NameArray" - }, - { - "ordinal": 3, - "name": "where_clause", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Name" - ] - }, - "nullable": [ - true, - true, - true, - true - ] - }, - "hash": "199a76c04e3f0891ad09af27b9534bbabdd8703bfdf4d43df2c65e50d4ca2c85" -} diff --git a/backend/.sqlx/query-1af5ccc82048df95a791949e7b141861dbfd5c08daea615dde081e29f7459b9d.json b/backend/.sqlx/query-1af5ccc82048df95a791949e7b141861dbfd5c08daea615dde081e29f7459b9d.json deleted file mode 100644 index 9ab47f361b..0000000000 --- a/backend/.sqlx/query-1af5ccc82048df95a791949e7b141861dbfd5c08daea615dde081e29f7459b9d.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id FROM queue WHERE schedule_path = $1 AND workspace_id = $2 AND id != $3 AND running = true", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Uuid" - ] - }, - "nullable": [ - false - ] - }, - "hash": "1af5ccc82048df95a791949e7b141861dbfd5c08daea615dde081e29f7459b9d" -} diff --git a/backend/.sqlx/query-1b33393fbbc7e681b4d355f6096d982b52b48c8c3da392b14963a1ec86811546.json b/backend/.sqlx/query-1b33393fbbc7e681b4d355f6096d982b52b48c8c3da392b14963a1ec86811546.json new file mode 100644 index 0000000000..271c60b480 --- /dev/null +++ b/backend/.sqlx/query-1b33393fbbc7e681b4d355f6096d982b52b48c8c3da392b14963a1ec86811546.json @@ -0,0 +1,50 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n path,\n is_flow,\n workspace_id,\n owner,\n email,\n trigger_config as \"trigger_config!: _\"\n FROM\n capture_config\n WHERE\n trigger_kind = 'sqs' AND\n last_client_ping > NOW() - INTERVAL '10 seconds' AND\n trigger_config IS NOT NULL AND\n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds')\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "owner", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "trigger_config!: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + false, + true + ] + }, + "hash": "1b33393fbbc7e681b4d355f6096d982b52b48c8c3da392b14963a1ec86811546" +} diff --git a/backend/.sqlx/query-1bbef6baa5b8e2522d685df2979bb1e4b9022f5e841afd9eeb08a81688f6c0c8.json b/backend/.sqlx/query-1bbef6baa5b8e2522d685df2979bb1e4b9022f5e841afd9eeb08a81688f6c0c8.json deleted file mode 100644 index 4fcd1f0969..0000000000 --- a/backend/.sqlx/query-1bbef6baa5b8e2522d685df2979bb1e4b9022f5e841afd9eeb08a81688f6c0c8.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE workspace_settings SET ai_resource = NULL, code_completion_model = $1, ai_models = '{}' WHERE workspace_id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Text" - ] - }, - "nullable": [] - }, - "hash": "1bbef6baa5b8e2522d685df2979bb1e4b9022f5e841afd9eeb08a81688f6c0c8" -} diff --git a/backend/.sqlx/query-1bceaf6e9f25745b7f70128054ca81d68f3d56d4782e99e05b4f1cb362683514.json b/backend/.sqlx/query-1bceaf6e9f25745b7f70128054ca81d68f3d56d4782e99e05b4f1cb362683514.json new file mode 100644 index 0000000000..1be74d47f2 --- /dev/null +++ b/backend/.sqlx/query-1bceaf6e9f25745b7f70128054ca81d68f3d56d4782e99e05b4f1cb362683514.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH inserted_concurrency_counter AS (\n INSERT INTO concurrency_counter (concurrency_id, job_uuids) \n VALUES ($1, '{}'::jsonb)\n ON CONFLICT DO NOTHING\n )\n INSERT INTO concurrency_key(key, job_id) VALUES ($1, $2)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "1bceaf6e9f25745b7f70128054ca81d68f3d56d4782e99e05b4f1cb362683514" +} diff --git a/backend/.sqlx/query-1bdf186d3b99bbd913cbf95150105470cd5f1d4ddbb147cb8ce46f9d1da5dfaf.json b/backend/.sqlx/query-1bdf186d3b99bbd913cbf95150105470cd5f1d4ddbb147cb8ce46f9d1da5dfaf.json new file mode 100644 index 0000000000..b58e3bf8d8 --- /dev/null +++ b/backend/.sqlx/query-1bdf186d3b99bbd913cbf95150105470cd5f1d4ddbb147cb8ce46f9d1da5dfaf.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH email_lookup AS (\n SELECT email FROM token WHERE token = $1\n )\n DELETE FROM token\n WHERE email = (SELECT email FROM email_lookup) AND label = 'session'\n RETURNING email", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "email", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + true + ] + }, + "hash": "1bdf186d3b99bbd913cbf95150105470cd5f1d4ddbb147cb8ce46f9d1da5dfaf" +} diff --git a/backend/.sqlx/query-1c2254c15696d3dbc091488311676641ba3c6f1f1b5e006fc75427c9b231d323.json b/backend/.sqlx/query-1c2254c15696d3dbc091488311676641ba3c6f1f1b5e006fc75427c9b231d323.json deleted file mode 100644 index 110713fd0e..0000000000 --- a/backend/.sqlx/query-1c2254c15696d3dbc091488311676641ba3c6f1f1b5e006fc75427c9b231d323.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE completed_job SET script_path = REGEXP_REPLACE(script_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE script_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "1c2254c15696d3dbc091488311676641ba3c6f1f1b5e006fc75427c9b231d323" -} diff --git a/backend/.sqlx/query-1c28baaadd7d0c86a92bf9880a4ea33457bf8cff669e983431f1fd26ff275f83.json b/backend/.sqlx/query-1c28baaadd7d0c86a92bf9880a4ea33457bf8cff669e983431f1fd26ff275f83.json deleted file mode 100644 index 0cb39dcb9b..0000000000 --- a/backend/.sqlx/query-1c28baaadd7d0c86a92bf9880a4ea33457bf8cff669e983431f1fd26ff275f83.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT id, flow_status, suspend, script_path\n FROM queue\n WHERE id = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "flow_status", - "type_info": "Jsonb" - }, - { - "ordinal": 2, - "name": "suspend", - "type_info": "Int4" - }, - { - "ordinal": 3, - "name": "script_path", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false, - true, - false, - true - ] - }, - "hash": "1c28baaadd7d0c86a92bf9880a4ea33457bf8cff669e983431f1fd26ff275f83" -} diff --git a/backend/.sqlx/query-1cad25c24d0f80d58a50d4da923f8b0672797299545ee0754b6ad74ece92c77e.json b/backend/.sqlx/query-1cad25c24d0f80d58a50d4da923f8b0672797299545ee0754b6ad74ece92c77e.json new file mode 100644 index 0000000000..0729adc8ba --- /dev/null +++ b/backend/.sqlx/query-1cad25c24d0f80d58a50d4da923f8b0672797299545ee0754b6ad74ece92c77e.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n last_server_ping = now(), \n error = $1 \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'sqs' AND \n server_id = $5 AND \n last_client_ping > NOW() - INTERVAL '10 seconds' \n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Bool", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "1cad25c24d0f80d58a50d4da923f8b0672797299545ee0754b6ad74ece92c77e" +} diff --git a/backend/.sqlx/query-354f88b23d20f92c6b6d5bdd8d6c69b08c6a86116cbfd0ecad8f112f7f49d8d1.json b/backend/.sqlx/query-1d0cd1c29ad48e31b4a3237cd34cc5c157241fbf274a3c525c6891c361293942.json similarity index 60% rename from backend/.sqlx/query-354f88b23d20f92c6b6d5bdd8d6c69b08c6a86116cbfd0ecad8f112f7f49d8d1.json rename to backend/.sqlx/query-1d0cd1c29ad48e31b4a3237cd34cc5c157241fbf274a3c525c6891c361293942.json index 4b03b16262..a448f7e413 100644 --- a/backend/.sqlx/query-354f88b23d20f92c6b6d5bdd8d6c69b08c6a86116cbfd0ecad8f112f7f49d8d1.json +++ b/backend/.sqlx/query-1d0cd1c29ad48e31b4a3237cd34cc5c157241fbf274a3c525c6891c361293942.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT SUBSTRING(name, 9) as \"name!\", (config.config->'min_alive_workers_alert_threshold')::INT as \"threshold!\" \n FROM config \n WHERE name LIKE 'worker__%' AND config->'min_alive_workers_alert_threshold' IS NOT NULL", + "query": "SELECT SUBSTRING(name, 9) as \"name!\", (config.config->'min_alive_workers_alert_threshold')::INT as \"threshold!\"\n FROM config\n WHERE name LIKE 'worker__%' AND config->'min_alive_workers_alert_threshold' IS NOT NULL", "describe": { "columns": [ { @@ -22,5 +22,5 @@ null ] }, - "hash": "354f88b23d20f92c6b6d5bdd8d6c69b08c6a86116cbfd0ecad8f112f7f49d8d1" + "hash": "1d0cd1c29ad48e31b4a3237cd34cc5c157241fbf274a3c525c6891c361293942" } diff --git a/backend/.sqlx/query-1d1098cc9367502faa1483627bf534472a6cae70d7964ba019aa2121c3929234.json b/backend/.sqlx/query-1d1098cc9367502faa1483627bf534472a6cae70d7964ba019aa2121c3929234.json deleted file mode 100644 index abbd8b2a6d..0000000000 --- a/backend/.sqlx/query-1d1098cc9367502faa1483627bf534472a6cae70d7964ba019aa2121c3929234.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n result AS \"result: sqlx::types::Json>\",\n language AS \"language: ScriptLang\",\n flow_status AS \"flow_status: sqlx::types::Json>\",\n success AS \"success!\"\n FROM completed_job\n WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "result: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 1, - "name": "language: ScriptLang", - "type_info": { - "Custom": { - "name": "script_lang", - "kind": { - "Enum": [ - "python3", - "deno", - "go", - "bash", - "postgresql", - "nativets", - "bun", - "mysql", - "bigquery", - "snowflake", - "graphql", - "powershell", - "mssql", - "php", - "bunnative", - "rust", - "ansible", - "csharp", - "oracledb" - ] - } - } - } - }, - { - "ordinal": 2, - "name": "flow_status: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 3, - "name": "success!", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true, - true, - true - ] - }, - "hash": "1d1098cc9367502faa1483627bf534472a6cae70d7964ba019aa2121c3929234" -} diff --git a/backend/.sqlx/query-1d819b829cd92995c39d29540df8cffbcc3334bada244a331a0bd8db06029d42.json b/backend/.sqlx/query-1d819b829cd92995c39d29540df8cffbcc3334bada244a331a0bd8db06029d42.json deleted file mode 100644 index 9f40c5293d..0000000000 --- a/backend/.sqlx/query-1d819b829cd92995c39d29540df8cffbcc3334bada244a331a0bd8db06029d42.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM v2_job_completed c\n USING v2_job j\n WHERE\n created_at <= now() - ($1::bigint::text || ' s')::interval\n AND completed_at + ($1::bigint::text || ' s')::interval <= now()\n AND c.id = j.id\n RETURNING c.id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - false - ] - }, - "hash": "1d819b829cd92995c39d29540df8cffbcc3334bada244a331a0bd8db06029d42" -} diff --git a/backend/.sqlx/query-1d842b4c940d788372ed377465e24faa490a4649f74c37bcd0b47ebffc81a2a9.json b/backend/.sqlx/query-1d842b4c940d788372ed377465e24faa490a4649f74c37bcd0b47ebffc81a2a9.json deleted file mode 100644 index 0d5b76374d..0000000000 --- a/backend/.sqlx/query-1d842b4c940d788372ed377465e24faa490a4649f74c37bcd0b47ebffc81a2a9.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = jsonb_set(\n jsonb_set(\n COALESCE(flow_status, '{}'::jsonb),\n array[$1],\n COALESCE(flow_status->$1, '{}'::jsonb)\n ),\n array[$1, 'duration_ms'],\n to_jsonb($2::bigint)\n )\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Int8", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "1d842b4c940d788372ed377465e24faa490a4649f74c37bcd0b47ebffc81a2a9" -} diff --git a/backend/.sqlx/query-1e188d8e427cab25dbe18aa900260e26e644a9d939e74a8317c4a09335f110fe.json b/backend/.sqlx/query-1e188d8e427cab25dbe18aa900260e26e644a9d939e74a8317c4a09335f110fe.json deleted file mode 100644 index 01fb19b1c2..0000000000 --- a/backend/.sqlx/query-1e188d8e427cab25dbe18aa900260e26e644a9d939e74a8317c4a09335f110fe.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4),\n ARRAY['modules', $1::TEXT, 'branchall', 'branch'], ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb),\n last_ping = NULL\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "int4", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int4", - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [ - null - ] - }, - "hash": "1e188d8e427cab25dbe18aa900260e26e644a9d939e74a8317c4a09335f110fe" -} diff --git a/backend/.sqlx/query-1e43e6040ac95b586d4d73999025f4a3e79c87fc1c5b43e787fd8e19c555b44b.json b/backend/.sqlx/query-1e43e6040ac95b586d4d73999025f4a3e79c87fc1c5b43e787fd8e19c555b44b.json deleted file mode 100644 index b64ace475a..0000000000 --- a/backend/.sqlx/query-1e43e6040ac95b586d4d73999025f4a3e79c87fc1c5b43e787fd8e19c555b44b.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET last_ping = null\n WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "1e43e6040ac95b586d4d73999025f4a3e79c87fc1c5b43e787fd8e19c555b44b" -} diff --git a/backend/.sqlx/query-1e659916501e668913e591eb7282d3881fa44468ed5330a501daa0d61c84cb71.json b/backend/.sqlx/query-1e659916501e668913e591eb7282d3881fa44468ed5330a501daa0d61c84cb71.json deleted file mode 100644 index 2cb0d351da..0000000000 --- a/backend/.sqlx/query-1e659916501e668913e591eb7282d3881fa44468ed5330a501daa0d61c84cb71.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE workspace_settings SET automatic_billing = false WHERE workspace_id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "1e659916501e668913e591eb7282d3881fa44468ed5330a501daa0d61c84cb71" -} diff --git a/backend/.sqlx/query-1f93b533fa6fee0db4340445da3fac8e6773bc1db1f88cd60fd3c1e8c9781eb0.json b/backend/.sqlx/query-1f93b533fa6fee0db4340445da3fac8e6773bc1db1f88cd60fd3c1e8c9781eb0.json deleted file mode 100644 index abb52bb3c0..0000000000 --- a/backend/.sqlx/query-1f93b533fa6fee0db4340445da3fac8e6773bc1db1f88cd60fd3c1e8c9781eb0.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET suspend = $1 WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int4", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "1f93b533fa6fee0db4340445da3fac8e6773bc1db1f88cd60fd3c1e8c9781eb0" -} diff --git a/backend/.sqlx/query-20d9a1b3a6631f97836e7b8d96cdec706ba1cc2d5d432e397633a1f79e67589a.json b/backend/.sqlx/query-20d9a1b3a6631f97836e7b8d96cdec706ba1cc2d5d432e397633a1f79e67589a.json deleted file mode 100644 index 7a1fe7377e..0000000000 --- a/backend/.sqlx/query-20d9a1b3a6631f97836e7b8d96cdec706ba1cc2d5d432e397633a1f79e67589a.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n result AS \"result: sqlx::types::Json>\",\n flow_status AS \"flow_status: sqlx::types::Json>\",\n language AS \"language: ScriptLang\",\n created_by AS \"created_by!\"\n FROM completed_job\n WHERE id = $1 AND workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "result: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 1, - "name": "flow_status: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 2, - "name": "language: ScriptLang", - "type_info": { - "Custom": { - "name": "script_lang", - "kind": { - "Enum": [ - "python3", - "deno", - "go", - "bash", - "postgresql", - "nativets", - "bun", - "mysql", - "bigquery", - "snowflake", - "graphql", - "powershell", - "mssql", - "php", - "bunnative", - "rust", - "ansible", - "csharp", - "oracledb" - ] - } - } - } - }, - { - "ordinal": 3, - "name": "created_by!", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "TextArray" - ] - }, - "nullable": [ - true, - true, - true, - true - ] - }, - "hash": "20d9a1b3a6631f97836e7b8d96cdec706ba1cc2d5d432e397633a1f79e67589a" -} diff --git a/backend/.sqlx/query-20e01ecb5d4aa4c532a8f906365c776994ae2c990bb7bb307c52296b9543fbb8.json b/backend/.sqlx/query-20e01ecb5d4aa4c532a8f906365c776994ae2c990bb7bb307c52296b9543fbb8.json new file mode 100644 index 0000000000..c9f7733011 --- /dev/null +++ b/backend/.sqlx/query-20e01ecb5d4aa4c532a8f906365c776994ae2c990bb7bb307c52296b9543fbb8.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE \n capture_config \n SET \n last_server_ping = NULL \n WHERE \n workspace_id = $1 AND \n path = $2 AND \n is_flow = $3 AND \n trigger_kind = 'sqs' AND \n server_id IS NULL\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "20e01ecb5d4aa4c532a8f906365c776994ae2c990bb7bb307c52296b9543fbb8" +} diff --git a/backend/.sqlx/query-21204693fa8608c78151f63fa76bb36bdece81385380a42ca06ca6be19694896.json b/backend/.sqlx/query-21204693fa8608c78151f63fa76bb36bdece81385380a42ca06ca6be19694896.json new file mode 100644 index 0000000000..5454fdf323 --- /dev/null +++ b/backend/.sqlx/query-21204693fa8608c78151f63fa76bb36bdece81385380a42ca06ca6be19694896.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_queue SET canceled_by = $1, canceled_reason = $2, scheduled_for = now(), suspend = 0 WHERE id = ANY($3) AND workspace_id = $4 AND (canceled_by IS NULL OR canceled_reason != $2) RETURNING id", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Text", + "UuidArray", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "21204693fa8608c78151f63fa76bb36bdece81385380a42ca06ca6be19694896" +} diff --git a/backend/.sqlx/query-215e0d320a304c8cb9ef12e7ea98a4eafb2456c123f9b6b96bb4ba2409166e5a.json b/backend/.sqlx/query-215e0d320a304c8cb9ef12e7ea98a4eafb2456c123f9b6b96bb4ba2409166e5a.json deleted file mode 100644 index 0889a223e6..0000000000 --- a/backend/.sqlx/query-215e0d320a304c8cb9ef12e7ea98a4eafb2456c123f9b6b96bb4ba2409166e5a.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET running = false, started_at = null WHERE id = $1 AND canceled = false", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "215e0d320a304c8cb9ef12e7ea98a4eafb2456c123f9b6b96bb4ba2409166e5a" -} diff --git a/backend/.sqlx/query-217a5291438d23597b2c7f05d2c481f406364d56a129089a268f6423c548bca6.json b/backend/.sqlx/query-217a5291438d23597b2c7f05d2c481f406364d56a129089a268f6423c548bca6.json deleted file mode 100644 index 9dac2a2451..0000000000 --- a/backend/.sqlx/query-217a5291438d23597b2c7f05d2c481f406364d56a129089a268f6423c548bca6.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT raw_code, raw_lock, raw_flow AS \"raw_flow: Json>\"\n FROM queue WHERE id = $1 AND workspace_id = $2 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "raw_code", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "raw_lock", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "raw_flow: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true, - true - ] - }, - "hash": "217a5291438d23597b2c7f05d2c481f406364d56a129089a268f6423c548bca6" -} diff --git a/backend/.sqlx/query-22dcd953d900fb0ddbe2099ccee27bcf833f56475bbc1aa3c8f6d79f146f41bf.json b/backend/.sqlx/query-22dcd953d900fb0ddbe2099ccee27bcf833f56475bbc1aa3c8f6d79f146f41bf.json new file mode 100644 index 0000000000..f94ca24f0f --- /dev/null +++ b/backend/.sqlx/query-22dcd953d900fb0ddbe2099ccee27bcf833f56475bbc1aa3c8f6d79f146f41bf.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n sqs_trigger\n SET \n last_server_ping = now(),\n error = $1\n WHERE\n workspace_id = $2\n AND path = $3\n AND server_id = $4 \n AND enabled IS TRUE\n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "22dcd953d900fb0ddbe2099ccee27bcf833f56475bbc1aa3c8f6d79f146f41bf" +} diff --git a/backend/.sqlx/query-230d58732a08164268ca10d248a93cced646632a76864b693ed2325d85b36c45.json b/backend/.sqlx/query-230d58732a08164268ca10d248a93cced646632a76864b693ed2325d85b36c45.json deleted file mode 100644 index 37d4e7d370..0000000000 --- a/backend/.sqlx/query-230d58732a08164268ca10d248a93cced646632a76864b693ed2325d85b36c45.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT canceled FROM queue WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "canceled", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false - ] - }, - "hash": "230d58732a08164268ca10d248a93cced646632a76864b693ed2325d85b36c45" -} diff --git a/backend/.sqlx/query-23419adcd74c326d716527293eff518b42f4cdb33e034441015494bd26c172d2.json b/backend/.sqlx/query-23419adcd74c326d716527293eff518b42f4cdb33e034441015494bd26c172d2.json new file mode 100644 index 0000000000..5b810738b3 --- /dev/null +++ b/backend/.sqlx/query-23419adcd74c326d716527293eff518b42f4cdb33e034441015494bd26c172d2.json @@ -0,0 +1,56 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n trigger_config AS \"trigger_config: _\", \n owner, \n email\n FROM \n capture_config\n WHERE \n workspace_id = $1\n AND path = $2\n AND is_flow = $3\n AND trigger_kind = $4\n AND last_client_ping > NOW() - INTERVAL '10 seconds'\n AND (\n $5::bool IS FALSE\n OR (\n trigger_config IS NOT NULL\n AND trigger_config ->> 'delivery_type' = 'push'\n )\n )\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "trigger_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 1, + "name": "owner", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "email", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Bool", + { + "Custom": { + "name": "trigger_kind", + "kind": { + "Enum": [ + "webhook", + "http", + "websocket", + "kafka", + "email", + "nats", + "postgres", + "sqs", + "mqtt", + "gcp" + ] + } + } + }, + "Bool" + ] + }, + "nullable": [ + true, + false, + false + ] + }, + "hash": "23419adcd74c326d716527293eff518b42f4cdb33e034441015494bd26c172d2" +} diff --git a/backend/.sqlx/query-240cf4ba63ec39a7ccfa8360824259d2fdc6681bbbd44e3ccde0a3893f6cf9a0.json b/backend/.sqlx/query-240cf4ba63ec39a7ccfa8360824259d2fdc6681bbbd44e3ccde0a3893f6cf9a0.json deleted file mode 100644 index 134cb58d17..0000000000 --- a/backend/.sqlx/query-240cf4ba63ec39a7ccfa8360824259d2fdc6681bbbd44e3ccde0a3893f6cf9a0.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT \n MAX (created_at) AS last_deploy, \n COUNT (*) AS deploys_count \n , 'python' AS language\n FROM metrics \n WHERE id = 'no_uv_usage_py'\n\n UNION ALL\n \n SELECT \n MAX (created_at) AS last_deploy, \n COUNT (*) AS deploys_count \n , 'ansible' AS language\n FROM metrics \n WHERE id = 'no_uv_usage_ansible'\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "last_deploy", - "type_info": "Timestamptz" - }, - { - "ordinal": 1, - "name": "deploys_count", - "type_info": "Int8" - }, - { - "ordinal": 2, - "name": "language", - "type_info": "Text" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - null, - null, - null - ] - }, - "hash": "240cf4ba63ec39a7ccfa8360824259d2fdc6681bbbd44e3ccde0a3893f6cf9a0" -} diff --git a/backend/.sqlx/query-24178c21aadc1aed90f31e9362c6505a642c8f04b883c278b07e7ef5956ce121.json b/backend/.sqlx/query-24178c21aadc1aed90f31e9362c6505a642c8f04b883c278b07e7ef5956ce121.json deleted file mode 100644 index d8cca020fe..0000000000 --- a/backend/.sqlx/query-24178c21aadc1aed90f31e9362c6505a642c8f04b883c278b07e7ef5956ce121.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT \n \n EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) AS \"websocket_used!\", \n \n EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) AS \"http_routes_used!\",\n EXISTS(SELECT 1 FROM kafka_trigger WHERE workspace_id = $1) as \"kafka_used!\",\n EXISTS(SELECT 1 FROM nats_trigger WHERE workspace_id = $1) as \"nats_used!\",\n EXISTS(SELECT 1 FROM postgres_trigger WHERE workspace_id = $1) AS \"postgres_used!\"\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "websocket_used!", - "type_info": "Bool" - }, - { - "ordinal": 1, - "name": "http_routes_used!", - "type_info": "Bool" - }, - { - "ordinal": 2, - "name": "kafka_used!", - "type_info": "Bool" - }, - { - "ordinal": 3, - "name": "nats_used!", - "type_info": "Bool" - }, - { - "ordinal": 4, - "name": "postgres_used!", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null, - null, - null, - null, - null - ] - }, - "hash": "24178c21aadc1aed90f31e9362c6505a642c8f04b883c278b07e7ef5956ce121" -} diff --git a/backend/.sqlx/query-25d05a1e10d1aaa3f7c3c3bea5f6b2fe3f690da7bc8dfd36b47ec619c4e31995.json b/backend/.sqlx/query-25d05a1e10d1aaa3f7c3c3bea5f6b2fe3f690da7bc8dfd36b47ec619c4e31995.json deleted file mode 100644 index eb652ef49d..0000000000 --- a/backend/.sqlx/query-25d05a1e10d1aaa3f7c3c3bea5f6b2fe3f690da7bc8dfd36b47ec619c4e31995.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n running AS \"running!\",\n substr(concat(coalesce(v2_as_queue.logs, ''), job_logs.logs), greatest($1 - job_logs.log_offset, 0)) AS logs,\n mem_peak,\n CASE WHEN is_flow_step is true then NULL else flow_status END AS \"flow_status: sqlx::types::Json>\",\n job_logs.log_offset + char_length(job_logs.logs) + 1 AS log_offset,\n created_by AS \"created_by!\"\n FROM v2_as_queue\n LEFT JOIN job_logs ON job_logs.job_id = v2_as_queue.id \n WHERE v2_as_queue.workspace_id = $2 AND v2_as_queue.id = $3", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "running!", - "type_info": "Bool" - }, - { - "ordinal": 1, - "name": "logs", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "mem_peak", - "type_info": "Int4" - }, - { - "ordinal": 3, - "name": "flow_status: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 4, - "name": "log_offset", - "type_info": "Int4" - }, - { - "ordinal": 5, - "name": "created_by!", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Int4", - "Text", - "Uuid" - ] - }, - "nullable": [ - true, - null, - true, - null, - null, - true - ] - }, - "hash": "25d05a1e10d1aaa3f7c3c3bea5f6b2fe3f690da7bc8dfd36b47ec619c4e31995" -} diff --git a/backend/.sqlx/query-26106be4d94c159cc8d9eb37a3b94927c5d3d43c18cdb5e98e979e761ed6ed0e.json b/backend/.sqlx/query-26106be4d94c159cc8d9eb37a3b94927c5d3d43c18cdb5e98e979e761ed6ed0e.json deleted file mode 100644 index fa5e4bff4c..0000000000 --- a/backend/.sqlx/query-26106be4d94c159cc8d9eb37a3b94927c5d3d43c18cdb5e98e979e761ed6ed0e.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['step'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "26106be4d94c159cc8d9eb37a3b94927c5d3d43c18cdb5e98e979e761ed6ed0e" -} diff --git a/backend/.sqlx/query-262c7b21e77a8d2943fefb9cabe1e60c7c3b4e3ce7ed6b2b3eb78dd99b7d8fcf.json b/backend/.sqlx/query-262c7b21e77a8d2943fefb9cabe1e60c7c3b4e3ce7ed6b2b3eb78dd99b7d8fcf.json deleted file mode 100644 index 0025d31dc8..0000000000 --- a/backend/.sqlx/query-262c7b21e77a8d2943fefb9cabe1e60c7c3b4e3ce7ed6b2b3eb78dd99b7d8fcf.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET last_ping = now()\n WHERE id = $1 AND last_ping < now()", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "262c7b21e77a8d2943fefb9cabe1e60c7c3b4e3ce7ed6b2b3eb78dd99b7d8fcf" -} diff --git a/backend/.sqlx/query-26761fbd7953416eb391de47b1694e0f4ab2bb96a6d838f1b1fdce4b58a8f5d4.json b/backend/.sqlx/query-26761fbd7953416eb391de47b1694e0f4ab2bb96a6d838f1b1fdce4b58a8f5d4.json new file mode 100644 index 0000000000..b14dab2503 --- /dev/null +++ b/backend/.sqlx/query-26761fbd7953416eb391de47b1694e0f4ab2bb96a6d838f1b1fdce4b58a8f5d4.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT jsonb_build_object(\n 'kind', jb.kind,\n 'script_path', jb.runnable_path,\n 'latest_schema', COALESCE(\n (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.workspace_id = $1 AND s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),\n (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.workspace_id = $1 AND flow.path = jb.runnable_path AND jb.kind = 'flow')\n ),\n 'schemas', ARRAY(\n SELECT jsonb_build_object(\n 'script_hash', LPAD(TO_HEX(COALESCE(s.hash, f.id)), 16, '0'),\n 'job_ids', ARRAY_AGG(DISTINCT j.id),\n 'schema', (ARRAY_AGG(COALESCE(s.schema, f.schema)))[1]\n ) FROM v2_job j\n LEFT JOIN script s ON s.hash = j.runnable_id AND j.kind = 'script'\n LEFT JOIN flow_version f ON f.id = j.runnable_id AND j.kind = 'flow'\n WHERE j.id = ANY(ARRAY_AGG(jb.id))\n GROUP BY COALESCE(s.hash, f.id)\n )\n ) FROM v2_job jb\n WHERE (jb.kind = 'flow' OR jb.kind = 'script')\n AND jb.workspace_id = $1 AND jb.id = ANY($2)\n GROUP BY jb.kind, jb.runnable_path", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "jsonb_build_object", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "UuidArray" + ] + }, + "nullable": [ + null + ] + }, + "hash": "26761fbd7953416eb391de47b1694e0f4ab2bb96a6d838f1b1fdce4b58a8f5d4" +} diff --git a/backend/.sqlx/query-280a361076d1c6317610765960f543252891c53351bdc98da66cc30ffc895866.json b/backend/.sqlx/query-280a361076d1c6317610765960f543252891c53351bdc98da66cc30ffc895866.json deleted file mode 100644 index e9705c23a6..0000000000 --- a/backend/.sqlx/query-280a361076d1c6317610765960f543252891c53351bdc98da66cc30ffc895866.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT script_path FROM v2_as_completed_job WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "script_path", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - true - ] - }, - "hash": "280a361076d1c6317610765960f543252891c53351bdc98da66cc30ffc895866" -} diff --git a/backend/.sqlx/query-288e99211bbd45a337fc9b79c43c5139ee535e23c8b3362c52eef49998349f15.json b/backend/.sqlx/query-288e99211bbd45a337fc9b79c43c5139ee535e23c8b3362c52eef49998349f15.json deleted file mode 100644 index eb2b1979d9..0000000000 --- a/backend/.sqlx/query-288e99211bbd45a337fc9b79c43c5139ee535e23c8b3362c52eef49998349f15.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM job_logs WHERE job_id = ANY($1) RETURNING log_file_index", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "log_file_index", - "type_info": "TextArray" - } - ], - "parameters": { - "Left": [ - "UuidArray" - ] - }, - "nullable": [ - true - ] - }, - "hash": "288e99211bbd45a337fc9b79c43c5139ee535e23c8b3362c52eef49998349f15" -} diff --git a/backend/.sqlx/query-28a878c59b6d52f42d315eabb34c96133b69542b01232295c26cc9e093c372f9.json b/backend/.sqlx/query-28a878c59b6d52f42d315eabb34c96133b69542b01232295c26cc9e093c372f9.json deleted file mode 100644 index fcdf2ac38d..0000000000 --- a/backend/.sqlx/query-28a878c59b6d52f42d315eabb34c96133b69542b01232295c26cc9e093c372f9.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT coalesce(COUNT(*) FILTER(WHERE suspend = 0 AND running = false), 0) as \"database_length!\", coalesce(COUNT(*) FILTER(WHERE suspend > 0), 0) as \"suspended!\" FROM queue WHERE (workspace_id = $1 OR $2) AND scheduled_for <= now()", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "database_length!", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "suspended!", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text", - "Bool" - ] - }, - "nullable": [ - null, - null - ] - }, - "hash": "28a878c59b6d52f42d315eabb34c96133b69542b01232295c26cc9e093c372f9" -} diff --git a/backend/.sqlx/query-28b42ab9c3ce0c2f05cf385e81f3b72fa7c4b3c458d52a5891a61f9c53a49c6d.json b/backend/.sqlx/query-28b42ab9c3ce0c2f05cf385e81f3b72fa7c4b3c458d52a5891a61f9c53a49c6d.json deleted file mode 100644 index 817b993d8f..0000000000 --- a/backend/.sqlx/query-28b42ab9c3ce0c2f05cf385e81f3b72fa7c4b3c458d52a5891a61f9c53a49c6d.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', $1::TEXT, 'branchall', 'branch'],\n ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "int4", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int4", - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "28b42ab9c3ce0c2f05cf385e81f3b72fa7c4b3c458d52a5891a61f9c53a49c6d" -} diff --git a/backend/.sqlx/query-293054a4d6a2b00228a5029a81c026e8891576c7e87cdab41d7fcc3afc32d583.json b/backend/.sqlx/query-293054a4d6a2b00228a5029a81c026e8891576c7e87cdab41d7fcc3afc32d583.json deleted file mode 100644 index 291b3f3601..0000000000 --- a/backend/.sqlx/query-293054a4d6a2b00228a5029a81c026e8891576c7e87cdab41d7fcc3afc32d583.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT COUNT(*) FROM queue LEFT JOIN concurrency_key ON concurrency_key.job_id = queue.id\n WHERE key = $1 AND running = false AND canceled = false AND scheduled_for >= $2 AND scheduled_for < $3", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text", - "Timestamptz", - "Timestamptz" - ] - }, - "nullable": [ - null - ] - }, - "hash": "293054a4d6a2b00228a5029a81c026e8891576c7e87cdab41d7fcc3afc32d583" -} diff --git a/backend/.sqlx/query-29624682d687790dd199c4af759132d79fdb2982de111cb5fd43e3d9ecd0f15e.json b/backend/.sqlx/query-29624682d687790dd199c4af759132d79fdb2982de111cb5fd43e3d9ecd0f15e.json deleted file mode 100644 index 7623243f07..0000000000 --- a/backend/.sqlx/query-29624682d687790dd199c4af759132d79fdb2982de111cb5fd43e3d9ecd0f15e.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO v2_job (id, workspace_id, raw_code, raw_lock, raw_flow, tag, parent_job,\n created_by, permissioned_as, runnable_id, runnable_path, args, kind, trigger,\n script_lang, same_worker, pre_run_error, permissioned_as_email, visible_to_owner,\n flow_innermost_root_job, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id,\n cache_ttl, priority, trigger_kind, script_entrypoint_override, preprocessed)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18,\n $19, $20, $21, $22, $23, $24, $25, $26,\n CASE WHEN $14::VARCHAR IS NOT NULL THEN 'schedule'::job_trigger_kind END,\n ($12::JSONB)->>'_ENTRYPOINT_OVERRIDE', $27)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Varchar", - "Text", - "Text", - "Jsonb", - "Varchar", - "Uuid", - "Varchar", - "Varchar", - "Int8", - "Varchar", - "Jsonb", - { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - }, - "Varchar", - { - "Custom": { - "name": "script_lang", - "kind": { - "Enum": [ - "python3", - "deno", - "go", - "bash", - "postgresql", - "nativets", - "bun", - "mysql", - "bigquery", - "snowflake", - "graphql", - "powershell", - "mssql", - "php", - "bunnative", - "rust", - "ansible", - "csharp", - "oracledb" - ] - } - } - }, - "Bool", - "Text", - "Varchar", - "Bool", - "Uuid", - "Int4", - "Int4", - "Int4", - "Varchar", - "Int4", - "Int2", - "Bool" - ] - }, - "nullable": [] - }, - "hash": "29624682d687790dd199c4af759132d79fdb2982de111cb5fd43e3d9ecd0f15e" -} diff --git a/backend/.sqlx/query-299e16725162888c01712f371785199960264b54c1ddf928c0c654ab15176f63.json b/backend/.sqlx/query-299e16725162888c01712f371785199960264b54c1ddf928c0c654ab15176f63.json new file mode 100644 index 0000000000..d64d8f404b --- /dev/null +++ b/backend/.sqlx/query-299e16725162888c01712f371785199960264b54c1ddf928c0c654ab15176f63.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n server_id = $1,\n last_server_ping = now(), \n error = 'Connecting...' \n WHERE \n last_client_ping > NOW() - INTERVAL '10 seconds' AND \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'gcp' AND \n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') \n RETURNING true\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [ + null + ] + }, + "hash": "299e16725162888c01712f371785199960264b54c1ddf928c0c654ab15176f63" +} diff --git a/backend/.sqlx/query-29f096ec62c4abb1435a5667e2b30e9c1724e419cdc23ef1b300e84c02a20427.json b/backend/.sqlx/query-29f096ec62c4abb1435a5667e2b30e9c1724e419cdc23ef1b300e84c02a20427.json new file mode 100644 index 0000000000..b7075f9df1 --- /dev/null +++ b/backend/.sqlx/query-29f096ec62c4abb1435a5667e2b30e9c1724e419cdc23ef1b300e84c02a20427.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'postgres'\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "29f096ec62c4abb1435a5667e2b30e9c1724e419cdc23ef1b300e84c02a20427" +} diff --git a/backend/.sqlx/query-29fbc3a8c35845a997cd548ed417b9cc3c82d815d99b3d435adcfbb5a9246124.json b/backend/.sqlx/query-29fbc3a8c35845a997cd548ed417b9cc3c82d815d99b3d435adcfbb5a9246124.json deleted file mode 100644 index 72f3f1f469..0000000000 --- a/backend/.sqlx/query-29fbc3a8c35845a997cd548ed417b9cc3c82d815d99b3d435adcfbb5a9246124.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT set_config('session.folders_read', $1, true)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "set_config", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "29fbc3a8c35845a997cd548ed417b9cc3c82d815d99b3d435adcfbb5a9246124" -} diff --git a/backend/.sqlx/query-2a0b59e2770b27a1f2a8baddc67dba29216a1aad733171c25cc4aae5b3c84d54.json b/backend/.sqlx/query-2a0b59e2770b27a1f2a8baddc67dba29216a1aad733171c25cc4aae5b3c84d54.json deleted file mode 100644 index 8ff5e29db1..0000000000 --- a/backend/.sqlx/query-2a0b59e2770b27a1f2a8baddc67dba29216a1aad733171c25cc4aae5b3c84d54.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET\n flow_status = JSONB_SET(flow_status, ARRAY['modules', flow_status->>'step'::text], $1),\n suspend = $2,\n suspend_until = now() + $3\n WHERE id = $4", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Int4", - "Interval", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "2a0b59e2770b27a1f2a8baddc67dba29216a1aad733171c25cc4aae5b3c84d54" -} diff --git a/backend/.sqlx/query-ea2b88dc050aec038641ea37399d68d4385c5bdc721d1351609f27ca45e4dbdc.json b/backend/.sqlx/query-2a49e5b5486b650d96f3e9038cba8a5f2e75d3b12ee4718452e82c7318b1bcf4.json similarity index 63% rename from backend/.sqlx/query-ea2b88dc050aec038641ea37399d68d4385c5bdc721d1351609f27ca45e4dbdc.json rename to backend/.sqlx/query-2a49e5b5486b650d96f3e9038cba8a5f2e75d3b12ee4718452e82c7318b1bcf4.json index 1d30071300..77f61ccc47 100644 --- a/backend/.sqlx/query-ea2b88dc050aec038641ea37399d68d4385c5bdc721d1351609f27ca45e4dbdc.json +++ b/backend/.sqlx/query-2a49e5b5486b650d96f3e9038cba8a5f2e75d3b12ee4718452e82c7318b1bcf4.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 AND\n created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2))", + "query": "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1)", "describe": { "columns": [ { @@ -19,5 +19,5 @@ null ] }, - "hash": "ea2b88dc050aec038641ea37399d68d4385c5bdc721d1351609f27ca45e4dbdc" + "hash": "2a49e5b5486b650d96f3e9038cba8a5f2e75d3b12ee4718452e82c7318b1bcf4" } diff --git a/backend/.sqlx/query-d2def87d7f7901eebc65082f7df5e0a33e5702b25c3db3affa06155e90480e42.json b/backend/.sqlx/query-2a510a8bec98055796f987d86c344ca116895d71de09db338ec09e425dcebe5e.json similarity index 63% rename from backend/.sqlx/query-d2def87d7f7901eebc65082f7df5e0a33e5702b25c3db3affa06155e90480e42.json rename to backend/.sqlx/query-2a510a8bec98055796f987d86c344ca116895d71de09db338ec09e425dcebe5e.json index 58f03dbec2..23337708f8 100644 --- a/backend/.sqlx/query-d2def87d7f7901eebc65082f7df5e0a33e5702b25c3db3affa06155e90480e42.json +++ b/backend/.sqlx/query-2a510a8bec98055796f987d86c344ca116895d71de09db338ec09e425dcebe5e.json @@ -1,50 +1,35 @@ { "db_name": "PostgreSQL", - "query": "SELECT * FROM job_perms WHERE job_id = $1 AND workspace_id = $2", + "query": "SELECT email, username, is_admin, is_operator, groups, folders FROM job_perms WHERE job_id = $1 AND workspace_id = $2", "describe": { "columns": [ { "ordinal": 0, - "name": "job_id", - "type_info": "Uuid" - }, - { - "ordinal": 1, "name": "email", "type_info": "Varchar" }, { - "ordinal": 2, + "ordinal": 1, "name": "username", "type_info": "Varchar" }, { - "ordinal": 3, + "ordinal": 2, "name": "is_admin", "type_info": "Bool" }, { - "ordinal": 4, + "ordinal": 3, "name": "is_operator", "type_info": "Bool" }, { - "ordinal": 5, - "name": "created_at", - "type_info": "Timestamp" - }, - { - "ordinal": 6, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 7, + "ordinal": 4, "name": "groups", "type_info": "TextArray" }, { - "ordinal": 8, + "ordinal": 5, "name": "folders", "type_info": "JsonbArray" } @@ -61,11 +46,8 @@ false, false, false, - false, - false, - false, false ] }, - "hash": "d2def87d7f7901eebc65082f7df5e0a33e5702b25c3db3affa06155e90480e42" + "hash": "2a510a8bec98055796f987d86c344ca116895d71de09db338ec09e425dcebe5e" } diff --git a/backend/.sqlx/query-04effcc6050250a02661323c880d493982dd1bfb63ca7373e035a98c268428e2.json b/backend/.sqlx/query-2ae44acae7ac80e191b37071baa26e57488489177fe596940d3698d843a93666.json similarity index 66% rename from backend/.sqlx/query-04effcc6050250a02661323c880d493982dd1bfb63ca7373e035a98c268428e2.json rename to backend/.sqlx/query-2ae44acae7ac80e191b37071baa26e57488489177fe596940d3698d843a93666.json index caf2c0b4c8..405902863c 100644 --- a/backend/.sqlx/query-04effcc6050250a02661323c880d493982dd1bfb63ca7373e035a98c268428e2.json +++ b/backend/.sqlx/query-2ae44acae7ac80e191b37071baa26e57488489177fe596940d3698d843a93666.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT script_path FROM queue WHERE id = $1", + "query": "SELECT script_path FROM v2_as_queue WHERE id = $1", "describe": { "columns": [ { @@ -18,5 +18,5 @@ true ] }, - "hash": "04effcc6050250a02661323c880d493982dd1bfb63ca7373e035a98c268428e2" + "hash": "2ae44acae7ac80e191b37071baa26e57488489177fe596940d3698d843a93666" } diff --git a/backend/.sqlx/query-ba8bde5018fdf7b12f85cd3a6557c4accfc78bf160c1277f35d9d8ddcd056963.json b/backend/.sqlx/query-2b9607ed838c8c62eb0f2856420389f7be648f52edbb875ff52c96219ed3ba84.json similarity index 53% rename from backend/.sqlx/query-ba8bde5018fdf7b12f85cd3a6557c4accfc78bf160c1277f35d9d8ddcd056963.json rename to backend/.sqlx/query-2b9607ed838c8c62eb0f2856420389f7be648f52edbb875ff52c96219ed3ba84.json index e6564a96fa..d04e105953 100644 --- a/backend/.sqlx/query-ba8bde5018fdf7b12f85cd3a6557c4accfc78bf160c1277f35d9d8ddcd056963.json +++ b/backend/.sqlx/query-2b9607ed838c8c62eb0f2856420389f7be648f52edbb875ff52c96219ed3ba84.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE flow SET path = $1, summary = $2, description = $3,dependency_job = NULL, draft_only = NULL, tag = $4, dedicated_worker = $5, visible_to_runner_only = $6, on_behalf_of_email = $7, value = $8, schema = $9::text::json, edited_by = $10, edited_at = now()\n WHERE path = $11 AND workspace_id = $12", + "query": "UPDATE flow SET path = $1, summary = $2, description = $3,dependency_job = NULL, lock_error_logs = '', draft_only = NULL, tag = $4, dedicated_worker = $5, visible_to_runner_only = $6, on_behalf_of_email = $7, value = $8, schema = $9::text::json, edited_by = $10, edited_at = now()\n WHERE path = $11 AND workspace_id = $12", "describe": { "columns": [], "parameters": { @@ -21,5 +21,5 @@ }, "nullable": [] }, - "hash": "ba8bde5018fdf7b12f85cd3a6557c4accfc78bf160c1277f35d9d8ddcd056963" + "hash": "2b9607ed838c8c62eb0f2856420389f7be648f52edbb875ff52c96219ed3ba84" } diff --git a/backend/.sqlx/query-2bfb918104568288bb57e64d1cf914c14cd493f96005017c299a805c48aef092.json b/backend/.sqlx/query-2bfb918104568288bb57e64d1cf914c14cd493f96005017c299a805c48aef092.json deleted file mode 100644 index 42d0fefc9f..0000000000 --- a/backend/.sqlx/query-2bfb918104568288bb57e64d1cf914c14cd493f96005017c299a805c48aef092.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM completed_job WHERE id = $1 AND workspace_id = $2 UNION ALL SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM input WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "args", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Bool" - ] - }, - "nullable": [ - null - ] - }, - "hash": "2bfb918104568288bb57e64d1cf914c14cd493f96005017c299a805c48aef092" -} diff --git a/backend/.sqlx/query-2d4d4564108376ab310cb4a50fa2fa84fefbb3df8bb1bc9996c40a86d464b8a1.json b/backend/.sqlx/query-2d4d4564108376ab310cb4a50fa2fa84fefbb3df8bb1bc9996c40a86d464b8a1.json deleted file mode 100644 index 8c26257ef8..0000000000 --- a/backend/.sqlx/query-2d4d4564108376ab310cb4a50fa2fa84fefbb3df8bb1bc9996c40a86d464b8a1.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE workspace_settings SET automatic_billing = TRUE WHERE workspace_id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "2d4d4564108376ab310cb4a50fa2fa84fefbb3df8bb1bc9996c40a86d464b8a1" -} diff --git a/backend/.sqlx/query-2dcff8b16da75740c362015b2293f578a21813f038d75184a5034c37d4daf36e.json b/backend/.sqlx/query-2dcff8b16da75740c362015b2293f578a21813f038d75184a5034c37d4daf36e.json new file mode 100644 index 0000000000..5622b3691e --- /dev/null +++ b/backend/.sqlx/query-2dcff8b16da75740c362015b2293f578a21813f038d75184a5034c37d4daf36e.json @@ -0,0 +1,142 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n subscription_mode AS \"subscription_mode: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n gcp_trigger\n WHERE\n delivery_type != 'push'::DELIVERY_MODE AND\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "gcp_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "subscription_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "topic_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "delivery_type: _", + "type_info": { + "Custom": { + "name": "delivery_mode", + "kind": { + "Enum": [ + "push", + "pull" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "delivery_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 6, + "name": "subscription_mode: _", + "type_info": { + "Custom": { + "name": "gcp_subscription_mode", + "kind": { + "Enum": [ + "create_update", + "existing" + ] + } + } + } + }, + { + "ordinal": 7, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "enabled", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false + ] + }, + "hash": "2dcff8b16da75740c362015b2293f578a21813f038d75184a5034c37d4daf36e" +} diff --git a/backend/.sqlx/query-2e6935811a6d818bc523f076674f794f8be6c6bad3d06e74586e8ab668d91861.json b/backend/.sqlx/query-2e6935811a6d818bc523f076674f794f8be6c6bad3d06e74586e8ab668d91861.json new file mode 100644 index 0000000000..2ebfb49b8e --- /dev/null +++ b/backend/.sqlx/query-2e6935811a6d818bc523f076674f794f8be6c6bad3d06e74586e8ab668d91861.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO v2_job_status (id, workflow_as_code_status)\n VALUES ($1, JSONB_SET('{}'::JSONB, array[$2], $3))\n ON CONFLICT (id) DO UPDATE SET\n workflow_as_code_status = JSONB_SET(\n COALESCE(v2_job_status.workflow_as_code_status, '{}'::JSONB), \n array[$2],\n $3\n )", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "2e6935811a6d818bc523f076674f794f8be6c6bad3d06e74586e8ab668d91861" +} diff --git a/backend/.sqlx/query-2ebb0463b790ddf7ba0ee22d8c9afc88eb57c4110a202775003fb48b2f4e317f.json b/backend/.sqlx/query-2ebb0463b790ddf7ba0ee22d8c9afc88eb57c4110a202775003fb48b2f4e317f.json new file mode 100644 index 0000000000..49e6e6c6ae --- /dev/null +++ b/backend/.sqlx/query-2ebb0463b790ddf7ba0ee22d8c9afc88eb57c4110a202775003fb48b2f4e317f.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n workflow_as_code_status = jsonb_set(\n jsonb_set(\n COALESCE(workflow_as_code_status, '{}'::jsonb),\n array[$1],\n COALESCE(workflow_as_code_status->$1, '{}'::jsonb)\n ),\n array[$1, 'started_at'],\n to_jsonb(now()::text)\n )\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "2ebb0463b790ddf7ba0ee22d8c9afc88eb57c4110a202775003fb48b2f4e317f" +} diff --git a/backend/.sqlx/query-2fa27b0a71740e4e168879cf9a58ed593004c22bb5d11170b3196e290dd1d966.json b/backend/.sqlx/query-2fa27b0a71740e4e168879cf9a58ed593004c22bb5d11170b3196e290dd1d966.json deleted file mode 100644 index 704778d04a..0000000000 --- a/backend/.sqlx/query-2fa27b0a71740e4e168879cf9a58ed593004c22bb5d11170b3196e290dd1d966.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n WITH assigned_teams AS (\n SELECT teams_team_id\n FROM workspace_settings\n ),\n all_teams AS (\n SELECT jsonb_array_elements(value::jsonb) AS team\n FROM global_settings\n WHERE name = 'teams'\n )\n SELECT team->>'team_name' AS team_name, team->>'team_internal_id' AS team_id\n FROM all_teams\n WHERE NOT EXISTS (\n SELECT 1\n FROM assigned_teams\n WHERE assigned_teams.teams_team_id = team->>'team_internal_id'\n )\n AND team->>'team_name' IS NOT NULL\n AND team->>'team_id' IS NOT NULL\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "team_name", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "team_id", - "type_info": "Text" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - null, - null - ] - }, - "hash": "2fa27b0a71740e4e168879cf9a58ed593004c22bb5d11170b3196e290dd1d966" -} diff --git a/backend/.sqlx/query-f7b1445ec1f0d86efb6f8e0939430e7294bcac06bb7930dcf4d46427571662cb.json b/backend/.sqlx/query-2faa27519624249f16cf89814ab5efe8f8daf928c1194cecacfa8223165fb9f2.json similarity index 51% rename from backend/.sqlx/query-f7b1445ec1f0d86efb6f8e0939430e7294bcac06bb7930dcf4d46427571662cb.json rename to backend/.sqlx/query-2faa27519624249f16cf89814ab5efe8f8daf928c1194cecacfa8223165fb9f2.json index 5948eedef5..dbe3786571 100644 --- a/backend/.sqlx/query-f7b1445ec1f0d86efb6f8e0939430e7294bcac06bb7930dcf4d46427571662cb.json +++ b/backend/.sqlx/query-2faa27519624249f16cf89814ab5efe8f8daf928c1194cecacfa8223165fb9f2.json @@ -1,20 +1,15 @@ { "db_name": "PostgreSQL", - "query": "UPDATE queue SET mem_peak = $1, last_ping = now()\n WHERE id = $2\n RETURNING canceled AS \"canceled!\", canceled_by, canceled_reason", + "query": "UPDATE v2_job_runtime r SET\n memory_peak = $1,\n ping = now()\n FROM v2_job_queue q\n WHERE r.id = $2 AND q.id = r.id\n RETURNING canceled_by, canceled_reason", "describe": { "columns": [ { "ordinal": 0, - "name": "canceled!", - "type_info": "Bool" - }, - { - "ordinal": 1, "name": "canceled_by", "type_info": "Varchar" }, { - "ordinal": 2, + "ordinal": 1, "name": "canceled_reason", "type_info": "Text" } @@ -26,10 +21,9 @@ ] }, "nullable": [ - true, true, true ] }, - "hash": "f7b1445ec1f0d86efb6f8e0939430e7294bcac06bb7930dcf4d46427571662cb" + "hash": "2faa27519624249f16cf89814ab5efe8f8daf928c1194cecacfa8223165fb9f2" } diff --git a/backend/.sqlx/query-30483ae46f6d0452126eb2cd07fc4d960961cc6ee61cf065113b7a48f97caecc.json b/backend/.sqlx/query-30483ae46f6d0452126eb2cd07fc4d960961cc6ee61cf065113b7a48f97caecc.json new file mode 100644 index 0000000000..959b66239f --- /dev/null +++ b/backend/.sqlx/query-30483ae46f6d0452126eb2cd07fc4d960961cc6ee61cf065113b7a48f97caecc.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT value as \"value: sqlx::types::Json>\" FROM resource WHERE path = $1 AND workspace_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "value: sqlx::types::Json>", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + true + ] + }, + "hash": "30483ae46f6d0452126eb2cd07fc4d960961cc6ee61cf065113b7a48f97caecc" +} diff --git a/backend/.sqlx/query-31df83e9eb6078e93ec5fe4168306caccb849db9e0f71d86da655b01c6a3e8d0.json b/backend/.sqlx/query-31df83e9eb6078e93ec5fe4168306caccb849db9e0f71d86da655b01c6a3e8d0.json deleted file mode 100644 index b96c05d674..0000000000 --- a/backend/.sqlx/query-31df83e9eb6078e93ec5fe4168306caccb849db9e0f71d86da655b01c6a3e8d0.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO v2_job_queue\n (workspace_id, id, running, scheduled_for, started_at, tag, priority)\n VALUES ($1, $2, $3, COALESCE($4, now()), CASE WHEN $3 THEN now() END, $5, $6) RETURNING id AS \"id!\"", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Uuid", - "Bool", - "Timestamptz", - "Varchar", - "Int2" - ] - }, - "nullable": [ - false - ] - }, - "hash": "31df83e9eb6078e93ec5fe4168306caccb849db9e0f71d86da655b01c6a3e8d0" -} diff --git a/backend/.sqlx/query-31fe5d2965f7b25dea785f8be529a9b2c4c83c910fd7e2a08f4d95ae195ab3ed.json b/backend/.sqlx/query-31fe5d2965f7b25dea785f8be529a9b2c4c83c910fd7e2a08f4d95ae195ab3ed.json index 85c0412f3b..7f79e83137 100644 --- a/backend/.sqlx/query-31fe5d2965f7b25dea785f8be529a9b2c4c83c910fd7e2a08f4d95ae195ab3ed.json +++ b/backend/.sqlx/query-31fe5d2965f7b25dea785f8be529a9b2c4c83c910fd7e2a08f4d95ae195ab3ed.json @@ -12,7 +12,7 @@ "parameters": { "Left": [ "Varchar", - "Json", + "Jsonb", "Text" ] }, diff --git a/backend/.sqlx/query-32fdc66931dcf34f6ef5cdf3fd335d9f990eaa3dbb396290477159012e86af14.json b/backend/.sqlx/query-32fdc66931dcf34f6ef5cdf3fd335d9f990eaa3dbb396290477159012e86af14.json deleted file mode 100644 index 4767ba9a23..0000000000 --- a/backend/.sqlx/query-32fdc66931dcf34f6ef5cdf3fd335d9f990eaa3dbb396290477159012e86af14.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id AS \"id!\", workspace_id AS \"workspace_id!\", parent_job, is_flow_step,\n flow_status AS \"flow_status: Box\", last_ping, same_worker\n FROM queue\n WHERE running = true AND suspend = 0 AND suspend_until IS null AND scheduled_for <= now()\n AND (job_kind = 'flow' OR job_kind = 'flowpreview' OR job_kind = 'flownode')\n AND last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval\n AND canceled = false\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "workspace_id!", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent_job", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "is_flow_step", - "type_info": "Bool" - }, - { - "ordinal": 4, - "name": "flow_status: Box", - "type_info": "Jsonb" - }, - { - "ordinal": 5, - "name": "last_ping", - "type_info": "Timestamptz" - }, - { - "ordinal": 6, - "name": "same_worker", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - true, - true, - true, - true, - true, - true, - true - ] - }, - "hash": "32fdc66931dcf34f6ef5cdf3fd335d9f990eaa3dbb396290477159012e86af14" -} diff --git a/backend/.sqlx/query-3325f8ed245b1bce27c3d9f5e62ffc14b5c8aabf5ab53384f5f2b20eafd66cb3.json b/backend/.sqlx/query-3325f8ed245b1bce27c3d9f5e62ffc14b5c8aabf5ab53384f5f2b20eafd66cb3.json new file mode 100644 index 0000000000..6c02ca51d6 --- /dev/null +++ b/backend/.sqlx/query-3325f8ed245b1bce27c3d9f5e62ffc14b5c8aabf5ab53384f5f2b20eafd66cb3.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['retry'], $1)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "3325f8ed245b1bce27c3d9f5e62ffc14b5c8aabf5ab53384f5f2b20eafd66cb3" +} diff --git a/backend/.sqlx/query-33351de09c72ccc0a39eb977d26f867595813bfa1ae0b26bc4181780801294bf.json b/backend/.sqlx/query-33351de09c72ccc0a39eb977d26f867595813bfa1ae0b26bc4181780801294bf.json deleted file mode 100644 index a3d39e0b59..0000000000 --- a/backend/.sqlx/query-33351de09c72ccc0a39eb977d26f867595813bfa1ae0b26bc4181780801294bf.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT success AS \"success!\" FROM v2_as_completed_job WHERE id = ANY($1)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "success!", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "UuidArray" - ] - }, - "nullable": [ - true - ] - }, - "hash": "33351de09c72ccc0a39eb977d26f867595813bfa1ae0b26bc4181780801294bf" -} diff --git a/backend/.sqlx/query-337f31c2172194cd594042c561998a03f751b246c40daf056fced0fd91f6dd73.json b/backend/.sqlx/query-337f31c2172194cd594042c561998a03f751b246c40daf056fced0fd91f6dd73.json deleted file mode 100644 index 075a898e29..0000000000 --- a/backend/.sqlx/query-337f31c2172194cd594042c561998a03f751b246c40daf056fced0fd91f6dd73.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH uuid_table as (\n select unnest($11::uuid[]) as uuid\n )\n INSERT INTO queue \n (id, script_hash, script_path, job_kind, language, args, tag, created_by, permissioned_as, email, scheduled_for, workspace_id, concurrent_limit, concurrency_time_window_s, timeout, flow_status)\n (SELECT uuid, $1, $2, $3, $4, ('{ \"uuid\": \"' || uuid || '\" }')::jsonb, $5, $6, $7, $8, $9, $10, $12, $13, $14, $15 FROM uuid_table) \n RETURNING id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Int8", - "Varchar", - { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - }, - { - "Custom": { - "name": "script_lang", - "kind": { - "Enum": [ - "python3", - "deno", - "go", - "bash", - "postgresql", - "nativets", - "bun", - "mysql", - "bigquery", - "snowflake", - "graphql", - "powershell", - "mssql", - "php", - "bunnative", - "rust", - "ansible", - "csharp", - "oracledb" - ] - } - } - }, - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Timestamptz", - "Varchar", - "UuidArray", - "Int4", - "Int4", - "Int4", - "Jsonb" - ] - }, - "nullable": [ - false - ] - }, - "hash": "337f31c2172194cd594042c561998a03f751b246c40daf056fced0fd91f6dd73" -} diff --git a/backend/.sqlx/query-33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe.json b/backend/.sqlx/query-33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe.json deleted file mode 100644 index 8728e35a0c..0000000000 --- a/backend/.sqlx/query-33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Varchar", - "Timestamp", - "Varchar", - "Int8", - "Int8", - "Bool" - ] - }, - "nullable": [] - }, - "hash": "33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe" -} diff --git a/backend/.sqlx/query-33ee913ce263600a3f94f90e4a42cf0e4086030f3b7994e4892392765cbe1517.json b/backend/.sqlx/query-33ee913ce263600a3f94f90e4a42cf0e4086030f3b7994e4892392765cbe1517.json new file mode 100644 index 0000000000..9b7581526e --- /dev/null +++ b/backend/.sqlx/query-33ee913ce263600a3f94f90e4a42cf0e4086030f3b7994e4892392765cbe1517.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT EXISTS(\n SELECT \n 1 \n FROM \n gcp_trigger \n WHERE \n path = $1 AND \n workspace_id = $2\n )", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "33ee913ce263600a3f94f90e4a42cf0e4086030f3b7994e4892392765cbe1517" +} diff --git a/backend/.sqlx/query-348f73fa8222ec195bb5b2260d1595e6e9fa9d73779cf352e94039fd47de4294.json b/backend/.sqlx/query-348f73fa8222ec195bb5b2260d1595e6e9fa9d73779cf352e94039fd47de4294.json new file mode 100644 index 0000000000..4f9e060ead --- /dev/null +++ b/backend/.sqlx/query-348f73fa8222ec195bb5b2260d1595e6e9fa9d73779cf352e94039fd47de4294.json @@ -0,0 +1,217 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE schedule SET\n schedule = $1,\n timezone = $2,\n args = $3,\n on_failure = $4,\n on_failure_times = $5,\n on_failure_exact = $6,\n on_failure_extra_args = $7,\n on_recovery = $8,\n on_recovery_times = $9,\n on_recovery_extra_args = $10,\n on_success = $11,\n on_success_extra_args = $12,\n ws_error_handler_muted = $13,\n retry = $14,\n summary = $15,\n no_flow_overlap = $16,\n tag = $17,\n paused_until = $18,\n path = $19,\n workspace_id = $20,\n cron_version = COALESCE($21, cron_version),\n description = $22\n WHERE path = $19 AND workspace_id = $20\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 4, + "name": "schedule", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "timezone", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 7, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 9, + "name": "args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 10, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 13, + "name": "on_failure", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "on_failure_times", + "type_info": "Int4" + }, + { + "ordinal": 15, + "name": "on_failure_exact", + "type_info": "Bool" + }, + { + "ordinal": 16, + "name": "on_failure_extra_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 17, + "name": "on_recovery", + "type_info": "Varchar" + }, + { + "ordinal": 18, + "name": "on_recovery_times", + "type_info": "Int4" + }, + { + "ordinal": 19, + "name": "on_recovery_extra_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 20, + "name": "on_success", + "type_info": "Varchar" + }, + { + "ordinal": 21, + "name": "on_success_extra_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 22, + "name": "ws_error_handler_muted", + "type_info": "Bool" + }, + { + "ordinal": 23, + "name": "retry", + "type_info": "Jsonb" + }, + { + "ordinal": 24, + "name": "no_flow_overlap", + "type_info": "Bool" + }, + { + "ordinal": 25, + "name": "summary", + "type_info": "Varchar" + }, + { + "ordinal": 26, + "name": "description", + "type_info": "Text" + }, + { + "ordinal": 27, + "name": "tag", + "type_info": "Varchar" + }, + { + "ordinal": 28, + "name": "paused_until", + "type_info": "Timestamptz" + }, + { + "ordinal": 29, + "name": "cron_version", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Jsonb", + "Varchar", + "Int4", + "Bool", + "Jsonb", + "Varchar", + "Int4", + "Jsonb", + "Varchar", + "Jsonb", + "Bool", + "Jsonb", + "Varchar", + "Bool", + "Varchar", + "Timestamptz", + "Text", + "Text", + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + false, + true, + false, + true, + true, + true, + true, + true + ] + }, + "hash": "348f73fa8222ec195bb5b2260d1595e6e9fa9d73779cf352e94039fd47de4294" +} diff --git a/backend/.sqlx/query-bf91cb319e5b83c2235292a9e3ce8aa1c097c94b01aad0d9f7bce76a2a272bcc.json b/backend/.sqlx/query-34d22638730d62e8bf7020ae0f7ccacf4b258877375ceff2da640fe65d270794.json similarity index 53% rename from backend/.sqlx/query-bf91cb319e5b83c2235292a9e3ce8aa1c097c94b01aad0d9f7bce76a2a272bcc.json rename to backend/.sqlx/query-34d22638730d62e8bf7020ae0f7ccacf4b258877375ceff2da640fe65d270794.json index 25caf0d4fe..c0dbe95a6e 100644 --- a/backend/.sqlx/query-bf91cb319e5b83c2235292a9e3ce8aa1c097c94b01aad0d9f7bce76a2a272bcc.json +++ b/backend/.sqlx/query-34d22638730d62e8bf7020ae0f7ccacf4b258877375ceff2da640fe65d270794.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT success AS \"success!\"\n FROM completed_job WHERE id = $1 AND workspace_id = $2", + "query": "SELECT status = 'success' OR status = 'skipped' AS \"success!\"\n FROM v2_job_completed WHERE id = $1 AND workspace_id = $2", "describe": { "columns": [ { @@ -16,8 +16,8 @@ ] }, "nullable": [ - true + null ] }, - "hash": "bf91cb319e5b83c2235292a9e3ce8aa1c097c94b01aad0d9f7bce76a2a272bcc" + "hash": "34d22638730d62e8bf7020ae0f7ccacf4b258877375ceff2da640fe65d270794" } diff --git a/backend/.sqlx/query-364248db86a9228bba6ff522e811d68c5c902ee0a07ea69bff772ff10d0dc5aa.json b/backend/.sqlx/query-364248db86a9228bba6ff522e811d68c5c902ee0a07ea69bff772ff10d0dc5aa.json deleted file mode 100644 index 7ead72a26d..0000000000 --- a/backend/.sqlx/query-364248db86a9228bba6ff522e811d68c5c902ee0a07ea69bff772ff10d0dc5aa.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE queue SET flow_status = JSONB_SET(flow_status, ARRAY['user_states'], JSONB_SET(COALESCE(flow_status->'user_states', '{}'::jsonb), ARRAY[$1], $2))\n WHERE id = $3 AND workspace_id = $4 AND job_kind IN ('flow', 'flowpreview', 'flownode') RETURNING 1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Uuid", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "364248db86a9228bba6ff522e811d68c5c902ee0a07ea69bff772ff10d0dc5aa" -} diff --git a/backend/.sqlx/query-36b26b3a6458d8a0b4f770d52c1bb09370b905d610b9ceb3cfac11365586320d.json b/backend/.sqlx/query-36b26b3a6458d8a0b4f770d52c1bb09370b905d610b9ceb3cfac11365586320d.json deleted file mode 100644 index 3b3fa11ffb..0000000000 --- a/backend/.sqlx/query-36b26b3a6458d8a0b4f770d52c1bb09370b905d610b9ceb3cfac11365586320d.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT running AS \"running!\" FROM queue WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "running!", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "36b26b3a6458d8a0b4f770d52c1bb09370b905d610b9ceb3cfac11365586320d" -} diff --git a/backend/.sqlx/query-36c4e57afcab22f4b6825ccebe47767b8a8fe0a638250f7c7777e5a9f7530e5c.json b/backend/.sqlx/query-36c4e57afcab22f4b6825ccebe47767b8a8fe0a638250f7c7777e5a9f7530e5c.json new file mode 100644 index 0000000000..e810fc4754 --- /dev/null +++ b/backend/.sqlx/query-36c4e57afcab22f4b6825ccebe47767b8a8fe0a638250f7c7777e5a9f7530e5c.json @@ -0,0 +1,31 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO v2_job_completed AS cj\n ( workspace_id\n , id\n , started_at\n , duration_ms\n , result\n , result_columns\n , canceled_by\n , canceled_reason\n , flow_status\n , workflow_as_code_status\n , memory_peak\n , status\n , worker\n )\n SELECT q.workspace_id, q.id, started_at, COALESCE($9::bigint, (EXTRACT('epoch' FROM (now())) - EXTRACT('epoch' FROM (COALESCE(started_at, now()))))*1000), $3, $10, $5, $6,\n flow_status, workflow_as_code_status,\n $8, CASE WHEN $4::BOOL THEN 'canceled'::job_status\n WHEN $7::BOOL THEN 'skipped'::job_status\n WHEN $2::BOOL THEN 'success'::job_status\n ELSE 'failure'::job_status END AS status,\n q.worker\n FROM v2_job_queue q LEFT JOIN v2_job_status USING (id) WHERE q.id = $1\n ON CONFLICT (id) DO UPDATE SET status = EXCLUDED.status, result = $3 RETURNING duration_ms AS \"duration_ms!\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "duration_ms!", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Bool", + "Jsonb", + "Bool", + "Varchar", + "Text", + "Bool", + "Int4", + "Int8", + "TextArray" + ] + }, + "nullable": [ + false + ] + }, + "hash": "36c4e57afcab22f4b6825ccebe47767b8a8fe0a638250f7c7777e5a9f7530e5c" +} diff --git a/backend/.sqlx/query-37011e7f4cdfc87294e44252cca4f4683a12b82b972842f88f5c01111580224d.json b/backend/.sqlx/query-37011e7f4cdfc87294e44252cca4f4683a12b82b972842f88f5c01111580224d.json new file mode 100644 index 0000000000..5ab1ed1bc3 --- /dev/null +++ b/backend/.sqlx/query-37011e7f4cdfc87294e44252cca4f4683a12b82b972842f88f5c01111580224d.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture\n SET \n path = $1\n WHERE \n path = $2 \n AND workspace_id = $3 \n AND is_flow = $4\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "37011e7f4cdfc87294e44252cca4f4683a12b82b972842f88f5c01111580224d" +} diff --git a/backend/.sqlx/query-3734117167a1269f78b3949eed005db96faeec6c18500ad96087cd06c2c85a8b.json b/backend/.sqlx/query-3734117167a1269f78b3949eed005db96faeec6c18500ad96087cd06c2c85a8b.json new file mode 100644 index 0000000000..6a3afafb5e --- /dev/null +++ b/backend/.sqlx/query-3734117167a1269f78b3949eed005db96faeec6c18500ad96087cd06c2c85a8b.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT EXISTS (SELECT 1\n FROM workspace_settings\n WHERE workspace_id <> $1\n AND slack_command_script IS NOT NULL\n AND slack_team_id IS NOT NULL\n AND slack_team_id = (SELECT slack_team_id FROM workspace_settings WHERE workspace_id = $1))\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "3734117167a1269f78b3949eed005db96faeec6c18500ad96087cd06c2c85a8b" +} diff --git a/backend/.sqlx/query-3738096c29ab9d964be8a74bfd14ff1d599049ebefdaf97a017c9cef8d52ce20.json b/backend/.sqlx/query-3738096c29ab9d964be8a74bfd14ff1d599049ebefdaf97a017c9cef8d52ce20.json new file mode 100644 index 0000000000..d804949078 --- /dev/null +++ b/backend/.sqlx/query-3738096c29ab9d964be8a74bfd14ff1d599049ebefdaf97a017c9cef8d52ce20.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DROP INDEX CONCURRENTLY IF EXISTS queue_sort_2", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "3738096c29ab9d964be8a74bfd14ff1d599049ebefdaf97a017c9cef8d52ce20" +} diff --git a/backend/.sqlx/query-38846b12201990f8e776b256ac419b24ffec46fa02d710544a3074745be9455f.json b/backend/.sqlx/query-38846b12201990f8e776b256ac419b24ffec46fa02d710544a3074745be9455f.json deleted file mode 100644 index f94429e4f9..0000000000 --- a/backend/.sqlx/query-38846b12201990f8e776b256ac419b24ffec46fa02d710544a3074745be9455f.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET suspend = 0 WHERE parent_job = $1 AND suspend = $2 AND (flow_status->'step')::int = 0", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Int4" - ] - }, - "nullable": [] - }, - "hash": "38846b12201990f8e776b256ac419b24ffec46fa02d710544a3074745be9455f" -} diff --git a/backend/.sqlx/query-38a3fbc28e827d08a928d441274c5eb28780abc8adffcc7175f6c8d4ff8849ca.json b/backend/.sqlx/query-38a3fbc28e827d08a928d441274c5eb28780abc8adffcc7175f6c8d4ff8849ca.json deleted file mode 100644 index a3929e5e4b..0000000000 --- a/backend/.sqlx/query-38a3fbc28e827d08a928d441274c5eb28780abc8adffcc7175f6c8d4ff8849ca.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE concurrency_counter SET job_uuids = job_uuids - $2 WHERE concurrency_id = $1 RETURNING (SELECT COUNT(*) FROM jsonb_object_keys(job_uuids))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "38a3fbc28e827d08a928d441274c5eb28780abc8adffcc7175f6c8d4ff8849ca" -} diff --git a/backend/.sqlx/query-2f2ef9b1ccff527c48fa01cf1b78cd0e58c8d534ac22ec0356d82a854b31d087.json b/backend/.sqlx/query-38b3945c04ae58aace881ed4d6aa4da72e61eeb8e081d2e3d1ab99a4fe450350.json similarity index 57% rename from backend/.sqlx/query-2f2ef9b1ccff527c48fa01cf1b78cd0e58c8d534ac22ec0356d82a854b31d087.json rename to backend/.sqlx/query-38b3945c04ae58aace881ed4d6aa4da72e61eeb8e081d2e3d1ab99a4fe450350.json index 77e16162bd..f4ed338505 100644 --- a/backend/.sqlx/query-2f2ef9b1ccff527c48fa01cf1b78cd0e58c8d534ac22ec0356d82a854b31d087.json +++ b/backend/.sqlx/query-38b3945c04ae58aace881ed4d6aa4da72e61eeb8e081d2e3d1ab99a4fe450350.json @@ -1,15 +1,14 @@ { "db_name": "PostgreSQL", - "query": "UPDATE v2_job_runtime SET ping = NULL\n WHERE id = $1 AND ping = $2", + "query": "UPDATE v2_job_runtime SET ping = NULL\n WHERE id = $1", "describe": { "columns": [], "parameters": { "Left": [ - "Uuid", - "Timestamptz" + "Uuid" ] }, "nullable": [] }, - "hash": "2f2ef9b1ccff527c48fa01cf1b78cd0e58c8d534ac22ec0356d82a854b31d087" + "hash": "38b3945c04ae58aace881ed4d6aa4da72e61eeb8e081d2e3d1ab99a4fe450350" } diff --git a/backend/.sqlx/query-16be0560028361d46bf3b842a5fa07472994d8942c684f5b75339fe71ea23cdd.json b/backend/.sqlx/query-38c20dba51b1e2b4b28e5eed51f9071e1d3cf78e89ec467927823177c5a439cc.json similarity index 52% rename from backend/.sqlx/query-16be0560028361d46bf3b842a5fa07472994d8942c684f5b75339fe71ea23cdd.json rename to backend/.sqlx/query-38c20dba51b1e2b4b28e5eed51f9071e1d3cf78e89ec467927823177c5a439cc.json index d273132682..66681c2377 100644 --- a/backend/.sqlx/query-16be0560028361d46bf3b842a5fa07472994d8942c684f5b75339fe71ea23cdd.json +++ b/backend/.sqlx/query-38c20dba51b1e2b4b28e5eed51f9071e1d3cf78e89ec467927823177c5a439cc.json @@ -1,22 +1,22 @@ { "db_name": "PostgreSQL", - "query": "SELECT script_path FROM completed_job WHERE id = $1", + "query": "SELECT workspace_id FROM usr WHERE email = $1", "describe": { "columns": [ { "ordinal": 0, - "name": "script_path", + "name": "workspace_id", "type_info": "Varchar" } ], "parameters": { "Left": [ - "Uuid" + "Text" ] }, "nullable": [ - true + false ] }, - "hash": "16be0560028361d46bf3b842a5fa07472994d8942c684f5b75339fe71ea23cdd" + "hash": "38c20dba51b1e2b4b28e5eed51f9071e1d3cf78e89ec467927823177c5a439cc" } diff --git a/backend/.sqlx/query-399a8337a2488fa2ce3da2ef3281a34f8f96ee0d833c2fe3c22a0aa43e306f09.json b/backend/.sqlx/query-399a8337a2488fa2ce3da2ef3281a34f8f96ee0d833c2fe3c22a0aa43e306f09.json deleted file mode 100644 index 51198fb924..0000000000 --- a/backend/.sqlx/query-399a8337a2488fa2ce3da2ef3281a34f8f96ee0d833c2fe3c22a0aa43e306f09.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM pip_resolution_cache WHERE expiration <= now() RETURNING hash", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "hash", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false - ] - }, - "hash": "399a8337a2488fa2ce3da2ef3281a34f8f96ee0d833c2fe3c22a0aa43e306f09" -} diff --git a/backend/.sqlx/query-3a47d8ec0f4ba1644951f0c88504c9f769ffe5004871a049e4232e0092532062.json b/backend/.sqlx/query-3a47d8ec0f4ba1644951f0c88504c9f769ffe5004871a049e4232e0092532062.json new file mode 100644 index 0000000000..a07fcd57db --- /dev/null +++ b/backend/.sqlx/query-3a47d8ec0f4ba1644951f0c88504c9f769ffe5004871a049e4232e0092532062.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT EXISTS (\n SELECT 1 FROM v2_job j JOIN v2_job_queue USING (id)\n WHERE j.workspace_id = $1 AND trigger_kind = 'schedule' AND trigger = $2 AND runnable_path = $4\n AND parent_job IS NULL\n AND scheduled_for = $3\n )", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Timestamptz", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "3a47d8ec0f4ba1644951f0c88504c9f769ffe5004871a049e4232e0092532062" +} diff --git a/backend/.sqlx/query-3a9441fe8fef1605d02e92b65d1df664b4de9aabf1e0e219596b295d52438008.json b/backend/.sqlx/query-3a9441fe8fef1605d02e92b65d1df664b4de9aabf1e0e219596b295d52438008.json deleted file mode 100644 index 4a789635ee..0000000000 --- a/backend/.sqlx/query-3a9441fe8fef1605d02e92b65d1df664b4de9aabf1e0e219596b295d52438008.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO v2_job_completed AS cj\n ( workspace_id\n , id\n , started_at\n , duration_ms\n , result\n , result_columns\n , canceled_by\n , canceled_reason\n , flow_status\n , workflow_as_code_status\n , memory_peak\n , status\n )\n SELECT q.workspace_id, q.id, started_at, COALESCE($9::bigint, (EXTRACT('epoch' FROM (now())) - EXTRACT('epoch' FROM (COALESCE(started_at, now()))))*1000), $3, $10, $5, $6,\n flow_status, workflow_as_code_status,\n $8, CASE WHEN $4::BOOL THEN 'canceled'::job_status\n WHEN $7::BOOL THEN 'skipped'::job_status\n WHEN $2::BOOL THEN 'success'::job_status\n ELSE 'failure'::job_status END AS status\n FROM v2_job_queue q LEFT JOIN v2_job_status USING (id) WHERE q.id = $1\n ON CONFLICT (id) DO UPDATE SET status = EXCLUDED.status, result = $3 RETURNING duration_ms AS \"duration_ms!\"", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "duration_ms!", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Bool", - "Jsonb", - "Bool", - "Varchar", - "Text", - "Bool", - "Int4", - "Int8", - "TextArray" - ] - }, - "nullable": [ - false - ] - }, - "hash": "3a9441fe8fef1605d02e92b65d1df664b4de9aabf1e0e219596b295d52438008" -} diff --git a/backend/.sqlx/query-3af32856235690827a8700bb2396f3ab44afe0d1a7c261a626d93fb44c00bdb7.json b/backend/.sqlx/query-3af32856235690827a8700bb2396f3ab44afe0d1a7c261a626d93fb44c00bdb7.json new file mode 100644 index 0000000000..73d2bc7c6a --- /dev/null +++ b/backend/.sqlx/query-3af32856235690827a8700bb2396f3ab44afe0d1a7c261a626d93fb44c00bdb7.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = flow_status - 'approval_conditions'\n WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "3af32856235690827a8700bb2396f3ab44afe0d1a7c261a626d93fb44c00bdb7" +} diff --git a/backend/.sqlx/query-3b4b62161a5197f37850c8c4197ea026d8e94a3cb9cdcfa5fde19343acf81ecc.json b/backend/.sqlx/query-3b4b62161a5197f37850c8c4197ea026d8e94a3cb9cdcfa5fde19343acf81ecc.json deleted file mode 100644 index 367aae4a31..0000000000 --- a/backend/.sqlx/query-3b4b62161a5197f37850c8c4197ea026d8e94a3cb9cdcfa5fde19343acf81ecc.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4) WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "3b4b62161a5197f37850c8c4197ea026d8e94a3cb9cdcfa5fde19343acf81ecc" -} diff --git a/backend/.sqlx/query-3bacf9cd9aa63f4bec5f983f4a0c3030216b5a4ed669f77962509d1c2c6cb780.json b/backend/.sqlx/query-3bacf9cd9aa63f4bec5f983f4a0c3030216b5a4ed669f77962509d1c2c6cb780.json deleted file mode 100644 index 200a5bf47f..0000000000 --- a/backend/.sqlx/query-3bacf9cd9aa63f4bec5f983f4a0c3030216b5a4ed669f77962509d1c2c6cb780.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "create index concurrently if not exists root_job_index_by_path_2 ON v2_job (workspace_id, runnable_path, created_at desc) WHERE parent_job IS NULL", - "describe": { - "columns": [], - "parameters": { - "Left": [] - }, - "nullable": [] - }, - "hash": "3bacf9cd9aa63f4bec5f983f4a0c3030216b5a4ed669f77962509d1c2c6cb780" -} diff --git a/backend/.sqlx/query-3bc1919515120116705d7c250a34f2b9bf7c4bcaedb87c28f974e46c9c42200c.json b/backend/.sqlx/query-3bc1919515120116705d7c250a34f2b9bf7c4bcaedb87c28f974e46c9c42200c.json deleted file mode 100644 index 125c9593b6..0000000000 --- a/backend/.sqlx/query-3bc1919515120116705d7c250a34f2b9bf7c4bcaedb87c28f974e46c9c42200c.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id AS \"id!\" FROM queue WHERE id = ANY($1) AND schedule_path IS NULL AND ($2::text[] IS NULL OR tag = ANY($2))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "UuidArray", - "TextArray" - ] - }, - "nullable": [ - true - ] - }, - "hash": "3bc1919515120116705d7c250a34f2b9bf7c4bcaedb87c28f974e46c9c42200c" -} diff --git a/backend/.sqlx/query-3c423bcb10668bdd131f7b6a9b0fcc8f9b1909f255cdfce0fb83496aac0fc021.json b/backend/.sqlx/query-3c423bcb10668bdd131f7b6a9b0fcc8f9b1909f255cdfce0fb83496aac0fc021.json deleted file mode 100644 index 090efe4964..0000000000 --- a/backend/.sqlx/query-3c423bcb10668bdd131f7b6a9b0fcc8f9b1909f255cdfce0fb83496aac0fc021.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id AS \"id!\", flow_status AS \"flow_status!: Json\"\n FROM completed_job\n WHERE parent_job = $1 AND workspace_id = $2 AND flow_status IS NOT NULL", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "flow_status!: Json", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true - ] - }, - "hash": "3c423bcb10668bdd131f7b6a9b0fcc8f9b1909f255cdfce0fb83496aac0fc021" -} diff --git a/backend/.sqlx/query-3d58b5861c62f0b092b6b95c17ba1dac2cbcf00db116624bd2fe27a4d0dfb436.json b/backend/.sqlx/query-3d58b5861c62f0b092b6b95c17ba1dac2cbcf00db116624bd2fe27a4d0dfb436.json deleted file mode 100644 index 4f1fe255f9..0000000000 --- a/backend/.sqlx/query-3d58b5861c62f0b092b6b95c17ba1dac2cbcf00db116624bd2fe27a4d0dfb436.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['approval_conditions'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "3d58b5861c62f0b092b6b95c17ba1dac2cbcf00db116624bd2fe27a4d0dfb436" -} diff --git a/backend/.sqlx/query-3da0cf7edc975cb365c36d167df34d9d30d3e86f231bb9d1d3cc3bad6a50dfff.json b/backend/.sqlx/query-3da0cf7edc975cb365c36d167df34d9d30d3e86f231bb9d1d3cc3bad6a50dfff.json deleted file mode 100644 index df93a90efc..0000000000 --- a/backend/.sqlx/query-3da0cf7edc975cb365c36d167df34d9d30d3e86f231bb9d1d3cc3bad6a50dfff.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT parent_job\n FROM queue\n WHERE id = $1 AND workspace_id = $2\n UNION ALL\n SELECT parent_job\n FROM completed_job\n WHERE id = $1 AND workspace_id = $2\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "parent_job", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "3da0cf7edc975cb365c36d167df34d9d30d3e86f231bb9d1d3cc3bad6a50dfff" -} diff --git a/backend/.sqlx/query-3df03ec2345c905f03450e1e3f0c3ce7f41a22e72c5180ef8cc910305e4d0fce.json b/backend/.sqlx/query-3df03ec2345c905f03450e1e3f0c3ce7f41a22e72c5180ef8cc910305e4d0fce.json deleted file mode 100644 index 3bfcb0a9f3..0000000000 --- a/backend/.sqlx/query-3df03ec2345c905f03450e1e3f0c3ce7f41a22e72c5180ef8cc910305e4d0fce.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n script_path, script_hash AS \"script_hash: ScriptHash\",\n job_kind AS \"job_kind: JobKind\",\n flow_status AS \"flow_status: Json>\",\n raw_flow AS \"raw_flow: Json>\"\n FROM completed_job WHERE id = $1 and workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "script_hash: ScriptHash", - "type_info": "Int8" - }, - { - "ordinal": 2, - "name": "job_kind: JobKind", - "type_info": { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - } - }, - { - "ordinal": 3, - "name": "flow_status: Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 4, - "name": "raw_flow: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true, - false, - true, - true - ] - }, - "hash": "3df03ec2345c905f03450e1e3f0c3ce7f41a22e72c5180ef8cc910305e4d0fce" -} diff --git a/backend/.sqlx/query-3e0cdd500dffc3bd1d8374ca3cc8fd60ce778c2fece27637d9985d4650778653.json b/backend/.sqlx/query-3e0cdd500dffc3bd1d8374ca3cc8fd60ce778c2fece27637d9985d4650778653.json deleted file mode 100644 index 9f5cb1d19d..0000000000 --- a/backend/.sqlx/query-3e0cdd500dffc3bd1d8374ca3cc8fd60ce778c2fece27637d9985d4650778653.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['cleanup_module', 'flow_jobs_to_clean'], COALESCE(flow_status->'cleanup_module'->'flow_jobs_to_clean', '[]'::jsonb) || $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "3e0cdd500dffc3bd1d8374ca3cc8fd60ce778c2fece27637d9985d4650778653" -} diff --git a/backend/.sqlx/query-3e33469f448fa86c7e0d0deb65ee2c13b5dcc4cbc4fcb0fe2bde1fdcb6d20e74.json b/backend/.sqlx/query-3e33469f448fa86c7e0d0deb65ee2c13b5dcc4cbc4fcb0fe2bde1fdcb6d20e74.json new file mode 100644 index 0000000000..113f888545 --- /dev/null +++ b/backend/.sqlx/query-3e33469f448fa86c7e0d0deb65ee2c13b5dcc4cbc4fcb0fe2bde1fdcb6d20e74.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE \n capture_config \n SET \n last_server_ping = NULL \n WHERE \n workspace_id = $1 AND \n path = $2 AND \n is_flow = $3 AND \n trigger_kind = 'mqtt' AND \n server_id IS NULL\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "3e33469f448fa86c7e0d0deb65ee2c13b5dcc4cbc4fcb0fe2bde1fdcb6d20e74" +} diff --git a/backend/.sqlx/query-3e3d12a51cb524fbd3d6949e150cb608acfbe8c8eade1939e813086380c205e0.json b/backend/.sqlx/query-3e3d12a51cb524fbd3d6949e150cb608acfbe8c8eade1939e813086380c205e0.json deleted file mode 100644 index d6fd72a8b5..0000000000 --- a/backend/.sqlx/query-3e3d12a51cb524fbd3d6949e150cb608acfbe8c8eade1939e813086380c205e0.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO concurrency_counter(concurrency_id, job_uuids) VALUES ($1, $2)\n ON CONFLICT (concurrency_id) \n DO UPDATE SET job_uuids = jsonb_set(concurrency_counter.job_uuids, array[$3], '{}')\n RETURNING (SELECT COUNT(*) FROM jsonb_object_keys(job_uuids))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Jsonb", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "3e3d12a51cb524fbd3d6949e150cb608acfbe8c8eade1939e813086380c205e0" -} diff --git a/backend/.sqlx/query-3e55d027327bd3c76810fbe22d3ccb1bbbf83c8cff69d8f5907d1417a2522e69.json b/backend/.sqlx/query-3e55d027327bd3c76810fbe22d3ccb1bbbf83c8cff69d8f5907d1417a2522e69.json deleted file mode 100644 index 2603a95877..0000000000 --- a/backend/.sqlx/query-3e55d027327bd3c76810fbe22d3ccb1bbbf83c8cff69d8f5907d1417a2522e69.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_queue SET running = false, started_at = null\n WHERE id = $1 AND canceled_by IS NULL", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "3e55d027327bd3c76810fbe22d3ccb1bbbf83c8cff69d8f5907d1417a2522e69" -} diff --git a/backend/.sqlx/query-3eb447ed317f3d8724b2309cfdf7cfb058a35a81314db981bc953a9505725082.json b/backend/.sqlx/query-3eb447ed317f3d8724b2309cfdf7cfb058a35a81314db981bc953a9505725082.json deleted file mode 100644 index 69266cc130..0000000000 --- a/backend/.sqlx/query-3eb447ed317f3d8724b2309cfdf7cfb058a35a81314db981bc953a9505725082.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO v2_job_status (id, workflow_as_code_status)\n VALUES ($1, JSONB_SET('{}'::JSONB, array[$2], $3))\n ON CONFLICT (id) DO UPDATE SET workflow_as_code_status =\n COALESCE(EXCLUDED.workflow_as_code_status, '{}'::JSONB) || $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "3eb447ed317f3d8724b2309cfdf7cfb058a35a81314db981bc953a9505725082" -} diff --git a/backend/.sqlx/query-3f5520e0ea00569bf169da9abde31617043fcc0bea3ef62c50fcd881f3d48605.json b/backend/.sqlx/query-3f5520e0ea00569bf169da9abde31617043fcc0bea3ef62c50fcd881f3d48605.json new file mode 100644 index 0000000000..b225a5a1da --- /dev/null +++ b/backend/.sqlx/query-3f5520e0ea00569bf169da9abde31617043fcc0bea3ef62c50fcd881f3d48605.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO gcp_trigger (\n gcp_resource_path,\n subscription_id,\n topic_id,\n delivery_type,\n delivery_config,\n workspace_id, \n path, \n script_path, \n is_flow, \n email, \n enabled, \n edited_by\n ) \n VALUES (\n $1, \n $2, \n $3, \n $4,\n $5,\n $6, \n $7, \n $8, \n $9,\n $10,\n $11,\n $12\n )", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + { + "Custom": { + "name": "delivery_mode", + "kind": { + "Enum": [ + "push", + "pull" + ] + } + } + }, + "Jsonb", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Bool", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "3f5520e0ea00569bf169da9abde31617043fcc0bea3ef62c50fcd881f3d48605" +} diff --git a/backend/.sqlx/query-3f67e7cf8d9f021a075f1c88703287a9f42e252a1246a584249a224afdbbbf8a.json b/backend/.sqlx/query-3f67e7cf8d9f021a075f1c88703287a9f42e252a1246a584249a224afdbbbf8a.json new file mode 100644 index 0000000000..8dec90897c --- /dev/null +++ b/backend/.sqlx/query-3f67e7cf8d9f021a075f1c88703287a9f42e252a1246a584249a224afdbbbf8a.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n sqs_trigger \n SET \n enabled = FALSE, \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "3f67e7cf8d9f021a075f1c88703287a9f42e252a1246a584249a224afdbbbf8a" +} diff --git a/backend/.sqlx/query-3fa3d1fa1add8e187fcbaf7351b721ad0f3e2888af207e8830ccf5e921c5fd60.json b/backend/.sqlx/query-3fa3d1fa1add8e187fcbaf7351b721ad0f3e2888af207e8830ccf5e921c5fd60.json new file mode 100644 index 0000000000..3bb4151982 --- /dev/null +++ b/backend/.sqlx/query-3fa3d1fa1add8e187fcbaf7351b721ad0f3e2888af207e8830ccf5e921c5fd60.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO concurrency_counter(concurrency_id, job_uuids) \n VALUES ($1, $2)\n ON CONFLICT (concurrency_id)\n DO UPDATE SET job_uuids = jsonb_set(concurrency_counter.job_uuids, array[$3], '{}')", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Jsonb", + "Text" + ] + }, + "nullable": [] + }, + "hash": "3fa3d1fa1add8e187fcbaf7351b721ad0f3e2888af207e8830ccf5e921c5fd60" +} diff --git a/backend/.sqlx/query-402fd5bff6e8420c9b3477f05df625cae355fda673b9e85284e0fcd7d9232eb7.json b/backend/.sqlx/query-402fd5bff6e8420c9b3477f05df625cae355fda673b9e85284e0fcd7d9232eb7.json deleted file mode 100644 index 83d1b15744..0000000000 --- a/backend/.sqlx/query-402fd5bff6e8420c9b3477f05df625cae355fda673b9e85284e0fcd7d9232eb7.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n script_path, script_hash AS \"script_hash: ScriptHash\",\n job_kind AS \"job_kind!: JobKind\",\n flow_status AS \"flow_status: Json>\",\n raw_flow AS \"raw_flow: Json>\"\n FROM completed_job WHERE id = $1 and workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "script_hash: ScriptHash", - "type_info": "Int8" - }, - { - "ordinal": 2, - "name": "job_kind!: JobKind", - "type_info": { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - } - }, - { - "ordinal": 3, - "name": "flow_status: Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 4, - "name": "raw_flow: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true, - true, - true, - true - ] - }, - "hash": "402fd5bff6e8420c9b3477f05df625cae355fda673b9e85284e0fcd7d9232eb7" -} diff --git a/backend/.sqlx/query-4053f0bb30f651ddf2214115748daca0ea457da8252394eeeead0897d184f6da.json b/backend/.sqlx/query-4053f0bb30f651ddf2214115748daca0ea457da8252394eeeead0897d184f6da.json new file mode 100644 index 0000000000..ff3968567c --- /dev/null +++ b/backend/.sqlx/query-4053f0bb30f651ddf2214115748daca0ea457da8252394eeeead0897d184f6da.json @@ -0,0 +1,133 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n path, \n script_path, \n is_flow, \n route_path, \n authentication_resource_path,\n workspace_id, \n is_async, \n authentication_method AS \"authentication_method: _\", \n edited_by, \n email, \n static_asset_config AS \"static_asset_config: _\",\n wrap_body,\n raw_string,\n workspaced_route,\n is_static_website\n FROM \n http_trigger \n WHERE \n http_method = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 3, + "name": "route_path", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "authentication_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "is_async", + "type_info": "Bool" + }, + { + "ordinal": 7, + "name": "authentication_method: _", + "type_info": { + "Custom": { + "name": "authentication_method", + "kind": { + "Enum": [ + "none", + "windmill", + "api_key", + "basic_http", + "custom_script", + "signature" + ] + } + } + } + }, + { + "ordinal": 8, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "static_asset_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 11, + "name": "wrap_body", + "type_info": "Bool" + }, + { + "ordinal": 12, + "name": "raw_string", + "type_info": "Bool" + }, + { + "ordinal": 13, + "name": "workspaced_route", + "type_info": "Bool" + }, + { + "ordinal": 14, + "name": "is_static_website", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + { + "Custom": { + "name": "http_method", + "kind": { + "Enum": [ + "get", + "post", + "put", + "delete", + "patch" + ] + } + } + } + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + true, + false, + false, + false, + false + ] + }, + "hash": "4053f0bb30f651ddf2214115748daca0ea457da8252394eeeead0897d184f6da" +} diff --git a/backend/.sqlx/query-41e557e1b63b13c9fcc195901c0bd0de7e03c539ee046955543d9693551246f7.json b/backend/.sqlx/query-41e557e1b63b13c9fcc195901c0bd0de7e03c539ee046955543d9693551246f7.json deleted file mode 100644 index 0df0167c42..0000000000 --- a/backend/.sqlx/query-41e557e1b63b13c9fcc195901c0bd0de7e03c539ee046955543d9693551246f7.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM capture WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [] - }, - "hash": "41e557e1b63b13c9fcc195901c0bd0de7e03c539ee046955543d9693551246f7" -} diff --git a/backend/.sqlx/query-41f68f4ce5bed783cf69e42da115e9ad2c9fcbd75f55817b2114c04207f66e4a.json b/backend/.sqlx/query-41f68f4ce5bed783cf69e42da115e9ad2c9fcbd75f55817b2114c04207f66e4a.json deleted file mode 100644 index 9397f52b83..0000000000 --- a/backend/.sqlx/query-41f68f4ce5bed783cf69e42da115e9ad2c9fcbd75f55817b2114c04207f66e4a.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO metrics (id, value)\n VALUES ($1, to_jsonb((SELECT EXTRACT(EPOCH FROM now() - scheduled_for)\n FROM queue WHERE tag = $2 AND running = false AND scheduled_for <= now() - ('3 seconds')::interval\n ORDER BY priority DESC NULLS LAST, scheduled_for LIMIT 1)))", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Text" - ] - }, - "nullable": [] - }, - "hash": "41f68f4ce5bed783cf69e42da115e9ad2c9fcbd75f55817b2114c04207f66e4a" -} diff --git a/backend/.sqlx/query-42543450a5c1b988b258fb1e0c00cd0a53a0395bc60e35817ff149c4f27a19fb.json b/backend/.sqlx/query-42543450a5c1b988b258fb1e0c00cd0a53a0395bc60e35817ff149c4f27a19fb.json deleted file mode 100644 index 89287ed48d..0000000000 --- a/backend/.sqlx/query-42543450a5c1b988b258fb1e0c00cd0a53a0395bc60e35817ff149c4f27a19fb.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE completed_job\n SET logs = '##DELETED##', args = '{}'::jsonb, result = '{}'::jsonb\n WHERE id = ANY($1)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "UuidArray" - ] - }, - "nullable": [] - }, - "hash": "42543450a5c1b988b258fb1e0c00cd0a53a0395bc60e35817ff149c4f27a19fb" -} diff --git a/backend/.sqlx/query-429aef2c320a152b16fe20c1ab84aab41142897db108dc8cbc2c51abb2e30c7c.json b/backend/.sqlx/query-429aef2c320a152b16fe20c1ab84aab41142897db108dc8cbc2c51abb2e30c7c.json deleted file mode 100644 index c40a18e4c9..0000000000 --- a/backend/.sqlx/query-429aef2c320a152b16fe20c1ab84aab41142897db108dc8cbc2c51abb2e30c7c.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2)\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "429aef2c320a152b16fe20c1ab84aab41142897db108dc8cbc2c51abb2e30c7c" -} diff --git a/backend/.sqlx/query-c5270ee815689e42b65df507b850da43239c9a5aaea41c9aed7ed33a6219a534.json b/backend/.sqlx/query-42b4b73e9d60348e2d90fcade9dcad6d8995242dc20a4e14c1a8fae4fc6a9fd2.json similarity index 51% rename from backend/.sqlx/query-c5270ee815689e42b65df507b850da43239c9a5aaea41c9aed7ed33a6219a534.json rename to backend/.sqlx/query-42b4b73e9d60348e2d90fcade9dcad6d8995242dc20a4e14c1a8fae4fc6a9fd2.json index b7ac98ff24..c1a38be77e 100644 --- a/backend/.sqlx/query-c5270ee815689e42b65df507b850da43239c9a5aaea41c9aed7ed33a6219a534.json +++ b/backend/.sqlx/query-42b4b73e9d60348e2d90fcade9dcad6d8995242dc20a4e14c1a8fae4fc6a9fd2.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE capture_config SET last_client_ping = now() WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4", + "query": "\n UPDATE \n capture_config\n SET \n last_client_ping = NOW()\n WHERE \n workspace_id = $1 \n AND path = $2 \n AND is_flow = $3 \n AND trigger_kind = $4\n ", "describe": { "columns": [], "parameters": { @@ -18,7 +18,11 @@ "websocket", "kafka", "email", - "nats" + "nats", + "postgres", + "sqs", + "mqtt", + "gcp" ] } } @@ -27,5 +31,5 @@ }, "nullable": [] }, - "hash": "c5270ee815689e42b65df507b850da43239c9a5aaea41c9aed7ed33a6219a534" + "hash": "42b4b73e9d60348e2d90fcade9dcad6d8995242dc20a4e14c1a8fae4fc6a9fd2" } diff --git a/backend/.sqlx/query-430ea56dea72c7d326735952bf85708dcc4811857b7e5631f0decf5d75ef3aa3.json b/backend/.sqlx/query-430ea56dea72c7d326735952bf85708dcc4811857b7e5631f0decf5d75ef3aa3.json new file mode 100644 index 0000000000..660b5cb402 --- /dev/null +++ b/backend/.sqlx/query-430ea56dea72c7d326735952bf85708dcc4811857b7e5631f0decf5d75ef3aa3.json @@ -0,0 +1,30 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n schedule.path, t.jobs FROM schedule,\n LATERAL(SELECT ARRAY(\n SELECT json_build_object('id', id, 'success', status = 'success', 'duration_ms', duration_ms)\n FROM v2_job_completed c JOIN v2_job j USING (id)\n WHERE trigger_kind = 'schedule'\n AND trigger = schedule.path\n AND c.workspace_id = $1\n AND j.workspace_id = $1\n AND parent_job IS NULL AND runnable_path = schedule.script_path\n AND status <> 'skipped'\n ORDER BY created_at DESC\n LIMIT 20\n ) AS jobs) t\n WHERE workspace_id = $1\n ORDER BY edited_at DESC\n LIMIT $2 OFFSET $3", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "jobs", + "type_info": "JsonArray" + } + ], + "parameters": { + "Left": [ + "Text", + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + null + ] + }, + "hash": "430ea56dea72c7d326735952bf85708dcc4811857b7e5631f0decf5d75ef3aa3" +} diff --git a/backend/.sqlx/query-433da02be85347333323480f7f279e9bd7e1b8348e91b33030df7181a55798a6.json b/backend/.sqlx/query-433da02be85347333323480f7f279e9bd7e1b8348e91b33030df7181a55798a6.json new file mode 100644 index 0000000000..bee9c556b2 --- /dev/null +++ b/backend/.sqlx/query-433da02be85347333323480f7f279e9bd7e1b8348e91b33030df7181a55798a6.json @@ -0,0 +1,30 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT j.id, jc.flow_status AS \"flow_status!: Json\"\n FROM v2_job j\n JOIN v2_job_completed jc ON j.id = jc.id\n WHERE j.parent_job = $1 AND j.workspace_id = $2 AND j.created_at >= $3 AND jc.flow_status IS NOT NULL", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "flow_status!: Json", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Timestamptz" + ] + }, + "nullable": [ + false, + true + ] + }, + "hash": "433da02be85347333323480f7f279e9bd7e1b8348e91b33030df7181a55798a6" +} diff --git a/backend/.sqlx/query-43aa468aac174529a74e6108af55a383f8a20e98b8c502929f4dc5041a55e72f.json b/backend/.sqlx/query-43aa468aac174529a74e6108af55a383f8a20e98b8c502929f4dc5041a55e72f.json deleted file mode 100644 index e33b0dc0b8..0000000000 --- a/backend/.sqlx/query-43aa468aac174529a74e6108af55a383f8a20e98b8c502929f4dc5041a55e72f.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT EXISTS (\n SELECT 1 FROM v2_as_completed_job \n WHERE workspace_id = $2 \n AND (job_kind = 'appscript' OR job_kind = 'preview')\n AND created_by = 'anonymous' \n AND started_at > now() - interval '3 hours'\n AND script_path LIKE $3 || '/%' \n AND result @> ('{\"s3\":\"' || $1 || '\"}')::jsonb \n )", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "43aa468aac174529a74e6108af55a383f8a20e98b8c502929f4dc5041a55e72f" -} diff --git a/backend/.sqlx/query-43c8cd9f8560412bb06d9966ccfa2524943ba277c5f9a37c06bd5ee14fd46bda.json b/backend/.sqlx/query-43c8cd9f8560412bb06d9966ccfa2524943ba277c5f9a37c06bd5ee14fd46bda.json new file mode 100644 index 0000000000..7d6ecdbf32 --- /dev/null +++ b/backend/.sqlx/query-43c8cd9f8560412bb06d9966ccfa2524943ba277c5f9a37c06bd5ee14fd46bda.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'gcp'\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "43c8cd9f8560412bb06d9966ccfa2524943ba277c5f9a37c06bd5ee14fd46bda" +} diff --git a/backend/.sqlx/query-43fcdf5243e17bfbdcd21f09feee6e104b40f4b937914f56f01c299cddfc17e9.json b/backend/.sqlx/query-43fcdf5243e17bfbdcd21f09feee6e104b40f4b937914f56f01c299cddfc17e9.json deleted file mode 100644 index edbade4821..0000000000 --- a/backend/.sqlx/query-43fcdf5243e17bfbdcd21f09feee6e104b40f4b937914f56f01c299cddfc17e9.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE job SET workspace_id = $1 WHERE workspace_id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Text" - ] - }, - "nullable": [] - }, - "hash": "43fcdf5243e17bfbdcd21f09feee6e104b40f4b937914f56f01c299cddfc17e9" -} diff --git a/backend/.sqlx/query-443bd83bcea1d37c79cb080095343c98104529879f991c49585cd181e34aa827.json b/backend/.sqlx/query-443bd83bcea1d37c79cb080095343c98104529879f991c49585cd181e34aa827.json new file mode 100644 index 0000000000..65b21050c0 --- /dev/null +++ b/backend/.sqlx/query-443bd83bcea1d37c79cb080095343c98104529879f991c49585cd181e34aa827.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT content FROM script WHERE path = $1 AND workspace_id = $2\n AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND\n workspace_id = $2)\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "content", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "443bd83bcea1d37c79cb080095343c98104529879f991c49585cd181e34aa827" +} diff --git a/backend/.sqlx/query-44a317f7647e2b515f90dc9c04f7ac75c2c87c7c3036acd96ba72fb2a21700db.json b/backend/.sqlx/query-44a317f7647e2b515f90dc9c04f7ac75c2c87c7c3036acd96ba72fb2a21700db.json deleted file mode 100644 index 8885d178e2..0000000000 --- a/backend/.sqlx/query-44a317f7647e2b515f90dc9c04f7ac75c2c87c7c3036acd96ba72fb2a21700db.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM completed_job WHERE created_at <= now() - ($1::bigint::text || ' s')::interval AND started_at + ((duration_ms/1000 + $1::bigint) || ' s')::interval <= now() RETURNING id AS \"id!\"", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - true - ] - }, - "hash": "44a317f7647e2b515f90dc9c04f7ac75c2c87c7c3036acd96ba72fb2a21700db" -} diff --git a/backend/.sqlx/query-44b9bea3651edc8ee732def1241b3d956c004376102ccc1707fc016801599dbd.json b/backend/.sqlx/query-44b9bea3651edc8ee732def1241b3d956c004376102ccc1707fc016801599dbd.json new file mode 100644 index 0000000000..87d384f5a1 --- /dev/null +++ b/backend/.sqlx/query-44b9bea3651edc8ee732def1241b3d956c004376102ccc1707fc016801599dbd.json @@ -0,0 +1,36 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n gcp_trigger \n SET \n gcp_resource_path = $1,\n subscription_id = $2,\n topic_id = $3,\n delivery_type = $4,\n delivery_config = $5,\n is_flow = $6, \n edited_by = $7, \n email = $8,\n script_path = $9,\n path = $10,\n enabled = $11,\n edited_at = now(), \n error = NULL,\n server_id = NULL\n WHERE \n workspace_id = $12 AND \n path = $13\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + { + "Custom": { + "name": "delivery_mode", + "kind": { + "Enum": [ + "push", + "pull" + ] + } + } + }, + "Jsonb", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "44b9bea3651edc8ee732def1241b3d956c004376102ccc1707fc016801599dbd" +} diff --git a/backend/.sqlx/query-44ce12725e09c1e32d82dfe9539ee42065315ac8df5fc8bd8004c0992e6f1bdb.json b/backend/.sqlx/query-44ce12725e09c1e32d82dfe9539ee42065315ac8df5fc8bd8004c0992e6f1bdb.json new file mode 100644 index 0000000000..1369de2911 --- /dev/null +++ b/backend/.sqlx/query-44ce12725e09c1e32d82dfe9539ee42065315ac8df5fc8bd8004c0992e6f1bdb.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE workspace_runnable_dependencies SET flow_path = REGEXP_REPLACE(flow_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE flow_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "44ce12725e09c1e32d82dfe9539ee42065315ac8df5fc8bd8004c0992e6f1bdb" +} diff --git a/backend/.sqlx/query-4504f3a5d3cffd56d51bd263e6759404a3a5889dd7c61cb077e17b877b027eff.json b/backend/.sqlx/query-4504f3a5d3cffd56d51bd263e6759404a3a5889dd7c61cb077e17b877b027eff.json deleted file mode 100644 index 76e4896aaa..0000000000 --- a/backend/.sqlx/query-4504f3a5d3cffd56d51bd263e6759404a3a5889dd7c61cb077e17b877b027eff.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE queue\n SET canceled = true\n , canceled_by = 'timeout'\n , canceled_reason = $1\n WHERE id = $2\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "4504f3a5d3cffd56d51bd263e6759404a3a5889dd7c61cb077e17b877b027eff" -} diff --git a/backend/.sqlx/query-4535c8effd1bae49894d13293a37e1ee949cf9108239032cb3addbf350fb33de.json b/backend/.sqlx/query-4535c8effd1bae49894d13293a37e1ee949cf9108239032cb3addbf350fb33de.json deleted file mode 100644 index a500b04785..0000000000 --- a/backend/.sqlx/query-4535c8effd1bae49894d13293a37e1ee949cf9108239032cb3addbf350fb33de.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n job_kind AS \"job_kind!: JobKind\",\n script_hash AS \"script_hash: ScriptHash\",\n flow_status AS \"flow_status!: Json>\",\n raw_flow AS \"raw_flow: Json>\"\n FROM v2_as_queue WHERE id = $1 AND workspace_id = $2 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "job_kind!: JobKind", - "type_info": { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - } - }, - { - "ordinal": 1, - "name": "script_hash: ScriptHash", - "type_info": "Int8" - }, - { - "ordinal": 2, - "name": "flow_status!: Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 3, - "name": "raw_flow: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true, - true, - true - ] - }, - "hash": "4535c8effd1bae49894d13293a37e1ee949cf9108239032cb3addbf350fb33de" -} diff --git a/backend/.sqlx/query-458e053b93b6e8fc56420fb1ad25cd2910d301b44b6e9c9a861dee3de352f929.json b/backend/.sqlx/query-458e053b93b6e8fc56420fb1ad25cd2910d301b44b6e9c9a861dee3de352f929.json new file mode 100644 index 0000000000..1acc3d1efc --- /dev/null +++ b/backend/.sqlx/query-458e053b93b6e8fc56420fb1ad25cd2910d301b44b6e9c9a861dee3de352f929.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "CREATE INDEX CONCURRENTLY IF NOT EXISTS queue_sort_v2 ON v2_job_queue (priority DESC NULLS LAST, scheduled_for, tag) WHERE running = false", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "458e053b93b6e8fc56420fb1ad25cd2910d301b44b6e9c9a861dee3de352f929" +} diff --git a/backend/.sqlx/query-45950064cce9f53f73a01ddcd6911ec677297009b71041d39019c4700a571c0f.json b/backend/.sqlx/query-45950064cce9f53f73a01ddcd6911ec677297009b71041d39019c4700a571c0f.json deleted file mode 100644 index ecba5ab5c0..0000000000 --- a/backend/.sqlx/query-45950064cce9f53f73a01ddcd6911ec677297009b71041d39019c4700a571c0f.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT raw_flow->'failure_module' != 'null'::jsonb FROM completed_job WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "45950064cce9f53f73a01ddcd6911ec677297009b71041d39019c4700a571c0f" -} diff --git a/backend/.sqlx/query-45d616c92ebcbe30a563e1fa7d2d0e53392e238144b039cfe042587d7fe1dea3.json b/backend/.sqlx/query-45d616c92ebcbe30a563e1fa7d2d0e53392e238144b039cfe042587d7fe1dea3.json deleted file mode 100644 index 052238874a..0000000000 --- a/backend/.sqlx/query-45d616c92ebcbe30a563e1fa7d2d0e53392e238144b039cfe042587d7fe1dea3.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n workflow_as_code_status = jsonb_set(\n jsonb_set(\n COALESCE(workflow_as_code_status, '{}'::jsonb),\n array[$1],\n COALESCE(workflow_as_code_status->$1, '{}'::jsonb)\n ),\n array[$1, 'started_at'],\n to_jsonb(now()::text)\n )\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "45d616c92ebcbe30a563e1fa7d2d0e53392e238144b039cfe042587d7fe1dea3" -} diff --git a/backend/.sqlx/query-4622d28e2fa09bc60b9d0c79397efe0ca030638ded82c2ffd2155cacdf36ec11.json b/backend/.sqlx/query-4622d28e2fa09bc60b9d0c79397efe0ca030638ded82c2ffd2155cacdf36ec11.json new file mode 100644 index 0000000000..f30ab8d49d --- /dev/null +++ b/backend/.sqlx/query-4622d28e2fa09bc60b9d0c79397efe0ca030638ded82c2ffd2155cacdf36ec11.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['step'], $1)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "4622d28e2fa09bc60b9d0c79397efe0ca030638ded82c2ffd2155cacdf36ec11" +} diff --git a/backend/.sqlx/query-4671f1727d0563490534c426375738478f3d93f6bb42aaf021794392328c8875.json b/backend/.sqlx/query-4671f1727d0563490534c426375738478f3d93f6bb42aaf021794392328c8875.json deleted file mode 100644 index e497d7f59e..0000000000 --- a/backend/.sqlx/query-4671f1727d0563490534c426375738478f3d93f6bb42aaf021794392328c8875.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT created_by AS \"created_by!\", args as \"args: sqlx::types::Json>\"\n FROM completed_job \n WHERE id = $1 AND workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_by!", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "args: sqlx::types::Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "TextArray" - ] - }, - "nullable": [ - true, - true - ] - }, - "hash": "4671f1727d0563490534c426375738478f3d93f6bb42aaf021794392328c8875" -} diff --git a/backend/.sqlx/query-47455b0ebaf999ab58b2cba3d74cb4bdd64075939a4aa2c117e18372511ea7e0.json b/backend/.sqlx/query-47455b0ebaf999ab58b2cba3d74cb4bdd64075939a4aa2c117e18372511ea7e0.json deleted file mode 100644 index 6ccaeece1c..0000000000 --- a/backend/.sqlx/query-47455b0ebaf999ab58b2cba3d74cb4bdd64075939a4aa2c117e18372511ea7e0.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "VACUUM (skip_locked) v2_job_queue, v2_job_runtime, v2_job_status", - "describe": { - "columns": [], - "parameters": { - "Left": [] - }, - "nullable": [] - }, - "hash": "47455b0ebaf999ab58b2cba3d74cb4bdd64075939a4aa2c117e18372511ea7e0" -} diff --git a/backend/.sqlx/query-4844a797ddec207f1d2cbf41836e65d6435840f6d0740f300d7c2cf88f8fe46a.json b/backend/.sqlx/query-4844a797ddec207f1d2cbf41836e65d6435840f6d0740f300d7c2cf88f8fe46a.json new file mode 100644 index 0000000000..3f66c4c9b4 --- /dev/null +++ b/backend/.sqlx/query-4844a797ddec207f1d2cbf41836e65d6435840f6d0740f300d7c2cf88f8fe46a.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n mqtt_trigger\n SET \n last_server_ping = now(),\n error = $1\n WHERE\n workspace_id = $2\n AND path = $3\n AND server_id = $4 \n AND enabled IS TRUE\n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "4844a797ddec207f1d2cbf41836e65d6435840f6d0740f300d7c2cf88f8fe46a" +} diff --git a/backend/.sqlx/query-488dd591096b2b47787afdc3a1d73917ed13269f2ee20b86df79fca2c8efe672.json b/backend/.sqlx/query-488dd591096b2b47787afdc3a1d73917ed13269f2ee20b86df79fca2c8efe672.json index 8622eb9125..da5eb1ceea 100644 --- a/backend/.sqlx/query-488dd591096b2b47787afdc3a1d73917ed13269f2ee20b86df79fca2c8efe672.json +++ b/backend/.sqlx/query-488dd591096b2b47787afdc3a1d73917ed13269f2ee20b86df79fca2c8efe672.json @@ -68,7 +68,9 @@ "rust", "ansible", "csharp", - "oracledb" + "oracledb", + "nu", + "java" ] } } diff --git a/backend/.sqlx/query-8ff25d890d3f7019c5c6f2b47f29f4c7b2c91094036d9b5037ef9a4ac986ee53.json b/backend/.sqlx/query-4931d4752357078ae3ae01f37742639dba0dde680ab934ba78abcb5fdda8117a.json similarity index 57% rename from backend/.sqlx/query-8ff25d890d3f7019c5c6f2b47f29f4c7b2c91094036d9b5037ef9a4ac986ee53.json rename to backend/.sqlx/query-4931d4752357078ae3ae01f37742639dba0dde680ab934ba78abcb5fdda8117a.json index de933d99e1..df88f6e892 100644 --- a/backend/.sqlx/query-8ff25d890d3f7019c5c6f2b47f29f4c7b2c91094036d9b5037ef9a4ac986ee53.json +++ b/backend/.sqlx/query-4931d4752357078ae3ae01f37742639dba0dde680ab934ba78abcb5fdda8117a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, static_asset_config as \"static_asset_config: _\", is_static_website FROM http_trigger WHERE workspace_id = $1 AND http_method = $2", + "query": "SELECT * FROM postgres_trigger\n WHERE workspace_id = $1", "describe": { "columns": [ { @@ -20,62 +20,68 @@ }, { "ordinal": 3, - "name": "route_path", - "type_info": "Varchar" - }, - { - "ordinal": 4, "name": "workspace_id", "type_info": "Varchar" }, { - "ordinal": 5, - "name": "is_async", - "type_info": "Bool" - }, - { - "ordinal": 6, - "name": "requires_auth", - "type_info": "Bool" - }, - { - "ordinal": 7, + "ordinal": 4, "name": "edited_by", "type_info": "Varchar" }, { - "ordinal": 8, + "ordinal": 5, "name": "email", "type_info": "Varchar" }, { - "ordinal": 9, - "name": "static_asset_config: _", + "ordinal": 6, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 7, + "name": "extra_perms", "type_info": "Jsonb" }, + { + "ordinal": 8, + "name": "postgres_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "error", + "type_info": "Text" + }, { "ordinal": 10, - "name": "is_static_website", + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 12, + "name": "replication_slot_name", + "type_info": "Varchar" + }, + { + "ordinal": 13, + "name": "publication_name", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "enabled", "type_info": "Bool" } ], "parameters": { "Left": [ - "Text", - { - "Custom": { - "name": "http_method", - "kind": { - "Enum": [ - "get", - "post", - "put", - "delete", - "patch" - ] - } - } - } + "Text" ] }, "nullable": [ @@ -86,11 +92,15 @@ false, false, false, - false, + true, false, true, + true, + true, + false, + false, false ] }, - "hash": "8ff25d890d3f7019c5c6f2b47f29f4c7b2c91094036d9b5037ef9a4ac986ee53" + "hash": "4931d4752357078ae3ae01f37742639dba0dde680ab934ba78abcb5fdda8117a" } diff --git a/backend/.sqlx/query-4a1cb9f3ad7f2a692dadb9f75cdc99135af62ccaae5ca122d88356fe7da6eedc.json b/backend/.sqlx/query-4a1cb9f3ad7f2a692dadb9f75cdc99135af62ccaae5ca122d88356fe7da6eedc.json deleted file mode 100644 index be6e8f22e7..0000000000 --- a/backend/.sqlx/query-4a1cb9f3ad7f2a692dadb9f75cdc99135af62ccaae5ca122d88356fe7da6eedc.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT script_entrypoint_override FROM v2_job WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "script_entrypoint_override", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - true - ] - }, - "hash": "4a1cb9f3ad7f2a692dadb9f75cdc99135af62ccaae5ca122d88356fe7da6eedc" -} diff --git a/backend/.sqlx/query-4a804ee30bfe86c4e2c15a9f6511be5adf0dd22cb942fac64b439fb4e20df447.json b/backend/.sqlx/query-4a804ee30bfe86c4e2c15a9f6511be5adf0dd22cb942fac64b439fb4e20df447.json deleted file mode 100644 index 1c75a48938..0000000000 --- a/backend/.sqlx/query-4a804ee30bfe86c4e2c15a9f6511be5adf0dd22cb942fac64b439fb4e20df447.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO metrics (id, value) \n VALUES ('no_uv_usage_ansible', $1)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "4a804ee30bfe86c4e2c15a9f6511be5adf0dd22cb942fac64b439fb4e20df447" -} diff --git a/backend/.sqlx/query-4aaab98ebdaa90f1edf49ac96fba6c391c4d0054a618b861464ee37239f1f1e0.json b/backend/.sqlx/query-4aaab98ebdaa90f1edf49ac96fba6c391c4d0054a618b861464ee37239f1f1e0.json new file mode 100644 index 0000000000..d8a93c797d --- /dev/null +++ b/backend/.sqlx/query-4aaab98ebdaa90f1edf49ac96fba6c391c4d0054a618b861464ee37239f1f1e0.json @@ -0,0 +1,277 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT \n v2_job_queue.workspace_id,\n v2_job_queue.id,\n v2_job.args as \"args: sqlx::types::Json>>\",\n v2_job.parent_job,\n v2_job.created_by,\n v2_job_queue.started_at,\n scheduled_for,\n runnable_path,\n kind as \"kind: JobKind\",\n runnable_id as \"runnable_id: ScriptHash\",\n canceled_reason,\n canceled_by,\n permissioned_as,\n permissioned_as_email,\n flow_status as \"flow_status: sqlx::types::Json>\",\n v2_job.tag,\n script_lang as \"script_lang: ScriptLang\",\n same_worker,\n pre_run_error,\n concurrent_limit,\n concurrency_time_window_s,\n flow_innermost_root_job,\n timeout,\n flow_step_id,\n cache_ttl,\n v2_job_queue.priority,\n preprocessed,\n script_entrypoint_override,\n trigger,\n trigger_kind as \"trigger_kind: JobTriggerKind\",\n visible_to_owner\n FROM v2_job_queue INNER JOIN v2_job ON v2_job.id = v2_job_queue.id LEFT JOIN v2_job_status ON v2_job_status.id = v2_job_queue.id WHERE v2_job_queue.id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 2, + "name": "args: sqlx::types::Json>>", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "parent_job", + "type_info": "Uuid" + }, + { + "ordinal": 4, + "name": "created_by", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "started_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 6, + "name": "scheduled_for", + "type_info": "Timestamptz" + }, + { + "ordinal": 7, + "name": "runnable_path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "kind: JobKind", + "type_info": { + "Custom": { + "name": "job_kind", + "kind": { + "Enum": [ + "script", + "preview", + "flow", + "dependencies", + "flowpreview", + "script_hub", + "identity", + "flowdependencies", + "http", + "graphql", + "postgresql", + "noop", + "appdependencies", + "deploymentcallback", + "singlescriptflow", + "flowscript", + "flownode", + "appscript" + ] + } + } + } + }, + { + "ordinal": 9, + "name": "runnable_id: ScriptHash", + "type_info": "Int8" + }, + { + "ordinal": 10, + "name": "canceled_reason", + "type_info": "Text" + }, + { + "ordinal": 11, + "name": "canceled_by", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "permissioned_as", + "type_info": "Varchar" + }, + { + "ordinal": 13, + "name": "permissioned_as_email", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "flow_status: sqlx::types::Json>", + "type_info": "Jsonb" + }, + { + "ordinal": 15, + "name": "tag", + "type_info": "Varchar" + }, + { + "ordinal": 16, + "name": "script_lang: ScriptLang", + "type_info": { + "Custom": { + "name": "script_lang", + "kind": { + "Enum": [ + "python3", + "deno", + "go", + "bash", + "postgresql", + "nativets", + "bun", + "mysql", + "bigquery", + "snowflake", + "graphql", + "powershell", + "mssql", + "php", + "bunnative", + "rust", + "ansible", + "csharp", + "oracledb", + "nu", + "java" + ] + } + } + } + }, + { + "ordinal": 17, + "name": "same_worker", + "type_info": "Bool" + }, + { + "ordinal": 18, + "name": "pre_run_error", + "type_info": "Text" + }, + { + "ordinal": 19, + "name": "concurrent_limit", + "type_info": "Int4" + }, + { + "ordinal": 20, + "name": "concurrency_time_window_s", + "type_info": "Int4" + }, + { + "ordinal": 21, + "name": "flow_innermost_root_job", + "type_info": "Uuid" + }, + { + "ordinal": 22, + "name": "timeout", + "type_info": "Int4" + }, + { + "ordinal": 23, + "name": "flow_step_id", + "type_info": "Varchar" + }, + { + "ordinal": 24, + "name": "cache_ttl", + "type_info": "Int4" + }, + { + "ordinal": 25, + "name": "priority", + "type_info": "Int2" + }, + { + "ordinal": 26, + "name": "preprocessed", + "type_info": "Bool" + }, + { + "ordinal": 27, + "name": "script_entrypoint_override", + "type_info": "Varchar" + }, + { + "ordinal": 28, + "name": "trigger", + "type_info": "Varchar" + }, + { + "ordinal": 29, + "name": "trigger_kind: JobTriggerKind", + "type_info": { + "Custom": { + "name": "job_trigger_kind", + "kind": { + "Enum": [ + "webhook", + "http", + "websocket", + "kafka", + "email", + "nats", + "schedule", + "app", + "ui", + "postgres", + "sqs", + "gcp" + ] + } + } + } + }, + { + "ordinal": 30, + "name": "visible_to_owner", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false, + false, + true, + true, + false, + true, + false, + true, + false, + true, + true, + true, + false, + false, + true, + false, + true, + false, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + false + ] + }, + "hash": "4aaab98ebdaa90f1edf49ac96fba6c391c4d0054a618b861464ee37239f1f1e0" +} diff --git a/backend/.sqlx/query-f8f893fb4f6f8c16bffb233a585df0b8c007c24993979fdc013aa57583905f50.json b/backend/.sqlx/query-4b8132b04e454eddfe6724c6cc3a2e60c9c24decb2a6b41125247bbf741e9c25.json similarity index 57% rename from backend/.sqlx/query-f8f893fb4f6f8c16bffb233a585df0b8c007c24993979fdc013aa57583905f50.json rename to backend/.sqlx/query-4b8132b04e454eddfe6724c6cc3a2e60c9c24decb2a6b41125247bbf741e9c25.json index 759f91c962..ab5b04afc6 100644 --- a/backend/.sqlx/query-f8f893fb4f6f8c16bffb233a585df0b8c007c24993979fdc013aa57583905f50.json +++ b/backend/.sqlx/query-4b8132b04e454eddfe6724c6cc3a2e60c9c24decb2a6b41125247bbf741e9c25.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT owner, premium, usage.usage as \"usage?\", workspace_settings.customer_id, workspace_settings.plan, workspace_settings.automatic_billing FROM workspace LEFT JOIN workspace_settings ON workspace_settings.workspace_id = $1 LEFT JOIN usage ON usage.id = $1 AND month_ = EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date) AND usage.is_workspace IS true WHERE workspace.id = $1", + "query": "SELECT owner, premium, usage.usage as \"usage?\", workspace_settings.customer_id, workspace_settings.plan FROM workspace LEFT JOIN workspace_settings ON workspace_settings.workspace_id = $1 LEFT JOIN usage ON usage.id = $1 AND month_ = EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date) AND usage.is_workspace IS true WHERE workspace.id = $1", "describe": { "columns": [ { @@ -27,11 +27,6 @@ "ordinal": 4, "name": "plan", "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "automatic_billing", - "type_info": "Bool" } ], "parameters": { @@ -44,9 +39,8 @@ false, false, true, - true, - false + true ] }, - "hash": "f8f893fb4f6f8c16bffb233a585df0b8c007c24993979fdc013aa57583905f50" + "hash": "4b8132b04e454eddfe6724c6cc3a2e60c9c24decb2a6b41125247bbf741e9c25" } diff --git a/backend/.sqlx/query-4b923c94f6adcc7a76e8073de5e46b116dba3211487c8408ce2777aafdf94a44.json b/backend/.sqlx/query-4b923c94f6adcc7a76e8073de5e46b116dba3211487c8408ce2777aafdf94a44.json deleted file mode 100644 index 9d14bc62cc..0000000000 --- a/backend/.sqlx/query-4b923c94f6adcc7a76e8073de5e46b116dba3211487c8408ce2777aafdf94a44.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO job (id, workspace_id, raw_code, raw_lock, raw_flow, tag)\n VALUES ($1, $2, $3, $4, $5, $6)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Varchar", - "Text", - "Text", - "Jsonb", - "Varchar" - ] - }, - "nullable": [] - }, - "hash": "4b923c94f6adcc7a76e8073de5e46b116dba3211487c8408ce2777aafdf94a44" -} diff --git a/backend/.sqlx/query-4c970f10d345bcdcf956dcbfa22b6e80888e511fb4787cb1a7976878abed1d30.json b/backend/.sqlx/query-4c970f10d345bcdcf956dcbfa22b6e80888e511fb4787cb1a7976878abed1d30.json deleted file mode 100644 index 26894a04ef..0000000000 --- a/backend/.sqlx/query-4c970f10d345bcdcf956dcbfa22b6e80888e511fb4787cb1a7976878abed1d30.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT premium FROM workspace WHERE workspace.id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "premium", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "4c970f10d345bcdcf956dcbfa22b6e80888e511fb4787cb1a7976878abed1d30" -} diff --git a/backend/.sqlx/query-4c97fcc93b31c4b3262419d6ee183773a95de3b6694a398cb80d288bef4f130f.json b/backend/.sqlx/query-4c97fcc93b31c4b3262419d6ee183773a95de3b6694a398cb80d288bef4f130f.json deleted file mode 100644 index 53a59847e7..0000000000 --- a/backend/.sqlx/query-4c97fcc93b31c4b3262419d6ee183773a95de3b6694a398cb80d288bef4f130f.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE completed_job SET args = '{\"reason\":\"PREPROCESSOR_ARGS_ARE_DISCARDED\"}'::jsonb WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "4c97fcc93b31c4b3262419d6ee183773a95de3b6694a398cb80d288bef4f130f" -} diff --git a/backend/.sqlx/query-4c9cf8c3176abc2b8b9a1c3f671949e16830671a458d5a73762cd8545d26172d.json b/backend/.sqlx/query-4c9cf8c3176abc2b8b9a1c3f671949e16830671a458d5a73762cd8545d26172d.json deleted file mode 100644 index 0d108538dc..0000000000 --- a/backend/.sqlx/query-4c9cf8c3176abc2b8b9a1c3f671949e16830671a458d5a73762cd8545d26172d.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2),\n ARRAY['step'],\n $3\n )\n WHERE id = $4", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "4c9cf8c3176abc2b8b9a1c3f671949e16830671a458d5a73762cd8545d26172d" -} diff --git a/backend/.sqlx/query-4cdb9b9d562f3c692e5597598db937511bc8431c3652746684ee803172053885.json b/backend/.sqlx/query-4cdb9b9d562f3c692e5597598db937511bc8431c3652746684ee803172053885.json deleted file mode 100644 index 48a2a5451d..0000000000 --- a/backend/.sqlx/query-4cdb9b9d562f3c692e5597598db937511bc8431c3652746684ee803172053885.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id FROM queue WHERE parent_job = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "4cdb9b9d562f3c692e5597598db937511bc8431c3652746684ee803172053885" -} diff --git a/backend/.sqlx/query-4cfa57b2a836242d55071ef237855b21bbc487b80bda7fd6250ac12e93938577.json b/backend/.sqlx/query-4cfa57b2a836242d55071ef237855b21bbc487b80bda7fd6250ac12e93938577.json deleted file mode 100644 index 5ac1cd561e..0000000000 --- a/backend/.sqlx/query-4cfa57b2a836242d55071ef237855b21bbc487b80bda7fd6250ac12e93938577.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO healthchecks (check_type, healthy) \n SELECT 'min_alive_workers_' || $1, true \n WHERE NOT EXISTS (\n SELECT 1 FROM healthchecks \n WHERE check_type = 'min_alive_workers_' || $1 AND created_at > NOW() - INTERVAL '2 minutes'\n )\n RETURNING id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "4cfa57b2a836242d55071ef237855b21bbc487b80bda7fd6250ac12e93938577" -} diff --git a/backend/.sqlx/query-4d3ef32120623584bf5c13d86ea6ad7b3aa41d9b581738d16fbfff4cc5b72a7a.json b/backend/.sqlx/query-4d3ef32120623584bf5c13d86ea6ad7b3aa41d9b581738d16fbfff4cc5b72a7a.json deleted file mode 100644 index 9fad8009ec..0000000000 --- a/backend/.sqlx/query-4d3ef32120623584bf5c13d86ea6ad7b3aa41d9b581738d16fbfff4cc5b72a7a.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT canceled AS \"canceled!\" FROM queue WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "canceled!", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - true - ] - }, - "hash": "4d3ef32120623584bf5c13d86ea6ad7b3aa41d9b581738d16fbfff4cc5b72a7a" -} diff --git a/backend/.sqlx/query-4d4aa16b2a55e57f9376d0cb253e671525969fb5f528ae07576e9dc5e77af1f1.json b/backend/.sqlx/query-4d4aa16b2a55e57f9376d0cb253e671525969fb5f528ae07576e9dc5e77af1f1.json new file mode 100644 index 0000000000..3dea443954 --- /dev/null +++ b/backend/.sqlx/query-4d4aa16b2a55e57f9376d0cb253e671525969fb5f528ae07576e9dc5e77af1f1.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DROP INDEX CONCURRENTLY IF EXISTS ix_job_workspace_id_created_at_new_7", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "4d4aa16b2a55e57f9376d0cb253e671525969fb5f528ae07576e9dc5e77af1f1" +} diff --git a/backend/.sqlx/query-b57a188ca137162a2848ebf81fa3aeca9a14ac628c61a358e2c6612c57249b0f.json b/backend/.sqlx/query-4e07dcc1ba4396ada2f1080a400ad9fad00b1d77ea44c8639b7598c5953635ff.json similarity index 56% rename from backend/.sqlx/query-b57a188ca137162a2848ebf81fa3aeca9a14ac628c61a358e2c6612c57249b0f.json rename to backend/.sqlx/query-4e07dcc1ba4396ada2f1080a400ad9fad00b1d77ea44c8639b7598c5953635ff.json index a730d4b588..506dce48dc 100644 --- a/backend/.sqlx/query-b57a188ca137162a2848ebf81fa3aeca9a14ac628c61a358e2c6612c57249b0f.json +++ b/backend/.sqlx/query-4e07dcc1ba4396ada2f1080a400ad9fad00b1d77ea44c8639b7598c5953635ff.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1 AND path = $2)", + "query": "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 AND archived = false ORDER BY created_at DESC LIMIT 1)", "describe": { "columns": [ { @@ -19,5 +19,5 @@ null ] }, - "hash": "b57a188ca137162a2848ebf81fa3aeca9a14ac628c61a358e2c6612c57249b0f" + "hash": "4e07dcc1ba4396ada2f1080a400ad9fad00b1d77ea44c8639b7598c5953635ff" } diff --git a/backend/.sqlx/query-4e3469185637ad8f06673261bfae1b38c30f7d2689baf647ea4dc6b398c3a651.json b/backend/.sqlx/query-4e3469185637ad8f06673261bfae1b38c30f7d2689baf647ea4dc6b398c3a651.json new file mode 100644 index 0000000000..c6a6f72876 --- /dev/null +++ b/backend/.sqlx/query-4e3469185637ad8f06673261bfae1b38c30f7d2689baf647ea4dc6b398c3a651.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT elem\n FROM workspace_settings,\n LATERAL jsonb_array_elements(git_app_installations) AS elem\n WHERE workspace_id = $1\n AND (elem->>'installation_id')::bigint = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "elem", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + }, + "nullable": [ + null + ] + }, + "hash": "4e3469185637ad8f06673261bfae1b38c30f7d2689baf647ea4dc6b398c3a651" +} diff --git a/backend/.sqlx/query-4ed74bbda2ad0ca5e4648787fe2d9c89e9b83571e30059861b6998935b35f78b.json b/backend/.sqlx/query-4ed74bbda2ad0ca5e4648787fe2d9c89e9b83571e30059861b6998935b35f78b.json new file mode 100644 index 0000000000..e5b87450d3 --- /dev/null +++ b/backend/.sqlx/query-4ed74bbda2ad0ca5e4648787fe2d9c89e9b83571e30059861b6998935b35f78b.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT EXISTS(\n SELECT \n 1 \n FROM \n mqtt_trigger \n WHERE \n path = $1 AND \n workspace_id = $2\n )", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "4ed74bbda2ad0ca5e4648787fe2d9c89e9b83571e30059861b6998935b35f78b" +} diff --git a/backend/.sqlx/query-4edf05cbf35325d444de0e74ee070aafd27ef61c940daba186e7e66f668c31ed.json b/backend/.sqlx/query-4edf05cbf35325d444de0e74ee070aafd27ef61c940daba186e7e66f668c31ed.json deleted file mode 100644 index 4eae8c22fb..0000000000 --- a/backend/.sqlx/query-4edf05cbf35325d444de0e74ee070aafd27ef61c940daba186e7e66f668c31ed.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE workspace_settings SET ai_resource = $1, code_completion_model = $2, ai_models = $3 WHERE workspace_id = $4", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Varchar", - "VarcharArray", - "Text" - ] - }, - "nullable": [] - }, - "hash": "4edf05cbf35325d444de0e74ee070aafd27ef61c940daba186e7e66f668c31ed" -} diff --git a/backend/.sqlx/query-4fb3a4712d88afed40082d8d8bd63b5dedad61caa68e0e470252083d80df605f.json b/backend/.sqlx/query-4fb3a4712d88afed40082d8d8bd63b5dedad61caa68e0e470252083d80df605f.json deleted file mode 100644 index 90bdd1c9b8..0000000000 --- a/backend/.sqlx/query-4fb3a4712d88afed40082d8d8bd63b5dedad61caa68e0e470252083d80df605f.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET suspend = 0 WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "4fb3a4712d88afed40082d8d8bd63b5dedad61caa68e0e470252083d80df605f" -} diff --git a/backend/.sqlx/query-505250098ab003ff0ca30046df283e54bf44be74305070f10a5720a04c4789f3.json b/backend/.sqlx/query-505250098ab003ff0ca30046df283e54bf44be74305070f10a5720a04c4789f3.json new file mode 100644 index 0000000000..fb3fbe4f0e --- /dev/null +++ b/backend/.sqlx/query-505250098ab003ff0ca30046df283e54bf44be74305070f10a5720a04c4789f3.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_queue\n SET canceled_by = $1\n , canceled_reason = $2\nWHERE id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "505250098ab003ff0ca30046df283e54bf44be74305070f10a5720a04c4789f3" +} diff --git a/backend/.sqlx/query-e968e879d3c52f7dd502c3cd15fc8fbd983a4a3ab25648c562497a27c74b5c8c.json b/backend/.sqlx/query-506066203c49424e9944eb3948dc1657d3d796e6233e9f0ec925879c705d4773.json similarity index 64% rename from backend/.sqlx/query-e968e879d3c52f7dd502c3cd15fc8fbd983a4a3ab25648c562497a27c74b5c8c.json rename to backend/.sqlx/query-506066203c49424e9944eb3948dc1657d3d796e6233e9f0ec925879c705d4773.json index 3bacda16d9..50d4a9595d 100644 --- a/backend/.sqlx/query-e968e879d3c52f7dd502c3cd15fc8fbd983a4a3ab25648c562497a27c74b5c8c.json +++ b/backend/.sqlx/query-506066203c49424e9944eb3948dc1657d3d796e6233e9f0ec925879c705d4773.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE worker_ping SET ping_at = now(), current_job_id = $1, current_job_workspace_id = $2, memory_usage = $3, wm_memory_usage = $4,\n occupancy_rate = $6, occupancy_rate_15s = $7, occupancy_rate_5m = $8, occupancy_rate_30m = $9 WHERE worker = $5", + "query": "UPDATE worker_ping SET ping_at = now(), current_job_id = $1, current_job_workspace_id = $2, memory_usage = $3, wm_memory_usage = $4,\n occupancy_rate = $6, occupancy_rate_15s = $7, occupancy_rate_5m = $8, occupancy_rate_30m = $9 WHERE worker = $5", "describe": { "columns": [], "parameters": { @@ -18,5 +18,5 @@ }, "nullable": [] }, - "hash": "e968e879d3c52f7dd502c3cd15fc8fbd983a4a3ab25648c562497a27c74b5c8c" + "hash": "506066203c49424e9944eb3948dc1657d3d796e6233e9f0ec925879c705d4773" } diff --git a/backend/.sqlx/query-50c17c7848760aaf0f869acfc444caecda6335eda5b2e97e5a7370361653ff48.json b/backend/.sqlx/query-50c17c7848760aaf0f869acfc444caecda6335eda5b2e97e5a7370361653ff48.json new file mode 100644 index 0000000000..f3dc153254 --- /dev/null +++ b/backend/.sqlx/query-50c17c7848760aaf0f869acfc444caecda6335eda5b2e97e5a7370361653ff48.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n WITH assigned_teams AS (\n SELECT teams_team_id\n FROM workspace_settings\n ),\n all_teams AS (\n SELECT jsonb_array_elements(CASE\n WHEN jsonb_typeof(value::jsonb) = 'array' THEN value::jsonb\n ELSE '[]'::jsonb\n END) AS team\n FROM global_settings\n WHERE name = 'teams'\n )\n SELECT team->>'team_name' AS team_name, team->>'team_internal_id' AS team_id\n FROM all_teams\n WHERE NOT EXISTS (\n SELECT 1\n FROM assigned_teams\n WHERE assigned_teams.teams_team_id = team->>'team_internal_id'\n )\n AND team->>'team_name' IS NOT NULL\n AND team->>'team_id' IS NOT NULL\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "team_name", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "team_id", + "type_info": "Text" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null, + null + ] + }, + "hash": "50c17c7848760aaf0f869acfc444caecda6335eda5b2e97e5a7370361653ff48" +} diff --git a/backend/.sqlx/query-51ddbfec67af268d9bbee12b2730d6109d2a6633e62ce708bad8af1a9f8c3925.json b/backend/.sqlx/query-51ddbfec67af268d9bbee12b2730d6109d2a6633e62ce708bad8af1a9f8c3925.json deleted file mode 100644 index e664011467..0000000000 --- a/backend/.sqlx/query-51ddbfec67af268d9bbee12b2730d6109d2a6633e62ce708bad8af1a9f8c3925.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH ping AS (UPDATE v2_job_runtime SET ping = NULL WHERE id = $2 RETURNING id)\n UPDATE v2_job_queue SET\n running = false,\n started_at = null,\n scheduled_for = $1\n WHERE id = (SELECT id FROM ping)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Timestamptz", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "51ddbfec67af268d9bbee12b2730d6109d2a6633e62ce708bad8af1a9f8c3925" -} diff --git a/backend/.sqlx/query-51ef2ee9dc252f7accc41b89d13a2aa1e4c11d4860894e9e661b6ee3bccd8522.json b/backend/.sqlx/query-51ef2ee9dc252f7accc41b89d13a2aa1e4c11d4860894e9e661b6ee3bccd8522.json new file mode 100644 index 0000000000..7cd9c5ce22 --- /dev/null +++ b/backend/.sqlx/query-51ef2ee9dc252f7accc41b89d13a2aa1e4c11d4860894e9e661b6ee3bccd8522.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_completed c SET\n result = NULL,\n deleted = TRUE\n FROM v2_job j\n WHERE c.id = $1\n AND j.id = c.id\n AND c.workspace_id = $2\n AND ($3::TEXT[] IS NULL OR tag = ANY($3))\n RETURNING c.id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text", + "TextArray" + ] + }, + "nullable": [ + false + ] + }, + "hash": "51ef2ee9dc252f7accc41b89d13a2aa1e4c11d4860894e9e661b6ee3bccd8522" +} diff --git a/backend/.sqlx/query-5237f9d3f57f4e799968a4a9630c984775d23c7a4dc1a7ad37528a46ab89d6bd.json b/backend/.sqlx/query-5237f9d3f57f4e799968a4a9630c984775d23c7a4dc1a7ad37528a46ab89d6bd.json new file mode 100644 index 0000000000..f9c6646e3a --- /dev/null +++ b/backend/.sqlx/query-5237f9d3f57f4e799968a4a9630c984775d23c7a4dc1a7ad37528a46ab89d6bd.json @@ -0,0 +1,123 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n aws_auth_resource_type AS \"aws_auth_resource_type: _\",\n aws_resource_path,\n message_attributes,\n queue_url,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n sqs_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "aws_auth_resource_type: _", + "type_info": { + "Custom": { + "name": "aws_auth_resource_type", + "kind": { + "Enum": [ + "oidc", + "credentials" + ] + } + } + } + }, + { + "ordinal": 1, + "name": "aws_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "message_attributes", + "type_info": "TextArray" + }, + { + "ordinal": 3, + "name": "queue_url", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 8, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 11, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 14, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 15, + "name": "enabled", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + true, + true, + false + ] + }, + "hash": "5237f9d3f57f4e799968a4a9630c984775d23c7a4dc1a7ad37528a46ab89d6bd" +} diff --git a/backend/.sqlx/query-525a9ef57c7d9fac86cb1bf47868fa6fb782e9d589852e51530cdd1a38322a9d.json b/backend/.sqlx/query-525a9ef57c7d9fac86cb1bf47868fa6fb782e9d589852e51530cdd1a38322a9d.json new file mode 100644 index 0000000000..73c1a95797 --- /dev/null +++ b/backend/.sqlx/query-525a9ef57c7d9fac86cb1bf47868fa6fb782e9d589852e51530cdd1a38322a9d.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "525a9ef57c7d9fac86cb1bf47868fa6fb782e9d589852e51530cdd1a38322a9d" +} diff --git a/backend/.sqlx/query-52ad0c838d19cbd9e90b8368abe71dd12655179f41f43896e7d30fdfb3ae5939.json b/backend/.sqlx/query-52ad0c838d19cbd9e90b8368abe71dd12655179f41f43896e7d30fdfb3ae5939.json new file mode 100644 index 0000000000..1fbc39ba44 --- /dev/null +++ b/backend/.sqlx/query-52ad0c838d19cbd9e90b8368abe71dd12655179f41f43896e7d30fdfb3ae5939.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH job_result AS (\n SELECT result \n FROM v2_job_completed \n WHERE id = $1\n )\n UPDATE v2_job \n SET args = COALESCE(\n CASE \n WHEN job_result.result IS NULL THEN NULL\n WHEN jsonb_typeof(job_result.result) = 'object' \n THEN job_result.result\n WHEN jsonb_typeof(job_result.result) = 'null'\n THEN NULL\n ELSE jsonb_build_object('value', job_result.result)\n END, \n '{}'::jsonb\n ),\n preprocessed = TRUE\n FROM job_result\n WHERE v2_job.id = $2;\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "52ad0c838d19cbd9e90b8368abe71dd12655179f41f43896e7d30fdfb3ae5939" +} diff --git a/backend/.sqlx/query-52bd8efeaec0d0c2aa77d777a0b6559a1aa4ca9ebd4f9b535014cbcb113f9b92.json b/backend/.sqlx/query-52bd8efeaec0d0c2aa77d777a0b6559a1aa4ca9ebd4f9b535014cbcb113f9b92.json deleted file mode 100644 index b4576d8682..0000000000 --- a/backend/.sqlx/query-52bd8efeaec0d0c2aa77d777a0b6559a1aa4ca9ebd4f9b535014cbcb113f9b92.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT created_by AS \"created_by!\", args as \"args: sqlx::types::Json>\"\n FROM queue\n WHERE id = $1 AND workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_by!", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "args: sqlx::types::Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "TextArray" - ] - }, - "nullable": [ - true, - true - ] - }, - "hash": "52bd8efeaec0d0c2aa77d777a0b6559a1aa4ca9ebd4f9b535014cbcb113f9b92" -} diff --git a/backend/.sqlx/query-5303206bbed76ee3ddc56d8057d8b82359c182ee2a5da6df35a4375d5d2d1ef7.json b/backend/.sqlx/query-5303206bbed76ee3ddc56d8057d8b82359c182ee2a5da6df35a4375d5d2d1ef7.json new file mode 100644 index 0000000000..f010362e6f --- /dev/null +++ b/backend/.sqlx/query-5303206bbed76ee3ddc56d8057d8b82359c182ee2a5da6df35a4375d5d2d1ef7.json @@ -0,0 +1,65 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n gcp_resource_path, \n script_path,\n is_flow, \n workspace_id,\n path,\n edited_by,\n email,\n delivery_config AS \"delivery_config: _\"\n FROM\n gcp_trigger\n WHERE\n workspace_id = $1 AND\n path = $2 AND\n delivery_type = 'push'::DELIVERY_MODE \n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "gcp_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 3, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "delivery_config: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + true + ] + }, + "hash": "5303206bbed76ee3ddc56d8057d8b82359c182ee2a5da6df35a4375d5d2d1ef7" +} diff --git a/backend/.sqlx/query-53ff0e14c35a3e84585a699e55093546db5c5f0ad0c5f92f34aaf2cdd125d130.json b/backend/.sqlx/query-53ff0e14c35a3e84585a699e55093546db5c5f0ad0c5f92f34aaf2cdd125d130.json deleted file mode 100644 index ba39c47062..0000000000 --- a/backend/.sqlx/query-53ff0e14c35a3e84585a699e55093546db5c5f0ad0c5f92f34aaf2cdd125d130.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET suspend = $1, suspend_until = now() + interval '14 day', running = true\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int4", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "53ff0e14c35a3e84585a699e55093546db5c5f0ad0c5f92f34aaf2cdd125d130" -} diff --git a/backend/.sqlx/query-548a1424a6b9ac9998b8fc5312bcff671ee7fe59154e9ef32800c117e64bce19.json b/backend/.sqlx/query-548a1424a6b9ac9998b8fc5312bcff671ee7fe59154e9ef32800c117e64bce19.json new file mode 100644 index 0000000000..554b0fff0b --- /dev/null +++ b/backend/.sqlx/query-548a1424a6b9ac9998b8fc5312bcff671ee7fe59154e9ef32800c117e64bce19.json @@ -0,0 +1,50 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n path,\n is_flow,\n workspace_id,\n owner,\n email,\n trigger_config as \"trigger_config!: _\"\n FROM\n capture_config\n WHERE\n trigger_kind = 'mqtt' AND\n last_client_ping > NOW() - INTERVAL '10 seconds' AND\n trigger_config IS NOT NULL AND\n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds')\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "owner", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "trigger_config!: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + false, + true + ] + }, + "hash": "548a1424a6b9ac9998b8fc5312bcff671ee7fe59154e9ef32800c117e64bce19" +} diff --git a/backend/.sqlx/query-553108ba3c0b8d579800bc8b5a4f887d79fb4c13b60b19c4913a8db18521958c.json b/backend/.sqlx/query-553108ba3c0b8d579800bc8b5a4f887d79fb4c13b60b19c4913a8db18521958c.json new file mode 100644 index 0000000000..e3d2e205f1 --- /dev/null +++ b/backend/.sqlx/query-553108ba3c0b8d579800bc8b5a4f887d79fb4c13b60b19c4913a8db18521958c.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_queue q SET suspend = 0\n FROM v2_job j, v2_job_status f\n WHERE parent_job = $1\n AND f.id = j.id AND q.id = j.id\n AND suspend = $2 AND (f.flow_status->'step')::int = 0", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "553108ba3c0b8d579800bc8b5a4f887d79fb4c13b60b19c4913a8db18521958c" +} diff --git a/backend/.sqlx/query-55541316c690e4f2e1b7a41071ef0a297a2e65c8e25a17d5c43715481e7633a0.json b/backend/.sqlx/query-55541316c690e4f2e1b7a41071ef0a297a2e65c8e25a17d5c43715481e7633a0.json deleted file mode 100644 index bbcba9903c..0000000000 --- a/backend/.sqlx/query-55541316c690e4f2e1b7a41071ef0a297a2e65c8e25a17d5c43715481e7633a0.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id FROM queue WHERE id = ANY($1) AND schedule_path IS NULL AND ($2::text[] IS NULL OR tag = ANY($2))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "UuidArray", - "TextArray" - ] - }, - "nullable": [ - false - ] - }, - "hash": "55541316c690e4f2e1b7a41071ef0a297a2e65c8e25a17d5c43715481e7633a0" -} diff --git a/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json b/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json index 9243288c9d..009d9fe5d1 100644 --- a/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json +++ b/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json @@ -65,7 +65,7 @@ }, { "ordinal": 12, - "name": "ai_resource", + "name": "ai_config", "type_info": "Jsonb" }, { @@ -100,58 +100,48 @@ }, { "ordinal": 19, - "name": "automatic_billing", - "type_info": "Bool" - }, - { - "ordinal": 20, "name": "default_scripts", "type_info": "Jsonb" }, { - "ordinal": 21, + "ordinal": 20, "name": "deploy_ui", "type_info": "Jsonb" }, { - "ordinal": 22, + "ordinal": 21, "name": "mute_critical_alerts", "type_info": "Bool" }, { - "ordinal": 23, + "ordinal": 22, "name": "color", "type_info": "Varchar" }, { - "ordinal": 24, + "ordinal": 23, "name": "operator_settings", "type_info": "Jsonb" }, { - "ordinal": 25, + "ordinal": 24, "name": "teams_command_script", "type_info": "Text" }, { - "ordinal": 26, + "ordinal": 25, "name": "teams_team_id", "type_info": "Text" }, { - "ordinal": 27, + "ordinal": 26, "name": "teams_team_name", "type_info": "Text" }, { - "ordinal": 28, - "name": "ai_models", - "type_info": "VarcharArray" - }, - { - "ordinal": 29, - "name": "code_completion_model", - "type_info": "Varchar" + "ordinal": 27, + "name": "git_app_installations", + "type_info": "Jsonb" } ], "parameters": { @@ -179,7 +169,6 @@ true, true, true, - false, true, true, true, @@ -188,8 +177,7 @@ true, true, true, - false, - true + false ] }, "hash": "55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2" diff --git a/backend/.sqlx/query-2a3992b5e9abcfbb032d10e142d98efa969dae26a7242eb7ac12593ed5421ef3.json b/backend/.sqlx/query-56264b88a9f428e79c6e531a0011e0dbed634f3b12dedaa5fee0362d670887f6.json similarity index 62% rename from backend/.sqlx/query-2a3992b5e9abcfbb032d10e142d98efa969dae26a7242eb7ac12593ed5421ef3.json rename to backend/.sqlx/query-56264b88a9f428e79c6e531a0011e0dbed634f3b12dedaa5fee0362d670887f6.json index 326e45bf24..e5e1ecd5de 100644 --- a/backend/.sqlx/query-2a3992b5e9abcfbb032d10e142d98efa969dae26a7242eb7ac12593ed5421ef3.json +++ b/backend/.sqlx/query-56264b88a9f428e79c6e531a0011e0dbed634f3b12dedaa5fee0362d670887f6.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO app_version\n (app_id, value, created_by)\n VALUES ($1, $2::text::json, $3) RETURNING id", + "query": "INSERT INTO app_version\n (app_id, value, created_by, raw_app)\n VALUES ($1, $2::text::json, $3, $4) RETURNING id", "describe": { "columns": [ { @@ -13,12 +13,13 @@ "Left": [ "Int8", "Text", - "Varchar" + "Varchar", + "Bool" ] }, "nullable": [ false ] }, - "hash": "2a3992b5e9abcfbb032d10e142d98efa969dae26a7242eb7ac12593ed5421ef3" + "hash": "56264b88a9f428e79c6e531a0011e0dbed634f3b12dedaa5fee0362d670887f6" } diff --git a/backend/.sqlx/query-56c2522a12f91515e38290e4680a55a4727195125cd49a2f92f89bcdf74dc364.json b/backend/.sqlx/query-56c2522a12f91515e38290e4680a55a4727195125cd49a2f92f89bcdf74dc364.json new file mode 100644 index 0000000000..d547567d28 --- /dev/null +++ b/backend/.sqlx/query-56c2522a12f91515e38290e4680a55a4727195125cd49a2f92f89bcdf74dc364.json @@ -0,0 +1,157 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n workspace_id, \n workspaced_route,\n path, \n route_path, \n route_path_key, \n authentication_resource_path,\n script_path, \n is_flow, \n edited_by, \n edited_at, \n email, \n extra_perms, \n is_async, \n authentication_method AS \"authentication_method: _\", \n http_method AS \"http_method: _\", \n static_asset_config AS \"static_asset_config: _\", \n is_static_website,\n wrap_body,\n raw_string\n FROM http_trigger\n WHERE workspace_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "workspaced_route", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "route_path", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "route_path_key", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "authentication_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 8, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 10, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 12, + "name": "is_async", + "type_info": "Bool" + }, + { + "ordinal": 13, + "name": "authentication_method: _", + "type_info": { + "Custom": { + "name": "authentication_method", + "kind": { + "Enum": [ + "none", + "windmill", + "api_key", + "basic_http", + "custom_script", + "signature" + ] + } + } + } + }, + { + "ordinal": 14, + "name": "http_method: _", + "type_info": { + "Custom": { + "name": "http_method", + "kind": { + "Enum": [ + "get", + "post", + "put", + "delete", + "patch" + ] + } + } + } + }, + { + "ordinal": 15, + "name": "static_asset_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "is_static_website", + "type_info": "Bool" + }, + { + "ordinal": 17, + "name": "wrap_body", + "type_info": "Bool" + }, + { + "ordinal": 18, + "name": "raw_string", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + false + ] + }, + "hash": "56c2522a12f91515e38290e4680a55a4727195125cd49a2f92f89bcdf74dc364" +} diff --git a/backend/.sqlx/query-56eafd6d3c72f7114e3d6764184825e2671283fc70abba9a036e88699430af0f.json b/backend/.sqlx/query-56eafd6d3c72f7114e3d6764184825e2671283fc70abba9a036e88699430af0f.json deleted file mode 100644 index 9f22b4df19..0000000000 --- a/backend/.sqlx/query-56eafd6d3c72f7114e3d6764184825e2671283fc70abba9a036e88699430af0f.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "CREATE INDEX CONCURRENTLY labeled_jobs_on_jobs ON completed_job USING GIN ((result -> 'wm_labels')) WHERE result ? 'wm_labels'", - "describe": { - "columns": [], - "parameters": { - "Left": [] - }, - "nullable": [] - }, - "hash": "56eafd6d3c72f7114e3d6764184825e2671283fc70abba9a036e88699430af0f" -} diff --git a/backend/.sqlx/query-a68754521bf751450602f04dd4243199a18885e1739a5a0e7f6100eab6f3c803.json b/backend/.sqlx/query-5930b2fa72fd15d692bcf3e14d95f85dd67ab1514d7c48668ab2f10aa6b201ba.json similarity index 52% rename from backend/.sqlx/query-a68754521bf751450602f04dd4243199a18885e1739a5a0e7f6100eab6f3c803.json rename to backend/.sqlx/query-5930b2fa72fd15d692bcf3e14d95f85dd67ab1514d7c48668ab2f10aa6b201ba.json index 8d6e6a2416..d616940b6b 100644 --- a/backend/.sqlx/query-a68754521bf751450602f04dd4243199a18885e1739a5a0e7f6100eab6f3c803.json +++ b/backend/.sqlx/query-5930b2fa72fd15d692bcf3e14d95f85dd67ab1514d7c48668ab2f10aa6b201ba.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO v2_job_runtime (id) VALUES ($1)", + "query": "UPDATE v2_job_runtime SET ping = now() WHERE id = $1", "describe": { "columns": [], "parameters": { @@ -10,5 +10,5 @@ }, "nullable": [] }, - "hash": "a68754521bf751450602f04dd4243199a18885e1739a5a0e7f6100eab6f3c803" + "hash": "5930b2fa72fd15d692bcf3e14d95f85dd67ab1514d7c48668ab2f10aa6b201ba" } diff --git a/backend/.sqlx/query-5a4fa8ff2148d92946e6ac95f70585d082435e0e79a09821a2045e3b550c3276.json b/backend/.sqlx/query-5a4fa8ff2148d92946e6ac95f70585d082435e0e79a09821a2045e3b550c3276.json new file mode 100644 index 0000000000..bec0922efc --- /dev/null +++ b/backend/.sqlx/query-5a4fa8ff2148d92946e6ac95f70585d082435e0e79a09821a2045e3b550c3276.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1),\n ARRAY['step'],\n $2\n )\n WHERE id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "5a4fa8ff2148d92946e6ac95f70585d082435e0e79a09821a2045e3b550c3276" +} diff --git a/backend/.sqlx/query-5bce731932a35dbecc38c7b9665ef1117a15acf7d0d41b93de165e788b55d93f.json b/backend/.sqlx/query-5bce731932a35dbecc38c7b9665ef1117a15acf7d0d41b93de165e788b55d93f.json deleted file mode 100644 index f3ff1e599d..0000000000 --- a/backend/.sqlx/query-5bce731932a35dbecc38c7b9665ef1117a15acf7d0d41b93de165e788b55d93f.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT created_by AS \"created_by!\", CONCAT(coalesce(completed_job.logs, ''), coalesce(job_logs.logs, '')) as logs, job_logs.log_offset, job_logs.log_file_index\n FROM completed_job \n LEFT JOIN job_logs ON job_logs.job_id = completed_job.id \n WHERE completed_job.id = $1 AND completed_job.workspace_id = $2 AND ($3::text[] IS NULL OR completed_job.tag = ANY($3))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_by!", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "logs", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "log_offset", - "type_info": "Int4" - }, - { - "ordinal": 3, - "name": "log_file_index", - "type_info": "TextArray" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "TextArray" - ] - }, - "nullable": [ - true, - null, - false, - true - ] - }, - "hash": "5bce731932a35dbecc38c7b9665ef1117a15acf7d0d41b93de165e788b55d93f" -} diff --git a/backend/.sqlx/query-5bd8ae8d694ac9f6afef762276141d95ba195f5543120e76e68f6e6715b65a71.json b/backend/.sqlx/query-5bd8ae8d694ac9f6afef762276141d95ba195f5543120e76e68f6e6715b65a71.json new file mode 100644 index 0000000000..31700319e2 --- /dev/null +++ b/backend/.sqlx/query-5bd8ae8d694ac9f6afef762276141d95ba195f5543120e76e68f6e6715b65a71.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n sqs_trigger \n SET \n server_id = $1, \n last_server_ping = now(),\n error = 'Connecting...'\n WHERE \n enabled IS TRUE \n AND workspace_id = $2 \n AND path = $3 \n AND (last_server_ping IS NULL \n OR last_server_ping < now() - INTERVAL '15 seconds'\n ) \n RETURNING true\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "5bd8ae8d694ac9f6afef762276141d95ba195f5543120e76e68f6e6715b65a71" +} diff --git a/backend/.sqlx/query-5d773db0a69c73e86ccd5fd978932e7ac64eb41a56d8ca9ff0a89c594432b531.json b/backend/.sqlx/query-5d773db0a69c73e86ccd5fd978932e7ac64eb41a56d8ca9ff0a89c594432b531.json new file mode 100644 index 0000000000..7dee8c0003 --- /dev/null +++ b/backend/.sqlx/query-5d773db0a69c73e86ccd5fd978932e7ac64eb41a56d8ca9ff0a89c594432b531.json @@ -0,0 +1,36 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n mqtt_trigger \n SET\n mqtt_resource_path = $1,\n subscribe_topics = $2,\n client_version = $3,\n client_id = $4,\n v3_config = $5,\n v5_config = $6,\n is_flow = $7, \n edited_by = $8, \n email = $9,\n script_path = $10,\n path = $11,\n edited_at = now(), \n error = NULL,\n server_id = NULL\n WHERE \n workspace_id = $12 AND \n path = $13\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "JsonbArray", + { + "Custom": { + "name": "mqtt_client_version", + "kind": { + "Enum": [ + "v3", + "v5" + ] + } + } + }, + "Varchar", + "Jsonb", + "Jsonb", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "5d773db0a69c73e86ccd5fd978932e7ac64eb41a56d8ca9ff0a89c594432b531" +} diff --git a/backend/.sqlx/query-5d79c4817696d0ba0d2062eef27ff3856fc4a732adf5796e9b06c826406584dc.json b/backend/.sqlx/query-5d79c4817696d0ba0d2062eef27ff3856fc4a732adf5796e9b06c826406584dc.json deleted file mode 100644 index 9bdc6fac55..0000000000 --- a/backend/.sqlx/query-5d79c4817696d0ba0d2062eef27ff3856fc4a732adf5796e9b06c826406584dc.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE completed_job SET permissioned_as = ('u/' || $1) WHERE permissioned_as = ('u/' || $2) AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "5d79c4817696d0ba0d2062eef27ff3856fc4a732adf5796e9b06c826406584dc" -} diff --git a/backend/.sqlx/query-5dfa6932d7c6d5006fe352da3041680b2ee1ebe9258355a0db24ce4fd26f23de.json b/backend/.sqlx/query-5dfa6932d7c6d5006fe352da3041680b2ee1ebe9258355a0db24ce4fd26f23de.json deleted file mode 100644 index 0416e38fb1..0000000000 --- a/backend/.sqlx/query-5dfa6932d7c6d5006fe352da3041680b2ee1ebe9258355a0db24ce4fd26f23de.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET canceled = true, canceled_by = $1, canceled_reason = $2, scheduled_for = now(), suspend = 0 WHERE id = $3 AND workspace_id = $4 AND (canceled = false OR canceled_reason != $2) RETURNING id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Uuid", - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "5dfa6932d7c6d5006fe352da3041680b2ee1ebe9258355a0db24ce4fd26f23de" -} diff --git a/backend/.sqlx/query-5e8ba1850b2520bd4bf030f53f1f2e6606bfc0f440fbdbbdf4dd3234068c9345.json b/backend/.sqlx/query-5e8ba1850b2520bd4bf030f53f1f2e6606bfc0f440fbdbbdf4dd3234068c9345.json new file mode 100644 index 0000000000..a3751998fb --- /dev/null +++ b/backend/.sqlx/query-5e8ba1850b2520bd4bf030f53f1f2e6606bfc0f440fbdbbdf4dd3234068c9345.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "\nWITH RECURSIVE job_tree AS (\n -- Base case: direct children of the given parent job\n SELECT id, parent_job, 1 AS depth\n FROM v2_job_queue \n INNER JOIN v2_job USING (id)\n WHERE parent_job = $1 AND v2_job.workspace_id = $2\n\n UNION ALL\n\n -- Recursive case: fetch children of previously found jobs\n SELECT q.id, j.parent_job, t.depth + 1\n FROM v2_job_queue q\n INNER JOIN v2_job j USING (id)\n INNER JOIN job_tree t ON t.id = j.parent_job\n WHERE j.workspace_id = $2 AND t.depth < 500 -- Limit recursion depth to 500\n)\nSELECT id AS id, depth\nFROM job_tree\nORDER BY depth, id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "depth", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + }, + "nullable": [ + null, + null + ] + }, + "hash": "5e8ba1850b2520bd4bf030f53f1f2e6606bfc0f440fbdbbdf4dd3234068c9345" +} diff --git a/backend/.sqlx/query-5f22a7c9170f035779782591b4bfb1d462d5c796f08c7481c42154f9bfacf388.json b/backend/.sqlx/query-5f22a7c9170f035779782591b4bfb1d462d5c796f08c7481c42154f9bfacf388.json deleted file mode 100644 index 1aea7e585c..0000000000 --- a/backend/.sqlx/query-5f22a7c9170f035779782591b4bfb1d462d5c796f08c7481c42154f9bfacf388.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO healthchecks (check_type, healthy) \n SELECT 'min_alive_workers_' || $1, false\n WHERE NOT EXISTS (\n SELECT 1 FROM healthchecks \n WHERE check_type = 'min_alive_workers_' || $1 AND created_at > NOW() - INTERVAL '2 minutes'\n )\n RETURNING id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "5f22a7c9170f035779782591b4bfb1d462d5c796f08c7481c42154f9bfacf388" -} diff --git a/backend/.sqlx/query-0a9dd1addaf48eeb46eed59abb6daf9819d07b08cf7ca442ea7ec78a9b2b63b2.json b/backend/.sqlx/query-5faba1042763a308c64e36cc2a331d90bc0aa384262e473c7006340dcb6e959c.json similarity index 59% rename from backend/.sqlx/query-0a9dd1addaf48eeb46eed59abb6daf9819d07b08cf7ca442ea7ec78a9b2b63b2.json rename to backend/.sqlx/query-5faba1042763a308c64e36cc2a331d90bc0aa384262e473c7006340dcb6e959c.json index a0f52168b9..9b7f1962a4 100644 --- a/backend/.sqlx/query-0a9dd1addaf48eeb46eed59abb6daf9819d07b08cf7ca442ea7ec78a9b2b63b2.json +++ b/backend/.sqlx/query-5faba1042763a308c64e36cc2a331d90bc0aa384262e473c7006340dcb6e959c.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT value\n FROM resource\n WHERE path = $1 AND workspace_id = $2", + "query": "SELECT value FROM resource WHERE workspace_id = $1 AND path = $2", "describe": { "columns": [ { @@ -19,5 +19,5 @@ true ] }, - "hash": "0a9dd1addaf48eeb46eed59abb6daf9819d07b08cf7ca442ea7ec78a9b2b63b2" + "hash": "5faba1042763a308c64e36cc2a331d90bc0aa384262e473c7006340dcb6e959c" } diff --git a/backend/.sqlx/query-5ff7df54c7908a7de494ddae5fc7bb9be8106a79e0683cd34459585bbd920ce4.json b/backend/.sqlx/query-5ff7df54c7908a7de494ddae5fc7bb9be8106a79e0683cd34459585bbd920ce4.json deleted file mode 100644 index 45bca31fd0..0000000000 --- a/backend/.sqlx/query-5ff7df54c7908a7de494ddae5fc7bb9be8106a79e0683cd34459585bbd920ce4.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT flow_version.value->>'concurrency_key'\n FROM flow \n LEFT JOIN flow_version\n ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]\n WHERE flow.path = $1 AND flow.workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "5ff7df54c7908a7de494ddae5fc7bb9be8106a79e0683cd34459585bbd920ce4" -} diff --git a/backend/.sqlx/query-611e3cd49d38a37db8912397c4eddd7cd50a4782101e971175d0c5c798593a40.json b/backend/.sqlx/query-611e3cd49d38a37db8912397c4eddd7cd50a4782101e971175d0c5c798593a40.json deleted file mode 100644 index 679b05d29d..0000000000 --- a/backend/.sqlx/query-611e3cd49d38a37db8912397c4eddd7cd50a4782101e971175d0c5c798593a40.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id FROM queue WHERE workspace_id = $1 and root_job = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Text", - "Uuid" - ] - }, - "nullable": [ - false - ] - }, - "hash": "611e3cd49d38a37db8912397c4eddd7cd50a4782101e971175d0c5c798593a40" -} diff --git a/backend/.sqlx/query-61656548991bf6d44c839373cb3e29d3ca1170a2ac0d3dce0b5df0e8677a4874.json b/backend/.sqlx/query-61656548991bf6d44c839373cb3e29d3ca1170a2ac0d3dce0b5df0e8677a4874.json deleted file mode 100644 index 1cb035f456..0000000000 --- a/backend/.sqlx/query-61656548991bf6d44c839373cb3e29d3ca1170a2ac0d3dce0b5df0e8677a4874.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2)\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "61656548991bf6d44c839373cb3e29d3ca1170a2ac0d3dce0b5df0e8677a4874" -} diff --git a/backend/.sqlx/query-61bed1bc6d3e6a3c1d640eeacc290a85d8b63ee36c39dfbf4348d120f6e561ae.json b/backend/.sqlx/query-61bed1bc6d3e6a3c1d640eeacc290a85d8b63ee36c39dfbf4348d120f6e561ae.json new file mode 100644 index 0000000000..7f9886ccb6 --- /dev/null +++ b/backend/.sqlx/query-61bed1bc6d3e6a3c1d640eeacc290a85d8b63ee36c39dfbf4348d120f6e561ae.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n postgres_trigger \n SET\n last_server_ping = NULL \n WHERE \n workspace_id = $1 \n AND path = $2 \n AND server_id IS NULL", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "61bed1bc6d3e6a3c1d640eeacc290a85d8b63ee36c39dfbf4348d120f6e561ae" +} diff --git a/backend/.sqlx/query-61c29d684e8e683e839a6d7210b3b9b96854e5bfd752e45922c358c42ebea0c4.json b/backend/.sqlx/query-61c29d684e8e683e839a6d7210b3b9b96854e5bfd752e45922c358c42ebea0c4.json index 6354dae16b..be472306a0 100644 --- a/backend/.sqlx/query-61c29d684e8e683e839a6d7210b3b9b96854e5bfd752e45922c358c42ebea0c4.json +++ b/backend/.sqlx/query-61c29d684e8e683e839a6d7210b3b9b96854e5bfd752e45922c358c42ebea0c4.json @@ -59,7 +59,9 @@ "rust", "ansible", "csharp", - "oracledb" + "oracledb", + "nu", + "java" ] } } diff --git a/backend/.sqlx/query-197321abfe4667256761884970334f58c1b3edfcc1e863ec4316c9742a1ac7c8.json b/backend/.sqlx/query-61e6070b8a1e3a138818c327d6dbe7efbe27f9e2c8e02258cf7aa06e1779fddb.json similarity index 58% rename from backend/.sqlx/query-197321abfe4667256761884970334f58c1b3edfcc1e863ec4316c9742a1ac7c8.json rename to backend/.sqlx/query-61e6070b8a1e3a138818c327d6dbe7efbe27f9e2c8e02258cf7aa06e1779fddb.json index bfc823c911..7b2d3ee996 100644 --- a/backend/.sqlx/query-197321abfe4667256761884970334f58c1b3edfcc1e863ec4316c9742a1ac7c8.json +++ b/backend/.sqlx/query-61e6070b8a1e3a138818c327d6dbe7efbe27f9e2c8e02258cf7aa06e1779fddb.json @@ -1,100 +1,96 @@ { "db_name": "PostgreSQL", - "query": "SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, http_method as \"http_method: _\", edited_by, email, edited_at, extra_perms, is_async, requires_auth, static_asset_config as \"static_asset_config: _\", is_static_website\n FROM http_trigger\n WHERE workspace_id = $1 AND path = $2", + "query": "SELECT * FROM nats_trigger\n WHERE workspace_id = $1", "describe": { "columns": [ { "ordinal": 0, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 1, "name": "path", "type_info": "Varchar" }, { - "ordinal": 2, - "name": "route_path", + "ordinal": 1, + "name": "nats_resource_path", "type_info": "Varchar" }, + { + "ordinal": 2, + "name": "subjects", + "type_info": "VarcharArray" + }, { "ordinal": 3, - "name": "route_path_key", + "name": "stream_name", "type_info": "Varchar" }, { "ordinal": 4, - "name": "script_path", + "name": "consumer_name", "type_info": "Varchar" }, { "ordinal": 5, - "name": "is_flow", + "name": "use_jetstream", "type_info": "Bool" }, { "ordinal": 6, - "name": "http_method: _", - "type_info": { - "Custom": { - "name": "http_method", - "kind": { - "Enum": [ - "get", - "post", - "put", - "delete", - "patch" - ] - } - } - } - }, - { - "ordinal": 7, - "name": "edited_by", + "name": "script_path", "type_info": "Varchar" }, + { + "ordinal": 7, + "name": "is_flow", + "type_info": "Bool" + }, { "ordinal": 8, - "name": "email", + "name": "workspace_id", "type_info": "Varchar" }, { "ordinal": 9, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 11, "name": "edited_at", "type_info": "Timestamptz" }, { - "ordinal": 10, + "ordinal": 12, "name": "extra_perms", "type_info": "Jsonb" }, - { - "ordinal": 11, - "name": "is_async", - "type_info": "Bool" - }, - { - "ordinal": 12, - "name": "requires_auth", - "type_info": "Bool" - }, { "ordinal": 13, - "name": "static_asset_config: _", - "type_info": "Jsonb" + "name": "server_id", + "type_info": "Varchar" }, { "ordinal": 14, - "name": "is_static_website", + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 16, + "name": "enabled", "type_info": "Bool" } ], "parameters": { "Left": [ - "Text", "Text" ] }, @@ -102,8 +98,8 @@ false, false, false, - false, - false, + true, + true, false, false, false, @@ -113,8 +109,10 @@ false, false, true, + true, + true, false ] }, - "hash": "197321abfe4667256761884970334f58c1b3edfcc1e863ec4316c9742a1ac7c8" + "hash": "61e6070b8a1e3a138818c327d6dbe7efbe27f9e2c8e02258cf7aa06e1779fddb" } diff --git a/backend/.sqlx/query-61ecf606dec2978f3e63b2dec92465202213b7ada2198ea1d9b4d4d1f5ed658f.json b/backend/.sqlx/query-61ecf606dec2978f3e63b2dec92465202213b7ada2198ea1d9b4d4d1f5ed658f.json new file mode 100644 index 0000000000..cb0a2c30c9 --- /dev/null +++ b/backend/.sqlx/query-61ecf606dec2978f3e63b2dec92465202213b7ada2198ea1d9b4d4d1f5ed658f.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_queue SET worker = $2 WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "61ecf606dec2978f3e63b2dec92465202213b7ada2198ea1d9b4d4d1f5ed658f" +} diff --git a/backend/.sqlx/query-6239e15d2389e24e290d86bb96e3ade1656cd403000a34a19a942993b60ff612.json b/backend/.sqlx/query-6239e15d2389e24e290d86bb96e3ade1656cd403000a34a19a942993b60ff612.json deleted file mode 100644 index 19ca4025d8..0000000000 --- a/backend/.sqlx/query-6239e15d2389e24e290d86bb96e3ade1656cd403000a34a19a942993b60ff612.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET schedule_path = REGEXP_REPLACE(schedule_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE schedule_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "6239e15d2389e24e290d86bb96e3ade1656cd403000a34a19a942993b60ff612" -} diff --git a/backend/.sqlx/query-62475252dcf54f32433b97ae011daf5d4205d160d2aedf463c7dfe944e93257a.json b/backend/.sqlx/query-62475252dcf54f32433b97ae011daf5d4205d160d2aedf463c7dfe944e93257a.json deleted file mode 100644 index 5c32a3af00..0000000000 --- a/backend/.sqlx/query-62475252dcf54f32433b97ae011daf5d4205d160d2aedf463c7dfe944e93257a.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO capture_config\n (workspace_id, path, is_flow, trigger_kind, trigger_config, owner, email)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (workspace_id, path, is_flow, trigger_kind)\n DO UPDATE SET trigger_config = $5, owner = $6, email = $7, server_id = NULL, error = NULL", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Varchar", - "Bool", - { - "Custom": { - "name": "trigger_kind", - "kind": { - "Enum": [ - "webhook", - "http", - "websocket", - "kafka", - "email", - "nats" - ] - } - } - }, - "Jsonb", - "Varchar", - "Varchar" - ] - }, - "nullable": [] - }, - "hash": "62475252dcf54f32433b97ae011daf5d4205d160d2aedf463c7dfe944e93257a" -} diff --git a/backend/.sqlx/query-631d4637e4137a0680ffa56e4639c009214eeaee8fd27a0d3050998b354c45ff.json b/backend/.sqlx/query-631d4637e4137a0680ffa56e4639c009214eeaee8fd27a0d3050998b354c45ff.json deleted file mode 100644 index d38bf22973..0000000000 --- a/backend/.sqlx/query-631d4637e4137a0680ffa56e4639c009214eeaee8fd27a0d3050998b354c45ff.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET last_ping = null\n WHERE id = $1 AND last_ping = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Timestamptz" - ] - }, - "nullable": [] - }, - "hash": "631d4637e4137a0680ffa56e4639c009214eeaee8fd27a0d3050998b354c45ff" -} diff --git a/backend/.sqlx/query-639dbfa0c98d8b91006823f4c645a1105d6c1cc58990937c9bcf693a8812920c.json b/backend/.sqlx/query-639dbfa0c98d8b91006823f4c645a1105d6c1cc58990937c9bcf693a8812920c.json deleted file mode 100644 index 739be2be53..0000000000 --- a/backend/.sqlx/query-639dbfa0c98d8b91006823f4c645a1105d6c1cc58990937c9bcf693a8812920c.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT result #> $3 AS \"result: Json>\"\n FROM completed_job WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "result: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "TextArray" - ] - }, - "nullable": [ - null - ] - }, - "hash": "639dbfa0c98d8b91006823f4c645a1105d6c1cc58990937c9bcf693a8812920c" -} diff --git a/backend/.sqlx/query-63e54fe57ec439b68eead00a02209f81076c5317d590e1441b557555b4d7ad96.json b/backend/.sqlx/query-63e54fe57ec439b68eead00a02209f81076c5317d590e1441b557555b4d7ad96.json deleted file mode 100644 index a30a87991e..0000000000 --- a/backend/.sqlx/query-63e54fe57ec439b68eead00a02209f81076c5317d590e1441b557555b4d7ad96.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH uuid_table as (\n select gen_random_uuid() as uuid from generate_series(1, $5)\n )\n INSERT INTO job\n (id, workspace_id, raw_code, raw_lock, raw_flow)\n (SELECT uuid, $1, $2, $3, $4 FROM uuid_table)\n RETURNING id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Text", - "Jsonb", - "Int4" - ] - }, - "nullable": [ - false - ] - }, - "hash": "63e54fe57ec439b68eead00a02209f81076c5317d590e1441b557555b4d7ad96" -} diff --git a/backend/.sqlx/query-641087f3166faee8baad063fd569b61aa4d21a15a9bc06e0c2fd15b47eb7beb0.json b/backend/.sqlx/query-641087f3166faee8baad063fd569b61aa4d21a15a9bc06e0c2fd15b47eb7beb0.json deleted file mode 100644 index b070f8eb7f..0000000000 --- a/backend/.sqlx/query-641087f3166faee8baad063fd569b61aa4d21a15a9bc06e0c2fd15b47eb7beb0.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n id As \"id!\",\n flow_status->'restarted_from'->'flow_job_id' AS \"restarted_from: Json\"\n FROM queue\n WHERE COALESCE((SELECT root_job FROM queue WHERE id = $1), $1) = id AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "restarted_from: Json", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - null - ] - }, - "hash": "641087f3166faee8baad063fd569b61aa4d21a15a9bc06e0c2fd15b47eb7beb0" -} diff --git a/backend/.sqlx/query-64adf72c19023b03a47d48444b1cf970559b3ef735274ef0fef6f47b4c68da7e.json b/backend/.sqlx/query-64adf72c19023b03a47d48444b1cf970559b3ef735274ef0fef6f47b4c68da7e.json index d90022a58d..d6fbd2ff35 100644 --- a/backend/.sqlx/query-64adf72c19023b03a47d48444b1cf970559b3ef735274ef0fef6f47b4c68da7e.json +++ b/backend/.sqlx/query-64adf72c19023b03a47d48444b1cf970559b3ef735274ef0fef6f47b4c68da7e.json @@ -13,7 +13,7 @@ "Left": [ "Bool", "Varchar", - "Json", + "Jsonb", "Int4", "Bool", "Text" diff --git a/backend/.sqlx/query-653574b381a31548d82c1f6f3f44ec826597c42310ab78f9aacd7d9448206c6a.json b/backend/.sqlx/query-653574b381a31548d82c1f6f3f44ec826597c42310ab78f9aacd7d9448206c6a.json deleted file mode 100644 index 3d3309cb5c..0000000000 --- a/backend/.sqlx/query-653574b381a31548d82c1f6f3f44ec826597c42310ab78f9aacd7d9448206c6a.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH zombie_jobs AS (\n UPDATE v2_job_queue q SET running = false, started_at = null\n FROM v2_job j, v2_job_runtime r\n WHERE j.id = q.id AND j.id = r.id\n AND ping < now() - ($1 || ' seconds')::interval\n AND running = true\n AND kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow')\n AND same_worker = false\n RETURNING q.id, q.workspace_id, ping\n ),\n update_concurrency AS (\n UPDATE concurrency_counter cc\n SET job_uuids = job_uuids - zj.id::text\n FROM zombie_jobs zj\n INNER JOIN concurrency_key ck ON ck.job_id = zj.id\n WHERE cc.concurrency_id = ck.key\n )\n SELECT id, workspace_id, ping FROM zombie_jobs", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "ping", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - true - ] - }, - "hash": "653574b381a31548d82c1f6f3f44ec826597c42310ab78f9aacd7d9448206c6a" -} diff --git a/backend/.sqlx/query-6536214f31e9d600e868b01385d8c6395e2440ea27553b7ccb18d7149b106728.json b/backend/.sqlx/query-6536214f31e9d600e868b01385d8c6395e2440ea27553b7ccb18d7149b106728.json new file mode 100644 index 0000000000..fce1c4942a --- /dev/null +++ b/backend/.sqlx/query-6536214f31e9d600e868b01385d8c6395e2440ea27553b7ccb18d7149b106728.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DROP INDEX CONCURRENTLY IF EXISTS root_job_index_by_path_2", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "6536214f31e9d600e868b01385d8c6395e2440ea27553b7ccb18d7149b106728" +} diff --git a/backend/.sqlx/query-65cd5a5a12eb79a2f21ed898616c5cd5c5e671ce1ed3d89639cd306c76a1d9db.json b/backend/.sqlx/query-65cd5a5a12eb79a2f21ed898616c5cd5c5e671ce1ed3d89639cd306c76a1d9db.json new file mode 100644 index 0000000000..6d0ec8bd68 --- /dev/null +++ b/backend/.sqlx/query-65cd5a5a12eb79a2f21ed898616c5cd5c5e671ce1ed3d89639cd306c76a1d9db.json @@ -0,0 +1,135 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n mqtt_resource_path,\n subscribe_topics as \"subscribe_topics!: Vec>\",\n v3_config as \"v3_config!: Option>\",\n v5_config as \"v5_config!: Option>\",\n client_version AS \"client_version: _\",\n client_id,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n mqtt_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mqtt_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "subscribe_topics!: Vec>", + "type_info": "JsonbArray" + }, + { + "ordinal": 2, + "name": "v3_config!: Option>", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "v5_config!: Option>", + "type_info": "Jsonb" + }, + { + "ordinal": 4, + "name": "client_version: _", + "type_info": { + "Custom": { + "name": "mqtt_client_version", + "kind": { + "Enum": [ + "v3", + "v5" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "client_id", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "enabled", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + true, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false + ] + }, + "hash": "65cd5a5a12eb79a2f21ed898616c5cd5c5e671ce1ed3d89639cd306c76a1d9db" +} diff --git a/backend/.sqlx/query-6664be80f0d72ea7b8b184c5348063db3bbfea67f4a056d6e3be1fc4255cfc06.json b/backend/.sqlx/query-6664be80f0d72ea7b8b184c5348063db3bbfea67f4a056d6e3be1fc4255cfc06.json deleted file mode 100644 index 0e7ae566fd..0000000000 --- a/backend/.sqlx/query-6664be80f0d72ea7b8b184c5348063db3bbfea67f4a056d6e3be1fc4255cfc06.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_leaf_jobs = JSONB_SET(coalesce(flow_leaf_jobs, '{}'::jsonb), ARRAY[$1::TEXT], $2)\n WHERE COALESCE((SELECT flow_innermost_root_job FROM v2_job WHERE id = $3), $3) = id", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "6664be80f0d72ea7b8b184c5348063db3bbfea67f4a056d6e3be1fc4255cfc06" -} diff --git a/backend/.sqlx/query-6681048ee83236e9eb33b407b5d3cf89f563e57d5d3e7981d58cecf147b9bf1e.json b/backend/.sqlx/query-6681048ee83236e9eb33b407b5d3cf89f563e57d5d3e7981d58cecf147b9bf1e.json deleted file mode 100644 index 0ca0331f8f..0000000000 --- a/backend/.sqlx/query-6681048ee83236e9eb33b407b5d3cf89f563e57d5d3e7981d58cecf147b9bf1e.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE completed_job SET workspace_id = $1 WHERE workspace_id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Text" - ] - }, - "nullable": [] - }, - "hash": "6681048ee83236e9eb33b407b5d3cf89f563e57d5d3e7981d58cecf147b9bf1e" -} diff --git a/backend/.sqlx/query-6682bf34caf7efa95b60c747b45dcdd41de7f8e197b163f7865810391471db5b.json b/backend/.sqlx/query-6682bf34caf7efa95b60c747b45dcdd41de7f8e197b163f7865810391471db5b.json deleted file mode 100644 index 86968663f2..0000000000 --- a/backend/.sqlx/query-6682bf34caf7efa95b60c747b45dcdd41de7f8e197b163f7865810391471db5b.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT created_by, CONCAT(coalesce(queue.logs, ''), coalesce(job_logs.logs, '')) as logs, coalesce(job_logs.log_offset, 0) as log_offset, job_logs.log_file_index\n FROM queue \n LEFT JOIN job_logs ON job_logs.job_id = queue.id \n WHERE queue.id = $1 AND queue.workspace_id = $2 AND ($3::text[] IS NULL OR queue.tag = ANY($3))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_by", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "logs", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "log_offset", - "type_info": "Int4" - }, - { - "ordinal": 3, - "name": "log_file_index", - "type_info": "TextArray" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "TextArray" - ] - }, - "nullable": [ - false, - null, - null, - true - ] - }, - "hash": "6682bf34caf7efa95b60c747b45dcdd41de7f8e197b163f7865810391471db5b" -} diff --git a/backend/.sqlx/query-66bf488f2eeaf5b4c4cb8c579d7a15eb87516c319c1fcbb3e46032bb9fdf718e.json b/backend/.sqlx/query-66bf488f2eeaf5b4c4cb8c579d7a15eb87516c319c1fcbb3e46032bb9fdf718e.json deleted file mode 100644 index faf1201af1..0000000000 --- a/backend/.sqlx/query-66bf488f2eeaf5b4c4cb8c579d7a15eb87516c319c1fcbb3e46032bb9fdf718e.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n success AS \"success!\",\n result AS \"result: Json>\",\n started_at AS \"started_at!\"\n FROM completed_job\n WHERE workspace_id = $1 AND schedule_path = $2 AND script_path = $3 AND id != $4\n ORDER BY created_at DESC\n LIMIT $5", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "success!", - "type_info": "Bool" - }, - { - "ordinal": 1, - "name": "result: Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 2, - "name": "started_at!", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Text", - "Uuid", - "Int8" - ] - }, - "nullable": [ - true, - true, - true - ] - }, - "hash": "66bf488f2eeaf5b4c4cb8c579d7a15eb87516c319c1fcbb3e46032bb9fdf718e" -} diff --git a/backend/.sqlx/query-672363560895871e4ab19e0dd0dc36afdbc58470664b6cefa8cec25515a42f13.json b/backend/.sqlx/query-672363560895871e4ab19e0dd0dc36afdbc58470664b6cefa8cec25515a42f13.json deleted file mode 100644 index 53ed43a6e0..0000000000 --- a/backend/.sqlx/query-672363560895871e4ab19e0dd0dc36afdbc58470664b6cefa8cec25515a42f13.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT result #> $3 AS \"result: Json>\"\n FROM completed_job WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "result: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "TextArray" - ] - }, - "nullable": [ - null - ] - }, - "hash": "672363560895871e4ab19e0dd0dc36afdbc58470664b6cefa8cec25515a42f13" -} diff --git a/backend/.sqlx/query-673564e6c4dcf30dae3d7a75c397998ea800860ede856e5fc6cd1f57d5408333.json b/backend/.sqlx/query-673564e6c4dcf30dae3d7a75c397998ea800860ede856e5fc6cd1f57d5408333.json new file mode 100644 index 0000000000..8b9a46c4d0 --- /dev/null +++ b/backend/.sqlx/query-673564e6c4dcf30dae3d7a75c397998ea800860ede856e5fc6cd1f57d5408333.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE \n capture_config \n SET \n last_server_ping = NULL \n WHERE \n workspace_id = $1 AND \n path = $2 AND \n is_flow = $3 AND \n trigger_kind = 'gcp' AND \n server_id IS NULL\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "673564e6c4dcf30dae3d7a75c397998ea800860ede856e5fc6cd1f57d5408333" +} diff --git a/backend/.sqlx/query-e17ec84003e2ec414622d100f5dfdda86bee33f31835317df512a20c805b35d7.json b/backend/.sqlx/query-6781ba76dfce321dca4634566496ea5d698ac09d8264e35dfaa4cd8edc9e8414.json similarity index 65% rename from backend/.sqlx/query-e17ec84003e2ec414622d100f5dfdda86bee33f31835317df512a20c805b35d7.json rename to backend/.sqlx/query-6781ba76dfce321dca4634566496ea5d698ac09d8264e35dfaa4cd8edc9e8414.json index 2ef165828e..2e4638f1f7 100644 --- a/backend/.sqlx/query-e17ec84003e2ec414622d100f5dfdda86bee33f31835317df512a20c805b35d7.json +++ b/backend/.sqlx/query-6781ba76dfce321dca4634566496ea5d698ac09d8264e35dfaa4cd8edc9e8414.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, created_at, trigger_kind as \"trigger_kind: _\", payload as \"payload!: _\", trigger_extra as \"trigger_extra: _\" FROM capture WHERE id = $1 AND workspace_id = $2", + "query": "\n SELECT \n id, \n created_at, \n trigger_kind AS \"trigger_kind: _\", \n payload AS \"payload!: _\", \n trigger_extra AS \"trigger_extra: _\"\n FROM \n capture\n WHERE \n id = $1 \n AND workspace_id = $2\n ", "describe": { "columns": [ { @@ -26,7 +26,11 @@ "websocket", "kafka", "email", - "nats" + "nats", + "postgres", + "sqs", + "mqtt", + "gcp" ] } } @@ -57,5 +61,5 @@ true ] }, - "hash": "e17ec84003e2ec414622d100f5dfdda86bee33f31835317df512a20c805b35d7" + "hash": "6781ba76dfce321dca4634566496ea5d698ac09d8264e35dfaa4cd8edc9e8414" } diff --git a/backend/.sqlx/query-a96ff22bc78b74d7234550a12d9fb5c555c1187b276f1353dae1b2ac0670a92a.json b/backend/.sqlx/query-679a9159a5fca976a3de99fe26806faded2cc63e8f16c201e99ab1725dcff294.json similarity index 58% rename from backend/.sqlx/query-a96ff22bc78b74d7234550a12d9fb5c555c1187b276f1353dae1b2ac0670a92a.json rename to backend/.sqlx/query-679a9159a5fca976a3de99fe26806faded2cc63e8f16c201e99ab1725dcff294.json index 060d87a941..1ce9e58cd4 100644 --- a/backend/.sqlx/query-a96ff22bc78b74d7234550a12d9fb5c555c1187b276f1353dae1b2ac0670a92a.json +++ b/backend/.sqlx/query-679a9159a5fca976a3de99fe26806faded2cc63e8f16c201e99ab1725dcff294.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE route_path_key = $1 AND workspace_id = $2 AND http_method = $3 AND ($4::TEXT IS NULL OR path != $4))", + "query": "\n SELECT EXISTS(\n SELECT 1 \n FROM http_trigger \n WHERE \n route_path_key = $1\n AND workspace_id = $2 \n AND http_method = $3 \n AND ($4::TEXT IS NULL OR path != $4)\n )\n ", "describe": { "columns": [ { @@ -34,5 +34,5 @@ null ] }, - "hash": "a96ff22bc78b74d7234550a12d9fb5c555c1187b276f1353dae1b2ac0670a92a" + "hash": "679a9159a5fca976a3de99fe26806faded2cc63e8f16c201e99ab1725dcff294" } diff --git a/backend/.sqlx/query-67afe352fc26dda9107c90e50e954642d877178ce2c0e73b72c3824135ef86f4.json b/backend/.sqlx/query-67afe352fc26dda9107c90e50e954642d877178ce2c0e73b72c3824135ef86f4.json deleted file mode 100644 index a09be9741e..0000000000 --- a/backend/.sqlx/query-67afe352fc26dda9107c90e50e954642d877178ce2c0e73b72c3824135ef86f4.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO job_logs (job_id, logs)\n VALUES ($1, 'Restarted job after not receiving job''s ping for too long the ' || now() || '\n\n')\n ON CONFLICT (job_id) DO UPDATE SET logs = job_logs.logs || '\n' || EXCLUDED.logs\n WHERE job_logs.job_id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "67afe352fc26dda9107c90e50e954642d877178ce2c0e73b72c3824135ef86f4" -} diff --git a/backend/.sqlx/query-6818cae88492f83baf55f54bf5dd5397e04dbe771445818f88903ee5677b3631.json b/backend/.sqlx/query-6818cae88492f83baf55f54bf5dd5397e04dbe771445818f88903ee5677b3631.json deleted file mode 100644 index 19ac6b90d3..0000000000 --- a/backend/.sqlx/query-6818cae88492f83baf55f54bf5dd5397e04dbe771445818f88903ee5677b3631.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET script_path = REGEXP_REPLACE(script_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE script_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "6818cae88492f83baf55f54bf5dd5397e04dbe771445818f88903ee5677b3631" -} diff --git a/backend/.sqlx/query-69518153298f8ca6f112bed156b520758656ce6bcd1e694a62e140e0af8c59fa.json b/backend/.sqlx/query-69518153298f8ca6f112bed156b520758656ce6bcd1e694a62e140e0af8c59fa.json new file mode 100644 index 0000000000..f96e0e349a --- /dev/null +++ b/backend/.sqlx/query-69518153298f8ca6f112bed156b520758656ce6bcd1e694a62e140e0af8c59fa.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE workspace_settings\n SET git_app_installations = COALESCE(\n (\n SELECT jsonb_agg(elem)\n FROM (\n SELECT elem\n FROM jsonb_array_elements(git_app_installations) AS elem\n WHERE (elem->>'installation_id')::bigint != ($1::jsonb->>'installation_id')::bigint\n UNION ALL\n SELECT $1::jsonb\n ) sub\n ),\n jsonb_build_array($1::jsonb)\n )\n WHERE workspace_id = $2;\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Text" + ] + }, + "nullable": [] + }, + "hash": "69518153298f8ca6f112bed156b520758656ce6bcd1e694a62e140e0af8c59fa" +} diff --git a/backend/.sqlx/query-69db5305aadd911b06ecdc4eeb9610c0e233bca35edebe2ad1772a4a1a1d5bbe.json b/backend/.sqlx/query-69db5305aadd911b06ecdc4eeb9610c0e233bca35edebe2ad1772a4a1a1d5bbe.json new file mode 100644 index 0000000000..ec22d86a79 --- /dev/null +++ b/backend/.sqlx/query-69db5305aadd911b06ecdc4eeb9610c0e233bca35edebe2ad1772a4a1a1d5bbe.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', $1::TEXT, 'branchall', 'branch'],\n ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "int4", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Int4", + "Uuid" + ] + }, + "nullable": [ + null + ] + }, + "hash": "69db5305aadd911b06ecdc4eeb9610c0e233bca35edebe2ad1772a4a1a1d5bbe" +} diff --git a/backend/.sqlx/query-69ff806066a24c60188d7547a7776c160ee65eaa01b259c996f3f96ed44fcaaf.json b/backend/.sqlx/query-69ff806066a24c60188d7547a7776c160ee65eaa01b259c996f3f96ed44fcaaf.json deleted file mode 100644 index 75ef2996a0..0000000000 --- a/backend/.sqlx/query-69ff806066a24c60188d7547a7776c160ee65eaa01b259c996f3f96ed44fcaaf.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['step'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "69ff806066a24c60188d7547a7776c160ee65eaa01b259c996f3f96ed44fcaaf" -} diff --git a/backend/.sqlx/query-6aa04cc83e746ebca45959294b6184d17c5100a3f3dc9fda02b1a7acc0b73fa5.json b/backend/.sqlx/query-6aa04cc83e746ebca45959294b6184d17c5100a3f3dc9fda02b1a7acc0b73fa5.json new file mode 100644 index 0000000000..44d1cfc7a5 --- /dev/null +++ b/backend/.sqlx/query-6aa04cc83e746ebca45959294b6184d17c5100a3f3dc9fda02b1a7acc0b73fa5.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT importer_path FROM dependency_map \n WHERE workspace_id = $1 AND imported_path = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "importer_path", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "6aa04cc83e746ebca45959294b6184d17c5100a3f3dc9fda02b1a7acc0b73fa5" +} diff --git a/backend/.sqlx/query-6b0115e40d4361b3ca72dbd071b0a8c0319c5ae0b92f289ec1e74d2478c9e740.json b/backend/.sqlx/query-6b0115e40d4361b3ca72dbd071b0a8c0319c5ae0b92f289ec1e74d2478c9e740.json deleted file mode 100644 index 265ededd17..0000000000 --- a/backend/.sqlx/query-6b0115e40d4361b3ca72dbd071b0a8c0319c5ae0b92f289ec1e74d2478c9e740.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT created_by AS \"created_by!\", CONCAT(coalesce(queue.logs, ''), coalesce(job_logs.logs, '')) as logs, coalesce(job_logs.log_offset, 0) as log_offset, job_logs.log_file_index\n FROM queue \n LEFT JOIN job_logs ON job_logs.job_id = queue.id \n WHERE queue.id = $1 AND queue.workspace_id = $2 AND ($3::text[] IS NULL OR queue.tag = ANY($3))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_by!", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "logs", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "log_offset", - "type_info": "Int4" - }, - { - "ordinal": 3, - "name": "log_file_index", - "type_info": "TextArray" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "TextArray" - ] - }, - "nullable": [ - true, - null, - null, - true - ] - }, - "hash": "6b0115e40d4361b3ca72dbd071b0a8c0319c5ae0b92f289ec1e74d2478c9e740" -} diff --git a/backend/.sqlx/query-1b58b90c184ca21d777ea4e264c79aecc2361134a4817c2b9580f2680425352d.json b/backend/.sqlx/query-6b0347da54d1b8646ece08a5ce78ed7e7c98780fef56f6ea6e1e1fd0458ae32f.json similarity index 71% rename from backend/.sqlx/query-1b58b90c184ca21d777ea4e264c79aecc2361134a4817c2b9580f2680425352d.json rename to backend/.sqlx/query-6b0347da54d1b8646ece08a5ce78ed7e7c98780fef56f6ea6e1e1fd0458ae32f.json index d0b582d17b..8259c07cd0 100644 --- a/backend/.sqlx/query-1b58b90c184ca21d777ea4e264c79aecc2361134a4817c2b9580f2680425352d.json +++ b/backend/.sqlx/query-6b0347da54d1b8646ece08a5ce78ed7e7c98780fef56f6ea6e1e1fd0458ae32f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT args AS \"args: Json>>\"\n FROM v2_job WHERE id = $1 AND workspace_id = $2", + "query": "SELECT args AS \"args: Json>>\"\n FROM v2_job WHERE id = $1 AND workspace_id = $2", "describe": { "columns": [ { @@ -19,5 +19,5 @@ true ] }, - "hash": "1b58b90c184ca21d777ea4e264c79aecc2361134a4817c2b9580f2680425352d" + "hash": "6b0347da54d1b8646ece08a5ce78ed7e7c98780fef56f6ea6e1e1fd0458ae32f" } diff --git a/backend/.sqlx/query-ab9e47e5b510e7df5a41db12896675393a6bb27f8e14245410751961218a7df5.json b/backend/.sqlx/query-6b6f8f7b4a6b6e7e41a9da8b6dfdbcae842ff252cc355bd91aeeb5e26dcc74f3.json similarity index 54% rename from backend/.sqlx/query-ab9e47e5b510e7df5a41db12896675393a6bb27f8e14245410751961218a7df5.json rename to backend/.sqlx/query-6b6f8f7b4a6b6e7e41a9da8b6dfdbcae842ff252cc355bd91aeeb5e26dcc74f3.json index 868541a175..80619c8b2a 100644 --- a/backend/.sqlx/query-ab9e47e5b510e7df5a41db12896675393a6bb27f8e14245410751961218a7df5.json +++ b/backend/.sqlx/query-6b6f8f7b4a6b6e7e41a9da8b6dfdbcae842ff252cc355bd91aeeb5e26dcc74f3.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT COALESCE((SELECT MIN(started_at) as min_started_at\n FROM v2_as_queue\n WHERE script_path = $1 AND job_kind != 'dependencies' AND running = true AND workspace_id = $2 AND canceled = false AND concurrent_limit > 0), $3) as min_started_at, now() AS now", + "query": "SELECT COALESCE((SELECT MIN(started_at) as min_started_at\n FROM v2_job_queue INNER JOIN v2_job ON v2_job.id = v2_job_queue.id\n WHERE v2_job.runnable_path = $1 AND v2_job.kind != 'dependencies' AND v2_job_queue.running = true AND v2_job_queue.workspace_id = $2 AND v2_job_queue.canceled_by IS NULL AND v2_job.concurrent_limit > 0), $3) as min_started_at, now() AS now", "describe": { "columns": [ { @@ -26,5 +26,5 @@ null ] }, - "hash": "ab9e47e5b510e7df5a41db12896675393a6bb27f8e14245410751961218a7df5" + "hash": "6b6f8f7b4a6b6e7e41a9da8b6dfdbcae842ff252cc355bd91aeeb5e26dcc74f3" } diff --git a/backend/.sqlx/query-6b776693091191f36eaf9e35fb3cabb01481a19bef5a956dc36cac41acad0e53.json b/backend/.sqlx/query-6b776693091191f36eaf9e35fb3cabb01481a19bef5a956dc36cac41acad0e53.json new file mode 100644 index 0000000000..97079df64f --- /dev/null +++ b/backend/.sqlx/query-6b776693091191f36eaf9e35fb3cabb01481a19bef5a956dc36cac41acad0e53.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n server_id = $1,\n last_server_ping = now(), \n error = 'Connecting...' \n WHERE \n last_client_ping > NOW() - INTERVAL '10 seconds' AND \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'sqs' AND \n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') \n RETURNING true\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [ + null + ] + }, + "hash": "6b776693091191f36eaf9e35fb3cabb01481a19bef5a956dc36cac41acad0e53" +} diff --git a/backend/.sqlx/query-6c90d5ea2a09b47b81fdab465062c25f8768b220e53bf469550a3a3697ab756a.json b/backend/.sqlx/query-6c90d5ea2a09b47b81fdab465062c25f8768b220e53bf469550a3a3697ab756a.json deleted file mode 100644 index fb5e174ced..0000000000 --- a/backend/.sqlx/query-6c90d5ea2a09b47b81fdab465062c25f8768b220e53bf469550a3a3697ab756a.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT set_config('session.user', $1, true)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "set_config", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "6c90d5ea2a09b47b81fdab465062c25f8768b220e53bf469550a3a3697ab756a" -} diff --git a/backend/.sqlx/query-6cb2c77bb90679a36189007b1f70406fe28923f51fc465ae0f45d7f317077bf5.json b/backend/.sqlx/query-6cb2c77bb90679a36189007b1f70406fe28923f51fc465ae0f45d7f317077bf5.json deleted file mode 100644 index e41413a7bf..0000000000 --- a/backend/.sqlx/query-6cb2c77bb90679a36189007b1f70406fe28923f51fc465ae0f45d7f317077bf5.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['cleanup_module', 'flow_jobs_to_clean'], COALESCE(flow_status->'cleanup_module'->'flow_jobs_to_clean', '[]'::jsonb) || $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "6cb2c77bb90679a36189007b1f70406fe28923f51fc465ae0f45d7f317077bf5" -} diff --git a/backend/.sqlx/query-6e7f234267fbb4720b29f288fba82c1df21ba601ac0989e175f834c569962d46.json b/backend/.sqlx/query-6e7f234267fbb4720b29f288fba82c1df21ba601ac0989e175f834c569962d46.json deleted file mode 100644 index e8db78adbd..0000000000 --- a/backend/.sqlx/query-6e7f234267fbb4720b29f288fba82c1df21ba601ac0989e175f834c569962d46.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'iterator', 'index'], ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb),\n last_ping = NULL\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "int4", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int4", - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "6e7f234267fbb4720b29f288fba82c1df21ba601ac0989e175f834c569962d46" -} diff --git a/backend/.sqlx/query-6f4817fad2739a11d89b6704edf62c3c267ca336a8b6bec5b29d4409030ed561.json b/backend/.sqlx/query-6f4817fad2739a11d89b6704edf62c3c267ca336a8b6bec5b29d4409030ed561.json deleted file mode 100644 index 3e2098f170..0000000000 --- a/backend/.sqlx/query-6f4817fad2739a11d89b6704edf62c3c267ca336a8b6bec5b29d4409030ed561.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO queue (id, script_hash, script_path, job_kind, language, tag, created_by, permissioned_as, email, scheduled_for, workspace_id, raw_flow, flow_status) (SELECT gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12 FROM generate_series(1, 1))", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8", - "Varchar", - { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - }, - { - "Custom": { - "name": "script_lang", - "kind": { - "Enum": [ - "python3", - "deno", - "go", - "bash", - "postgresql", - "nativets", - "bun", - "mysql", - "bigquery", - "snowflake", - "graphql", - "powershell", - "mssql", - "php", - "bunnative", - "rust", - "ansible", - "csharp", - "oracledb" - ] - } - } - }, - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Timestamptz", - "Varchar", - "Jsonb", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "6f4817fad2739a11d89b6704edf62c3c267ca336a8b6bec5b29d4409030ed561" -} diff --git a/backend/.sqlx/query-6f56acb985aa7141ea1891d7ad58a32c35d1b02fe7070c92a2e62c1a5339c396.json b/backend/.sqlx/query-6f56acb985aa7141ea1891d7ad58a32c35d1b02fe7070c92a2e62c1a5339c396.json new file mode 100644 index 0000000000..f3bda55eec --- /dev/null +++ b/backend/.sqlx/query-6f56acb985aa7141ea1891d7ad58a32c35d1b02fe7070c92a2e62c1a5339c396.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT slot_name FROM pg_replication_slots where slot_name = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "slot_name", + "type_info": "Name" + } + ], + "parameters": { + "Left": [ + "Name" + ] + }, + "nullable": [ + true + ] + }, + "hash": "6f56acb985aa7141ea1891d7ad58a32c35d1b02fe7070c92a2e62c1a5339c396" +} diff --git a/backend/.sqlx/query-6fa6fa8eb511119c6adb18bd4f9f174bbb48eef8952f91911b8d9b45357372d9.json b/backend/.sqlx/query-6fa6fa8eb511119c6adb18bd4f9f174bbb48eef8952f91911b8d9b45357372d9.json deleted file mode 100644 index 9623039711..0000000000 --- a/backend/.sqlx/query-6fa6fa8eb511119c6adb18bd4f9f174bbb48eef8952f91911b8d9b45357372d9.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM completed_job WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "args", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Bool" - ] - }, - "nullable": [ - null - ] - }, - "hash": "6fa6fa8eb511119c6adb18bd4f9f174bbb48eef8952f91911b8d9b45357372d9" -} diff --git a/backend/.sqlx/query-6ff7a025f529c077c1b6c9632a367aa29e2f0fdac3f1984550484d5a06a6ea21.json b/backend/.sqlx/query-6ff7a025f529c077c1b6c9632a367aa29e2f0fdac3f1984550484d5a06a6ea21.json deleted file mode 100644 index b045ed2b07..0000000000 --- a/backend/.sqlx/query-6ff7a025f529c077c1b6c9632a367aa29e2f0fdac3f1984550484d5a06a6ea21.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_runtime r SET\n memory_peak = $1,\n ping = now()\n FROM v2_job_queue q\n WHERE r.id = $2 AND q.id = r.id\n RETURNING canceled_by, canceled_reason", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "canceled_by", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "canceled_reason", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Int4", - "Uuid" - ] - }, - "nullable": [ - true, - true - ] - }, - "hash": "6ff7a025f529c077c1b6c9632a367aa29e2f0fdac3f1984550484d5a06a6ea21" -} diff --git a/backend/.sqlx/query-7023184bfeece73252aac9e3e89c5954f40cf46905b40b40ba97a21558a1ac01.json b/backend/.sqlx/query-7023184bfeece73252aac9e3e89c5954f40cf46905b40b40ba97a21558a1ac01.json new file mode 100644 index 0000000000..e1d8d14548 --- /dev/null +++ b/backend/.sqlx/query-7023184bfeece73252aac9e3e89c5954f40cf46905b40b40ba97a21558a1ac01.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM workspace_runnable_dependencies WHERE flow_path = $1 AND workspace_id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "7023184bfeece73252aac9e3e89c5954f40cf46905b40b40ba97a21558a1ac01" +} diff --git a/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json b/backend/.sqlx/query-71a040866adbd192080da165eb120abf7531b2542a2da225152e19144400f950.json similarity index 82% rename from backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json rename to backend/.sqlx/query-71a040866adbd192080da165eb120abf7531b2542a2da225152e19144400f950.json index f24e0700e9..d17cf3c524 100644 --- a/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json +++ b/backend/.sqlx/query-71a040866adbd192080da165eb120abf7531b2542a2da225152e19144400f950.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT * FROM workspace_settings WHERE workspace_id = $1", + "query": "SELECT workspace_id, slack_team_id, teams_team_id, teams_team_name, slack_name, slack_command_script, teams_command_script, slack_email, auto_invite_domain, auto_invite_operator, auto_add, customer_id, plan, webhook, deploy_to, ai_config, error_handler, error_handler_extra_args, error_handler_muted_on_cancel, large_file_storage, git_sync, deploy_ui, default_app, default_scripts, mute_critical_alerts, color, operator_settings, git_app_installations FROM workspace_settings WHERE workspace_id = $1", "describe": { "columns": [ { @@ -15,97 +15,97 @@ }, { "ordinal": 2, + "name": "teams_team_id", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "teams_team_name", + "type_info": "Text" + }, + { + "ordinal": 4, "name": "slack_name", "type_info": "Varchar" }, { - "ordinal": 3, + "ordinal": 5, "name": "slack_command_script", "type_info": "Varchar" }, { - "ordinal": 4, + "ordinal": 6, + "name": "teams_command_script", + "type_info": "Text" + }, + { + "ordinal": 7, "name": "slack_email", "type_info": "Varchar" }, { - "ordinal": 5, + "ordinal": 8, "name": "auto_invite_domain", "type_info": "Varchar" }, { - "ordinal": 6, + "ordinal": 9, "name": "auto_invite_operator", "type_info": "Bool" }, - { - "ordinal": 7, - "name": "customer_id", - "type_info": "Varchar" - }, - { - "ordinal": 8, - "name": "plan", - "type_info": "Varchar" - }, - { - "ordinal": 9, - "name": "webhook", - "type_info": "Text" - }, { "ordinal": 10, - "name": "deploy_to", - "type_info": "Varchar" - }, - { - "ordinal": 11, - "name": "error_handler", - "type_info": "Varchar" - }, - { - "ordinal": 12, - "name": "ai_resource", - "type_info": "Jsonb" - }, - { - "ordinal": 13, - "name": "error_handler_extra_args", - "type_info": "Json" - }, - { - "ordinal": 14, - "name": "error_handler_muted_on_cancel", - "type_info": "Bool" - }, - { - "ordinal": 15, - "name": "large_file_storage", - "type_info": "Jsonb" - }, - { - "ordinal": 16, - "name": "git_sync", - "type_info": "Jsonb" - }, - { - "ordinal": 17, - "name": "default_app", - "type_info": "Varchar" - }, - { - "ordinal": 18, "name": "auto_add", "type_info": "Bool" }, { - "ordinal": 19, - "name": "automatic_billing", + "ordinal": 11, + "name": "customer_id", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "plan", + "type_info": "Varchar" + }, + { + "ordinal": 13, + "name": "webhook", + "type_info": "Text" + }, + { + "ordinal": 14, + "name": "deploy_to", + "type_info": "Varchar" + }, + { + "ordinal": 15, + "name": "ai_config", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error_handler", + "type_info": "Varchar" + }, + { + "ordinal": 17, + "name": "error_handler_extra_args", + "type_info": "Json" + }, + { + "ordinal": 18, + "name": "error_handler_muted_on_cancel", "type_info": "Bool" }, + { + "ordinal": 19, + "name": "large_file_storage", + "type_info": "Jsonb" + }, { "ordinal": 20, - "name": "default_scripts", + "name": "git_sync", "type_info": "Jsonb" }, { @@ -115,43 +115,33 @@ }, { "ordinal": 22, + "name": "default_app", + "type_info": "Varchar" + }, + { + "ordinal": 23, + "name": "default_scripts", + "type_info": "Jsonb" + }, + { + "ordinal": 24, "name": "mute_critical_alerts", "type_info": "Bool" }, { - "ordinal": 23, + "ordinal": 25, "name": "color", "type_info": "Varchar" }, { - "ordinal": 24, + "ordinal": 26, "name": "operator_settings", "type_info": "Jsonb" }, - { - "ordinal": 25, - "name": "teams_command_script", - "type_info": "Text" - }, - { - "ordinal": 26, - "name": "teams_team_id", - "type_info": "Text" - }, { "ordinal": 27, - "name": "teams_team_name", - "type_info": "Text" - }, - { - "ordinal": 28, - "name": "ai_models", - "type_info": "VarcharArray" - }, - { - "ordinal": 29, - "name": "code_completion_model", - "type_info": "Varchar" + "name": "git_app_installations", + "type_info": "Jsonb" } ], "parameters": { @@ -164,6 +154,9 @@ true, true, true, + true, + true, + true, false, true, true, @@ -174,23 +167,18 @@ true, true, true, + true, false, true, true, true, true, - false, true, true, true, true, - true, - true, - true, - true, - false, - true + false ] }, - "hash": "1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597" + "hash": "71a040866adbd192080da165eb120abf7531b2542a2da225152e19144400f950" } diff --git a/backend/.sqlx/query-71d51bbc35da7b9930e3ea3a634451217ccb9f1bc35b1ad6e10d16bc19c41447.json b/backend/.sqlx/query-71d51bbc35da7b9930e3ea3a634451217ccb9f1bc35b1ad6e10d16bc19c41447.json deleted file mode 100644 index a8dcf9e011..0000000000 --- a/backend/.sqlx/query-71d51bbc35da7b9930e3ea3a634451217ccb9f1bc35b1ad6e10d16bc19c41447.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT owner, email\n FROM capture_config\n WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4 AND last_client_ping > NOW() - INTERVAL '10 seconds'", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "owner", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "email", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Bool", - { - "Custom": { - "name": "trigger_kind", - "kind": { - "Enum": [ - "webhook", - "http", - "websocket", - "kafka", - "email", - "nats" - ] - } - } - } - ] - }, - "nullable": [ - false, - false - ] - }, - "hash": "71d51bbc35da7b9930e3ea3a634451217ccb9f1bc35b1ad6e10d16bc19c41447" -} diff --git a/backend/.sqlx/query-362419eb262c83d6a98a0200b116e831ada60399fe5f55a56d930cc69aff2675.json b/backend/.sqlx/query-726e956cfcd3ac7c07abeecdf92cf0996efe7fa7b671ac2b3b000ead0ea307de.json similarity index 82% rename from backend/.sqlx/query-362419eb262c83d6a98a0200b116e831ada60399fe5f55a56d930cc69aff2675.json rename to backend/.sqlx/query-726e956cfcd3ac7c07abeecdf92cf0996efe7fa7b671ac2b3b000ead0ea307de.json index 183062aa6d..4baf932023 100644 --- a/backend/.sqlx/query-362419eb262c83d6a98a0200b116e831ada60399fe5f55a56d930cc69aff2675.json +++ b/backend/.sqlx/query-726e956cfcd3ac7c07abeecdf92cf0996efe7fa7b671ac2b3b000ead0ea307de.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT s.hash as hash, dm.deployment_msg as deployment_msg \n FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash\n WHERE s.workspace_id = $1 AND s.path = $2\n ORDER by created_at DESC", + "query": "SELECT s.hash as hash, dm.deployment_msg as deployment_msg \n FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash\n WHERE s.workspace_id = $1 AND s.path = $2\n ORDER by s.created_at DESC", "describe": { "columns": [ { @@ -25,5 +25,5 @@ true ] }, - "hash": "362419eb262c83d6a98a0200b116e831ada60399fe5f55a56d930cc69aff2675" + "hash": "726e956cfcd3ac7c07abeecdf92cf0996efe7fa7b671ac2b3b000ead0ea307de" } diff --git a/backend/.sqlx/query-72cea8e73e1974534163ef6515afed378e69232fb38bbfc44c4edeeb27574947.json b/backend/.sqlx/query-72cea8e73e1974534163ef6515afed378e69232fb38bbfc44c4edeeb27574947.json new file mode 100644 index 0000000000..de206ee13d --- /dev/null +++ b/backend/.sqlx/query-72cea8e73e1974534163ef6515afed378e69232fb38bbfc44c4edeeb27574947.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO healthchecks (check_type, healthy)\n SELECT 'min_alive_workers_' || $1, true\n WHERE NOT EXISTS (\n SELECT 1 FROM healthchecks\n WHERE check_type = 'min_alive_workers_' || $1 AND created_at > NOW() - INTERVAL '2 minutes'\n )\n RETURNING id", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "72cea8e73e1974534163ef6515afed378e69232fb38bbfc44c4edeeb27574947" +} diff --git a/backend/.sqlx/query-6c0f74c56789ac51ccb06cd8a14986071ccc94df0de137b56d63d673db11d8aa.json b/backend/.sqlx/query-7315c588aac4ff3a1aa6972a7553ac47452a4ed4e8dff0e94f22e6225f4eebc7.json similarity index 50% rename from backend/.sqlx/query-6c0f74c56789ac51ccb06cd8a14986071ccc94df0de137b56d63d673db11d8aa.json rename to backend/.sqlx/query-7315c588aac4ff3a1aa6972a7553ac47452a4ed4e8dff0e94f22e6225f4eebc7.json index d50a50ebd3..ebbd00889d 100644 --- a/backend/.sqlx/query-6c0f74c56789ac51ccb06cd8a14986071ccc94df0de137b56d63d673db11d8aa.json +++ b/backend/.sqlx/query-7315c588aac4ff3a1aa6972a7553ac47452a4ed4e8dff0e94f22e6225f4eebc7.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "DELETE FROM job_stats WHERE job_id = ANY($1)", + "query": "INSERT INTO v2_job_runtime (id, ping) SELECT unnest($1::uuid[]), null", "describe": { "columns": [], "parameters": { @@ -10,5 +10,5 @@ }, "nullable": [] }, - "hash": "6c0f74c56789ac51ccb06cd8a14986071ccc94df0de137b56d63d673db11d8aa" + "hash": "7315c588aac4ff3a1aa6972a7553ac47452a4ed4e8dff0e94f22e6225f4eebc7" } diff --git a/backend/.sqlx/query-7470e7067b948509d14828c24a8725da747e6b967554eb7e088ee3a018ec1f8b.json b/backend/.sqlx/query-7470e7067b948509d14828c24a8725da747e6b967554eb7e088ee3a018ec1f8b.json new file mode 100644 index 0000000000..a6979f19db --- /dev/null +++ b/backend/.sqlx/query-7470e7067b948509d14828c24a8725da747e6b967554eb7e088ee3a018ec1f8b.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id\n FROM v2_job j JOIN v2_job_queue USING (id)\n WHERE j.workspace_id = $2 AND trigger_kind = 'schedule' AND trigger = $1 AND runnable_path = $4\n AND parent_job IS NULL\n AND j.id != $3\n AND running = true", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Uuid", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "7470e7067b948509d14828c24a8725da747e6b967554eb7e088ee3a018ec1f8b" +} diff --git a/backend/.sqlx/query-74a2a90d12ca0179c8a80f9bf574066db4e8735c0f717d91391a28bf832c0e71.json b/backend/.sqlx/query-74a2a90d12ca0179c8a80f9bf574066db4e8735c0f717d91391a28bf832c0e71.json deleted file mode 100644 index fc80d816cf..0000000000 --- a/backend/.sqlx/query-74a2a90d12ca0179c8a80f9bf574066db4e8735c0f717d91391a28bf832c0e71.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET canceled = true, canceled_by = $1, canceled_reason = $2, scheduled_for = now(), suspend = 0 WHERE id = $3 AND workspace_id = $4 AND (canceled = false OR canceled_reason != $2) RETURNING id AS \"id!\"", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Uuid", - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "74a2a90d12ca0179c8a80f9bf574066db4e8735c0f717d91391a28bf832c0e71" -} diff --git a/backend/.sqlx/query-74d928f4c3f0de191f414471b9a4fbe9c20f9685b06ad5bbded424948b2dc88c.json b/backend/.sqlx/query-74d928f4c3f0de191f414471b9a4fbe9c20f9685b06ad5bbded424948b2dc88c.json new file mode 100644 index 0000000000..0f72e41b9f --- /dev/null +++ b/backend/.sqlx/query-74d928f4c3f0de191f414471b9a4fbe9c20f9685b06ad5bbded424948b2dc88c.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n server_id = $1,\n last_server_ping = now(), \n error = 'Connecting...' \n WHERE \n last_client_ping > NOW() - INTERVAL '10 seconds' AND \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'postgres' AND \n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') \n RETURNING true\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [ + null + ] + }, + "hash": "74d928f4c3f0de191f414471b9a4fbe9c20f9685b06ad5bbded424948b2dc88c" +} diff --git a/backend/.sqlx/query-74dbd5a09255c30991078492ba3850e02ffbef25fbfd29cbedc041b0e439e580.json b/backend/.sqlx/query-74dbd5a09255c30991078492ba3850e02ffbef25fbfd29cbedc041b0e439e580.json deleted file mode 100644 index 77515a838f..0000000000 --- a/backend/.sqlx/query-74dbd5a09255c30991078492ba3850e02ffbef25fbfd29cbedc041b0e439e580.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id AS \"id!\", flow_status AS \"flow_status!: Json\"\n FROM v2_as_completed_job\n WHERE parent_job = $1 AND workspace_id = $2 AND flow_status IS NOT NULL", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "flow_status!: Json", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true - ] - }, - "hash": "74dbd5a09255c30991078492ba3850e02ffbef25fbfd29cbedc041b0e439e580" -} diff --git a/backend/.sqlx/query-75451b6d48e4c26812ae64981d0d968b8fb0bf4374a2fccc167fa879bad7078f.json b/backend/.sqlx/query-75451b6d48e4c26812ae64981d0d968b8fb0bf4374a2fccc167fa879bad7078f.json index 287284b86b..cf2b476448 100644 --- a/backend/.sqlx/query-75451b6d48e4c26812ae64981d0d968b8fb0bf4374a2fccc167fa879bad7078f.json +++ b/backend/.sqlx/query-75451b6d48e4c26812ae64981d0d968b8fb0bf4374a2fccc167fa879bad7078f.json @@ -59,7 +59,9 @@ "rust", "ansible", "csharp", - "oracledb" + "oracledb", + "nu", + "java" ] } } diff --git a/backend/.sqlx/query-76ca60e456022cf3d1931245b7daf22783c81bc757d735a4b247cc693dfed719.json b/backend/.sqlx/query-76ca60e456022cf3d1931245b7daf22783c81bc757d735a4b247cc693dfed719.json deleted file mode 100644 index 530638af1f..0000000000 --- a/backend/.sqlx/query-76ca60e456022cf3d1931245b7daf22783c81bc757d735a4b247cc693dfed719.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', flow_status->>'step', 'progress'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "76ca60e456022cf3d1931245b7daf22783c81bc757d735a4b247cc693dfed719" -} diff --git a/backend/.sqlx/query-776909d3452aaf9e0ed41a5ec314b7bfc5bc8b6a76c98bf4daf7d715d18c52e6.json b/backend/.sqlx/query-776909d3452aaf9e0ed41a5ec314b7bfc5bc8b6a76c98bf4daf7d715d18c52e6.json new file mode 100644 index 0000000000..5c7405cec6 --- /dev/null +++ b/backend/.sqlx/query-776909d3452aaf9e0ed41a5ec314b7bfc5bc8b6a76c98bf4daf7d715d18c52e6.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO sqs_trigger (\n aws_auth_resource_type,\n aws_resource_path,\n queue_url,\n message_attributes,\n workspace_id, \n path, \n script_path, \n is_flow, \n email, \n enabled, \n edited_by\n ) \n VALUES (\n $1, \n $2, \n $3, \n $4, \n $5, \n $6, \n $7,\n $8,\n $9,\n $10,\n $11\n )", + "describe": { + "columns": [], + "parameters": { + "Left": [ + { + "Custom": { + "name": "aws_auth_resource_type", + "kind": { + "Enum": [ + "oidc", + "credentials" + ] + } + } + }, + "Varchar", + "Varchar", + "TextArray", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Bool", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "776909d3452aaf9e0ed41a5ec314b7bfc5bc8b6a76c98bf4daf7d715d18c52e6" +} diff --git a/backend/.sqlx/query-0a1c95c4376b944661bab13271091cf3ea0afe68fb8e08e7aea239dc735c625c.json b/backend/.sqlx/query-77701b16ee1f6dd827372835db59bbffc7254af47a8d48b7ba3cf969c2f8398c.json similarity index 53% rename from backend/.sqlx/query-0a1c95c4376b944661bab13271091cf3ea0afe68fb8e08e7aea239dc735c625c.json rename to backend/.sqlx/query-77701b16ee1f6dd827372835db59bbffc7254af47a8d48b7ba3cf969c2f8398c.json index b8f224dbc2..e6e754579f 100644 --- a/backend/.sqlx/query-0a1c95c4376b944661bab13271091cf3ea0afe68fb8e08e7aea239dc735c625c.json +++ b/backend/.sqlx/query-77701b16ee1f6dd827372835db59bbffc7254af47a8d48b7ba3cf969c2f8398c.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO concurrency_key(key, job_id) VALUES ($1, $2)", + "query": "UPDATE v2_job_queue SET tag = $1, running = false WHERE id = $2", "describe": { "columns": [], "parameters": { @@ -11,5 +11,5 @@ }, "nullable": [] }, - "hash": "0a1c95c4376b944661bab13271091cf3ea0afe68fb8e08e7aea239dc735c625c" + "hash": "77701b16ee1f6dd827372835db59bbffc7254af47a8d48b7ba3cf969c2f8398c" } diff --git a/backend/.sqlx/query-777190559e27c8c8fb6718b0a0c1d7db9b956abd88b94db3948f2c579c3826d0.json b/backend/.sqlx/query-777190559e27c8c8fb6718b0a0c1d7db9b956abd88b94db3948f2c579c3826d0.json deleted file mode 100644 index 0c1d65bb6b..0000000000 --- a/backend/.sqlx/query-777190559e27c8c8fb6718b0a0c1d7db9b956abd88b94db3948f2c579c3826d0.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'branchall', 'branch'], ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb),\n last_ping = NULL\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "int4", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int4", - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "777190559e27c8c8fb6718b0a0c1d7db9b956abd88b94db3948f2c579c3826d0" -} diff --git a/backend/.sqlx/query-77f12f22d7c9da1799e5720c9a8fab3b6f26ccbd822fc05b7a0fb3d6a17c5435.json b/backend/.sqlx/query-77f12f22d7c9da1799e5720c9a8fab3b6f26ccbd822fc05b7a0fb3d6a17c5435.json new file mode 100644 index 0000000000..42289a7791 --- /dev/null +++ b/backend/.sqlx/query-77f12f22d7c9da1799e5720c9a8fab3b6f26ccbd822fc05b7a0fb3d6a17c5435.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT jc.id, jc.flow_status AS \"flow_status!: Json\", j.created_at\n FROM v2_job_completed jc\n JOIN v2_job j ON j.id = jc.id\n WHERE jc.id = $1 AND jc.workspace_id = $2 AND jc.flow_status IS NOT NULL", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "flow_status!: Json", + "type_info": "Jsonb" + }, + { + "ordinal": 2, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + }, + "nullable": [ + false, + true, + false + ] + }, + "hash": "77f12f22d7c9da1799e5720c9a8fab3b6f26ccbd822fc05b7a0fb3d6a17c5435" +} diff --git a/backend/.sqlx/query-7b084617bf2de5ed9a657d2bd2cbc9de1246427bf0302c032f8bd26ff93313f7.json b/backend/.sqlx/query-7b084617bf2de5ed9a657d2bd2cbc9de1246427bf0302c032f8bd26ff93313f7.json deleted file mode 100644 index 3f72b7b760..0000000000 --- a/backend/.sqlx/query-7b084617bf2de5ed9a657d2bd2cbc9de1246427bf0302c032f8bd26ff93313f7.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['failure_module'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "7b084617bf2de5ed9a657d2bd2cbc9de1246427bf0302c032f8bd26ff93313f7" -} diff --git a/backend/.sqlx/query-7b1e6b67a20ae1128118d5f5cc0db4007fb9dc6fd20582a46ebb951fca3a7abd.json b/backend/.sqlx/query-7b1e6b67a20ae1128118d5f5cc0db4007fb9dc6fd20582a46ebb951fca3a7abd.json deleted file mode 100644 index a094b8580f..0000000000 --- a/backend/.sqlx/query-7b1e6b67a20ae1128118d5f5cc0db4007fb9dc6fd20582a46ebb951fca3a7abd.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT CAST(ROUND(AVG(duration_ms), 0) AS BIGINT) AS avg_duration_s FROM\n (SELECT duration_ms FROM concurrency_key LEFT JOIN v2_as_completed_job ON v2_as_completed_job.id = concurrency_key.job_id WHERE key = $1 AND ended_at IS NOT NULL\n ORDER BY ended_at\n DESC LIMIT 10) AS t", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "avg_duration_s", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "7b1e6b67a20ae1128118d5f5cc0db4007fb9dc6fd20582a46ebb951fca3a7abd" -} diff --git a/backend/.sqlx/query-7b6e8dac5f83fcbae95056c4206bf4de1b12d6c8aab32f1f55d7f2c230c5c08e.json b/backend/.sqlx/query-7b6e8dac5f83fcbae95056c4206bf4de1b12d6c8aab32f1f55d7f2c230c5c08e.json new file mode 100644 index 0000000000..a30054b9dc --- /dev/null +++ b/backend/.sqlx/query-7b6e8dac5f83fcbae95056c4206bf4de1b12d6c8aab32f1f55d7f2c230c5c08e.json @@ -0,0 +1,132 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n mqtt_resource_path,\n subscribe_topics as \"subscribe_topics!: Vec>\",\n v3_config as \"v3_config!: Option>\",\n v5_config as \"v5_config!: Option>\",\n client_version as \"client_version: _\",\n client_id,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n mqtt_trigger\n WHERE\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mqtt_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "subscribe_topics!: Vec>", + "type_info": "JsonbArray" + }, + { + "ordinal": 2, + "name": "v3_config!: Option>", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "v5_config!: Option>", + "type_info": "Jsonb" + }, + { + "ordinal": 4, + "name": "client_version: _", + "type_info": { + "Custom": { + "name": "mqtt_client_version", + "kind": { + "Enum": [ + "v3", + "v5" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "client_id", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "enabled", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + true, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false + ] + }, + "hash": "7b6e8dac5f83fcbae95056c4206bf4de1b12d6c8aab32f1f55d7f2c230c5c08e" +} diff --git a/backend/.sqlx/query-7bd7505b008954aae6152554c1f9bb0611d0ec451b48aa1a4de309dadeb53315.json b/backend/.sqlx/query-7bd7505b008954aae6152554c1f9bb0611d0ec451b48aa1a4de309dadeb53315.json deleted file mode 100644 index c7f3146594..0000000000 --- a/backend/.sqlx/query-7bd7505b008954aae6152554c1f9bb0611d0ec451b48aa1a4de309dadeb53315.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = flow_status - 'approval_conditions'\n WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "7bd7505b008954aae6152554c1f9bb0611d0ec451b48aa1a4de309dadeb53315" -} diff --git a/backend/.sqlx/query-7c31a680436fd91db30c089e694cc5d8c7fb768a6ddc7cf337469a140fa37106.json b/backend/.sqlx/query-7c31a680436fd91db30c089e694cc5d8c7fb768a6ddc7cf337469a140fa37106.json deleted file mode 100644 index b3483a00ab..0000000000 --- a/backend/.sqlx/query-7c31a680436fd91db30c089e694cc5d8c7fb768a6ddc7cf337469a140fa37106.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job SET args = $1 WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "7c31a680436fd91db30c089e694cc5d8c7fb768a6ddc7cf337469a140fa37106" -} diff --git a/backend/.sqlx/query-7c9a464ac807051b99fe37f2078f1b17f824e6d9b1124db618855a15a98e31f6.json b/backend/.sqlx/query-7c9a464ac807051b99fe37f2078f1b17f824e6d9b1124db618855a15a98e31f6.json deleted file mode 100644 index c7a6d92bbc..0000000000 --- a/backend/.sqlx/query-7c9a464ac807051b99fe37f2078f1b17f824e6d9b1124db618855a15a98e31f6.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM metrics WHERE id LIKE 'queue_%' AND created_at < NOW() - INTERVAL '14 day'", - "describe": { - "columns": [], - "parameters": { - "Left": [] - }, - "nullable": [] - }, - "hash": "7c9a464ac807051b99fe37f2078f1b17f824e6d9b1124db618855a15a98e31f6" -} diff --git a/backend/.sqlx/query-5c1de8473e0e96c1063a9a735a064c5a91e3ed8d9260c72b783fc12542b88fbd.json b/backend/.sqlx/query-7cba31d597215a343cb0bca5b204a15fbba193262f7895c2bba90feb4215d6f3.json similarity index 58% rename from backend/.sqlx/query-5c1de8473e0e96c1063a9a735a064c5a91e3ed8d9260c72b783fc12542b88fbd.json rename to backend/.sqlx/query-7cba31d597215a343cb0bca5b204a15fbba193262f7895c2bba90feb4215d6f3.json index 890a282ad1..a313dd6076 100644 --- a/backend/.sqlx/query-5c1de8473e0e96c1063a9a735a064c5a91e3ed8d9260c72b783fc12542b88fbd.json +++ b/backend/.sqlx/query-7cba31d597215a343cb0bca5b204a15fbba193262f7895c2bba90feb4215d6f3.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, created_at, trigger_kind as \"trigger_kind: _\", CASE WHEN pg_column_size(payload) < 40000 THEN payload ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as \"payload!: _\", trigger_extra as \"trigger_extra: _\"\n FROM capture\n WHERE workspace_id = $1\n AND path = $2 AND is_flow = $3\n AND ($4::trigger_kind IS NULL OR trigger_kind = $4)\n ORDER BY created_at DESC\n OFFSET $5\n LIMIT $6", + "query": "\n SELECT \n id, \n created_at, \n trigger_kind AS \"trigger_kind: _\",\n CASE \n WHEN pg_column_size(payload) < 40000 THEN payload \n ELSE '\"WINDMILL_TOO_BIG\"'::jsonb \n END AS \"payload!: _\",\n trigger_extra AS \"trigger_extra: _\"\n FROM \n capture\n WHERE \n workspace_id = $1 \n AND path = $2 \n AND is_flow = $3 \n AND ($4::trigger_kind IS NULL OR trigger_kind = $4)\n ORDER BY \n created_at DESC\n OFFSET $5\n LIMIT $6\n ", "describe": { "columns": [ { @@ -26,7 +26,11 @@ "websocket", "kafka", "email", - "nats" + "nats", + "postgres", + "sqs", + "mqtt", + "gcp" ] } } @@ -58,7 +62,11 @@ "websocket", "kafka", "email", - "nats" + "nats", + "postgres", + "sqs", + "mqtt", + "gcp" ] } } @@ -75,5 +83,5 @@ true ] }, - "hash": "5c1de8473e0e96c1063a9a735a064c5a91e3ed8d9260c72b783fc12542b88fbd" + "hash": "7cba31d597215a343cb0bca5b204a15fbba193262f7895c2bba90feb4215d6f3" } diff --git a/backend/.sqlx/query-099894523449a70eb301ecd1d744210d39f405016ffec374e4b15a2528baccb5.json b/backend/.sqlx/query-7cd070aaca3b4f95bb6e669ac07f288bd1cc8a85625255d86e45f63e8267d34c.json similarity index 68% rename from backend/.sqlx/query-099894523449a70eb301ecd1d744210d39f405016ffec374e4b15a2528baccb5.json rename to backend/.sqlx/query-7cd070aaca3b4f95bb6e669ac07f288bd1cc8a85625255d86e45f63e8267d34c.json index 8a4a9b9c20..99095e3fcc 100644 --- a/backend/.sqlx/query-099894523449a70eb301ecd1d744210d39f405016ffec374e4b15a2528baccb5.json +++ b/backend/.sqlx/query-7cd070aaca3b4f95bb6e669ac07f288bd1cc8a85625255d86e45f63e8267d34c.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT schedule.*, t.jobs FROM schedule, LATERAL ( SELECT ARRAY (SELECT json_build_object('id', id, 'success', success, 'duration_ms', duration_ms) FROM v2_as_completed_job WHERE\n v2_as_completed_job.schedule_path = schedule.path AND v2_as_completed_job.workspace_id = $1 AND parent_job IS NULL AND is_skipped = False ORDER BY started_at DESC LIMIT 20) AS jobs ) t\n WHERE schedule.workspace_id = $1 ORDER BY schedule.edited_at desc LIMIT $2 OFFSET $3", + "query": "\n UPDATE schedule SET\n enabled = $1,\n email = $2\n WHERE path = $3 AND workspace_id = $4\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version\n ", "describe": { "columns": [ { @@ -30,44 +30,44 @@ }, { "ordinal": 5, + "name": "timezone", + "type_info": "Varchar" + }, + { + "ordinal": 6, "name": "enabled", "type_info": "Bool" }, { - "ordinal": 6, + "ordinal": 7, "name": "script_path", "type_info": "Varchar" }, - { - "ordinal": 7, - "name": "args", - "type_info": "Jsonb" - }, { "ordinal": 8, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 9, "name": "is_flow", "type_info": "Bool" }, + { + "ordinal": 9, + "name": "args: _", + "type_info": "Jsonb" + }, { "ordinal": 10, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 11, "name": "email", "type_info": "Varchar" }, { - "ordinal": 11, + "ordinal": 12, "name": "error", "type_info": "Text" }, - { - "ordinal": 12, - "name": "timezone", - "type_info": "Varchar" - }, { "ordinal": 13, "name": "on_failure", @@ -75,23 +75,23 @@ }, { "ordinal": 14, - "name": "on_recovery", - "type_info": "Varchar" - }, - { - "ordinal": 15, "name": "on_failure_times", "type_info": "Int4" }, { - "ordinal": 16, + "ordinal": 15, "name": "on_failure_exact", "type_info": "Bool" }, + { + "ordinal": 16, + "name": "on_failure_extra_args: _", + "type_info": "Jsonb" + }, { "ordinal": 17, - "name": "on_failure_extra_args", - "type_info": "Json" + "name": "on_recovery", + "type_info": "Varchar" }, { "ordinal": 18, @@ -100,65 +100,66 @@ }, { "ordinal": 19, - "name": "on_recovery_extra_args", - "type_info": "Json" - }, - { - "ordinal": 20, - "name": "ws_error_handler_muted", - "type_info": "Bool" - }, - { - "ordinal": 21, - "name": "retry", + "name": "on_recovery_extra_args: _", "type_info": "Jsonb" }, { - "ordinal": 22, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 23, - "name": "no_flow_overlap", - "type_info": "Bool" - }, - { - "ordinal": 24, - "name": "tag", - "type_info": "Varchar" - }, - { - "ordinal": 25, - "name": "paused_until", - "type_info": "Timestamptz" - }, - { - "ordinal": 26, + "ordinal": 20, "name": "on_success", "type_info": "Varchar" }, { - "ordinal": 27, - "name": "on_success_extra_args", - "type_info": "Json" + "ordinal": 21, + "name": "on_success_extra_args: _", + "type_info": "Jsonb" }, { - "ordinal": 28, - "name": "cron_version", + "ordinal": 22, + "name": "ws_error_handler_muted", + "type_info": "Bool" + }, + { + "ordinal": 23, + "name": "retry", + "type_info": "Jsonb" + }, + { + "ordinal": 24, + "name": "no_flow_overlap", + "type_info": "Bool" + }, + { + "ordinal": 25, + "name": "summary", + "type_info": "Varchar" + }, + { + "ordinal": 26, + "name": "description", "type_info": "Text" }, + { + "ordinal": 27, + "name": "tag", + "type_info": "Varchar" + }, + { + "ordinal": 28, + "name": "paused_until", + "type_info": "Timestamptz" + }, { "ordinal": 29, - "name": "jobs", - "type_info": "JsonArray" + "name": "cron_version", + "type_info": "Text" } ], "parameters": { "Left": [ + "Bool", + "Varchar", "Text", - "Int8", - "Int8" + "Text" ] }, "nullable": [ @@ -169,12 +170,14 @@ false, false, false, - true, - false, false, false, true, false, + false, + true, + true, + true, true, true, true, @@ -184,15 +187,13 @@ true, false, true, - true, false, true, true, true, true, - true, - null + true ] }, - "hash": "099894523449a70eb301ecd1d744210d39f405016ffec374e4b15a2528baccb5" + "hash": "7cd070aaca3b4f95bb6e669ac07f288bd1cc8a85625255d86e45f63e8267d34c" } diff --git a/backend/.sqlx/query-7cf5a1c434c8d84eb2400cd394c8fed2e35cb1943d63692dfbf1b997e1263da0.json b/backend/.sqlx/query-7cf5a1c434c8d84eb2400cd394c8fed2e35cb1943d63692dfbf1b997e1263da0.json deleted file mode 100644 index c171169e32..0000000000 --- a/backend/.sqlx/query-7cf5a1c434c8d84eb2400cd394c8fed2e35cb1943d63692dfbf1b997e1263da0.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job SET\n args = (SELECT result FROM v2_job_completed WHERE id = $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "7cf5a1c434c8d84eb2400cd394c8fed2e35cb1943d63692dfbf1b997e1263da0" -} diff --git a/backend/.sqlx/query-7d3180d119da0f6215a571118439469569c31e0234bb073af33f2cd4d6ca71d4.json b/backend/.sqlx/query-7d3180d119da0f6215a571118439469569c31e0234bb073af33f2cd4d6ca71d4.json deleted file mode 100644 index b332ab2871..0000000000 --- a/backend/.sqlx/query-7d3180d119da0f6215a571118439469569c31e0234bb073af33f2cd4d6ca71d4.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM queue WHERE workspace_id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "7d3180d119da0f6215a571118439469569c31e0234bb073af33f2cd4d6ca71d4" -} diff --git a/backend/.sqlx/query-7d5a161d916cb8d1485f8d72e6b4044f505f49edcd2ebeee4a77eae737f48795.json b/backend/.sqlx/query-7d5a161d916cb8d1485f8d72e6b4044f505f49edcd2ebeee4a77eae737f48795.json new file mode 100644 index 0000000000..e1ab284262 --- /dev/null +++ b/backend/.sqlx/query-7d5a161d916cb8d1485f8d72e6b4044f505f49edcd2ebeee4a77eae737f48795.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM \n capture\n WHERE \n id = $1\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [] + }, + "hash": "7d5a161d916cb8d1485f8d72e6b4044f505f49edcd2ebeee4a77eae737f48795" +} diff --git a/backend/.sqlx/query-e6f85cdbe681ace495fde31e67339dc58460b6914440473fe94de7a7bc292af4.json b/backend/.sqlx/query-7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f.json similarity index 51% rename from backend/.sqlx/query-e6f85cdbe681ace495fde31e67339dc58460b6914440473fe94de7a7bc292af4.json rename to backend/.sqlx/query-7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f.json index b35f375b8c..93ed95dd5e 100644 --- a/backend/.sqlx/query-e6f85cdbe681ace495fde31e67339dc58460b6914440473fe94de7a7bc292af4.json +++ b/backend/.sqlx/query-7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "DELETE FROM completed_job WHERE workspace_id = $1", + "query": "DELETE FROM token WHERE email = $1 AND label = 'session'", "describe": { "columns": [], "parameters": { @@ -10,5 +10,5 @@ }, "nullable": [] }, - "hash": "e6f85cdbe681ace495fde31e67339dc58460b6914440473fe94de7a7bc292af4" + "hash": "7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f" } diff --git a/backend/.sqlx/query-7dc7bc4e22942792938d273655962a95486f6da82cdc08f79dd6cef508256474.json b/backend/.sqlx/query-7dc7bc4e22942792938d273655962a95486f6da82cdc08f79dd6cef508256474.json deleted file mode 100644 index 94ad208788..0000000000 --- a/backend/.sqlx/query-7dc7bc4e22942792938d273655962a95486f6da82cdc08f79dd6cef508256474.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO completed_job AS cj\n ( workspace_id\n , id\n , parent_job\n , created_by\n , created_at\n , started_at\n , duration_ms\n , success\n , script_hash\n , script_path\n , args\n , result\n , raw_code\n , raw_lock\n , canceled\n , canceled_by\n , canceled_reason\n , job_kind\n , schedule_path\n , permissioned_as\n , flow_status\n , raw_flow\n , is_flow_step\n , is_skipped\n , language\n , email\n , visible_to_owner\n , mem_peak\n , tag\n , priority\n )\n SELECT workspace_id\n , id\n , parent_job\n , created_by\n , created_at\n , now()\n , 0\n , false\n , script_hash\n , script_path\n , args\n , $4\n , raw_code\n , raw_lock\n , true\n , $1\n , canceled_reason\n , job_kind\n , schedule_path\n , permissioned_as\n , flow_status\n , raw_flow\n , is_flow_step\n , false\n , language\n , email\n , visible_to_owner\n , mem_peak\n , tag\n , priority FROM queue \n WHERE id = any($2) AND running = false AND parent_job IS NULL AND workspace_id = $3 AND schedule_path IS NULL FOR UPDATE SKIP LOCKED\n ON CONFLICT (id) DO NOTHING RETURNING id AS \"id!\"", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Varchar", - "UuidArray", - "Text", - "Jsonb" - ] - }, - "nullable": [ - true - ] - }, - "hash": "7dc7bc4e22942792938d273655962a95486f6da82cdc08f79dd6cef508256474" -} diff --git a/backend/.sqlx/query-7e31c47e02492b74cbb5702dfc5ef9f4c8faa986843024a2cd58e212f706aae8.json b/backend/.sqlx/query-7e31c47e02492b74cbb5702dfc5ef9f4c8faa986843024a2cd58e212f706aae8.json new file mode 100644 index 0000000000..eea1cfa434 --- /dev/null +++ b/backend/.sqlx/query-7e31c47e02492b74cbb5702dfc5ef9f4c8faa986843024a2cd58e212f706aae8.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'sqs'\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "7e31c47e02492b74cbb5702dfc5ef9f4c8faa986843024a2cd58e212f706aae8" +} diff --git a/backend/.sqlx/query-7e64ba7e2362cc19d2aed9f34c9879983922e96a9baab7c1a2b09ed2b1c261e2.json b/backend/.sqlx/query-7e64ba7e2362cc19d2aed9f34c9879983922e96a9baab7c1a2b09ed2b1c261e2.json new file mode 100644 index 0000000000..9984af6e6e --- /dev/null +++ b/backend/.sqlx/query-7e64ba7e2362cc19d2aed9f34c9879983922e96a9baab7c1a2b09ed2b1c261e2.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT \n active_pid \n FROM \n pg_replication_slots \n WHERE \n slot_name = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "active_pid", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Name" + ] + }, + "nullable": [ + true + ] + }, + "hash": "7e64ba7e2362cc19d2aed9f34c9879983922e96a9baab7c1a2b09ed2b1c261e2" +} diff --git a/backend/.sqlx/query-7ecb01893c46823a207bf59aa57d6f4c6e63f7b7b6d4cf0b4ae1237da0e17b4b.json b/backend/.sqlx/query-7ecb01893c46823a207bf59aa57d6f4c6e63f7b7b6d4cf0b4ae1237da0e17b4b.json new file mode 100644 index 0000000000..628794aae4 --- /dev/null +++ b/backend/.sqlx/query-7ecb01893c46823a207bf59aa57d6f4c6e63f7b7b6d4cf0b4ae1237da0e17b4b.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n jsonb_array_elements(git_sync->'repositories')->>'script_path' AS script_path,\n jsonb_array_elements(git_sync->'repositories')->>'git_repo_resource_path' AS git_repo_resource_path\n FROM workspace_settings\n WHERE workspace_id = $1;\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "script_path", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "git_repo_resource_path", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null, + null + ] + }, + "hash": "7ecb01893c46823a207bf59aa57d6f4c6e63f7b7b6d4cf0b4ae1237da0e17b4b" +} diff --git a/backend/.sqlx/query-7f6d6952abee71fb6bb5604766c91e9605494a797b9c01c54fbdbe8949ab625d.json b/backend/.sqlx/query-7f6d6952abee71fb6bb5604766c91e9605494a797b9c01c54fbdbe8949ab625d.json new file mode 100644 index 0000000000..0667ac7d65 --- /dev/null +++ b/backend/.sqlx/query-7f6d6952abee71fb6bb5604766c91e9605494a797b9c01c54fbdbe8949ab625d.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n WITH ping AS (\n UPDATE v2_job_runtime SET ping = null WHERE id = $2\n )\n UPDATE v2_job_queue SET\n running = false,\n started_at = null,\n scheduled_for = $1\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Timestamptz", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "7f6d6952abee71fb6bb5604766c91e9605494a797b9c01c54fbdbe8949ab625d" +} diff --git a/backend/.sqlx/query-7f717130a398c8d52f814a968c2b0bc4dbb9cd654307f5167d8dbe794f17a1cf.json b/backend/.sqlx/query-7f717130a398c8d52f814a968c2b0bc4dbb9cd654307f5167d8dbe794f17a1cf.json deleted file mode 100644 index 1df4da3a93..0000000000 --- a/backend/.sqlx/query-7f717130a398c8d52f814a968c2b0bc4dbb9cd654307f5167d8dbe794f17a1cf.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT success FROM completed_job WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "success", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "7f717130a398c8d52f814a968c2b0bc4dbb9cd654307f5167d8dbe794f17a1cf" -} diff --git a/backend/.sqlx/query-7f772ceace10008d7d40afd331a585bfa19b70315840585c81b8e7a3911ab5e7.json b/backend/.sqlx/query-7f772ceace10008d7d40afd331a585bfa19b70315840585c81b8e7a3911ab5e7.json new file mode 100644 index 0000000000..1d7b5ddba5 --- /dev/null +++ b/backend/.sqlx/query-7f772ceace10008d7d40afd331a585bfa19b70315840585c81b8e7a3911ab5e7.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE workspace_settings\n SET git_app_installations = COALESCE(\n (\n SELECT jsonb_agg(elem)\n FROM jsonb_array_elements(git_app_installations) AS elem\n WHERE (elem->>'installation_id')::bigint != $1\n ),\n '[]'::jsonb\n )\n WHERE workspace_id = $2;\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Text" + ] + }, + "nullable": [] + }, + "hash": "7f772ceace10008d7d40afd331a585bfa19b70315840585c81b8e7a3911ab5e7" +} diff --git a/backend/.sqlx/query-c223f8b7fa4ef1aa06e1ba2a56d677774aa237508d5610714efd2e9b8b93c7b8.json b/backend/.sqlx/query-7fbf72d9059fcd77e4c1112fa4fa22e4276c1da653475628889ce17dc904fbaa.json similarity index 63% rename from backend/.sqlx/query-c223f8b7fa4ef1aa06e1ba2a56d677774aa237508d5610714efd2e9b8b93c7b8.json rename to backend/.sqlx/query-7fbf72d9059fcd77e4c1112fa4fa22e4276c1da653475628889ce17dc904fbaa.json index 4d79556b49..ca02b5c648 100644 --- a/backend/.sqlx/query-c223f8b7fa4ef1aa06e1ba2a56d677774aa237508d5610714efd2e9b8b93c7b8.json +++ b/backend/.sqlx/query-7fbf72d9059fcd77e4c1112fa4fa22e4276c1da653475628889ce17dc904fbaa.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT trigger_config as \"trigger_config: _\", trigger_kind as \"trigger_kind: _\", error, last_server_ping\n FROM capture_config\n WHERE workspace_id = $1 AND path = $2 AND is_flow = $3", + "query": "\n SELECT \n trigger_config AS \"trigger_config: _\", \n trigger_kind AS \"trigger_kind: _\", \n error, \n last_server_ping\n FROM \n capture_config\n WHERE \n workspace_id = $1 \n AND path = $2 \n AND is_flow = $3\n ", "describe": { "columns": [ { @@ -21,7 +21,11 @@ "websocket", "kafka", "email", - "nats" + "nats", + "postgres", + "sqs", + "mqtt", + "gcp" ] } } @@ -52,5 +56,5 @@ true ] }, - "hash": "c223f8b7fa4ef1aa06e1ba2a56d677774aa237508d5610714efd2e9b8b93c7b8" + "hash": "7fbf72d9059fcd77e4c1112fa4fa22e4276c1da653475628889ce17dc904fbaa" } diff --git a/backend/.sqlx/query-803b9c1373541cf52f416cde9e9e99ab79072e21dfaafb498aac25a059bd2f30.json b/backend/.sqlx/query-803b9c1373541cf52f416cde9e9e99ab79072e21dfaafb498aac25a059bd2f30.json deleted file mode 100644 index b416f93d3b..0000000000 --- a/backend/.sqlx/query-803b9c1373541cf52f416cde9e9e99ab79072e21dfaafb498aac25a059bd2f30.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM job WHERE id = ANY($1)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "UuidArray" - ] - }, - "nullable": [] - }, - "hash": "803b9c1373541cf52f416cde9e9e99ab79072e21dfaafb498aac25a059bd2f30" -} diff --git a/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json b/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json index 2179199223..89745202b0 100644 --- a/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json +++ b/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json @@ -32,7 +32,9 @@ "rust", "ansible", "csharp", - "oracledb" + "oracledb", + "nu", + "java" ] } } diff --git a/backend/.sqlx/query-808790cc01ec68be41dfeb80dc560d447fd719f2a56ea954c8cce49ffabd4245.json b/backend/.sqlx/query-808790cc01ec68be41dfeb80dc560d447fd719f2a56ea954c8cce49ffabd4245.json new file mode 100644 index 0000000000..846f94dcbb --- /dev/null +++ b/backend/.sqlx/query-808790cc01ec68be41dfeb80dc560d447fd719f2a56ea954c8cce49ffabd4245.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT f.path\n FROM workspace_runnable_dependencies wru \n JOIN flow f\n ON wru.flow_path = f.path AND wru.workspace_id = f.workspace_id\n WHERE wru.runnable_path = $1 AND wru.runnable_is_flow = $2 AND wru.workspace_id = $3", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text", + "Bool", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "808790cc01ec68be41dfeb80dc560d447fd719f2a56ea954c8cce49ffabd4245" +} diff --git a/backend/.sqlx/query-8123ba05f6e7b9bd395175ee4ec0c36c3726b2da9ff3592589ac7e83df1c537c.json b/backend/.sqlx/query-8123ba05f6e7b9bd395175ee4ec0c36c3726b2da9ff3592589ac7e83df1c537c.json deleted file mode 100644 index 1126431b46..0000000000 --- a/backend/.sqlx/query-8123ba05f6e7b9bd395175ee4ec0c36c3726b2da9ff3592589ac7e83df1c537c.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id, flow_status AS \"flow_status!: Json\"\n FROM v2_job_completed WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "flow_status!: Json", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - false, - true - ] - }, - "hash": "8123ba05f6e7b9bd395175ee4ec0c36c3726b2da9ff3592589ac7e83df1c537c" -} diff --git a/backend/.sqlx/query-829130d74c107e4e1a86f3e772657ae030463c139c39072777e453bfb7e9c0c3.json b/backend/.sqlx/query-829130d74c107e4e1a86f3e772657ae030463c139c39072777e453bfb7e9c0c3.json deleted file mode 100644 index e00aba3aab..0000000000 --- a/backend/.sqlx/query-829130d74c107e4e1a86f3e772657ae030463c139c39072777e453bfb7e9c0c3.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT job_kind = 'identity' FROM completed_job WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "829130d74c107e4e1a86f3e772657ae030463c139c39072777e453bfb7e9c0c3" -} diff --git a/backend/.sqlx/query-82f6674f19e8ad51a992505a46f46fc4a48172f104e9e849f755ac041c3eef92.json b/backend/.sqlx/query-82f6674f19e8ad51a992505a46f46fc4a48172f104e9e849f755ac041c3eef92.json deleted file mode 100644 index f98f3fdeea..0000000000 --- a/backend/.sqlx/query-82f6674f19e8ad51a992505a46f46fc4a48172f104e9e849f755ac041c3eef92.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT created_at FROM metrics WHERE id LIKE 'queue_count_%' ORDER BY created_at DESC LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false - ] - }, - "hash": "82f6674f19e8ad51a992505a46f46fc4a48172f104e9e849f755ac041c3eef92" -} diff --git a/backend/.sqlx/query-830297547ea33969f96a5c4c2b82a540f1d147948e1b1a7523b21151ffa22305.json b/backend/.sqlx/query-830297547ea33969f96a5c4c2b82a540f1d147948e1b1a7523b21151ffa22305.json deleted file mode 100644 index 2e67b21966..0000000000 --- a/backend/.sqlx/query-830297547ea33969f96a5c4c2b82a540f1d147948e1b1a7523b21151ffa22305.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n success AS \"success!\",\n result AS \"result: Json>\",\n started_at AS \"started_at!\"FROM v2_as_completed_job WHERE workspace_id = $1 AND schedule_path = $2 AND script_path = $3 AND id != $4\n ORDER BY created_at DESC\n LIMIT $5", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "success!", - "type_info": "Bool" - }, - { - "ordinal": 1, - "name": "result: Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 2, - "name": "started_at!", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Text", - "Uuid", - "Int8" - ] - }, - "nullable": [ - true, - true, - true - ] - }, - "hash": "830297547ea33969f96a5c4c2b82a540f1d147948e1b1a7523b21151ffa22305" -} diff --git a/backend/.sqlx/query-833a4ecec12dfe67f28016a135ffe682b023d1868a182b7cac16ce799433c257.json b/backend/.sqlx/query-833a4ecec12dfe67f28016a135ffe682b023d1868a182b7cac16ce799433c257.json deleted file mode 100644 index 44653c2265..0000000000 --- a/backend/.sqlx/query-833a4ecec12dfe67f28016a135ffe682b023d1868a182b7cac16ce799433c257.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE \n postgres_trigger\n SET \n last_server_ping = now(),\n error = $1\n WHERE\n workspace_id = $2\n AND path = $3\n AND server_id = $4 \n AND enabled IS TRUE\n RETURNING 1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Text", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "833a4ecec12dfe67f28016a135ffe682b023d1868a182b7cac16ce799433c257" -} diff --git a/backend/.sqlx/query-848c8371eeb17ebd4b36a33f7d8a61eb8f07c54d291bb857ddd41a549cbc88dd.json b/backend/.sqlx/query-848c8371eeb17ebd4b36a33f7d8a61eb8f07c54d291bb857ddd41a549cbc88dd.json new file mode 100644 index 0000000000..7751c14adb --- /dev/null +++ b/backend/.sqlx/query-848c8371eeb17ebd4b36a33f7d8a61eb8f07c54d291bb857ddd41a549cbc88dd.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE script \n SET ws_error_handler_muted = $3 \n WHERE ctid = (\n SELECT ctid FROM script\n WHERE path = $1 AND workspace_id = $2\n ORDER BY created_at DESC\n LIMIT 1\n )\n", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "848c8371eeb17ebd4b36a33f7d8a61eb8f07c54d291bb857ddd41a549cbc88dd" +} diff --git a/backend/.sqlx/query-8490024b96aa689d3c1bc5cbf94fcd8c5491732818404fee2397f00cccee0ad3.json b/backend/.sqlx/query-8490024b96aa689d3c1bc5cbf94fcd8c5491732818404fee2397f00cccee0ad3.json new file mode 100644 index 0000000000..9700ab141f --- /dev/null +++ b/backend/.sqlx/query-8490024b96aa689d3c1bc5cbf94fcd8c5491732818404fee2397f00cccee0ad3.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n sqs_trigger \n SET \n aws_auth_resource_type = $1,\n aws_resource_path = $2,\n queue_url = $3,\n message_attributes = $4, \n is_flow = $5, \n edited_by = $6, \n email = $7,\n script_path = $8,\n path = $9,\n edited_at = now(), \n error = NULL,\n server_id = NULL\n WHERE \n workspace_id = $10 AND \n path = $11\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + { + "Custom": { + "name": "aws_auth_resource_type", + "kind": { + "Enum": [ + "oidc", + "credentials" + ] + } + } + }, + "Varchar", + "Varchar", + "TextArray", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "8490024b96aa689d3c1bc5cbf94fcd8c5491732818404fee2397f00cccee0ad3" +} diff --git a/backend/.sqlx/query-858db2a501abcfffbcce19d60cc241060c93354de19f0fa80b8f45290e8b992d.json b/backend/.sqlx/query-858db2a501abcfffbcce19d60cc241060c93354de19f0fa80b8f45290e8b992d.json deleted file mode 100644 index f3d1c484fe..0000000000 --- a/backend/.sqlx/query-858db2a501abcfffbcce19d60cc241060c93354de19f0fa80b8f45290e8b992d.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2)\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "858db2a501abcfffbcce19d60cc241060c93354de19f0fa80b8f45290e8b992d" -} diff --git a/backend/.sqlx/query-85bbf1244848682702be93527cad26e44e6ce1308505d1fbc9c28eaabcbe463e.json b/backend/.sqlx/query-85bbf1244848682702be93527cad26e44e6ce1308505d1fbc9c28eaabcbe463e.json deleted file mode 100644 index de49a218fe..0000000000 --- a/backend/.sqlx/query-85bbf1244848682702be93527cad26e44e6ce1308505d1fbc9c28eaabcbe463e.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET flow_status = jsonb_set(COALESCE(flow_status, '{}'::jsonb), array[$1], jsonb_set(jsonb_set('{}'::jsonb, '{scheduled_for}', to_jsonb(now()::text)), '{name}', to_jsonb($3::text))) WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Uuid", - "Text" - ] - }, - "nullable": [] - }, - "hash": "85bbf1244848682702be93527cad26e44e6ce1308505d1fbc9c28eaabcbe463e" -} diff --git a/backend/.sqlx/query-866c1b86d63466df84877e81655ce999284f3d2d854a00fefcaf7c044dcf71ca.json b/backend/.sqlx/query-866c1b86d63466df84877e81655ce999284f3d2d854a00fefcaf7c044dcf71ca.json deleted file mode 100644 index c4905590b6..0000000000 --- a/backend/.sqlx/query-866c1b86d63466df84877e81655ce999284f3d2d854a00fefcaf7c044dcf71ca.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = jsonb_set(\n jsonb_set(flow_status, ARRAY['failure_module', 'job'], to_jsonb($1::UUID::TEXT)),\n ARRAY['failure_module', 'type'],\n to_jsonb('InProgress'::text)\n )\n WHERE id = $2 AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Text" - ] - }, - "nullable": [] - }, - "hash": "866c1b86d63466df84877e81655ce999284f3d2d854a00fefcaf7c044dcf71ca" -} diff --git a/backend/.sqlx/query-867d5c75ddc6c5d20136880c7294844b4c1a38701190795a801fa43c74a0beeb.json b/backend/.sqlx/query-867d5c75ddc6c5d20136880c7294844b4c1a38701190795a801fa43c74a0beeb.json deleted file mode 100644 index 9232baf39c..0000000000 --- a/backend/.sqlx/query-867d5c75ddc6c5d20136880c7294844b4c1a38701190795a801fa43c74a0beeb.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT workflow_as_code_status FROM v2_job_completed WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "workflow_as_code_status", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - true - ] - }, - "hash": "867d5c75ddc6c5d20136880c7294844b4c1a38701190795a801fa43c74a0beeb" -} diff --git a/backend/.sqlx/query-86cc1e3c18e936a700d8842a51a6f8377ec190669e2e22f8d511871d6fbe07b8.json b/backend/.sqlx/query-86cc1e3c18e936a700d8842a51a6f8377ec190669e2e22f8d511871d6fbe07b8.json deleted file mode 100644 index 47b30f2895..0000000000 --- a/backend/.sqlx/query-86cc1e3c18e936a700d8842a51a6f8377ec190669e2e22f8d511871d6fbe07b8.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n success AS \"success!\",\n result AS \"result: Json>\",\n started_at AS \"started_at!\"\n FROM v2_as_completed_job\n WHERE workspace_id = $1 AND schedule_path = $2 AND script_path = $3 AND id != $4\n ORDER BY created_at DESC\n LIMIT $5", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "success!", - "type_info": "Bool" - }, - { - "ordinal": 1, - "name": "result: Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 2, - "name": "started_at!", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Text", - "Uuid", - "Int8" - ] - }, - "nullable": [ - true, - true, - true - ] - }, - "hash": "86cc1e3c18e936a700d8842a51a6f8377ec190669e2e22f8d511871d6fbe07b8" -} diff --git a/backend/.sqlx/query-870d7feec169f70d140b7477562315ed2fd4662975bf0d01cfbffac86a959368.json b/backend/.sqlx/query-870d7feec169f70d140b7477562315ed2fd4662975bf0d01cfbffac86a959368.json deleted file mode 100644 index f5c36ed27f..0000000000 --- a/backend/.sqlx/query-870d7feec169f70d140b7477562315ed2fd4662975bf0d01cfbffac86a959368.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2), ARRAY['step'], $3)\n WHERE id = $4", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "870d7feec169f70d140b7477562315ed2fd4662975bf0d01cfbffac86a959368" -} diff --git a/backend/.sqlx/query-873fde22f7947882edae7d15bc54e8df105d5e241eeb842a83e3444be0d2736d.json b/backend/.sqlx/query-873fde22f7947882edae7d15bc54e8df105d5e241eeb842a83e3444be0d2736d.json deleted file mode 100644 index 39613fc721..0000000000 --- a/backend/.sqlx/query-873fde22f7947882edae7d15bc54e8df105d5e241eeb842a83e3444be0d2736d.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM resource WHERE resource_type = 'cache' AND to_timestamp((value->>'expire')::int) < now() RETURNING path", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "path", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false - ] - }, - "hash": "873fde22f7947882edae7d15bc54e8df105d5e241eeb842a83e3444be0d2736d" -} diff --git a/backend/.sqlx/query-e23e110e1f0438d21534fc4323e0e7bc1f0dbeca2e4f44ced05bae0ca5ca1039.json b/backend/.sqlx/query-87564a196a1662f524407d853db506bf08c28efe82b68b3d44bafbd3d0e91c29.json similarity index 57% rename from backend/.sqlx/query-e23e110e1f0438d21534fc4323e0e7bc1f0dbeca2e4f44ced05bae0ca5ca1039.json rename to backend/.sqlx/query-87564a196a1662f524407d853db506bf08c28efe82b68b3d44bafbd3d0e91c29.json index 950d7662ad..e2acac048f 100644 --- a/backend/.sqlx/query-e23e110e1f0438d21534fc4323e0e7bc1f0dbeca2e4f44ced05bae0ca5ca1039.json +++ b/backend/.sqlx/query-87564a196a1662f524407d853db506bf08c28efe82b68b3d44bafbd3d0e91c29.json @@ -1,20 +1,15 @@ { "db_name": "PostgreSQL", - "query": "SELECT trigger_config as \"trigger_config: _\", owner, email\n FROM capture_config\n WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4 AND last_client_ping > NOW() - INTERVAL '10 seconds'", + "query": "\n SELECT \n owner, \n email\n FROM \n capture_config\n WHERE \n workspace_id = $1 \n AND path = $2 \n AND is_flow = $3 \n AND trigger_kind = $4 \n AND last_client_ping > NOW() - INTERVAL '10 seconds'\n ", "describe": { "columns": [ { "ordinal": 0, - "name": "trigger_config: _", - "type_info": "Jsonb" - }, - { - "ordinal": 1, "name": "owner", "type_info": "Varchar" }, { - "ordinal": 2, + "ordinal": 1, "name": "email", "type_info": "Varchar" } @@ -34,7 +29,11 @@ "websocket", "kafka", "email", - "nats" + "nats", + "postgres", + "sqs", + "mqtt", + "gcp" ] } } @@ -42,10 +41,9 @@ ] }, "nullable": [ - true, false, false ] }, - "hash": "e23e110e1f0438d21534fc4323e0e7bc1f0dbeca2e4f44ced05bae0ca5ca1039" + "hash": "87564a196a1662f524407d853db506bf08c28efe82b68b3d44bafbd3d0e91c29" } diff --git a/backend/.sqlx/query-d988e91087695742d75946100cf2b7593cb8eed2a97411697819849958c022b3.json b/backend/.sqlx/query-8780a8cd6781f86041ae8df58477913c01b5d8d27dd4251f0cabbe3e974c0b0a.json similarity index 70% rename from backend/.sqlx/query-d988e91087695742d75946100cf2b7593cb8eed2a97411697819849958c022b3.json rename to backend/.sqlx/query-8780a8cd6781f86041ae8df58477913c01b5d8d27dd4251f0cabbe3e974c0b0a.json index 15625b26c2..2e19aa5a0e 100644 --- a/backend/.sqlx/query-d988e91087695742d75946100cf2b7593cb8eed2a97411697819849958c022b3.json +++ b/backend/.sqlx/query-8780a8cd6781f86041ae8df58477913c01b5d8d27dd4251f0cabbe3e974c0b0a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT result AS \"result!: Json>\"\n FROM v2_job_completed WHERE id = $1 AND workspace_id = $2", + "query": "SELECT result AS \"result!: Json>\"\n FROM v2_job_completed WHERE id = $1 AND workspace_id = $2", "describe": { "columns": [ { @@ -19,5 +19,5 @@ true ] }, - "hash": "d988e91087695742d75946100cf2b7593cb8eed2a97411697819849958c022b3" + "hash": "8780a8cd6781f86041ae8df58477913c01b5d8d27dd4251f0cabbe3e974c0b0a" } diff --git a/backend/.sqlx/query-8824b382c4e98dfa17b4aa656af3a6c1ff99973e778d71bd598a50d022da8f15.json b/backend/.sqlx/query-8824b382c4e98dfa17b4aa656af3a6c1ff99973e778d71bd598a50d022da8f15.json deleted file mode 100644 index 124a6e36c5..0000000000 --- a/backend/.sqlx/query-8824b382c4e98dfa17b4aa656af3a6c1ff99973e778d71bd598a50d022da8f15.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO metrics (id, value) VALUES ($1, $2)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "8824b382c4e98dfa17b4aa656af3a6c1ff99973e778d71bd598a50d022da8f15" -} diff --git a/backend/.sqlx/query-884039108b98daa6279975e65348e460f99a0d2155fe2ff7b1d2840c5d9b76d0.json b/backend/.sqlx/query-884039108b98daa6279975e65348e460f99a0d2155fe2ff7b1d2840c5d9b76d0.json new file mode 100644 index 0000000000..64048e92f8 --- /dev/null +++ b/backend/.sqlx/query-884039108b98daa6279975e65348e460f99a0d2155fe2ff7b1d2840c5d9b76d0.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT elem\n FROM workspace_settings,\n LATERAL jsonb_array_elements(git_app_installations) AS elem\n WHERE workspace_id = $1\n AND (elem->>'installation_id')::bigint = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "elem", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + }, + "nullable": [ + null + ] + }, + "hash": "884039108b98daa6279975e65348e460f99a0d2155fe2ff7b1d2840c5d9b76d0" +} diff --git a/backend/.sqlx/query-8929150cb9262623eb12c908cb96c4d7e8cac594900ab0956c098e1cfa75e2f0.json b/backend/.sqlx/query-8929150cb9262623eb12c908cb96c4d7e8cac594900ab0956c098e1cfa75e2f0.json deleted file mode 100644 index 9e66b2ae2c..0000000000 --- a/backend/.sqlx/query-8929150cb9262623eb12c908cb96c4d7e8cac594900ab0956c098e1cfa75e2f0.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET leaf_jobs = JSONB_SET(coalesce(leaf_jobs, '{}'::jsonb), ARRAY[$1::TEXT], $2)\n WHERE COALESCE((SELECT root_job FROM queue WHERE id = $3), $3) = id", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "8929150cb9262623eb12c908cb96c4d7e8cac594900ab0956c098e1cfa75e2f0" -} diff --git a/backend/.sqlx/query-89c08575afb31b70984f6b2b7dd4297af93b5b83ffdfb3ec91eba5df7ad3fd95.json b/backend/.sqlx/query-89c08575afb31b70984f6b2b7dd4297af93b5b83ffdfb3ec91eba5df7ad3fd95.json deleted file mode 100644 index a463eafdb7..0000000000 --- a/backend/.sqlx/query-89c08575afb31b70984f6b2b7dd4297af93b5b83ffdfb3ec91eba5df7ad3fd95.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE http_trigger SET script_path = $1, path = $2, is_flow = $3, http_method = $4, static_asset_config = $5, edited_by = $6, email = $7, is_async = $8, requires_auth = $9, edited_at = now(), is_static_website = $10\n WHERE workspace_id = $11 AND path = $12", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Varchar", - "Bool", - { - "Custom": { - "name": "http_method", - "kind": { - "Enum": [ - "get", - "post", - "put", - "delete", - "patch" - ] - } - } - }, - "Jsonb", - "Varchar", - "Varchar", - "Bool", - "Bool", - "Bool", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "89c08575afb31b70984f6b2b7dd4297af93b5b83ffdfb3ec91eba5df7ad3fd95" -} diff --git a/backend/.sqlx/query-89de3ff8ab32e545efcbcda05f994cb1a32c4991cbd25046282d34272587d2de.json b/backend/.sqlx/query-89de3ff8ab32e545efcbcda05f994cb1a32c4991cbd25046282d34272587d2de.json index d90a9646de..99c2dc90a2 100644 --- a/backend/.sqlx/query-89de3ff8ab32e545efcbcda05f994cb1a32c4991cbd25046282d34272587d2de.json +++ b/backend/.sqlx/query-89de3ff8ab32e545efcbcda05f994cb1a32c4991cbd25046282d34272587d2de.json @@ -59,7 +59,9 @@ "rust", "ansible", "csharp", - "oracledb" + "oracledb", + "nu", + "java" ] } } diff --git a/backend/.sqlx/query-8a1c9119f6f4763f64597684dc7b312404e637212de1897e97999755a2e492fd.json b/backend/.sqlx/query-8a1c9119f6f4763f64597684dc7b312404e637212de1897e97999755a2e492fd.json deleted file mode 100644 index 8a92be2af8..0000000000 --- a/backend/.sqlx/query-8a1c9119f6f4763f64597684dc7b312404e637212de1897e97999755a2e492fd.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job SET\n args = (SELECT result FROM v2_job_completed WHERE id = $1),\n preprocessed = TRUE\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "8a1c9119f6f4763f64597684dc7b312404e637212de1897e97999755a2e492fd" -} diff --git a/backend/.sqlx/query-8a44ca0cfe1e154138cbbf5ebddebbba2e1149d2e329d1d89eecc1c008c93a31.json b/backend/.sqlx/query-8a44ca0cfe1e154138cbbf5ebddebbba2e1149d2e329d1d89eecc1c008c93a31.json deleted file mode 100644 index fdfaa0c029..0000000000 --- a/backend/.sqlx/query-8a44ca0cfe1e154138cbbf5ebddebbba2e1149d2e329d1d89eecc1c008c93a31.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE completed_job\n SET logs = '##DELETED##', args = '{}'::jsonb, result = '{}'::jsonb\n WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "8a44ca0cfe1e154138cbbf5ebddebbba2e1149d2e329d1d89eecc1c008c93a31" -} diff --git a/backend/.sqlx/query-b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27.json b/backend/.sqlx/query-8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d.json similarity index 75% rename from backend/.sqlx/query-b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27.json rename to backend/.sqlx/query-8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d.json index 75f58e98e3..90a2f78d2c 100644 --- a/backend/.sqlx/query-b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27.json +++ b/backend/.sqlx/query-8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO token\n (token, email, label, expiration, super_admin)\n VALUES ($1, $2, $3, now() + ($4 || ' hours')::interval, $5)", + "query": "INSERT INTO token\n (token, email, label, expiration, super_admin)\n VALUES ($1, $2, $3, now() + ($4 || ' seconds')::interval, $5)", "describe": { "columns": [], "parameters": { @@ -14,5 +14,5 @@ }, "nullable": [] }, - "hash": "b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27" + "hash": "8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d" } diff --git a/backend/.sqlx/query-1961d15ae075072bd5f677c95f9b4dac7f747585d98aa8bcf60dcfb4c8124028.json b/backend/.sqlx/query-8b784784fe63d91cc5ebe27022f803caf85d5916960308cff512047d4f0dcba4.json similarity index 57% rename from backend/.sqlx/query-1961d15ae075072bd5f677c95f9b4dac7f747585d98aa8bcf60dcfb4c8124028.json rename to backend/.sqlx/query-8b784784fe63d91cc5ebe27022f803caf85d5916960308cff512047d4f0dcba4.json index 418be57757..73cc08b89d 100644 --- a/backend/.sqlx/query-1961d15ae075072bd5f677c95f9b4dac7f747585d98aa8bcf60dcfb4c8124028.json +++ b/backend/.sqlx/query-8b784784fe63d91cc5ebe27022f803caf85d5916960308cff512047d4f0dcba4.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, static_asset_config as \"static_asset_config: _\", is_static_website FROM http_trigger WHERE http_method = $1", + "query": "SELECT * FROM kafka_trigger\n WHERE workspace_id = $1", "describe": { "columns": [ { @@ -10,33 +10,33 @@ }, { "ordinal": 1, - "name": "script_path", + "name": "kafka_resource_path", "type_info": "Varchar" }, { "ordinal": 2, - "name": "is_flow", - "type_info": "Bool" + "name": "topics", + "type_info": "VarcharArray" }, { "ordinal": 3, - "name": "route_path", + "name": "group_id", "type_info": "Varchar" }, { "ordinal": 4, - "name": "workspace_id", + "name": "script_path", "type_info": "Varchar" }, { "ordinal": 5, - "name": "is_async", + "name": "is_flow", "type_info": "Bool" }, { "ordinal": 6, - "name": "requires_auth", - "type_info": "Bool" + "name": "workspace_id", + "type_info": "Varchar" }, { "ordinal": 7, @@ -50,31 +50,38 @@ }, { "ordinal": 9, - "name": "static_asset_config: _", - "type_info": "Jsonb" + "name": "edited_at", + "type_info": "Timestamptz" }, { "ordinal": 10, - "name": "is_static_website", + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 11, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 14, + "name": "enabled", "type_info": "Bool" } ], "parameters": { "Left": [ - { - "Custom": { - "name": "http_method", - "kind": { - "Enum": [ - "get", - "post", - "put", - "delete", - "patch" - ] - } - } - } + "Text" ] }, "nullable": [ @@ -87,9 +94,13 @@ false, false, false, + false, + false, + true, + true, true, false ] }, - "hash": "1961d15ae075072bd5f677c95f9b4dac7f747585d98aa8bcf60dcfb4c8124028" + "hash": "8b784784fe63d91cc5ebe27022f803caf85d5916960308cff512047d4f0dcba4" } diff --git a/backend/.sqlx/query-8be1ddb20ffd8c375b7d1ecb14bdb3a7c2f0c8f9308946b9262e14b8c584dd99.json b/backend/.sqlx/query-8be1ddb20ffd8c375b7d1ecb14bdb3a7c2f0c8f9308946b9262e14b8c584dd99.json deleted file mode 100644 index 60ca066b36..0000000000 --- a/backend/.sqlx/query-8be1ddb20ffd8c375b7d1ecb14bdb3a7c2f0c8f9308946b9262e14b8c584dd99.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = flow_status - 'retry'\n WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "8be1ddb20ffd8c375b7d1ecb14bdb3a7c2f0c8f9308946b9262e14b8c584dd99" -} diff --git a/backend/.sqlx/query-8be277b89102a26dda506202a3ef7eb05342cfb3aa9b4f5d80c70fbc50d437ba.json b/backend/.sqlx/query-8be277b89102a26dda506202a3ef7eb05342cfb3aa9b4f5d80c70fbc50d437ba.json deleted file mode 100644 index e5819f5c6a..0000000000 --- a/backend/.sqlx/query-8be277b89102a26dda506202a3ef7eb05342cfb3aa9b4f5d80c70fbc50d437ba.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "create index concurrently if not exists ix_job_workspace_id_created_at_new_6 ON v2_job (workspace_id, created_at DESC) where kind in ('script', 'flow') AND parent_job IS NULL", - "describe": { - "columns": [], - "parameters": { - "Left": [] - }, - "nullable": [] - }, - "hash": "8be277b89102a26dda506202a3ef7eb05342cfb3aa9b4f5d80c70fbc50d437ba" -} diff --git a/backend/.sqlx/query-4115a8e75ad5ed74f737f24aa64edc68e71b29ddc32a00a754678ea4f2c167e5.json b/backend/.sqlx/query-8be2919c3511575c89b882b112b987fd5724c299cb285f819a2561260404e513.json similarity index 82% rename from backend/.sqlx/query-4115a8e75ad5ed74f737f24aa64edc68e71b29ddc32a00a754678ea4f2c167e5.json rename to backend/.sqlx/query-8be2919c3511575c89b882b112b987fd5724c299cb285f819a2561260404e513.json index 06eaac04ff..191b010aec 100644 --- a/backend/.sqlx/query-4115a8e75ad5ed74f737f24aa64edc68e71b29ddc32a00a754678ea4f2c167e5.json +++ b/backend/.sqlx/query-8be2919c3511575c89b882b112b987fd5724c299cb285f819a2561260404e513.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes FROM token WHERE email = $1 AND label != 'ephemeral-script'\n ORDER BY created_at DESC LIMIT $2 OFFSET $3", + "query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes FROM token WHERE email = $1 AND (label != 'ephemeral-script' OR label IS NULL)\n ORDER BY created_at DESC LIMIT $2 OFFSET $3", "describe": { "columns": [ { @@ -50,5 +50,5 @@ true ] }, - "hash": "4115a8e75ad5ed74f737f24aa64edc68e71b29ddc32a00a754678ea4f2c167e5" + "hash": "8be2919c3511575c89b882b112b987fd5724c299cb285f819a2561260404e513" } diff --git a/backend/.sqlx/query-c845c06aa46b52a1e3672fd379dbcec167f4044e45e932aeeefd3d9237e5042c.json b/backend/.sqlx/query-8c2541cdfb84bfdbdc28285641166fe4c284dd6ed5245fbb90650d99afbf3812.json similarity index 59% rename from backend/.sqlx/query-c845c06aa46b52a1e3672fd379dbcec167f4044e45e932aeeefd3d9237e5042c.json rename to backend/.sqlx/query-8c2541cdfb84bfdbdc28285641166fe4c284dd6ed5245fbb90650d99afbf3812.json index e698dac427..88dcd47312 100644 --- a/backend/.sqlx/query-c845c06aa46b52a1e3672fd379dbcec167f4044e45e932aeeefd3d9237e5042c.json +++ b/backend/.sqlx/query-8c2541cdfb84bfdbdc28285641166fe4c284dd6ed5245fbb90650d99afbf3812.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT CAST(ROUND(AVG(duration_ms), 0) AS BIGINT) AS avg_duration_s FROM\n (SELECT duration_ms FROM concurrency_key LEFT JOIN completed_job ON completed_job.id = concurrency_key.job_id WHERE key = $1 AND ended_at IS NOT NULL\n ORDER BY ended_at\n DESC LIMIT 10) AS t", + "query": "SELECT CAST(ROUND(AVG(duration_ms), 0) AS BIGINT) AS avg_duration_s FROM\n (SELECT duration_ms FROM concurrency_key LEFT JOIN v2_job_completed ON v2_job_completed.id = concurrency_key.job_id WHERE key = $1 AND ended_at IS NOT NULL\n ORDER BY ended_at\n DESC LIMIT 10) AS t", "describe": { "columns": [ { @@ -18,5 +18,5 @@ null ] }, - "hash": "c845c06aa46b52a1e3672fd379dbcec167f4044e45e932aeeefd3d9237e5042c" + "hash": "8c2541cdfb84bfdbdc28285641166fe4c284dd6ed5245fbb90650d99afbf3812" } diff --git a/backend/.sqlx/query-8c30e91c2486f7511563621e7e805d0588a9ec8bbea9db10e95783e27e35bc12.json b/backend/.sqlx/query-8c30e91c2486f7511563621e7e805d0588a9ec8bbea9db10e95783e27e35bc12.json new file mode 100644 index 0000000000..961a55d8b1 --- /dev/null +++ b/backend/.sqlx/query-8c30e91c2486f7511563621e7e805d0588a9ec8bbea9db10e95783e27e35bc12.json @@ -0,0 +1,57 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO http_trigger (\n workspace_id, \n path, \n route_path, \n route_path_key,\n workspaced_route,\n authentication_resource_path,\n wrap_body,\n raw_string,\n script_path, \n is_flow, \n is_async, \n authentication_method, \n http_method, \n static_asset_config, \n edited_by, \n email, \n edited_at, \n is_static_website\n ) \n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, now(), $17\n )\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Bool", + "Bool", + "Varchar", + "Bool", + "Bool", + { + "Custom": { + "name": "authentication_method", + "kind": { + "Enum": [ + "none", + "windmill", + "api_key", + "basic_http", + "custom_script", + "signature" + ] + } + } + }, + { + "Custom": { + "name": "http_method", + "kind": { + "Enum": [ + "get", + "post", + "put", + "delete", + "patch" + ] + } + } + }, + "Jsonb", + "Varchar", + "Varchar", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "8c30e91c2486f7511563621e7e805d0588a9ec8bbea9db10e95783e27e35bc12" +} diff --git a/backend/.sqlx/query-8cb755510f2cfb23bdd0d1cf66b69949549a44855529f77a530f681a6e714646.json b/backend/.sqlx/query-8cb755510f2cfb23bdd0d1cf66b69949549a44855529f77a530f681a6e714646.json deleted file mode 100644 index 82b70ebe92..0000000000 --- a/backend/.sqlx/query-8cb755510f2cfb23bdd0d1cf66b69949549a44855529f77a530f681a6e714646.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT],\n $4\n )\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "8cb755510f2cfb23bdd0d1cf66b69949549a44855529f77a530f681a6e714646" -} diff --git a/backend/.sqlx/query-8d31b4a531c59a2385210d1213c205100d6673a94e90000c8db4eb5809f17365.json b/backend/.sqlx/query-8d31b4a531c59a2385210d1213c205100d6673a94e90000c8db4eb5809f17365.json new file mode 100644 index 0000000000..0d30c8ca0a --- /dev/null +++ b/backend/.sqlx/query-8d31b4a531c59a2385210d1213c205100d6673a94e90000c8db4eb5809f17365.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n mqtt_trigger \n SET \n enabled = $1, \n email = $2, \n edited_by = $3, \n edited_at = now(), \n server_id = NULL, \n error = NULL\n WHERE \n path = $4 AND \n workspace_id = $5 \n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Bool", + "Varchar", + "Varchar", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "8d31b4a531c59a2385210d1213c205100d6673a94e90000c8db4eb5809f17365" +} diff --git a/backend/.sqlx/query-8d4235984f27d8b939ffd5c660d5b57dc38dd3b2643361ed3b7cdcd1534d2e21.json b/backend/.sqlx/query-8d4235984f27d8b939ffd5c660d5b57dc38dd3b2643361ed3b7cdcd1534d2e21.json deleted file mode 100644 index 824deba7fa..0000000000 --- a/backend/.sqlx/query-8d4235984f27d8b939ffd5c660d5b57dc38dd3b2643361ed3b7cdcd1534d2e21.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM queue WHERE schedule_path = $1 AND running = false AND workspace_id = $2 AND is_flow_step = false", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "8d4235984f27d8b939ffd5c660d5b57dc38dd3b2643361ed3b7cdcd1534d2e21" -} diff --git a/backend/.sqlx/query-8d655c34a00510699d2ad7044f7e526ba5082e5d1945c76a98404fe5d92e32ee.json b/backend/.sqlx/query-8d655c34a00510699d2ad7044f7e526ba5082e5d1945c76a98404fe5d92e32ee.json deleted file mode 100644 index 028e35f5dc..0000000000 --- a/backend/.sqlx/query-8d655c34a00510699d2ad7044f7e526ba5082e5d1945c76a98404fe5d92e32ee.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM queue WHERE id = any($1) AND workspace_id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "UuidArray", - "Text" - ] - }, - "nullable": [] - }, - "hash": "8d655c34a00510699d2ad7044f7e526ba5082e5d1945c76a98404fe5d92e32ee" -} diff --git a/backend/.sqlx/query-8dd1de2aca8c6c9ffaddd2c41c3a614a50fa5fd03c2d3b9a41bd85a7f156345e.json b/backend/.sqlx/query-8dd1de2aca8c6c9ffaddd2c41c3a614a50fa5fd03c2d3b9a41bd85a7f156345e.json deleted file mode 100644 index c1a63e668f..0000000000 --- a/backend/.sqlx/query-8dd1de2aca8c6c9ffaddd2c41c3a614a50fa5fd03c2d3b9a41bd85a7f156345e.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT count(*) \n FROM worker_ping \n WHERE worker_group LIKE $1 AND ping_at > now() - INTERVAL '2 minutes'", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "8dd1de2aca8c6c9ffaddd2c41c3a614a50fa5fd03c2d3b9a41bd85a7f156345e" -} diff --git a/backend/.sqlx/query-8dd93be44f66c0744ddaff12a9664d9ad745a4a2bb4c0fa36d3caf77fa60e035.json b/backend/.sqlx/query-8dd93be44f66c0744ddaff12a9664d9ad745a4a2bb4c0fa36d3caf77fa60e035.json deleted file mode 100644 index dead4b0a55..0000000000 --- a/backend/.sqlx/query-8dd93be44f66c0744ddaff12a9664d9ad745a4a2bb4c0fa36d3caf77fa60e035.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n running AS \"running!\",\n substr(concat(coalesce(queue.logs, ''), job_logs.logs), greatest($1 - job_logs.log_offset, 0)) AS logs,\n mem_peak,\n CASE WHEN is_flow_step is true then NULL else flow_status END AS \"flow_status: sqlx::types::Json>\",\n job_logs.log_offset + char_length(job_logs.logs) + 1 AS log_offset,\n created_by AS \"created_by!\"\n FROM queue\n LEFT JOIN job_logs ON job_logs.job_id = queue.id \n WHERE queue.workspace_id = $2 AND queue.id = $3", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "running!", - "type_info": "Bool" - }, - { - "ordinal": 1, - "name": "logs", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "mem_peak", - "type_info": "Int4" - }, - { - "ordinal": 3, - "name": "flow_status: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 4, - "name": "log_offset", - "type_info": "Int4" - }, - { - "ordinal": 5, - "name": "created_by!", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Int4", - "Text", - "Uuid" - ] - }, - "nullable": [ - true, - null, - true, - null, - null, - true - ] - }, - "hash": "8dd93be44f66c0744ddaff12a9664d9ad745a4a2bb4c0fa36d3caf77fa60e035" -} diff --git a/backend/.sqlx/query-8eabeee5527da4aad3f46ad185015262b4304667449f3e1a71efd7477a39f7fc.json b/backend/.sqlx/query-8eabeee5527da4aad3f46ad185015262b4304667449f3e1a71efd7477a39f7fc.json new file mode 100644 index 0000000000..9302c273c3 --- /dev/null +++ b/backend/.sqlx/query-8eabeee5527da4aad3f46ad185015262b4304667449f3e1a71efd7477a39f7fc.json @@ -0,0 +1,118 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n workspace_id,\n path,\n url,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n filters AS \"filters: _\",\n initial_messages AS \"initial_messages: _\",\n url_runnable_args AS \"url_runnable_args: _\",\n can_return_message\n FROM \n websocket_trigger\n WHERE \n workspace_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "url", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 5, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 8, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 10, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 11, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 12, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 13, + "name": "filters: _", + "type_info": "JsonbArray" + }, + { + "ordinal": 14, + "name": "initial_messages: _", + "type_info": "JsonbArray" + }, + { + "ordinal": 15, + "name": "url_runnable_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "can_return_message", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false, + false, + true, + true, + false + ] + }, + "hash": "8eabeee5527da4aad3f46ad185015262b4304667449f3e1a71efd7477a39f7fc" +} diff --git a/backend/.sqlx/query-8efd06387ded837d7849adafe5bc93acb882ef90fc58b023650c875e0fd17047.json b/backend/.sqlx/query-8efd06387ded837d7849adafe5bc93acb882ef90fc58b023650c875e0fd17047.json deleted file mode 100644 index 005e8fedc9..0000000000 --- a/backend/.sqlx/query-8efd06387ded837d7849adafe5bc93acb882ef90fc58b023650c875e0fd17047.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) \n values ($1, $2, $3, $4, $5, $6, $7, $8) \n ON CONFLICT (job_id) DO UPDATE SET email = $2, username = $3, is_admin = $4, is_operator = $5, folders = $6, groups = $7, workspace_id = $8", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Varchar", - "Varchar", - "Bool", - "Bool", - "JsonbArray", - "TextArray", - "Varchar" - ] - }, - "nullable": [] - }, - "hash": "8efd06387ded837d7849adafe5bc93acb882ef90fc58b023650c875e0fd17047" -} diff --git a/backend/.sqlx/query-8f3ed45a0290cd9989f40f34775de5e8c3762597e6f55f8b9575a54ccc31e085.json b/backend/.sqlx/query-8f3ed45a0290cd9989f40f34775de5e8c3762597e6f55f8b9575a54ccc31e085.json deleted file mode 100644 index 4fd9cd8a4f..0000000000 --- a/backend/.sqlx/query-8f3ed45a0290cd9989f40f34775de5e8c3762597e6f55f8b9575a54ccc31e085.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4),\n ARRAY['modules', $1::TEXT, 'branchall', 'branch'],\n ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "int4", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int4", - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [ - null - ] - }, - "hash": "8f3ed45a0290cd9989f40f34775de5e8c3762597e6f55f8b9575a54ccc31e085" -} diff --git a/backend/.sqlx/query-8fcf755b4a57ed4ebf10a57c0c82589075c240b16d872576a048349b56f468e5.json b/backend/.sqlx/query-8fcf755b4a57ed4ebf10a57c0c82589075c240b16d872576a048349b56f468e5.json new file mode 100644 index 0000000000..49153b05aa --- /dev/null +++ b/backend/.sqlx/query-8fcf755b4a57ed4ebf10a57c0c82589075c240b16d872576a048349b56f468e5.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_queue SET\n suspend = $1,\n suspend_until = now() + interval '14 day',\n running = true\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "8fcf755b4a57ed4ebf10a57c0c82589075c240b16d872576a048349b56f468e5" +} diff --git a/backend/.sqlx/query-fdedd3909a97db5d43d9c46ff77b800b8efd647121b538deb023f96dbaac3715.json b/backend/.sqlx/query-903cf23d6b620388c645d5b8ac7d106bb6eea8af03e350d4ba19a4aba2cb9625.json similarity index 72% rename from backend/.sqlx/query-fdedd3909a97db5d43d9c46ff77b800b8efd647121b538deb023f96dbaac3715.json rename to backend/.sqlx/query-903cf23d6b620388c645d5b8ac7d106bb6eea8af03e350d4ba19a4aba2cb9625.json index 905707255f..abef34ebc0 100644 --- a/backend/.sqlx/query-fdedd3909a97db5d43d9c46ff77b800b8efd647121b538deb023f96dbaac3715.json +++ b/backend/.sqlx/query-903cf23d6b620388c645d5b8ac7d106bb6eea8af03e350d4ba19a4aba2cb9625.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT args AS \"args: Json>>\"\n FROM v2_job WHERE id = $1", + "query": "SELECT args AS \"args: Json>>\"\n FROM v2_job WHERE id = $1", "describe": { "columns": [ { @@ -18,5 +18,5 @@ true ] }, - "hash": "fdedd3909a97db5d43d9c46ff77b800b8efd647121b538deb023f96dbaac3715" + "hash": "903cf23d6b620388c645d5b8ac7d106bb6eea8af03e350d4ba19a4aba2cb9625" } diff --git a/backend/.sqlx/query-90635149190c59396ca557bf1670554a1e40d0ce9cc686ad09adca0904324cd8.json b/backend/.sqlx/query-90635149190c59396ca557bf1670554a1e40d0ce9cc686ad09adca0904324cd8.json deleted file mode 100644 index 9dce5f08b4..0000000000 --- a/backend/.sqlx/query-90635149190c59396ca557bf1670554a1e40d0ce9cc686ad09adca0904324cd8.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_queue q SET suspend = 0\n FROM v2_job j, v2_job_status f\n WHERE parent_job = $1\n AND f.id = j.id AND q.id = j.id\n AND suspend = $2 AND (f.flow_status->'step')::int = 0", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Int4" - ] - }, - "nullable": [] - }, - "hash": "90635149190c59396ca557bf1670554a1e40d0ce9cc686ad09adca0904324cd8" -} diff --git a/backend/.sqlx/query-910b9b8afb3df5e437e43ff4adc97d0a03f6c26f0b14fc92900f7600e70a7a8b.json b/backend/.sqlx/query-910b9b8afb3df5e437e43ff4adc97d0a03f6c26f0b14fc92900f7600e70a7a8b.json new file mode 100644 index 0000000000..569a5122ba --- /dev/null +++ b/backend/.sqlx/query-910b9b8afb3df5e437e43ff4adc97d0a03f6c26f0b14fc92900f7600e70a7a8b.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT status = 'success' AS \"success!\"\n FROM v2_job j JOIN v2_job_completed USING (id)\n WHERE j.workspace_id = $1 AND trigger_kind = 'schedule' AND trigger = $2\n AND parent_job IS NULL\n AND runnable_path = $3\n AND j.id != $4\n ORDER BY created_at DESC\n LIMIT $5", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "success!", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Uuid", + "Int8" + ] + }, + "nullable": [ + null + ] + }, + "hash": "910b9b8afb3df5e437e43ff4adc97d0a03f6c26f0b14fc92900f7600e70a7a8b" +} diff --git a/backend/.sqlx/query-9116102c6ccad5b0d752d5d690c233dfe48062aef23072b4f4ae4ab5ca269082.json b/backend/.sqlx/query-9116102c6ccad5b0d752d5d690c233dfe48062aef23072b4f4ae4ab5ca269082.json new file mode 100644 index 0000000000..6e58a79dd3 --- /dev/null +++ b/backend/.sqlx/query-9116102c6ccad5b0d752d5d690c233dfe48062aef23072b4f4ae4ab5ca269082.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n postgres_trigger\n SET \n last_server_ping = now(),\n error = $1\n WHERE\n workspace_id = $2\n AND path = $3\n AND server_id = $4 \n AND enabled IS TRUE\n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "9116102c6ccad5b0d752d5d690c233dfe48062aef23072b4f4ae4ab5ca269082" +} diff --git a/backend/.sqlx/query-9250b087485e51af83aef1f412c85edc114add7a6b6c1e3845ffda344effa03c.json b/backend/.sqlx/query-9250b087485e51af83aef1f412c85edc114add7a6b6c1e3845ffda344effa03c.json deleted file mode 100644 index b46a8f1a22..0000000000 --- a/backend/.sqlx/query-9250b087485e51af83aef1f412c85edc114add7a6b6c1e3845ffda344effa03c.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = flow_status - 'approval_conditions'\n WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "9250b087485e51af83aef1f412c85edc114add7a6b6c1e3845ffda344effa03c" -} diff --git a/backend/.sqlx/query-927149213e0f8ae983652ef80464f646d6be80e702193f1acdd40dd6033652e4.json b/backend/.sqlx/query-927149213e0f8ae983652ef80464f646d6be80e702193f1acdd40dd6033652e4.json new file mode 100644 index 0000000000..e4682cfedf --- /dev/null +++ b/backend/.sqlx/query-927149213e0f8ae983652ef80464f646d6be80e702193f1acdd40dd6033652e4.json @@ -0,0 +1,134 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n path, \n script_path, \n is_flow, \n route_path, \n workspace_id, \n is_async, \n authentication_method AS \"authentication_method: _\", \n edited_by, \n email,\n static_asset_config AS \"static_asset_config: _\",\n wrap_body,\n raw_string,\n workspaced_route,\n is_static_website,\n authentication_resource_path\n FROM \n http_trigger \n WHERE \n workspace_id = $1 AND \n http_method = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 3, + "name": "route_path", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "is_async", + "type_info": "Bool" + }, + { + "ordinal": 6, + "name": "authentication_method: _", + "type_info": { + "Custom": { + "name": "authentication_method", + "kind": { + "Enum": [ + "none", + "windmill", + "api_key", + "basic_http", + "custom_script", + "signature" + ] + } + } + } + }, + { + "ordinal": 7, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "static_asset_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 10, + "name": "wrap_body", + "type_info": "Bool" + }, + { + "ordinal": 11, + "name": "raw_string", + "type_info": "Bool" + }, + { + "ordinal": 12, + "name": "workspaced_route", + "type_info": "Bool" + }, + { + "ordinal": 13, + "name": "is_static_website", + "type_info": "Bool" + }, + { + "ordinal": 14, + "name": "authentication_resource_path", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text", + { + "Custom": { + "name": "http_method", + "kind": { + "Enum": [ + "get", + "post", + "put", + "delete", + "patch" + ] + } + } + } + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + true + ] + }, + "hash": "927149213e0f8ae983652ef80464f646d6be80e702193f1acdd40dd6033652e4" +} diff --git a/backend/.sqlx/query-92b80a77d292ec734b097b815261ce0cd51d7f699ff296314c9801e115c52228.json b/backend/.sqlx/query-92b80a77d292ec734b097b815261ce0cd51d7f699ff296314c9801e115c52228.json deleted file mode 100644 index 1975b59d5a..0000000000 --- a/backend/.sqlx/query-92b80a77d292ec734b097b815261ce0cd51d7f699ff296314c9801e115c52228.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(JSONB_SET(flow_status, ARRAY['retry'], $1), ARRAY['modules', $3::TEXT, 'failed_retries'], $4)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "92b80a77d292ec734b097b815261ce0cd51d7f699ff296314c9801e115c52228" -} diff --git a/backend/.sqlx/query-92c2b66eb6287449f5f8cc9f8d1329f748006ab131489d2fcac21d32641bb633.json b/backend/.sqlx/query-92c2b66eb6287449f5f8cc9f8d1329f748006ab131489d2fcac21d32641bb633.json new file mode 100644 index 0000000000..14d37809d5 --- /dev/null +++ b/backend/.sqlx/query-92c2b66eb6287449f5f8cc9f8d1329f748006ab131489d2fcac21d32641bb633.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n gcp_trigger \n SET \n server_id = $1, \n last_server_ping = now(),\n error = 'Connecting...'\n WHERE \n enabled IS TRUE \n AND workspace_id = $2 \n AND path = $3 \n AND (last_server_ping IS NULL \n OR last_server_ping < now() - INTERVAL '15 seconds'\n ) \n RETURNING true\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "92c2b66eb6287449f5f8cc9f8d1329f748006ab131489d2fcac21d32641bb633" +} diff --git a/backend/.sqlx/query-c0b96d2f421afc43e256a8475825623bcb3dd4cbc37d570fc4273127bbf77c24.json b/backend/.sqlx/query-92c7c961198e506426bf3f97a8ddbb34af450041c675b30b708fed3ef9e01d2d.json similarity index 72% rename from backend/.sqlx/query-c0b96d2f421afc43e256a8475825623bcb3dd4cbc37d570fc4273127bbf77c24.json rename to backend/.sqlx/query-92c7c961198e506426bf3f97a8ddbb34af450041c675b30b708fed3ef9e01d2d.json index 2015ab01d6..fcb9657c8a 100644 --- a/backend/.sqlx/query-c0b96d2f421afc43e256a8475825623bcb3dd4cbc37d570fc4273127bbf77c24.json +++ b/backend/.sqlx/query-92c7c961198e506426bf3f97a8ddbb34af450041c675b30b708fed3ef9e01d2d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n job_kind AS \"job_kind!: JobKind\",\n script_hash AS \"script_hash: ScriptHash\",\n flow_status AS \"flow_status!: Json>\",\n raw_flow AS \"raw_flow: Json>\"\n FROM queue WHERE id = $1 AND workspace_id = $2 LIMIT 1", + "query": "SELECT\n kind AS \"job_kind!: JobKind\",\n runnable_id AS \"script_hash: ScriptHash\",\n flow_status AS \"flow_status!: Json>\",\n raw_flow AS \"raw_flow: Json>\"\n FROM v2_job INNER JOIN v2_job_status ON v2_job.id = v2_job_status.id WHERE v2_job.id = $1 AND v2_job.workspace_id = $2 LIMIT 1", "describe": { "columns": [ { @@ -57,11 +57,11 @@ ] }, "nullable": [ - true, + false, true, true, true ] }, - "hash": "c0b96d2f421afc43e256a8475825623bcb3dd4cbc37d570fc4273127bbf77c24" + "hash": "92c7c961198e506426bf3f97a8ddbb34af450041c675b30b708fed3ef9e01d2d" } diff --git a/backend/.sqlx/query-9422431d79de41518f651ef24e86819d7b6a2f5531740a7deea1b51775335977.json b/backend/.sqlx/query-9422431d79de41518f651ef24e86819d7b6a2f5531740a7deea1b51775335977.json deleted file mode 100644 index 7f2936f195..0000000000 --- a/backend/.sqlx/query-9422431d79de41518f651ef24e86819d7b6a2f5531740a7deea1b51775335977.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT scalar_int FROM job_stats WHERE workspace_id = $1 AND job_id = $2 AND metric_id = $3", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "scalar_int", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Text", - "Uuid", - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "9422431d79de41518f651ef24e86819d7b6a2f5531740a7deea1b51775335977" -} diff --git a/backend/.sqlx/query-94831baa639d7546f98f24847c0f93697ee1edcee0acf4a0684a28ff66ef735a.json b/backend/.sqlx/query-94831baa639d7546f98f24847c0f93697ee1edcee0acf4a0684a28ff66ef735a.json deleted file mode 100644 index 01a61edd5f..0000000000 --- a/backend/.sqlx/query-94831baa639d7546f98f24847c0f93697ee1edcee0acf4a0684a28ff66ef735a.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n substr(concat(coalesce(v2_as_completed_job.logs, ''), job_logs.logs), greatest($1 - job_logs.log_offset, 0)) AS logs,\n mem_peak,\n CASE WHEN is_flow_step is true then NULL else flow_status END AS \"flow_status: sqlx::types::Json>\",\n job_logs.log_offset + char_length(job_logs.logs) + 1 AS log_offset,\n created_by AS \"created_by!\"\n FROM v2_as_completed_job\n LEFT JOIN job_logs ON job_logs.job_id = v2_as_completed_job.id \n WHERE v2_as_completed_job.workspace_id = $2 AND v2_as_completed_job.id = $3", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "logs", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "mem_peak", - "type_info": "Int4" - }, - { - "ordinal": 2, - "name": "flow_status: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 3, - "name": "log_offset", - "type_info": "Int4" - }, - { - "ordinal": 4, - "name": "created_by!", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Int4", - "Text", - "Uuid" - ] - }, - "nullable": [ - null, - true, - null, - null, - true - ] - }, - "hash": "94831baa639d7546f98f24847c0f93697ee1edcee0acf4a0684a28ff66ef735a" -} diff --git a/backend/.sqlx/query-94da1e7feb4f58cc7ebe99752736f956d47810a94cb052fdcffb5cfe440f8033.json b/backend/.sqlx/query-94da1e7feb4f58cc7ebe99752736f956d47810a94cb052fdcffb5cfe440f8033.json deleted file mode 100644 index 24a5ee62dd..0000000000 --- a/backend/.sqlx/query-94da1e7feb4f58cc7ebe99752736f956d47810a94cb052fdcffb5cfe440f8033.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM log_file WHERE log_ts <= now() - ($1::bigint::text || ' s')::interval RETURNING file_path, hostname", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "file_path", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "hostname", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - false, - false - ] - }, - "hash": "94da1e7feb4f58cc7ebe99752736f956d47810a94cb052fdcffb5cfe440f8033" -} diff --git a/backend/.sqlx/query-94f11d70062eebce384fe0fde527f3d6cebca1aa84a6f792c2a962b798f8da22.json b/backend/.sqlx/query-94f11d70062eebce384fe0fde527f3d6cebca1aa84a6f792c2a962b798f8da22.json new file mode 100644 index 0000000000..79d7fdcf4d --- /dev/null +++ b/backend/.sqlx/query-94f11d70062eebce384fe0fde527f3d6cebca1aa84a6f792c2a962b798f8da22.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['failure_module'], $1)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "94f11d70062eebce384fe0fde527f3d6cebca1aa84a6f792c2a962b798f8da22" +} diff --git a/backend/.sqlx/query-95e420b60fba20b36b2c6675998587d8cad3b67d4dfa9de52777d4ea9490b6b7.json b/backend/.sqlx/query-95e420b60fba20b36b2c6675998587d8cad3b67d4dfa9de52777d4ea9490b6b7.json deleted file mode 100644 index 74f7f38890..0000000000 --- a/backend/.sqlx/query-95e420b60fba20b36b2c6675998587d8cad3b67d4dfa9de52777d4ea9490b6b7.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE \n postgres_trigger \n SET\n last_server_ping = NULL \n WHERE \n workspace_id = $1 \n AND path = $2 \n AND server_id IS NULL", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "95e420b60fba20b36b2c6675998587d8cad3b67d4dfa9de52777d4ea9490b6b7" -} diff --git a/backend/.sqlx/query-96a9357888af26e5ec1e314bb565af05de561a8f9899e4ddca958982fdb67803.json b/backend/.sqlx/query-96a9357888af26e5ec1e314bb565af05de561a8f9899e4ddca958982fdb67803.json deleted file mode 100644 index 5fbab5bf20..0000000000 --- a/backend/.sqlx/query-96a9357888af26e5ec1e314bb565af05de561a8f9899e4ddca958982fdb67803.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id AS \"id!\" FROM queue WHERE schedule_path = $1 AND workspace_id = $2 AND id != $3 AND running = true", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Uuid" - ] - }, - "nullable": [ - true - ] - }, - "hash": "96a9357888af26e5ec1e314bb565af05de561a8f9899e4ddca958982fdb67803" -} diff --git a/backend/.sqlx/query-96c0e34708bbba29db162e7289a942addd4581dddc88663b6c2cbae87ec205fc.json b/backend/.sqlx/query-96c0e34708bbba29db162e7289a942addd4581dddc88663b6c2cbae87ec205fc.json new file mode 100644 index 0000000000..0f6659264a --- /dev/null +++ b/backend/.sqlx/query-96c0e34708bbba29db162e7289a942addd4581dddc88663b6c2cbae87ec205fc.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4),\n ARRAY['modules', $1::TEXT, 'branchall', 'branch'],\n ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "int4", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Int4", + "Uuid", + "Text", + "Jsonb" + ] + }, + "nullable": [ + null + ] + }, + "hash": "96c0e34708bbba29db162e7289a942addd4581dddc88663b6c2cbae87ec205fc" +} diff --git a/backend/.sqlx/query-971175f6169857c3e1cdc08ac8aeed57300b7792e1797a9cdd73c9b3967cd7b9.json b/backend/.sqlx/query-971175f6169857c3e1cdc08ac8aeed57300b7792e1797a9cdd73c9b3967cd7b9.json deleted file mode 100644 index f389cb6432..0000000000 --- a/backend/.sqlx/query-971175f6169857c3e1cdc08ac8aeed57300b7792e1797a9cdd73c9b3967cd7b9.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT root_job FROM queue WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "root_job", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - true - ] - }, - "hash": "971175f6169857c3e1cdc08ac8aeed57300b7792e1797a9cdd73c9b3967cd7b9" -} diff --git a/backend/.sqlx/query-97942578df746c8c8103b403cfc4e44ef5a0f082bdde854900064325adc4dd77.json b/backend/.sqlx/query-97942578df746c8c8103b403cfc4e44ef5a0f082bdde854900064325adc4dd77.json deleted file mode 100644 index 2014d9e6e4..0000000000 --- a/backend/.sqlx/query-97942578df746c8c8103b403cfc4e44ef5a0f082bdde854900064325adc4dd77.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM capture\n WHERE workspace_id = $1\n AND created_at <=\n (\n SELECT created_at\n FROM capture\n WHERE workspace_id = $1\n ORDER BY created_at DESC\n OFFSET $2\n LIMIT 1\n )", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Int8" - ] - }, - "nullable": [] - }, - "hash": "97942578df746c8c8103b403cfc4e44ef5a0f082bdde854900064325adc4dd77" -} diff --git a/backend/.sqlx/query-6199e8be5cb13db71108e555ea20f0b76dc38476670f9fc0667b057d2766d42e.json b/backend/.sqlx/query-97bf27f210572499b42ce04f19f116cc87ed06c49dcca04360250ddfd89d7ab3.json similarity index 50% rename from backend/.sqlx/query-6199e8be5cb13db71108e555ea20f0b76dc38476670f9fc0667b057d2766d42e.json rename to backend/.sqlx/query-97bf27f210572499b42ce04f19f116cc87ed06c49dcca04360250ddfd89d7ab3.json index 2313dd087c..739184045f 100644 --- a/backend/.sqlx/query-6199e8be5cb13db71108e555ea20f0b76dc38476670f9fc0667b057d2766d42e.json +++ b/backend/.sqlx/query-97bf27f210572499b42ce04f19f116cc87ed06c49dcca04360250ddfd89d7ab3.json @@ -1,22 +1,23 @@ { "db_name": "PostgreSQL", - "query": "SELECT set_config('session.groups', $1, true)", + "query": "SELECT lock_error_logs FROM flow WHERE path = $1 AND workspace_id = $2", "describe": { "columns": [ { "ordinal": 0, - "name": "set_config", + "name": "lock_error_logs", "type_info": "Text" } ], "parameters": { "Left": [ + "Text", "Text" ] }, "nullable": [ - null + true ] }, - "hash": "6199e8be5cb13db71108e555ea20f0b76dc38476670f9fc0667b057d2766d42e" + "hash": "97bf27f210572499b42ce04f19f116cc87ed06c49dcca04360250ddfd89d7ab3" } diff --git a/backend/.sqlx/query-9897705b7c265333559df757a725711a40924ec551a7589a64ee96f8aa7f6a21.json b/backend/.sqlx/query-9897705b7c265333559df757a725711a40924ec551a7589a64ee96f8aa7f6a21.json deleted file mode 100644 index abad579224..0000000000 --- a/backend/.sqlx/query-9897705b7c265333559df757a725711a40924ec551a7589a64ee96f8aa7f6a21.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT set_config('session.folders_write', $1, true)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "set_config", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "9897705b7c265333559df757a725711a40924ec551a7589a64ee96f8aa7f6a21" -} diff --git a/backend/.sqlx/query-999edc6f54a9efb6dc6237992dad59f418ed6fc2a98ddb9bbc33dce5f029d904.json b/backend/.sqlx/query-999edc6f54a9efb6dc6237992dad59f418ed6fc2a98ddb9bbc33dce5f029d904.json new file mode 100644 index 0000000000..8748572af7 --- /dev/null +++ b/backend/.sqlx/query-999edc6f54a9efb6dc6237992dad59f418ed6fc2a98ddb9bbc33dce5f029d904.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n gcp_trigger \n SET\n last_server_ping = NULL \n WHERE \n workspace_id = $1 \n AND path = $2 \n AND server_id IS NULL", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "999edc6f54a9efb6dc6237992dad59f418ed6fc2a98ddb9bbc33dce5f029d904" +} diff --git a/backend/.sqlx/query-9a9b639611459659ae355a43f219c6da2c3d1e04d49306adaaaaf06e54ee8357.json b/backend/.sqlx/query-9a9b639611459659ae355a43f219c6da2c3d1e04d49306adaaaaf06e54ee8357.json deleted file mode 100644 index 8ae3d80ca6..0000000000 --- a/backend/.sqlx/query-9a9b639611459659ae355a43f219c6da2c3d1e04d49306adaaaaf06e54ee8357.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH zombie_jobs AS (\n UPDATE queue SET running = false, started_at = null\n WHERE last_ping < now() - ($1 || ' seconds')::interval\n AND running = true AND job_kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow') AND same_worker = false \n RETURNING id, workspace_id, last_ping\n ),\n update_concurrency AS (\n UPDATE concurrency_counter cc\n SET job_uuids = job_uuids - zj.id::text\n FROM zombie_jobs zj\n INNER JOIN concurrency_key ck ON ck.job_id = zj.id\n WHERE cc.concurrency_id = ck.key\n )\n SELECT id, workspace_id, last_ping FROM zombie_jobs", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "last_ping", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - true - ] - }, - "hash": "9a9b639611459659ae355a43f219c6da2c3d1e04d49306adaaaaf06e54ee8357" -} diff --git a/backend/.sqlx/query-9b716307fdbd479879224ba60b77eeb7e8487b02b13710c68be99057e2c32cb9.json b/backend/.sqlx/query-9b716307fdbd479879224ba60b77eeb7e8487b02b13710c68be99057e2c32cb9.json deleted file mode 100644 index 49663a5013..0000000000 --- a/backend/.sqlx/query-9b716307fdbd479879224ba60b77eeb7e8487b02b13710c68be99057e2c32cb9.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET flow_status = jsonb_set(jsonb_set(COALESCE(flow_status, '{}'::jsonb), array[$1], COALESCE(flow_status->$1, '{}'::jsonb)), array[$1, 'started_at'], to_jsonb(now()::text)) WHERE id = $2 AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Uuid", - "Text" - ] - }, - "nullable": [] - }, - "hash": "9b716307fdbd479879224ba60b77eeb7e8487b02b13710c68be99057e2c32cb9" -} diff --git a/backend/.sqlx/query-9bdad9fbe8990588d8d769d4a38e2397ee789f6732199a5259f5f4ee2c5a166d.json b/backend/.sqlx/query-9bdad9fbe8990588d8d769d4a38e2397ee789f6732199a5259f5f4ee2c5a166d.json deleted file mode 100644 index b49d76d6cf..0000000000 --- a/backend/.sqlx/query-9bdad9fbe8990588d8d769d4a38e2397ee789f6732199a5259f5f4ee2c5a166d.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id AS \"id!\", result AS \"result: Json>\"\n FROM completed_job WHERE id = ANY($1) AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "result: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "UuidArray", - "Text" - ] - }, - "nullable": [ - true, - true - ] - }, - "hash": "9bdad9fbe8990588d8d769d4a38e2397ee789f6732199a5259f5f4ee2c5a166d" -} diff --git a/backend/.sqlx/query-9c17ddca92e0a93051b36bb688ad6c1c24108e45c94be52595ca6cb82135f4eb.json b/backend/.sqlx/query-9c17ddca92e0a93051b36bb688ad6c1c24108e45c94be52595ca6cb82135f4eb.json deleted file mode 100644 index 5de165462e..0000000000 --- a/backend/.sqlx/query-9c17ddca92e0a93051b36bb688ad6c1c24108e45c94be52595ca6cb82135f4eb.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET args = (select result FROM completed_job WHERE id = $1) WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "9c17ddca92e0a93051b36bb688ad6c1c24108e45c94be52595ca6cb82135f4eb" -} diff --git a/backend/.sqlx/query-9c19ad9ab14325587d662539c04e18e8dfbdb0bf1dd4c0dc07a55f4eeb4eb5f8.json b/backend/.sqlx/query-9c19ad9ab14325587d662539c04e18e8dfbdb0bf1dd4c0dc07a55f4eeb4eb5f8.json deleted file mode 100644 index 89368ea0c8..0000000000 --- a/backend/.sqlx/query-9c19ad9ab14325587d662539c04e18e8dfbdb0bf1dd4c0dc07a55f4eeb4eb5f8.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT COUNT(*) FROM v2_as_queue LEFT JOIN concurrency_key ON concurrency_key.job_id = v2_as_queue.id\n WHERE key = $1 AND running = false AND canceled = false AND scheduled_for >= $2 AND scheduled_for < $3", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text", - "Timestamptz", - "Timestamptz" - ] - }, - "nullable": [ - null - ] - }, - "hash": "9c19ad9ab14325587d662539c04e18e8dfbdb0bf1dd4c0dc07a55f4eeb4eb5f8" -} diff --git a/backend/.sqlx/query-9c34c717b218c09e3784a5413f7972e5e805dae837a075da4e503494624b518a.json b/backend/.sqlx/query-9c34c717b218c09e3784a5413f7972e5e805dae837a075da4e503494624b518a.json deleted file mode 100644 index d22420d6e4..0000000000 --- a/backend/.sqlx/query-9c34c717b218c09e3784a5413f7972e5e805dae837a075da4e503494624b518a.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = jsonb_set(\n jsonb_set(flow_status, ARRAY['preprocessor_module', 'job'], to_jsonb($1::UUID::TEXT)),\n ARRAY['preprocessor_module', 'type'],\n to_jsonb('InProgress'::text)\n )\n WHERE id = $2 AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Text" - ] - }, - "nullable": [] - }, - "hash": "9c34c717b218c09e3784a5413f7972e5e805dae837a075da4e503494624b518a" -} diff --git a/backend/.sqlx/query-9d3556319411a27a875bf6cf0e5eda837cc63e4d8be912c0b5bfeea4a0c8db2e.json b/backend/.sqlx/query-9d3556319411a27a875bf6cf0e5eda837cc63e4d8be912c0b5bfeea4a0c8db2e.json deleted file mode 100644 index 2abc565def..0000000000 --- a/backend/.sqlx/query-9d3556319411a27a875bf6cf0e5eda837cc63e4d8be912c0b5bfeea4a0c8db2e.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO queue (id, script_hash, script_path, job_kind, language, tag, created_by, permissioned_as, email, scheduled_for, workspace_id) (SELECT gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7, $8, $9, $10 FROM generate_series(1, $11))", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8", - "Varchar", - { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - }, - { - "Custom": { - "name": "script_lang", - "kind": { - "Enum": [ - "python3", - "deno", - "go", - "bash", - "postgresql", - "nativets", - "bun", - "mysql", - "bigquery", - "snowflake", - "graphql", - "powershell", - "mssql", - "php", - "bunnative", - "rust", - "ansible", - "csharp", - "oracledb" - ] - } - } - }, - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Timestamptz", - "Varchar", - "Int4" - ] - }, - "nullable": [] - }, - "hash": "9d3556319411a27a875bf6cf0e5eda837cc63e4d8be912c0b5bfeea4a0c8db2e" -} diff --git a/backend/.sqlx/query-9d518842a9ad90ff9c28dc39690deb0ee6b62cf1d8ae1a02b28c23255d377b3d.json b/backend/.sqlx/query-9d518842a9ad90ff9c28dc39690deb0ee6b62cf1d8ae1a02b28c23255d377b3d.json deleted file mode 100644 index 79bd8d0666..0000000000 --- a/backend/.sqlx/query-9d518842a9ad90ff9c28dc39690deb0ee6b62cf1d8ae1a02b28c23255d377b3d.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT labels FROM v2_job WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "labels", - "type_info": "TextArray" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - true - ] - }, - "hash": "9d518842a9ad90ff9c28dc39690deb0ee6b62cf1d8ae1a02b28c23255d377b3d" -} diff --git a/backend/.sqlx/query-9d616812c5a6ae514f047ce2d035a07ff11d13472c25533824dec93c41ea609c.json b/backend/.sqlx/query-9d616812c5a6ae514f047ce2d035a07ff11d13472c25533824dec93c41ea609c.json deleted file mode 100644 index 7471bb0bf3..0000000000 --- a/backend/.sqlx/query-9d616812c5a6ae514f047ce2d035a07ff11d13472c25533824dec93c41ea609c.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT args AS \"args: Json>>\"\n FROM completed_job WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "args: Json>>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "9d616812c5a6ae514f047ce2d035a07ff11d13472c25533824dec93c41ea609c" -} diff --git a/backend/.sqlx/query-9da0cea2a5d0464ca78cfeccf6cedf2b1c0e6e6cb3c9183a937a68465debdb06.json b/backend/.sqlx/query-9da0cea2a5d0464ca78cfeccf6cedf2b1c0e6e6cb3c9183a937a68465debdb06.json deleted file mode 100644 index 9f42f0f54d..0000000000 --- a/backend/.sqlx/query-9da0cea2a5d0464ca78cfeccf6cedf2b1c0e6e6cb3c9183a937a68465debdb06.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM concurrency_key WHERE ended_at <= now() - ($1::bigint::text || ' s')::interval ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [] - }, - "hash": "9da0cea2a5d0464ca78cfeccf6cedf2b1c0e6e6cb3c9183a937a68465debdb06" -} diff --git a/backend/.sqlx/query-9dfc48c84c52b8b027594b1c6638080f1174e06c8621245fd82ea1515e2d9a96.json b/backend/.sqlx/query-9dfc48c84c52b8b027594b1c6638080f1174e06c8621245fd82ea1515e2d9a96.json index 737090f5b8..b35ca6aa5b 100644 --- a/backend/.sqlx/query-9dfc48c84c52b8b027594b1c6638080f1174e06c8621245fd82ea1515e2d9a96.json +++ b/backend/.sqlx/query-9dfc48c84c52b8b027594b1c6638080f1174e06c8621245fd82ea1515e2d9a96.json @@ -12,7 +12,7 @@ "parameters": { "Left": [ "Varchar", - "Json", + "Jsonb", "Int4", "Text" ] diff --git a/backend/.sqlx/query-9e2810312302b36d3b4d761481c00296ee84c9536228496e19b4ec5df1781bc5.json b/backend/.sqlx/query-9e2810312302b36d3b4d761481c00296ee84c9536228496e19b4ec5df1781bc5.json deleted file mode 100644 index 70df593afe..0000000000 --- a/backend/.sqlx/query-9e2810312302b36d3b4d761481c00296ee84c9536228496e19b4ec5df1781bc5.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT COUNT(*) as count, \n MIN(scheduled_for) as oldest_job\n FROM v2_job_queue \n WHERE tag = $1 \n AND scheduled_for <= NOW() - $2::interval \n AND running = false\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "oldest_job", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text", - "Interval" - ] - }, - "nullable": [ - null, - null - ] - }, - "hash": "9e2810312302b36d3b4d761481c00296ee84c9536228496e19b4ec5df1781bc5" -} diff --git a/backend/.sqlx/query-9e7e6fe1dfba032e586f64531e12529d57faf5e77f6792d5bda608ff9658d7c9.json b/backend/.sqlx/query-9e7e6fe1dfba032e586f64531e12529d57faf5e77f6792d5bda608ff9658d7c9.json deleted file mode 100644 index 2ee7a1acef..0000000000 --- a/backend/.sqlx/query-9e7e6fe1dfba032e586f64531e12529d57faf5e77f6792d5bda608ff9658d7c9.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT EXISTS (SELECT 1 FROM v2_as_queue WHERE workspace_id = $1 AND schedule_path = $2 AND scheduled_for = $3)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Timestamptz" - ] - }, - "nullable": [ - null - ] - }, - "hash": "9e7e6fe1dfba032e586f64531e12529d57faf5e77f6792d5bda608ff9658d7c9" -} diff --git a/backend/.sqlx/query-9e88a43de7315052668619002321aadbc27ff5bd0c554bf5339060cc35797d3f.json b/backend/.sqlx/query-9e88a43de7315052668619002321aadbc27ff5bd0c554bf5339060cc35797d3f.json new file mode 100644 index 0000000000..bb2a3c682b --- /dev/null +++ b/backend/.sqlx/query-9e88a43de7315052668619002321aadbc27ff5bd0c554bf5339060cc35797d3f.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE \n FROM \n mqtt_trigger \n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "9e88a43de7315052668619002321aadbc27ff5bd0c554bf5339060cc35797d3f" +} diff --git a/backend/.sqlx/query-9f16a61d6a9a42f3fd3e30a1e7776503cee1b45eba150c3082eb246ea3f98d47.json b/backend/.sqlx/query-9f16a61d6a9a42f3fd3e30a1e7776503cee1b45eba150c3082eb246ea3f98d47.json deleted file mode 100644 index aa64996dc8..0000000000 --- a/backend/.sqlx/query-9f16a61d6a9a42f3fd3e30a1e7776503cee1b45eba150c3082eb246ea3f98d47.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT EXISTS(SELECT 1 FROM completed_job WHERE id = $1 AND workspace_id = $2)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "9f16a61d6a9a42f3fd3e30a1e7776503cee1b45eba150c3082eb246ea3f98d47" -} diff --git a/backend/.sqlx/query-a00e61e770e20157bbd9e4cdedf7fb5f9de7c8c9e50282e3ecf2e3ce917ec37a.json b/backend/.sqlx/query-a00e61e770e20157bbd9e4cdedf7fb5f9de7c8c9e50282e3ecf2e3ce917ec37a.json deleted file mode 100644 index 9f95781136..0000000000 --- a/backend/.sqlx/query-a00e61e770e20157bbd9e4cdedf7fb5f9de7c8c9e50282e3ecf2e3ce917ec37a.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "a00e61e770e20157bbd9e4cdedf7fb5f9de7c8c9e50282e3ecf2e3ce917ec37a" -} diff --git a/backend/.sqlx/query-a0c20436e0506bf9e0e50bd3dcabdd35131cc374edac0a08c5492954f08c9d98.json b/backend/.sqlx/query-a0c20436e0506bf9e0e50bd3dcabdd35131cc374edac0a08c5492954f08c9d98.json new file mode 100644 index 0000000000..1724d969cf --- /dev/null +++ b/backend/.sqlx/query-a0c20436e0506bf9e0e50bd3dcabdd35131cc374edac0a08c5492954f08c9d98.json @@ -0,0 +1,222 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO schedule (\n workspace_id, path, schedule, timezone, edited_by, script_path,\n is_flow, args, enabled, email,\n on_failure, on_failure_times, on_failure_exact, on_failure_extra_args,\n on_recovery, on_recovery_times, on_recovery_extra_args,\n on_success, on_success_extra_args,\n ws_error_handler_muted, retry, summary, no_flow_overlap,\n tag, paused_until, cron_version, description\n ) VALUES (\n $1, $2, $3, $4, $5, $6,\n $7, $8, $9, $10,\n $11, $12, $13, $14,\n $15, $16, $17,\n $18, $19,\n $20, $21, $22, $23,\n $24, $25, $26, $27\n )\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 4, + "name": "schedule", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "timezone", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 7, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 9, + "name": "args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 10, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 13, + "name": "on_failure", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "on_failure_times", + "type_info": "Int4" + }, + { + "ordinal": 15, + "name": "on_failure_exact", + "type_info": "Bool" + }, + { + "ordinal": 16, + "name": "on_failure_extra_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 17, + "name": "on_recovery", + "type_info": "Varchar" + }, + { + "ordinal": 18, + "name": "on_recovery_times", + "type_info": "Int4" + }, + { + "ordinal": 19, + "name": "on_recovery_extra_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 20, + "name": "on_success", + "type_info": "Varchar" + }, + { + "ordinal": 21, + "name": "on_success_extra_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 22, + "name": "ws_error_handler_muted", + "type_info": "Bool" + }, + { + "ordinal": 23, + "name": "retry", + "type_info": "Jsonb" + }, + { + "ordinal": 24, + "name": "no_flow_overlap", + "type_info": "Bool" + }, + { + "ordinal": 25, + "name": "summary", + "type_info": "Varchar" + }, + { + "ordinal": 26, + "name": "description", + "type_info": "Text" + }, + { + "ordinal": 27, + "name": "tag", + "type_info": "Varchar" + }, + { + "ordinal": 28, + "name": "paused_until", + "type_info": "Timestamptz" + }, + { + "ordinal": 29, + "name": "cron_version", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Jsonb", + "Bool", + "Varchar", + "Varchar", + "Int4", + "Bool", + "Jsonb", + "Varchar", + "Int4", + "Jsonb", + "Varchar", + "Jsonb", + "Bool", + "Jsonb", + "Varchar", + "Bool", + "Varchar", + "Timestamptz", + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + false, + true, + false, + true, + true, + true, + true, + true + ] + }, + "hash": "a0c20436e0506bf9e0e50bd3dcabdd35131cc374edac0a08c5492954f08c9d98" +} diff --git a/backend/.sqlx/query-a0c35cb515a842067b294343c90f1bfbe4e2db85da9a478a07460733999e9beb.json b/backend/.sqlx/query-a0c35cb515a842067b294343c90f1bfbe4e2db85da9a478a07460733999e9beb.json deleted file mode 100644 index 298a66c89c..0000000000 --- a/backend/.sqlx/query-a0c35cb515a842067b294343c90f1bfbe4e2db85da9a478a07460733999e9beb.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT count(*) AS \"count!\" FROM resume_job", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count!", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - null - ] - }, - "hash": "a0c35cb515a842067b294343c90f1bfbe4e2db85da9a478a07460733999e9beb" -} diff --git a/backend/.sqlx/query-a10ec229d7ed89f563b6b33e70e8ede5135a849e7b9108c37bfd90990a4be780.json b/backend/.sqlx/query-a10ec229d7ed89f563b6b33e70e8ede5135a849e7b9108c37bfd90990a4be780.json new file mode 100644 index 0000000000..9c49c0794c --- /dev/null +++ b/backend/.sqlx/query-a10ec229d7ed89f563b6b33e70e8ede5135a849e7b9108c37bfd90990a4be780.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', $1::TEXT, 'iterator', 'index'],\n ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "int4", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Int4", + "Uuid" + ] + }, + "nullable": [ + null + ] + }, + "hash": "a10ec229d7ed89f563b6b33e70e8ede5135a849e7b9108c37bfd90990a4be780" +} diff --git a/backend/.sqlx/query-0cc3618495d5d024b2a173c58a3a8bb2a9d69b7b6e7ed6b0d0064fa2ce9c2e31.json b/backend/.sqlx/query-a21a16064b51580a8f5c2505cb0c701281dbfa94e40994fdd1cadc86a26c294e.json similarity index 54% rename from backend/.sqlx/query-0cc3618495d5d024b2a173c58a3a8bb2a9d69b7b6e7ed6b0d0064fa2ce9c2e31.json rename to backend/.sqlx/query-a21a16064b51580a8f5c2505cb0c701281dbfa94e40994fdd1cadc86a26c294e.json index 79e36950f6..dc20d9d86b 100644 --- a/backend/.sqlx/query-0cc3618495d5d024b2a173c58a3a8bb2a9d69b7b6e7ed6b0d0064fa2ce9c2e31.json +++ b/backend/.sqlx/query-a21a16064b51580a8f5c2505cb0c701281dbfa94e40994fdd1cadc86a26c294e.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT value\n FROM global_settings\n WHERE name = 'openai_azure_base_path'", + "query": "SELECT value\n FROM global_settings\n WHERE name = 'openai_azure_base_path'", "describe": { "columns": [ { @@ -16,5 +16,5 @@ false ] }, - "hash": "0cc3618495d5d024b2a173c58a3a8bb2a9d69b7b6e7ed6b0d0064fa2ce9c2e31" + "hash": "a21a16064b51580a8f5c2505cb0c701281dbfa94e40994fdd1cadc86a26c294e" } diff --git a/backend/.sqlx/query-a241c56415759105ccbcbf7fff77287fa4ec2cc096c0060d14db421115d63e2d.json b/backend/.sqlx/query-a241c56415759105ccbcbf7fff77287fa4ec2cc096c0060d14db421115d63e2d.json deleted file mode 100644 index 226f49f2fe..0000000000 --- a/backend/.sqlx/query-a241c56415759105ccbcbf7fff77287fa4ec2cc096c0060d14db421115d63e2d.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT EXISTS (SELECT 1 FROM queue WHERE workspace_id = $1 AND schedule_path = $2 AND scheduled_for = $3)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Timestamptz" - ] - }, - "nullable": [ - null - ] - }, - "hash": "a241c56415759105ccbcbf7fff77287fa4ec2cc096c0060d14db421115d63e2d" -} diff --git a/backend/.sqlx/query-a269c388056eabe4b045948f451ea74ffbb4c0ed7e694f8f03d92f2a7c118af9.json b/backend/.sqlx/query-a269c388056eabe4b045948f451ea74ffbb4c0ed7e694f8f03d92f2a7c118af9.json new file mode 100644 index 0000000000..e81acc3584 --- /dev/null +++ b/backend/.sqlx/query-a269c388056eabe4b045948f451ea74ffbb4c0ed7e694f8f03d92f2a7c118af9.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM http_trigger \n WHERE workspace_id = $1 \n AND path = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "a269c388056eabe4b045948f451ea74ffbb4c0ed7e694f8f03d92f2a7c118af9" +} diff --git a/backend/.sqlx/query-a2e86f169ffbf8acee5f7c7b71db5859ac94ffbad267c9cd6c652e8ce8fc5d3c.json b/backend/.sqlx/query-a2e86f169ffbf8acee5f7c7b71db5859ac94ffbad267c9cd6c652e8ce8fc5d3c.json new file mode 100644 index 0000000000..4e0d53b0f3 --- /dev/null +++ b/backend/.sqlx/query-a2e86f169ffbf8acee5f7c7b71db5859ac94ffbad267c9cd6c652e8ce8fc5d3c.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO job_logs (logs, job_id, workspace_id) VALUES ($1, $2, $3) ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, $1::text) RETURNING length(logs)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "length", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Uuid", + "Varchar" + ] + }, + "nullable": [ + null + ] + }, + "hash": "a2e86f169ffbf8acee5f7c7b71db5859ac94ffbad267c9cd6c652e8ce8fc5d3c" +} diff --git a/backend/.sqlx/query-a33e282d02c53e5d6142dc7e6882a6d8f3d068c55cddf209eb6b6431ca26c910.json b/backend/.sqlx/query-a33e282d02c53e5d6142dc7e6882a6d8f3d068c55cddf209eb6b6431ca26c910.json deleted file mode 100644 index 20d1bfb045..0000000000 --- a/backend/.sqlx/query-a33e282d02c53e5d6142dc7e6882a6d8f3d068c55cddf209eb6b6431ca26c910.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id AS \"id!\" FROM queue WHERE workspace_id = $1 and root_job = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Text", - "Uuid" - ] - }, - "nullable": [ - true - ] - }, - "hash": "a33e282d02c53e5d6142dc7e6882a6d8f3d068c55cddf209eb6b6431ca26c910" -} diff --git a/backend/.sqlx/query-a3ccf362b4f6df400b3c7a084795dbf541eb14c5c374656ffb96da7283a2a6f1.json b/backend/.sqlx/query-a3ccf362b4f6df400b3c7a084795dbf541eb14c5c374656ffb96da7283a2a6f1.json deleted file mode 100644 index bc5e2351f9..0000000000 --- a/backend/.sqlx/query-a3ccf362b4f6df400b3c7a084795dbf541eb14c5c374656ffb96da7283a2a6f1.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT concurrency_key FROM script WHERE hash = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "concurrency_key", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Int8", - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "a3ccf362b4f6df400b3c7a084795dbf541eb14c5c374656ffb96da7283a2a6f1" -} diff --git a/backend/.sqlx/query-a3debece1a4171881431640f6af264d402d32e2b6ce925d1ebf1f60f3b688207.json b/backend/.sqlx/query-a3debece1a4171881431640f6af264d402d32e2b6ce925d1ebf1f60f3b688207.json new file mode 100644 index 0000000000..9448456dbb --- /dev/null +++ b/backend/.sqlx/query-a3debece1a4171881431640f6af264d402d32e2b6ce925d1ebf1f60f3b688207.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2)\n WHERE id = $3\n RETURNING flow_status AS \"flow_status: Json>\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "flow_status: Json>", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Jsonb", + "Uuid" + ] + }, + "nullable": [ + true + ] + }, + "hash": "a3debece1a4171881431640f6af264d402d32e2b6ce925d1ebf1f60f3b688207" +} diff --git a/backend/.sqlx/query-a3f315fdae54e51b56b0681fab2bbff779a4a62d129916dd4c3054b45e0b654e.json b/backend/.sqlx/query-a3f315fdae54e51b56b0681fab2bbff779a4a62d129916dd4c3054b45e0b654e.json new file mode 100644 index 0000000000..dc0b37ca8c --- /dev/null +++ b/backend/.sqlx/query-a3f315fdae54e51b56b0681fab2bbff779a4a62d129916dd4c3054b45e0b654e.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'approvers'], $2)\n WHERE id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "a3f315fdae54e51b56b0681fab2bbff779a4a62d129916dd4c3054b45e0b654e" +} diff --git a/backend/.sqlx/query-a405e637f5f3b3203de6d65dfcb0ba1be406ee5167f7b8aa90213ef52c97441f.json b/backend/.sqlx/query-a405e637f5f3b3203de6d65dfcb0ba1be406ee5167f7b8aa90213ef52c97441f.json deleted file mode 100644 index 21dacfcc9b..0000000000 --- a/backend/.sqlx/query-a405e637f5f3b3203de6d65dfcb0ba1be406ee5167f7b8aa90213ef52c97441f.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT usage.usage + 1 FROM usage \n WHERE is_workspace IS TRUE AND\n month_ = EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date)\n AND id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "a405e637f5f3b3203de6d65dfcb0ba1be406ee5167f7b8aa90213ef52c97441f" -} diff --git a/backend/.sqlx/query-a56eef5f5ecbe1a8d309ff65d9a8c456a3c165f7f2a107cf7fa6a4cdd30d55c0.json b/backend/.sqlx/query-a56eef5f5ecbe1a8d309ff65d9a8c456a3c165f7f2a107cf7fa6a4cdd30d55c0.json deleted file mode 100644 index eb852c75ee..0000000000 --- a/backend/.sqlx/query-a56eef5f5ecbe1a8d309ff65d9a8c456a3c165f7f2a107cf7fa6a4cdd30d55c0.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT preprocessed, script_entrypoint_override FROM v2_job WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "preprocessed", - "type_info": "Bool" - }, - { - "ordinal": 1, - "name": "script_entrypoint_override", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - true, - true - ] - }, - "hash": "a56eef5f5ecbe1a8d309ff65d9a8c456a3c165f7f2a107cf7fa6a4cdd30d55c0" -} diff --git a/backend/.sqlx/query-a5fbef9db2308920ea26f6154f0f3490f88ced636c9ebdecba2205f964b1d0f1.json b/backend/.sqlx/query-a5fbef9db2308920ea26f6154f0f3490f88ced636c9ebdecba2205f964b1d0f1.json new file mode 100644 index 0000000000..219558ac68 --- /dev/null +++ b/backend/.sqlx/query-a5fbef9db2308920ea26f6154f0f3490f88ced636c9ebdecba2205f964b1d0f1.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n sqs_trigger \n SET\n last_server_ping = NULL \n WHERE \n workspace_id = $1 \n AND path = $2 \n AND server_id IS NULL", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "a5fbef9db2308920ea26f6154f0f3490f88ced636c9ebdecba2205f964b1d0f1" +} diff --git a/backend/.sqlx/query-a6c168c60bc8c42f70b18565e824efe29311aabfba6e09efa10bab6a551d658b.json b/backend/.sqlx/query-a6c168c60bc8c42f70b18565e824efe29311aabfba6e09efa10bab6a551d658b.json deleted file mode 100644 index ce9457cc6a..0000000000 --- a/backend/.sqlx/query-a6c168c60bc8c42f70b18565e824efe29311aabfba6e09efa10bab6a551d658b.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE postgres_trigger SET enabled = FALSE, error = $1, server_id = NULL, last_server_ping = NULL WHERE workspace_id = $2 AND path = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "a6c168c60bc8c42f70b18565e824efe29311aabfba6e09efa10bab6a551d658b" -} diff --git a/backend/.sqlx/query-a6feede7d9c3060b1e4c94bb4fa700d38a0d1b9d25429021c08fc94d57f952d2.json b/backend/.sqlx/query-a6feede7d9c3060b1e4c94bb4fa700d38a0d1b9d25429021c08fc94d57f952d2.json deleted file mode 100644 index c3658e02a2..0000000000 --- a/backend/.sqlx/query-a6feede7d9c3060b1e4c94bb4fa700d38a0d1b9d25429021c08fc94d57f952d2.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET created_by = $1 WHERE created_by = $2 AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "a6feede7d9c3060b1e4c94bb4fa700d38a0d1b9d25429021c08fc94d57f952d2" -} diff --git a/backend/.sqlx/query-a7df493316f632fc636e4c3c90bef4c98035b5dc808bb8379251c0b35d945ba0.json b/backend/.sqlx/query-a7df493316f632fc636e4c3c90bef4c98035b5dc808bb8379251c0b35d945ba0.json new file mode 100644 index 0000000000..39ba075077 --- /dev/null +++ b/backend/.sqlx/query-a7df493316f632fc636e4c3c90bef4c98035b5dc808bb8379251c0b35d945ba0.json @@ -0,0 +1,122 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n aws_auth_resource_type AS \"aws_auth_resource_type: _\",\n aws_resource_path,\n message_attributes,\n queue_url,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n sqs_trigger\n WHERE \n workspace_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "aws_auth_resource_type: _", + "type_info": { + "Custom": { + "name": "aws_auth_resource_type", + "kind": { + "Enum": [ + "oidc", + "credentials" + ] + } + } + } + }, + { + "ordinal": 1, + "name": "aws_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "message_attributes", + "type_info": "TextArray" + }, + { + "ordinal": 3, + "name": "queue_url", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 8, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 11, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 14, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 15, + "name": "enabled", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + true, + true, + false + ] + }, + "hash": "a7df493316f632fc636e4c3c90bef4c98035b5dc808bb8379251c0b35d945ba0" +} diff --git a/backend/.sqlx/query-a7ffc5b983d365159ef379ec6a2ab0dc7217d1b3f86f362c213982984d2cf652.json b/backend/.sqlx/query-a7ffc5b983d365159ef379ec6a2ab0dc7217d1b3f86f362c213982984d2cf652.json new file mode 100644 index 0000000000..03b05af6fb --- /dev/null +++ b/backend/.sqlx/query-a7ffc5b983d365159ef379ec6a2ab0dc7217d1b3f86f362c213982984d2cf652.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n last_server_ping = now(), \n error = $1 \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'gcp' AND \n server_id = $5 AND \n last_client_ping > NOW() - INTERVAL '10 seconds' \n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Bool", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "a7ffc5b983d365159ef379ec6a2ab0dc7217d1b3f86f362c213982984d2cf652" +} diff --git a/backend/.sqlx/query-a91798f58fa5948cd1739df4fa2e07cbb3eb08c5d2d22b057796e1156ae2a122.json b/backend/.sqlx/query-a91798f58fa5948cd1739df4fa2e07cbb3eb08c5d2d22b057796e1156ae2a122.json deleted file mode 100644 index 3dfa6d79e2..0000000000 --- a/backend/.sqlx/query-a91798f58fa5948cd1739df4fa2e07cbb3eb08c5d2d22b057796e1156ae2a122.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT leaf_jobs->$1::text AS \"leaf_jobs: Json>\", parent_job\n FROM queue\n WHERE COALESCE((SELECT root_job FROM queue WHERE id = $2), $2) = id AND workspace_id = $3", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "leaf_jobs: Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 1, - "name": "parent_job", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Text", - "Uuid", - "Text" - ] - }, - "nullable": [ - null, - true - ] - }, - "hash": "a91798f58fa5948cd1739df4fa2e07cbb3eb08c5d2d22b057796e1156ae2a122" -} diff --git a/backend/.sqlx/query-d55eabd42559893bdf91c5fdab1ae58006013be26fcb9367b3c6830f38749799.json b/backend/.sqlx/query-a9db7b2f435bb82acb8c5eeb7f800b28f3256491fdaa168591adc7b4b9f3327a.json similarity index 68% rename from backend/.sqlx/query-d55eabd42559893bdf91c5fdab1ae58006013be26fcb9367b3c6830f38749799.json rename to backend/.sqlx/query-a9db7b2f435bb82acb8c5eeb7f800b28f3256491fdaa168591adc7b4b9f3327a.json index 93a338c13f..8cc09349ec 100644 --- a/backend/.sqlx/query-d55eabd42559893bdf91c5fdab1ae58006013be26fcb9367b3c6830f38749799.json +++ b/backend/.sqlx/query-a9db7b2f435bb82acb8c5eeb7f800b28f3256491fdaa168591adc7b4b9f3327a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT content AS \"content!: String\",\n lock AS \"lock: String\", language AS \"language: Option\", envs AS \"envs: Vec\", codebase LIKE '%.tar' as use_tar FROM script WHERE hash = $1 LIMIT 1", + "query": "SELECT content AS \"content!: String\",\n lock AS \"lock: String\", language AS \"language: Option\", envs AS \"envs: Vec\", schema AS \"schema: String\", schema_validation AS \"schema_validation: bool\", codebase LIKE '%.tar' as use_tar FROM script WHERE hash = $1 LIMIT 1", "describe": { "columns": [ { @@ -39,7 +39,9 @@ "rust", "ansible", "csharp", - "oracledb" + "oracledb", + "nu", + "java" ] } } @@ -52,6 +54,16 @@ }, { "ordinal": 4, + "name": "schema: String", + "type_info": "Json" + }, + { + "ordinal": 5, + "name": "schema_validation: bool", + "type_info": "Bool" + }, + { + "ordinal": 6, "name": "use_tar", "type_info": "Bool" } @@ -66,8 +78,10 @@ true, false, true, + true, + false, null ] }, - "hash": "d55eabd42559893bdf91c5fdab1ae58006013be26fcb9367b3c6830f38749799" + "hash": "a9db7b2f435bb82acb8c5eeb7f800b28f3256491fdaa168591adc7b4b9f3327a" } diff --git a/backend/.sqlx/query-83d18f4e1cda2c1867168551d855d5626e766b5469c6976c986bdafc8b9407c5.json b/backend/.sqlx/query-a9f6e5720f748418ae52cf0e31778b9bbef26008803f2dd2a043bdb091b2ff20.json similarity index 63% rename from backend/.sqlx/query-83d18f4e1cda2c1867168551d855d5626e766b5469c6976c986bdafc8b9407c5.json rename to backend/.sqlx/query-a9f6e5720f748418ae52cf0e31778b9bbef26008803f2dd2a043bdb091b2ff20.json index 24cabff562..0310f2197b 100644 --- a/backend/.sqlx/query-83d18f4e1cda2c1867168551d855d5626e766b5469c6976c986bdafc8b9407c5.json +++ b/backend/.sqlx/query-a9f6e5720f748418ae52cf0e31778b9bbef26008803f2dd2a043bdb091b2ff20.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO app_version\n (app_id, value, created_by)\n VALUES ($1, $2::text::json, $3) RETURNING id", + "query": "INSERT INTO app_version\n (app_id, value, created_by, raw_app)\n VALUES ($1, $2::text::json, $3, $4) RETURNING id", "describe": { "columns": [ { @@ -13,12 +13,13 @@ "Left": [ "Int8", "Text", - "Varchar" + "Varchar", + "Bool" ] }, "nullable": [ false ] }, - "hash": "83d18f4e1cda2c1867168551d855d5626e766b5469c6976c986bdafc8b9407c5" + "hash": "a9f6e5720f748418ae52cf0e31778b9bbef26008803f2dd2a043bdb091b2ff20" } diff --git a/backend/.sqlx/query-aa0215d4174c1aeda8631bcd582c895329d2daf722d360fbcbdef6f04bb1400f.json b/backend/.sqlx/query-aa0215d4174c1aeda8631bcd582c895329d2daf722d360fbcbdef6f04bb1400f.json new file mode 100644 index 0000000000..9526b72f55 --- /dev/null +++ b/backend/.sqlx/query-aa0215d4174c1aeda8631bcd582c895329d2daf722d360fbcbdef6f04bb1400f.json @@ -0,0 +1,64 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) AS \"websocket_used!\",\n EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) AS \"http_routes_used!\",\n EXISTS(SELECT 1 FROM kafka_trigger WHERE workspace_id = $1) as \"kafka_used!\",\n EXISTS(SELECT 1 FROM nats_trigger WHERE workspace_id = $1) as \"nats_used!\",\n EXISTS(SELECT 1 FROM postgres_trigger WHERE workspace_id = $1) AS \"postgres_used!\",\n EXISTS(SELECT 1 FROM mqtt_trigger WHERE workspace_id = $1) AS \"mqtt_used!\",\n EXISTS(SELECT 1 FROM sqs_trigger WHERE workspace_id = $1) AS \"sqs_used!\",\n EXISTS(SELECT 1 FROM gcp_trigger WHERE workspace_id = $1) AS \"gcp_used!\"\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "websocket_used!", + "type_info": "Bool" + }, + { + "ordinal": 1, + "name": "http_routes_used!", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "kafka_used!", + "type_info": "Bool" + }, + { + "ordinal": 3, + "name": "nats_used!", + "type_info": "Bool" + }, + { + "ordinal": 4, + "name": "postgres_used!", + "type_info": "Bool" + }, + { + "ordinal": 5, + "name": "mqtt_used!", + "type_info": "Bool" + }, + { + "ordinal": 6, + "name": "sqs_used!", + "type_info": "Bool" + }, + { + "ordinal": 7, + "name": "gcp_used!", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null, + null, + null, + null, + null, + null, + null, + null + ] + }, + "hash": "aa0215d4174c1aeda8631bcd582c895329d2daf722d360fbcbdef6f04bb1400f" +} diff --git a/backend/.sqlx/query-4507c3907bf49f93f6c17956d9cf9495f4538b20ce0299acde7578386db4278c.json b/backend/.sqlx/query-aa4ecf6b7ab078544c280957ad6614c4ccd1bc9b4695ca56f56ba0899b23562f.json similarity index 66% rename from backend/.sqlx/query-4507c3907bf49f93f6c17956d9cf9495f4538b20ce0299acde7578386db4278c.json rename to backend/.sqlx/query-aa4ecf6b7ab078544c280957ad6614c4ccd1bc9b4695ca56f56ba0899b23562f.json index dc57aefcdc..c231ff2bd0 100644 --- a/backend/.sqlx/query-4507c3907bf49f93f6c17956d9cf9495f4538b20ce0299acde7578386db4278c.json +++ b/backend/.sqlx/query-aa4ecf6b7ab078544c280957ad6614c4ccd1bc9b4695ca56f56ba0899b23562f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO parallel_monitor_lock (parent_flow_id, job_id)\n VALUES ($1, $2)", + "query": "INSERT INTO parallel_monitor_lock (parent_flow_id, job_id)\n VALUES ($1, $2)", "describe": { "columns": [], "parameters": { @@ -11,5 +11,5 @@ }, "nullable": [] }, - "hash": "4507c3907bf49f93f6c17956d9cf9495f4538b20ce0299acde7578386db4278c" + "hash": "aa4ecf6b7ab078544c280957ad6614c4ccd1bc9b4695ca56f56ba0899b23562f" } diff --git a/backend/.sqlx/query-a439552f74ed0ba305e3d9cb99ae9e5d24834082ebf2fe9fd3964fdd80b69ccb.json b/backend/.sqlx/query-aa523c363186575b4bd2537b8e2430e6938e7cc35f8c9e2d1c5459a85443cbdd.json similarity index 52% rename from backend/.sqlx/query-a439552f74ed0ba305e3d9cb99ae9e5d24834082ebf2fe9fd3964fdd80b69ccb.json rename to backend/.sqlx/query-aa523c363186575b4bd2537b8e2430e6938e7cc35f8c9e2d1c5459a85443cbdd.json index 691a6d5e31..520fb2c559 100644 --- a/backend/.sqlx/query-a439552f74ed0ba305e3d9cb99ae9e5d24834082ebf2fe9fd3964fdd80b69ccb.json +++ b/backend/.sqlx/query-aa523c363186575b4bd2537b8e2430e6938e7cc35f8c9e2d1c5459a85443cbdd.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2,\n occupancy_rate = $3, memory_usage = $4, wm_memory_usage = $5, vcpus = COALESCE($7, vcpus),\n memory = COALESCE($8, memory), occupancy_rate_15s = $9, occupancy_rate_5m = $10, occupancy_rate_30m = $11 WHERE worker = $6", + "query": "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2,\n occupancy_rate = $3, memory_usage = $4, wm_memory_usage = $5, vcpus = COALESCE($7, vcpus),\n memory = COALESCE($8, memory), occupancy_rate_15s = $9, occupancy_rate_5m = $10, occupancy_rate_30m = $11 WHERE worker = $6", "describe": { "columns": [], "parameters": { @@ -20,5 +20,5 @@ }, "nullable": [] }, - "hash": "a439552f74ed0ba305e3d9cb99ae9e5d24834082ebf2fe9fd3964fdd80b69ccb" + "hash": "aa523c363186575b4bd2537b8e2430e6938e7cc35f8c9e2d1c5459a85443cbdd" } diff --git a/backend/.sqlx/query-ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338.json b/backend/.sqlx/query-ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338.json index 3284fb1846..47e4f47005 100644 --- a/backend/.sqlx/query-ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338.json +++ b/backend/.sqlx/query-ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338.json @@ -63,7 +63,9 @@ "rust", "ansible", "csharp", - "oracledb" + "oracledb", + "nu", + "java" ] } } diff --git a/backend/.sqlx/query-ac01e45d3335015f53f3d63fe159e631efb65c3d326b6b6ae8361a2116bff145.json b/backend/.sqlx/query-ac01e45d3335015f53f3d63fe159e631efb65c3d326b6b6ae8361a2116bff145.json deleted file mode 100644 index 929157b5d7..0000000000 --- a/backend/.sqlx/query-ac01e45d3335015f53f3d63fe159e631efb65c3d326b6b6ae8361a2116bff145.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT content FROM script WHERE path = $1 AND workspace_id = $2\n AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND\n workspace_id = $2)\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "content", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "ac01e45d3335015f53f3d63fe159e631efb65c3d326b6b6ae8361a2116bff145" -} diff --git a/backend/.sqlx/query-ac94c16bff27deab63b898c1cdca9818c93ea0b4c3aaacbd9dcef11fcd9e68a3.json b/backend/.sqlx/query-ac94c16bff27deab63b898c1cdca9818c93ea0b4c3aaacbd9dcef11fcd9e68a3.json deleted file mode 100644 index 602dcb8206..0000000000 --- a/backend/.sqlx/query-ac94c16bff27deab63b898c1cdca9818c93ea0b4c3aaacbd9dcef11fcd9e68a3.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO v2_job (id, workspace_id, raw_code, raw_lock, raw_flow, tag, parent_job,\n created_by, permissioned_as, runnable_id, runnable_path, args, kind, trigger,\n script_lang, same_worker, pre_run_error, permissioned_as_email, visible_to_owner,\n flow_innermost_root_job, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id,\n cache_ttl, priority, trigger_kind, script_entrypoint_override)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18,\n $19, $20, $21, $22, $23, $24, $25, $26,\n CASE WHEN $14::VARCHAR IS NOT NULL THEN 'schedule'::job_trigger_kind END,\n ($12::JSONB)->>'_ENTRYPOINT_OVERRIDE')", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Varchar", - "Text", - "Text", - "Jsonb", - "Varchar", - "Uuid", - "Varchar", - "Varchar", - "Int8", - "Varchar", - "Jsonb", - { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - }, - "Varchar", - { - "Custom": { - "name": "script_lang", - "kind": { - "Enum": [ - "python3", - "deno", - "go", - "bash", - "postgresql", - "nativets", - "bun", - "mysql", - "bigquery", - "snowflake", - "graphql", - "powershell", - "mssql", - "php", - "bunnative", - "rust", - "ansible", - "csharp", - "oracledb" - ] - } - } - }, - "Bool", - "Text", - "Varchar", - "Bool", - "Uuid", - "Int4", - "Int4", - "Int4", - "Varchar", - "Int4", - "Int2" - ] - }, - "nullable": [] - }, - "hash": "ac94c16bff27deab63b898c1cdca9818c93ea0b4c3aaacbd9dcef11fcd9e68a3" -} diff --git a/backend/.sqlx/query-ad88831c9021b79c9037a925c2b53be3a93dbe2a67bdc4d4342c84f439b5eaf9.json b/backend/.sqlx/query-ad88831c9021b79c9037a925c2b53be3a93dbe2a67bdc4d4342c84f439b5eaf9.json new file mode 100644 index 0000000000..8d51b798e9 --- /dev/null +++ b/backend/.sqlx/query-ad88831c9021b79c9037a925c2b53be3a93dbe2a67bdc4d4342c84f439b5eaf9.json @@ -0,0 +1,85 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT \n j.id,\n j.kind AS \"kind: _\",\n COALESCE(s.path, f.path) AS \"script_path!\",\n COALESCE(s.hash, f.id) AS \"script_hash!: _\",\n COALESCE(jc.started_at, jq.scheduled_for, make_date(1970, 1, 1)) AS \"scheduled_for!: _\",\n args AS input,\n COALESCE(s.schema, f.schema) AS \"schema: _\"\n FROM v2_job j\n LEFT JOIN script s ON j.runnable_id = s.hash AND j.kind = 'script'\n LEFT JOIN flow_version f ON j.runnable_id = f.id AND j.runnable_path = f.path AND j.kind = 'flow'\n LEFT JOIN v2_job_completed jc ON jc.id = j.id\n LEFT JOIN v2_job_queue jq ON jq.id = j.id\n WHERE j.id = ANY($1)\n AND j.workspace_id = $2\n AND COALESCE(s.hash, f.id) IS NOT NULL\n AND COALESCE(s.path, f.path) IS NOT NULL", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "kind: _", + "type_info": { + "Custom": { + "name": "job_kind", + "kind": { + "Enum": [ + "script", + "preview", + "flow", + "dependencies", + "flowpreview", + "script_hub", + "identity", + "flowdependencies", + "http", + "graphql", + "postgresql", + "noop", + "appdependencies", + "deploymentcallback", + "singlescriptflow", + "flowscript", + "flownode", + "appscript" + ] + } + } + } + }, + { + "ordinal": 2, + "name": "script_path!", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "script_hash!: _", + "type_info": "Int8" + }, + { + "ordinal": 4, + "name": "scheduled_for!: _", + "type_info": "Timestamptz" + }, + { + "ordinal": 5, + "name": "input", + "type_info": "Jsonb" + }, + { + "ordinal": 6, + "name": "schema: _", + "type_info": "Json" + } + ], + "parameters": { + "Left": [ + "UuidArray", + "Text" + ] + }, + "nullable": [ + false, + false, + null, + null, + null, + true, + null + ] + }, + "hash": "ad88831c9021b79c9037a925c2b53be3a93dbe2a67bdc4d4342c84f439b5eaf9" +} diff --git a/backend/.sqlx/query-ad8f9e0b06f288051cbec3c91877430a1d61353b9122fd42777d92e4cbc9f4fa.json b/backend/.sqlx/query-ad8f9e0b06f288051cbec3c91877430a1d61353b9122fd42777d92e4cbc9f4fa.json deleted file mode 100644 index 00453be45b..0000000000 --- a/backend/.sqlx/query-ad8f9e0b06f288051cbec3c91877430a1d61353b9122fd42777d92e4cbc9f4fa.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['failure_module'], $1), ARRAY['step'], $2)\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "ad8f9e0b06f288051cbec3c91877430a1d61353b9122fd42777d92e4cbc9f4fa" -} diff --git a/backend/.sqlx/query-9a050ab74cfc13a4b855408e61ebcb0a9d27e5563fa7af5e00a4916b03a62ddb.json b/backend/.sqlx/query-adb0090afd3ce918d8b80ff51d9f6104a430a11d7c5cb9447025d11506585708.json similarity index 57% rename from backend/.sqlx/query-9a050ab74cfc13a4b855408e61ebcb0a9d27e5563fa7af5e00a4916b03a62ddb.json rename to backend/.sqlx/query-adb0090afd3ce918d8b80ff51d9f6104a430a11d7c5cb9447025d11506585708.json index a0c5bd7642..bbe9b8bb86 100644 --- a/backend/.sqlx/query-9a050ab74cfc13a4b855408e61ebcb0a9d27e5563fa7af5e00a4916b03a62ddb.json +++ b/backend/.sqlx/query-adb0090afd3ce918d8b80ff51d9f6104a430a11d7c5cb9447025d11506585708.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE path = $1 AND workspace_id = $2)", + "query": "SELECT EXISTS(\n SELECT 1 FROM http_trigger \n WHERE path = $1 AND workspace_id = $2\n )", "describe": { "columns": [ { @@ -19,5 +19,5 @@ null ] }, - "hash": "9a050ab74cfc13a4b855408e61ebcb0a9d27e5563fa7af5e00a4916b03a62ddb" + "hash": "adb0090afd3ce918d8b80ff51d9f6104a430a11d7c5cb9447025d11506585708" } diff --git a/backend/.sqlx/query-ae25fae1aca2cffc43a6054cd54639ba9deaaef419ad4cb2655e7e95f602a688.json b/backend/.sqlx/query-ae25fae1aca2cffc43a6054cd54639ba9deaaef419ad4cb2655e7e95f602a688.json deleted file mode 100644 index 5634e60233..0000000000 --- a/backend/.sqlx/query-ae25fae1aca2cffc43a6054cd54639ba9deaaef419ad4cb2655e7e95f602a688.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO job_logs (job_id, logs) VALUES ($1,'Restarted job after not receiving job''s ping for too long the ' || now() || '\n\n') \n ON CONFLICT (job_id) DO UPDATE SET logs = job_logs.logs || '\nRestarted job after not receiving job''s ping for too long the ' || now() || '\n\n' WHERE job_logs.job_id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "ae25fae1aca2cffc43a6054cd54639ba9deaaef419ad4cb2655e7e95f602a688" -} diff --git a/backend/.sqlx/query-ae8dfecd46425d5f86003eea9a578e9831fc0e700cc76ab9627afe9040a4efe0.json b/backend/.sqlx/query-ae8dfecd46425d5f86003eea9a578e9831fc0e700cc76ab9627afe9040a4efe0.json deleted file mode 100644 index c0703bc518..0000000000 --- a/backend/.sqlx/query-ae8dfecd46425d5f86003eea9a578e9831fc0e700cc76ab9627afe9040a4efe0.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "create index concurrently if not exists ix_job_workspace_id_created_at_new_7 ON v2_job (workspace_id, created_at DESC) where kind in ('script', 'flow') AND parent_job IS NULL", - "describe": { - "columns": [], - "parameters": { - "Left": [] - }, - "nullable": [] - }, - "hash": "ae8dfecd46425d5f86003eea9a578e9831fc0e700cc76ab9627afe9040a4efe0" -} diff --git a/backend/.sqlx/query-aeca239a2997efc514ee56a6b3766fa5f32b5a56e28100a2487b478e5dc3eaec.json b/backend/.sqlx/query-aeca239a2997efc514ee56a6b3766fa5f32b5a56e28100a2487b478e5dc3eaec.json new file mode 100644 index 0000000000..db6ed5a29a --- /dev/null +++ b/backend/.sqlx/query-aeca239a2997efc514ee56a6b3766fa5f32b5a56e28100a2487b478e5dc3eaec.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO workspace_runnable_dependencies (flow_path, runnable_path, script_hash, runnable_is_flow, workspace_id) VALUES ($1, $2, $3, FALSE, $4) ON CONFLICT DO NOTHING", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Int8", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "aeca239a2997efc514ee56a6b3766fa5f32b5a56e28100a2487b478e5dc3eaec" +} diff --git a/backend/.sqlx/query-aed8bd751c3e988f422216e74acfb77dc03469355d2a0da0b2d6b4aeeea37d3e.json b/backend/.sqlx/query-aed8bd751c3e988f422216e74acfb77dc03469355d2a0da0b2d6b4aeeea37d3e.json new file mode 100644 index 0000000000..d8f93f74c4 --- /dev/null +++ b/backend/.sqlx/query-aed8bd751c3e988f422216e74acfb77dc03469355d2a0da0b2d6b4aeeea37d3e.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_leaf_jobs = JSONB_SET(coalesce(flow_leaf_jobs, '{}'::jsonb), ARRAY[$1::TEXT], $2)\n WHERE COALESCE((SELECT flow_innermost_root_job FROM v2_job WHERE id = $3), $3) = id", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "aed8bd751c3e988f422216e74acfb77dc03469355d2a0da0b2d6b4aeeea37d3e" +} diff --git a/backend/.sqlx/query-af925931f3217bbd32313678989ad1a66bbd8dacd12dea36608cc20197df358f.json b/backend/.sqlx/query-af925931f3217bbd32313678989ad1a66bbd8dacd12dea36608cc20197df358f.json deleted file mode 100644 index b90300787b..0000000000 --- a/backend/.sqlx/query-af925931f3217bbd32313678989ad1a66bbd8dacd12dea36608cc20197df358f.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1),\n ARRAY['step'],\n $2\n )\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "af925931f3217bbd32313678989ad1a66bbd8dacd12dea36608cc20197df358f" -} diff --git a/backend/.sqlx/query-afd0d1b0511f32ba1981bc8d09b729639a6c0b468aa3f9108071d160d2dee250.json b/backend/.sqlx/query-afd0d1b0511f32ba1981bc8d09b729639a6c0b468aa3f9108071d160d2dee250.json deleted file mode 100644 index 5ccb236cc6..0000000000 --- a/backend/.sqlx/query-afd0d1b0511f32ba1981bc8d09b729639a6c0b468aa3f9108071d160d2dee250.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT created_by, CONCAT(coalesce(completed_job.logs, ''), coalesce(job_logs.logs, '')) as logs, job_logs.log_offset, job_logs.log_file_index\n FROM completed_job \n LEFT JOIN job_logs ON job_logs.job_id = completed_job.id \n WHERE completed_job.id = $1 AND completed_job.workspace_id = $2 AND ($3::text[] IS NULL OR completed_job.tag = ANY($3))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_by", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "logs", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "log_offset", - "type_info": "Int4" - }, - { - "ordinal": 3, - "name": "log_file_index", - "type_info": "TextArray" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "TextArray" - ] - }, - "nullable": [ - false, - null, - false, - true - ] - }, - "hash": "afd0d1b0511f32ba1981bc8d09b729639a6c0b468aa3f9108071d160d2dee250" -} diff --git a/backend/.sqlx/query-b01160fe44d69834ac08bbf60feacb3e3caa02a04b084da44cdcb9103794b39e.json b/backend/.sqlx/query-b01160fe44d69834ac08bbf60feacb3e3caa02a04b084da44cdcb9103794b39e.json new file mode 100644 index 0000000000..0e87ff0632 --- /dev/null +++ b/backend/.sqlx/query-b01160fe44d69834ac08bbf60feacb3e3caa02a04b084da44cdcb9103794b39e.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['cleanup_module', 'flow_jobs_to_clean'], COALESCE(flow_status->'cleanup_module'->'flow_jobs_to_clean', '[]'::jsonb) || $1)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "b01160fe44d69834ac08bbf60feacb3e3caa02a04b084da44cdcb9103794b39e" +} diff --git a/backend/.sqlx/query-b053117536c067095e2fb2864ce5af33f84b22c24c92fcb870f37f0501f8ea9a.json b/backend/.sqlx/query-b053117536c067095e2fb2864ce5af33f84b22c24c92fcb870f37f0501f8ea9a.json deleted file mode 100644 index 316f6d7eda..0000000000 --- a/backend/.sqlx/query-b053117536c067095e2fb2864ce5af33f84b22c24c92fcb870f37f0501f8ea9a.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT COUNT(id) FROM queue WHERE running = true AND email = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "b053117536c067095e2fb2864ce5af33f84b22c24c92fcb870f37f0501f8ea9a" -} diff --git a/backend/.sqlx/query-b06915e02398511033717ea13b710c86a24fe666884cfd49996dee961751ce51.json b/backend/.sqlx/query-b06915e02398511033717ea13b710c86a24fe666884cfd49996dee961751ce51.json deleted file mode 100644 index 194fda66ef..0000000000 --- a/backend/.sqlx/query-b06915e02398511033717ea13b710c86a24fe666884cfd49996dee961751ce51.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH uuid_table as (\n select unnest($11::uuid[]) as uuid\n )\n INSERT INTO queue \n (id, script_hash, script_path, job_kind, language, args, tag, created_by, permissioned_as, email, scheduled_for, workspace_id, concurrent_limit, concurrency_time_window_s, timeout, flow_status)\n (SELECT uuid, $1, $2, $3, $4, ('{ \"uuid\": \"' || uuid || '\" }')::jsonb, $5, $6, $7, $8, $9, $10, $12, $13, $14, $15 FROM uuid_table) \n RETURNING id AS \"id!\"", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Int8", - "Varchar", - { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - }, - { - "Custom": { - "name": "script_lang", - "kind": { - "Enum": [ - "python3", - "deno", - "go", - "bash", - "postgresql", - "nativets", - "bun", - "mysql", - "bigquery", - "snowflake", - "graphql", - "powershell", - "mssql", - "php", - "bunnative", - "rust", - "ansible", - "csharp", - "oracledb" - ] - } - } - }, - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Timestamptz", - "Varchar", - "UuidArray", - "Int4", - "Int4", - "Int4", - "Jsonb" - ] - }, - "nullable": [ - true - ] - }, - "hash": "b06915e02398511033717ea13b710c86a24fe666884cfd49996dee961751ce51" -} diff --git a/backend/.sqlx/query-b0890c1bac6931d848afd88539a0b766a018957c1a325940df8914b28df60aca.json b/backend/.sqlx/query-b0890c1bac6931d848afd88539a0b766a018957c1a325940df8914b28df60aca.json deleted file mode 100644 index 15b7b27002..0000000000 --- a/backend/.sqlx/query-b0890c1bac6931d848afd88539a0b766a018957c1a325940df8914b28df60aca.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT tag AS \"tag!\", count(*) AS \"count!\" FROM queue WHERE\n scheduled_for <= now() - ('3 seconds')::interval AND running = false\n GROUP BY tag", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "tag!", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "count!", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - true, - null - ] - }, - "hash": "b0890c1bac6931d848afd88539a0b766a018957c1a325940df8914b28df60aca" -} diff --git a/backend/.sqlx/query-b08bf73ca7da6af302eeb5a5f443d71e03478e642a7999157a631a7ff0b7c63e.json b/backend/.sqlx/query-b08bf73ca7da6af302eeb5a5f443d71e03478e642a7999157a631a7ff0b7c63e.json deleted file mode 100644 index 53fc18f0fc..0000000000 --- a/backend/.sqlx/query-b08bf73ca7da6af302eeb5a5f443d71e03478e642a7999157a631a7ff0b7c63e.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE completed_job SET flow_status = q.flow_status FROM queue q WHERE completed_job.id = $1 AND q.id = $1 AND q.workspace_id = $2 AND completed_job.workspace_id = $2 AND q.flow_status IS NOT NULL", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [] - }, - "hash": "b08bf73ca7da6af302eeb5a5f443d71e03478e642a7999157a631a7ff0b7c63e" -} diff --git a/backend/.sqlx/query-b0c2f470f7d2df567eca550db1ae638fcb554622b61a5f4fb6b6696f6283516a.json b/backend/.sqlx/query-b0c2f470f7d2df567eca550db1ae638fcb554622b61a5f4fb6b6696f6283516a.json deleted file mode 100644 index 86a8abb432..0000000000 --- a/backend/.sqlx/query-b0c2f470f7d2df567eca550db1ae638fcb554622b61a5f4fb6b6696f6283516a.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM variable WHERE expires_at IS NOT NULL AND expires_at < now() RETURNING path", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "path", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false - ] - }, - "hash": "b0c2f470f7d2df567eca550db1ae638fcb554622b61a5f4fb6b6696f6283516a" -} diff --git a/backend/.sqlx/query-b186efe51e7bb1924efbd7e9b36085502a1e77e30e59c7310c78976f77a810a3.json b/backend/.sqlx/query-b186efe51e7bb1924efbd7e9b36085502a1e77e30e59c7310c78976f77a810a3.json deleted file mode 100644 index d856ab0109..0000000000 --- a/backend/.sqlx/query-b186efe51e7bb1924efbd7e9b36085502a1e77e30e59c7310c78976f77a810a3.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT ai_resource, code_completion_model, ai_models FROM workspace_settings WHERE workspace_id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "ai_resource", - "type_info": "Jsonb" - }, - { - "ordinal": 1, - "name": "code_completion_model", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "ai_models", - "type_info": "VarcharArray" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - true, - true, - false - ] - }, - "hash": "b186efe51e7bb1924efbd7e9b36085502a1e77e30e59c7310c78976f77a810a3" -} diff --git a/backend/.sqlx/query-b190ad25e22367f71c1e16e34bfbbe4303249c8d8664962d479056971f0409a2.json b/backend/.sqlx/query-b190ad25e22367f71c1e16e34bfbbe4303249c8d8664962d479056971f0409a2.json new file mode 100644 index 0000000000..def6dfdd14 --- /dev/null +++ b/backend/.sqlx/query-b190ad25e22367f71c1e16e34bfbbe4303249c8d8664962d479056971f0409a2.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM workspace_runnable_dependencies WHERE app_path = $1 AND workspace_id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "b190ad25e22367f71c1e16e34bfbbe4303249c8d8664962d479056971f0409a2" +} diff --git a/backend/.sqlx/query-47e6b25cc092ec8718a6581c76aca10b275653e10ea4aa17a8ef5091ca09294a.json b/backend/.sqlx/query-b1c96c527c4b263b5155d689eb88894ea93f0eaba37874f828a733062af17640.json similarity index 67% rename from backend/.sqlx/query-47e6b25cc092ec8718a6581c76aca10b275653e10ea4aa17a8ef5091ca09294a.json rename to backend/.sqlx/query-b1c96c527c4b263b5155d689eb88894ea93f0eaba37874f828a733062af17640.json index 3206f22bc5..8fd4c311c5 100644 --- a/backend/.sqlx/query-47e6b25cc092ec8718a6581c76aca10b275653e10ea4aa17a8ef5091ca09294a.json +++ b/backend/.sqlx/query-b1c96c527c4b263b5155d689eb88894ea93f0eaba37874f828a733062af17640.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT result, id\n FROM v2_job_completed\n WHERE id = ANY($1) AND workspace_id = $2", + "query": "SELECT result, id\n FROM v2_job_completed\n WHERE id = ANY($1) AND workspace_id = $2", "describe": { "columns": [ { @@ -25,5 +25,5 @@ false ] }, - "hash": "47e6b25cc092ec8718a6581c76aca10b275653e10ea4aa17a8ef5091ca09294a" + "hash": "b1c96c527c4b263b5155d689eb88894ea93f0eaba37874f828a733062af17640" } diff --git a/backend/.sqlx/query-b386d320f9fe1d569a16e6626b723b4376a93342702856da2ac70f6bbfc7933e.json b/backend/.sqlx/query-b386d320f9fe1d569a16e6626b723b4376a93342702856da2ac70f6bbfc7933e.json new file mode 100644 index 0000000000..13ac3c15ea --- /dev/null +++ b/backend/.sqlx/query-b386d320f9fe1d569a16e6626b723b4376a93342702856da2ac70f6bbfc7933e.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT EXISTS(\n SELECT \n 1 \n FROM \n sqs_trigger \n WHERE \n path = $1 AND \n workspace_id = $2\n )", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "b386d320f9fe1d569a16e6626b723b4376a93342702856da2ac70f6bbfc7933e" +} diff --git a/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json b/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json index 54e94cfb8f..99269c9851 100644 --- a/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json +++ b/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json @@ -18,8 +18,8 @@ "Left": [] }, "nullable": [ - false, - true + true, + false ] }, "hash": "b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76" diff --git a/backend/.sqlx/query-b3e41eaff54c5da5e38cff785c17b2d9e014be9d0794e72dc8566485e61492cd.json b/backend/.sqlx/query-b3e41eaff54c5da5e38cff785c17b2d9e014be9d0794e72dc8566485e61492cd.json deleted file mode 100644 index ce125516d2..0000000000 --- a/backend/.sqlx/query-b3e41eaff54c5da5e38cff785c17b2d9e014be9d0794e72dc8566485e61492cd.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n script_path, args AS \"args: sqlx::types::Json>>\",\n tag AS \"tag!\", priority\n FROM completed_job\n WHERE id = $1 and workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "args: sqlx::types::Json>>", - "type_info": "Jsonb" - }, - { - "ordinal": 2, - "name": "tag!", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "priority", - "type_info": "Int2" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true, - true, - true - ] - }, - "hash": "b3e41eaff54c5da5e38cff785c17b2d9e014be9d0794e72dc8566485e61492cd" -} diff --git a/backend/.sqlx/query-b3f0595cacba194e08b9a3e244d9e637e9e156cd85b69126c87dfff89a47711d.json b/backend/.sqlx/query-b3f0595cacba194e08b9a3e244d9e637e9e156cd85b69126c87dfff89a47711d.json new file mode 100644 index 0000000000..a20f4e47ad --- /dev/null +++ b/backend/.sqlx/query-b3f0595cacba194e08b9a3e244d9e637e9e156cd85b69126c87dfff89a47711d.json @@ -0,0 +1,38 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO capture_config (\n workspace_id, path, is_flow, trigger_kind, trigger_config, owner, email\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7\n )\n ON CONFLICT (workspace_id, path, is_flow, trigger_kind)\n DO UPDATE \n SET \n trigger_config = $5, \n owner = $6, \n email = $7, \n server_id = NULL, \n error = NULL\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Bool", + { + "Custom": { + "name": "trigger_kind", + "kind": { + "Enum": [ + "webhook", + "http", + "websocket", + "kafka", + "email", + "nats", + "postgres", + "sqs", + "mqtt", + "gcp" + ] + } + } + }, + "Jsonb", + "Varchar", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "b3f0595cacba194e08b9a3e244d9e637e9e156cd85b69126c87dfff89a47711d" +} diff --git a/backend/.sqlx/query-b41cef713e822bbd89b49b1f35cc662539d5e4af1dd0b5923d8ee17b772c5677.json b/backend/.sqlx/query-b41cef713e822bbd89b49b1f35cc662539d5e4af1dd0b5923d8ee17b772c5677.json deleted file mode 100644 index 8475175c69..0000000000 --- a/backend/.sqlx/query-b41cef713e822bbd89b49b1f35cc662539d5e4af1dd0b5923d8ee17b772c5677.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO http_trigger (workspace_id, path, route_path, route_path_key, script_path, is_flow, is_async, requires_auth, http_method, static_asset_config, edited_by, email, edited_at, is_static_website) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, now(), $13)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Bool", - "Bool", - "Bool", - { - "Custom": { - "name": "http_method", - "kind": { - "Enum": [ - "get", - "post", - "put", - "delete", - "patch" - ] - } - } - }, - "Jsonb", - "Varchar", - "Varchar", - "Bool" - ] - }, - "nullable": [] - }, - "hash": "b41cef713e822bbd89b49b1f35cc662539d5e4af1dd0b5923d8ee17b772c5677" -} diff --git a/backend/.sqlx/query-597b148ff09a1e0f369bb04781ee4e429ebce64a4d5c16b0f136142ad213cdb1.json b/backend/.sqlx/query-b41fa341e65ee348f468ed04ac1160770b19c0a00cd333abc48b29c54f863149.json similarity index 52% rename from backend/.sqlx/query-597b148ff09a1e0f369bb04781ee4e429ebce64a4d5c16b0f136142ad213cdb1.json rename to backend/.sqlx/query-b41fa341e65ee348f468ed04ac1160770b19c0a00cd333abc48b29c54f863149.json index 4a704e58d8..f37bea0531 100644 --- a/backend/.sqlx/query-597b148ff09a1e0f369bb04781ee4e429ebce64a4d5c16b0f136142ad213cdb1.json +++ b/backend/.sqlx/query-b41fa341e65ee348f468ed04ac1160770b19c0a00cd333abc48b29c54f863149.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n args AS \"args: Json>>\"\n FROM v2_job\n WHERE id = $1", + "query": "SELECT\n args AS \"args: Json>>\"\n FROM v2_job\n WHERE id = $1", "describe": { "columns": [ { @@ -18,5 +18,5 @@ true ] }, - "hash": "597b148ff09a1e0f369bb04781ee4e429ebce64a4d5c16b0f136142ad213cdb1" + "hash": "b41fa341e65ee348f468ed04ac1160770b19c0a00cd333abc48b29c54f863149" } diff --git a/backend/.sqlx/query-b4d48c820bf41619bffa8f62367e98369e1d93514e1618723a34bf96080d4ebc.json b/backend/.sqlx/query-b4d48c820bf41619bffa8f62367e98369e1d93514e1618723a34bf96080d4ebc.json new file mode 100644 index 0000000000..0efca7e867 --- /dev/null +++ b/backend/.sqlx/query-b4d48c820bf41619bffa8f62367e98369e1d93514e1618723a34bf96080d4ebc.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE workspace_settings\n SET git_app_installations = (\n SELECT jsonb_agg(\n CASE\n WHEN (elem->>'installation_id')::bigint = $2 THEN $1::jsonb\n ELSE elem\n END\n )\n FROM jsonb_array_elements(git_app_installations) AS elem\n )\n WHERE workspace_id = $3\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Int8", + "Text" + ] + }, + "nullable": [] + }, + "hash": "b4d48c820bf41619bffa8f62367e98369e1d93514e1618723a34bf96080d4ebc" +} diff --git a/backend/.sqlx/query-b50b8d3367bd1f74986e6f923fe8497bcbfffea2570a3f3d1f8100207bc1557c.json b/backend/.sqlx/query-b50b8d3367bd1f74986e6f923fe8497bcbfffea2570a3f3d1f8100207bc1557c.json deleted file mode 100644 index dd2a7b9ede..0000000000 --- a/backend/.sqlx/query-b50b8d3367bd1f74986e6f923fe8497bcbfffea2570a3f3d1f8100207bc1557c.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT COUNT(*) FROM queue WHERE canceled = false AND (scheduled_for <= now()\n OR (suspend_until IS NOT NULL\n AND ( suspend <= 0\n OR suspend_until <= now())))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - null - ] - }, - "hash": "b50b8d3367bd1f74986e6f923fe8497bcbfffea2570a3f3d1f8100207bc1557c" -} diff --git a/backend/.sqlx/query-a17260a1f1ee02e786690994d98c84ddf81e2eeb883f895c9cfc47e144d422cb.json b/backend/.sqlx/query-b5860f6a7672a368d740dcd367a8d5ab98fa93e0382a57a698564695db6c40ac.json similarity index 62% rename from backend/.sqlx/query-a17260a1f1ee02e786690994d98c84ddf81e2eeb883f895c9cfc47e144d422cb.json rename to backend/.sqlx/query-b5860f6a7672a368d740dcd367a8d5ab98fa93e0382a57a698564695db6c40ac.json index 5f8386be37..a08c31e743 100644 --- a/backend/.sqlx/query-a17260a1f1ee02e786690994d98c84ddf81e2eeb883f895c9cfc47e144d422cb.json +++ b/backend/.sqlx/query-b5860f6a7672a368d740dcd367a8d5ab98fa93e0382a57a698564695db6c40ac.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT content FROM script WHERE path = $1 AND workspace_id = $2 AND\n created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND archived = false AND workspace_id = $2)", + "query": "SELECT content FROM script WHERE path = $1 AND workspace_id = $2 AND archived = false ORDER BY created_at DESC LIMIT 1", "describe": { "columns": [ { @@ -19,5 +19,5 @@ false ] }, - "hash": "a17260a1f1ee02e786690994d98c84ddf81e2eeb883f895c9cfc47e144d422cb" + "hash": "b5860f6a7672a368d740dcd367a8d5ab98fa93e0382a57a698564695db6c40ac" } diff --git a/backend/.sqlx/query-b9b38d63af3670d1f11d5cbb82a8008a9479bf4b3d7231371ebf26382ecde365.json b/backend/.sqlx/query-b9b38d63af3670d1f11d5cbb82a8008a9479bf4b3d7231371ebf26382ecde365.json new file mode 100644 index 0000000000..641c94c555 --- /dev/null +++ b/backend/.sqlx/query-b9b38d63af3670d1f11d5cbb82a8008a9479bf4b3d7231371ebf26382ecde365.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "VACUUM v2_job_queue, v2_job_runtime, v2_job_status", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "b9b38d63af3670d1f11d5cbb82a8008a9479bf4b3d7231371ebf26382ecde365" +} diff --git a/backend/.sqlx/query-ba117e89b5da7ef46a9e702a2f80c624cd1b00bcab9855edb9284d8afec46ff0.json b/backend/.sqlx/query-ba117e89b5da7ef46a9e702a2f80c624cd1b00bcab9855edb9284d8afec46ff0.json new file mode 100644 index 0000000000..773dc5d24d --- /dev/null +++ b/backend/.sqlx/query-ba117e89b5da7ef46a9e702a2f80c624cd1b00bcab9855edb9284d8afec46ff0.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE workspace_settings SET ai_config = $1 WHERE workspace_id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Text" + ] + }, + "nullable": [] + }, + "hash": "ba117e89b5da7ef46a9e702a2f80c624cd1b00bcab9855edb9284d8afec46ff0" +} diff --git a/backend/.sqlx/query-ba285edd1c1b1e400e85168ff4f05cf5281fd341096d433c7c0e5712e7726fb0.json b/backend/.sqlx/query-ba285edd1c1b1e400e85168ff4f05cf5281fd341096d433c7c0e5712e7726fb0.json new file mode 100644 index 0000000000..c942cc37f1 --- /dev/null +++ b/backend/.sqlx/query-ba285edd1c1b1e400e85168ff4f05cf5281fd341096d433c7c0e5712e7726fb0.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE flow SET lock_error_logs = $1 WHERE path = $2 AND workspace_id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "ba285edd1c1b1e400e85168ff4f05cf5281fd341096d433c7c0e5712e7726fb0" +} diff --git a/backend/.sqlx/query-baa1dddc616419bf4b923715f0a863bc0ff69c98db0f0c8f55e4ac89fdde7a60.json b/backend/.sqlx/query-baa1dddc616419bf4b923715f0a863bc0ff69c98db0f0c8f55e4ac89fdde7a60.json new file mode 100644 index 0000000000..e772354ea8 --- /dev/null +++ b/backend/.sqlx/query-baa1dddc616419bf4b923715f0a863bc0ff69c98db0f0c8f55e4ac89fdde7a60.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT pubname FROM pg_publication WHERE pubname = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "pubname", + "type_info": "Name" + } + ], + "parameters": { + "Left": [ + "Name" + ] + }, + "nullable": [ + false + ] + }, + "hash": "baa1dddc616419bf4b923715f0a863bc0ff69c98db0f0c8f55e4ac89fdde7a60" +} diff --git a/backend/.sqlx/query-bb0ee03198bcad69a1777447cf5c42fc22700e3cbf8cd44621a8380536d77552.json b/backend/.sqlx/query-bb0ee03198bcad69a1777447cf5c42fc22700e3cbf8cd44621a8380536d77552.json deleted file mode 100644 index 91a88794f4..0000000000 --- a/backend/.sqlx/query-bb0ee03198bcad69a1777447cf5c42fc22700e3cbf8cd44621a8380536d77552.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM completed_job WHERE created_at <= now() - ($1::bigint::text || ' s')::interval AND started_at + ((duration_ms/1000 + $1::bigint) || ' s')::interval <= now() RETURNING id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - false - ] - }, - "hash": "bb0ee03198bcad69a1777447cf5c42fc22700e3cbf8cd44621a8380536d77552" -} diff --git a/backend/.sqlx/query-bb31ee7266192f0df19fbf972bab2d94e8377509f3da999f41537b6598ace5f2.json b/backend/.sqlx/query-bb31ee7266192f0df19fbf972bab2d94e8377509f3da999f41537b6598ace5f2.json new file mode 100644 index 0000000000..9396723e8a --- /dev/null +++ b/backend/.sqlx/query-bb31ee7266192f0df19fbf972bab2d94e8377509f3da999f41537b6598ace5f2.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO healthchecks (check_type, healthy)\n SELECT 'min_alive_workers_' || $1, false\n WHERE NOT EXISTS (\n SELECT 1 FROM healthchecks\n WHERE check_type = 'min_alive_workers_' || $1 AND created_at > NOW() - INTERVAL '2 minutes'\n )\n RETURNING id", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "bb31ee7266192f0df19fbf972bab2d94e8377509f3da999f41537b6598ace5f2" +} diff --git a/backend/.sqlx/query-bb3b644d630dc2040fdbebfaf7649617c1e5a22fc418a2a6ee3276a09b9a8cf8.json b/backend/.sqlx/query-bb3b644d630dc2040fdbebfaf7649617c1e5a22fc418a2a6ee3276a09b9a8cf8.json new file mode 100644 index 0000000000..88b910fb04 --- /dev/null +++ b/backend/.sqlx/query-bb3b644d630dc2040fdbebfaf7649617c1e5a22fc418a2a6ee3276a09b9a8cf8.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "CREATE INDEX CONCURRENTLY IF NOT EXISTS ix_audit_timestamps ON audit (timestamp DESC)", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "bb3b644d630dc2040fdbebfaf7649617c1e5a22fc418a2a6ee3276a09b9a8cf8" +} diff --git a/backend/.sqlx/query-bb93ba18709648b47cfbd04d91afd3b38546b1a718d0abff6b2795d7c2a29c97.json b/backend/.sqlx/query-bb93ba18709648b47cfbd04d91afd3b38546b1a718d0abff6b2795d7c2a29c97.json deleted file mode 100644 index d6288765fe..0000000000 --- a/backend/.sqlx/query-bb93ba18709648b47cfbd04d91afd3b38546b1a718d0abff6b2795d7c2a29c97.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT id AS \"id!\", flow_status, suspend AS \"suspend!\", script_path\n FROM queue\n WHERE id = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "flow_status", - "type_info": "Jsonb" - }, - { - "ordinal": 2, - "name": "suspend!", - "type_info": "Int4" - }, - { - "ordinal": 3, - "name": "script_path", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - true, - true, - true, - true - ] - }, - "hash": "bb93ba18709648b47cfbd04d91afd3b38546b1a718d0abff6b2795d7c2a29c97" -} diff --git a/backend/.sqlx/query-bbc2c0769bf833f4e95bfc7908897ecbfe662efb13ffdd8ee3f1930bff4cd9c4.json b/backend/.sqlx/query-bbc2c0769bf833f4e95bfc7908897ecbfe662efb13ffdd8ee3f1930bff4cd9c4.json new file mode 100644 index 0000000000..e8f8de3ef8 --- /dev/null +++ b/backend/.sqlx/query-bbc2c0769bf833f4e95bfc7908897ecbfe662efb13ffdd8ee3f1930bff4cd9c4.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2),\n ARRAY['step'],\n $3\n )\n WHERE id = $4", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Jsonb", + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "bbc2c0769bf833f4e95bfc7908897ecbfe662efb13ffdd8ee3f1930bff4cd9c4" +} diff --git a/backend/.sqlx/query-bc01d5ca0138527b796a373f76404b8b6e4c6d711d0f6a557f929d831b8cfd3e.json b/backend/.sqlx/query-bc01d5ca0138527b796a373f76404b8b6e4c6d711d0f6a557f929d831b8cfd3e.json deleted file mode 100644 index 6fcbd0a266..0000000000 --- a/backend/.sqlx/query-bc01d5ca0138527b796a373f76404b8b6e4c6d711d0f6a557f929d831b8cfd3e.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT null FROM queue WHERE id = $1 FOR UPDATE", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "bc01d5ca0138527b796a373f76404b8b6e4c6d711d0f6a557f929d831b8cfd3e" -} diff --git a/backend/.sqlx/query-bc0bedddcafad216c30c5061eef3f4ed8573cda89f655d6490d22f182e3f2f36.json b/backend/.sqlx/query-bc0bedddcafad216c30c5061eef3f4ed8573cda89f655d6490d22f182e3f2f36.json new file mode 100644 index 0000000000..2ccfd91e32 --- /dev/null +++ b/backend/.sqlx/query-bc0bedddcafad216c30c5061eef3f4ed8573cda89f655d6490d22f182e3f2f36.json @@ -0,0 +1,120 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n aws_auth_resource_type AS \"aws_auth_resource_type: _\",\n queue_url,\n aws_resource_path,\n message_attributes,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n sqs_trigger\n WHERE\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "aws_auth_resource_type: _", + "type_info": { + "Custom": { + "name": "aws_auth_resource_type", + "kind": { + "Enum": [ + "oidc", + "credentials" + ] + } + } + } + }, + { + "ordinal": 1, + "name": "queue_url", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "aws_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "message_attributes", + "type_info": "TextArray" + }, + { + "ordinal": 4, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 8, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 11, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 14, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 15, + "name": "enabled", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + true, + true, + false + ] + }, + "hash": "bc0bedddcafad216c30c5061eef3f4ed8573cda89f655d6490d22f182e3f2f36" +} diff --git a/backend/.sqlx/query-bc8ac03254669951654cda4bcfa12491341e745aef5e0e5090c2f4e4a4dc54fb.json b/backend/.sqlx/query-bc8ac03254669951654cda4bcfa12491341e745aef5e0e5090c2f4e4a4dc54fb.json deleted file mode 100644 index fc27de30d0..0000000000 --- a/backend/.sqlx/query-bc8ac03254669951654cda4bcfa12491341e745aef5e0e5090c2f4e4a4dc54fb.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT raw_code, raw_lock, raw_flow AS \"raw_flow: Json>\" FROM job WHERE id = $1 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "raw_code", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "raw_lock", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "raw_flow: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - true, - true, - true - ] - }, - "hash": "bc8ac03254669951654cda4bcfa12491341e745aef5e0e5090c2f4e4a4dc54fb" -} diff --git a/backend/.sqlx/query-bca733ef3969c055269db2bc20dacd5ecb22e6d5378ca6bc5a83dae6b8e525c1.json b/backend/.sqlx/query-bca733ef3969c055269db2bc20dacd5ecb22e6d5378ca6bc5a83dae6b8e525c1.json deleted file mode 100644 index 2307dbbce5..0000000000 --- a/backend/.sqlx/query-bca733ef3969c055269db2bc20dacd5ecb22e6d5378ca6bc5a83dae6b8e525c1.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_completed SET flow_status = f.flow_status FROM v2_job_status f WHERE v2_job_completed.id = $1 AND f.id = $1 AND v2_job_completed.workspace_id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [] - }, - "hash": "bca733ef3969c055269db2bc20dacd5ecb22e6d5378ca6bc5a83dae6b8e525c1" -} diff --git a/backend/.sqlx/query-bcfe877749ff7b944fef302ea37481b170c221349a793c9608c4ccd52ba8a5af.json b/backend/.sqlx/query-bcfe877749ff7b944fef302ea37481b170c221349a793c9608c4ccd52ba8a5af.json new file mode 100644 index 0000000000..4460ab7b36 --- /dev/null +++ b/backend/.sqlx/query-bcfe877749ff7b944fef302ea37481b170c221349a793c9608c4ccd52ba8a5af.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['approval_conditions'], $1)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "bcfe877749ff7b944fef302ea37481b170c221349a793c9608c4ccd52ba8a5af" +} diff --git a/backend/.sqlx/query-bd5a0c06e2f2361c9fc670eb0b975b58d65ca93d68b29124d04bd526239b9df2.json b/backend/.sqlx/query-bd5a0c06e2f2361c9fc670eb0b975b58d65ca93d68b29124d04bd526239b9df2.json deleted file mode 100644 index 81a97984ee..0000000000 --- a/backend/.sqlx/query-bd5a0c06e2f2361c9fc670eb0b975b58d65ca93d68b29124d04bd526239b9df2.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job SET labels = $2 WHERE id = $1 AND $2::TEXT[] IS NOT NULL", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "TextArray" - ] - }, - "nullable": [] - }, - "hash": "bd5a0c06e2f2361c9fc670eb0b975b58d65ca93d68b29124d04bd526239b9df2" -} diff --git a/backend/.sqlx/query-bef2776351e8489559609d390b92d688519e8af27b228202c872061cbda7e30a.json b/backend/.sqlx/query-bef2776351e8489559609d390b92d688519e8af27b228202c872061cbda7e30a.json new file mode 100644 index 0000000000..d75ffd0339 --- /dev/null +++ b/backend/.sqlx/query-bef2776351e8489559609d390b92d688519e8af27b228202c872061cbda7e30a.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT scheduled_for FROM v2_job_queue INNER JOIN concurrency_key ON concurrency_key.job_id = v2_job_queue.id\n WHERE key = $1 AND running = false AND canceled_by IS NULL AND scheduled_for >= $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "scheduled_for", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text", + "Timestamptz" + ] + }, + "nullable": [ + false + ] + }, + "hash": "bef2776351e8489559609d390b92d688519e8af27b228202c872061cbda7e30a" +} diff --git a/backend/.sqlx/query-bf04a017bc55f6e1f5f25f697b3c4828611591c76dd32541735095c010d6cdf2.json b/backend/.sqlx/query-bf04a017bc55f6e1f5f25f697b3c4828611591c76dd32541735095c010d6cdf2.json deleted file mode 100644 index aad7404583..0000000000 --- a/backend/.sqlx/query-bf04a017bc55f6e1f5f25f697b3c4828611591c76dd32541735095c010d6cdf2.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT coalesce(COUNT(*), 0) as \"database_length!\", null::bigint as suspended FROM completed_job WHERE workspace_id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "database_length!", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "suspended", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null, - null - ] - }, - "hash": "bf04a017bc55f6e1f5f25f697b3c4828611591c76dd32541735095c010d6cdf2" -} diff --git a/backend/.sqlx/query-bf282f7ed77ed09bb44b5e253725a15acefac87bedb2725827938c54a3c0e8f8.json b/backend/.sqlx/query-bf282f7ed77ed09bb44b5e253725a15acefac87bedb2725827938c54a3c0e8f8.json deleted file mode 100644 index 356aa78547..0000000000 --- a/backend/.sqlx/query-bf282f7ed77ed09bb44b5e253725a15acefac87bedb2725827938c54a3c0e8f8.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET canceled_by = $1 WHERE canceled_by = $2 AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "bf282f7ed77ed09bb44b5e253725a15acefac87bedb2725827938c54a3c0e8f8" -} diff --git a/backend/.sqlx/query-bfc534d87d701d7ac78cc97d0054d829165ba3f22fba75c3161e4cddb72264ee.json b/backend/.sqlx/query-bfc534d87d701d7ac78cc97d0054d829165ba3f22fba75c3161e4cddb72264ee.json new file mode 100644 index 0000000000..76f0f1486f --- /dev/null +++ b/backend/.sqlx/query-bfc534d87d701d7ac78cc97d0054d829165ba3f22fba75c3161e4cddb72264ee.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n postgres_trigger \n SET \n enabled = FALSE, \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "bfc534d87d701d7ac78cc97d0054d829165ba3f22fba75c3161e4cddb72264ee" +} diff --git a/backend/.sqlx/query-c00bae0d8c9bee37cbad4de4cb02c80d00f52a3fc32bf32271ebc90f7837abda.json b/backend/.sqlx/query-c00bae0d8c9bee37cbad4de4cb02c80d00f52a3fc32bf32271ebc90f7837abda.json deleted file mode 100644 index 74aa6c4ffb..0000000000 --- a/backend/.sqlx/query-c00bae0d8c9bee37cbad4de4cb02c80d00f52a3fc32bf32271ebc90f7837abda.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_queue\n SET canceled_by = 'timeout'\n , canceled_reason = $1\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "c00bae0d8c9bee37cbad4de4cb02c80d00f52a3fc32bf32271ebc90f7837abda" -} diff --git a/backend/.sqlx/query-c0e6dbce7a401b06e1bf45155c3f81572818177a6024e743b12f558b46edf74c.json b/backend/.sqlx/query-c0e6dbce7a401b06e1bf45155c3f81572818177a6024e743b12f558b46edf74c.json new file mode 100644 index 0000000000..e65e3cb60e --- /dev/null +++ b/backend/.sqlx/query-c0e6dbce7a401b06e1bf45155c3f81572818177a6024e743b12f558b46edf74c.json @@ -0,0 +1,50 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n path,\n is_flow,\n workspace_id,\n owner,\n email,\n trigger_config as \"trigger_config!: _\"\n FROM\n capture_config\n WHERE\n trigger_kind = 'gcp' AND\n last_client_ping > NOW() - INTERVAL '10 seconds' AND\n trigger_config IS NOT NULL AND\n trigger_config->>'delivery_type' IS DISTINCT FROM 'push' AND\n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds')\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "owner", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "trigger_config!: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + false, + true + ] + }, + "hash": "c0e6dbce7a401b06e1bf45155c3f81572818177a6024e743b12f558b46edf74c" +} diff --git a/backend/.sqlx/query-51648e377d47815d0b15694572d5c9cc0a303d70980346e1f3c4096a8922d7d5.json b/backend/.sqlx/query-c12a0b0d423577afbb2772f971bffe2635785b53d31d0ceb3d72226a0c316e8a.json similarity index 55% rename from backend/.sqlx/query-51648e377d47815d0b15694572d5c9cc0a303d70980346e1f3c4096a8922d7d5.json rename to backend/.sqlx/query-c12a0b0d423577afbb2772f971bffe2635785b53d31d0ceb3d72226a0c316e8a.json index 813981bae2..ca8fd862d2 100644 --- a/backend/.sqlx/query-51648e377d47815d0b15694572d5c9cc0a303d70980346e1f3c4096a8922d7d5.json +++ b/backend/.sqlx/query-c12a0b0d423577afbb2772f971bffe2635785b53d31d0ceb3d72226a0c316e8a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n -- slack_team_id, \n -- slack_name, \n -- slack_command_script, \n -- CASE WHEN slack_email = 'missing@email.xyz' THEN NULL ELSE slack_email END AS slack_email,\n auto_invite_domain IS NOT NULL AS \"auto_invite_enabled!\",\n CASE WHEN auto_invite_operator IS TRUE THEN 'operator' ELSE 'developer' END AS \"auto_invite_as!\", \n CASE WHEN auto_add IS TRUE THEN 'add' ELSE 'invite' END AS \"auto_invite_mode!\", \n webhook, \n deploy_to, \n error_handler, \n ai_resource, \n ai_models,\n code_completion_model,\n error_handler_extra_args, \n error_handler_muted_on_cancel, \n large_file_storage, \n git_sync,\n default_app,\n default_scripts,\n workspace.name\n FROM workspace_settings\n LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id\n WHERE workspace_id = $1", + "query": "SELECT\n -- slack_team_id,\n -- slack_name,\n -- slack_command_script,\n -- CASE WHEN slack_email = 'missing@email.xyz' THEN NULL ELSE slack_email END AS slack_email,\n auto_invite_domain IS NOT NULL AS \"auto_invite_enabled!\",\n CASE WHEN auto_invite_operator IS TRUE THEN 'operator' ELSE 'developer' END AS \"auto_invite_as!\",\n CASE WHEN auto_add IS TRUE THEN 'add' ELSE 'invite' END AS \"auto_invite_mode!\",\n webhook,\n deploy_to,\n error_handler,\n ai_config,\n error_handler_extra_args,\n error_handler_muted_on_cancel,\n large_file_storage,\n git_sync,\n default_app,\n default_scripts,\n workspace.name,\n mute_critical_alerts,\n color,\n operator_settings\n FROM workspace_settings\n LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id\n WHERE workspace_id = $1", "describe": { "columns": [ { @@ -35,53 +35,58 @@ }, { "ordinal": 6, - "name": "ai_resource", + "name": "ai_config", "type_info": "Jsonb" }, { "ordinal": 7, - "name": "ai_models", - "type_info": "VarcharArray" - }, - { - "ordinal": 8, - "name": "code_completion_model", - "type_info": "Varchar" - }, - { - "ordinal": 9, "name": "error_handler_extra_args", "type_info": "Json" }, { - "ordinal": 10, + "ordinal": 8, "name": "error_handler_muted_on_cancel", "type_info": "Bool" }, { - "ordinal": 11, + "ordinal": 9, "name": "large_file_storage", "type_info": "Jsonb" }, { - "ordinal": 12, + "ordinal": 10, "name": "git_sync", "type_info": "Jsonb" }, { - "ordinal": 13, + "ordinal": 11, "name": "default_app", "type_info": "Varchar" }, { - "ordinal": 14, + "ordinal": 12, "name": "default_scripts", "type_info": "Jsonb" }, { - "ordinal": 15, + "ordinal": 13, "name": "name", "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "mute_critical_alerts", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "color", + "type_info": "Varchar" + }, + { + "ordinal": 16, + "name": "operator_settings", + "type_info": "Jsonb" } ], "parameters": { @@ -97,16 +102,17 @@ true, true, true, - false, - true, true, false, true, true, true, true, - false + false, + true, + true, + true ] }, - "hash": "51648e377d47815d0b15694572d5c9cc0a303d70980346e1f3c4096a8922d7d5" + "hash": "c12a0b0d423577afbb2772f971bffe2635785b53d31d0ceb3d72226a0c316e8a" } diff --git a/backend/.sqlx/query-c14009d133956710f4435a5984c18b9517e256978512f014e1bf5c270f499772.json b/backend/.sqlx/query-c14009d133956710f4435a5984c18b9517e256978512f014e1bf5c270f499772.json deleted file mode 100644 index 7e9e9b84aa..0000000000 --- a/backend/.sqlx/query-c14009d133956710f4435a5984c18b9517e256978512f014e1bf5c270f499772.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "c14009d133956710f4435a5984c18b9517e256978512f014e1bf5c270f499772" -} diff --git a/backend/.sqlx/query-c14cc34a5350865b8a5b57205b6099e8f0ea697e279f6a72484ab031b7e1e952.json b/backend/.sqlx/query-c14cc34a5350865b8a5b57205b6099e8f0ea697e279f6a72484ab031b7e1e952.json new file mode 100644 index 0000000000..828dc6f04a --- /dev/null +++ b/backend/.sqlx/query-c14cc34a5350865b8a5b57205b6099e8f0ea697e279f6a72484ab031b7e1e952.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE workspace_runnable_dependencies SET runnable_path = REGEXP_REPLACE(runnable_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE runnable_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "c14cc34a5350865b8a5b57205b6099e8f0ea697e279f6a72484ab031b7e1e952" +} diff --git a/backend/.sqlx/query-c19a60a9dc3f95af218baf40c62f14572ac204cfe377166aa1d91cf58f731f50.json b/backend/.sqlx/query-c19a60a9dc3f95af218baf40c62f14572ac204cfe377166aa1d91cf58f731f50.json new file mode 100644 index 0000000000..65cc991931 --- /dev/null +++ b/backend/.sqlx/query-c19a60a9dc3f95af218baf40c62f14572ac204cfe377166aa1d91cf58f731f50.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE \n FROM \n gcp_trigger \n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "c19a60a9dc3f95af218baf40c62f14572ac204cfe377166aa1d91cf58f731f50" +} diff --git a/backend/.sqlx/query-c202f6fbae6a727f88f3ac692985c70e6ebc68e4a16d02e4e36b79f3cfb1c661.json b/backend/.sqlx/query-c202f6fbae6a727f88f3ac692985c70e6ebc68e4a16d02e4e36b79f3cfb1c661.json deleted file mode 100644 index e1094f38ef..0000000000 --- a/backend/.sqlx/query-c202f6fbae6a727f88f3ac692985c70e6ebc68e4a16d02e4e36b79f3cfb1c661.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH suspend AS (\n UPDATE v2_job_queue SET suspend = $2, suspend_until = now() + $3\n WHERE id = $4\n RETURNING id\n ) UPDATE v2_job_status SET flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', flow_status->>'step'::TEXT],\n $1\n ) WHERE id = (SELECT id FROM suspend)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Int4", - "Interval", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "c202f6fbae6a727f88f3ac692985c70e6ebc68e4a16d02e4e36b79f3cfb1c661" -} diff --git a/backend/.sqlx/query-c24d63fb137b805f1e674261681d15e6405de27d40b799b0a65b31fd41bcc625.json b/backend/.sqlx/query-c24d63fb137b805f1e674261681d15e6405de27d40b799b0a65b31fd41bcc625.json deleted file mode 100644 index d30c48c5ca..0000000000 --- a/backend/.sqlx/query-c24d63fb137b805f1e674261681d15e6405de27d40b799b0a65b31fd41bcc625.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "VACUUM (skip_locked) queue", - "describe": { - "columns": [], - "parameters": { - "Left": [] - }, - "nullable": [] - }, - "hash": "c24d63fb137b805f1e674261681d15e6405de27d40b799b0a65b31fd41bcc625" -} diff --git a/backend/.sqlx/query-c2925a28212265bd9ac8e2d498b3588fc040c7a29ae6d9bab1f05af0b535e2ac.json b/backend/.sqlx/query-c2925a28212265bd9ac8e2d498b3588fc040c7a29ae6d9bab1f05af0b535e2ac.json new file mode 100644 index 0000000000..eec208dbbb --- /dev/null +++ b/backend/.sqlx/query-c2925a28212265bd9ac8e2d498b3588fc040c7a29ae6d9bab1f05af0b535e2ac.json @@ -0,0 +1,144 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n subscription_mode AS \"subscription_mode: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "gcp_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "subscription_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "topic_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "delivery_type: _", + "type_info": { + "Custom": { + "name": "delivery_mode", + "kind": { + "Enum": [ + "push", + "pull" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "delivery_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 6, + "name": "subscription_mode: _", + "type_info": { + "Custom": { + "name": "gcp_subscription_mode", + "kind": { + "Enum": [ + "create_update", + "existing" + ] + } + } + } + }, + { + "ordinal": 7, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "enabled", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false + ] + }, + "hash": "c2925a28212265bd9ac8e2d498b3588fc040c7a29ae6d9bab1f05af0b535e2ac" +} diff --git a/backend/.sqlx/query-c3025cdb6e421e1225d420e8b1efd18d1dd3bb2fac53c1f2df648b61fb7488aa.json b/backend/.sqlx/query-c3025cdb6e421e1225d420e8b1efd18d1dd3bb2fac53c1f2df648b61fb7488aa.json new file mode 100644 index 0000000000..638417aabb --- /dev/null +++ b/backend/.sqlx/query-c3025cdb6e421e1225d420e8b1efd18d1dd3bb2fac53c1f2df648b61fb7488aa.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE worker_ping SET \nping_at = now(), \njobs_executed = 1, \ncurrent_job_id = $1, \ncurrent_job_workspace_id = 'admins' \nWHERE worker = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + }, + "nullable": [] + }, + "hash": "c3025cdb6e421e1225d420e8b1efd18d1dd3bb2fac53c1f2df648b61fb7488aa" +} diff --git a/backend/.sqlx/query-c353aa9abe673749f0836fd16894d91ba89aab2318d85621ad2868017adfb48a.json b/backend/.sqlx/query-c353aa9abe673749f0836fd16894d91ba89aab2318d85621ad2868017adfb48a.json new file mode 100644 index 0000000000..5a67ebda84 --- /dev/null +++ b/backend/.sqlx/query-c353aa9abe673749f0836fd16894d91ba89aab2318d85621ad2868017adfb48a.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config\n SET \n path = $1\n WHERE \n path = $2 \n AND workspace_id = $3 \n AND is_flow = $4\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "c353aa9abe673749f0836fd16894d91ba89aab2318d85621ad2868017adfb48a" +} diff --git a/backend/.sqlx/query-c3b5abbf2c9079d597a55f7c63bc83b8b4da98bda204a40f045a62172cfb4ebb.json b/backend/.sqlx/query-c3b5abbf2c9079d597a55f7c63bc83b8b4da98bda204a40f045a62172cfb4ebb.json deleted file mode 100644 index b545c48599..0000000000 --- a/backend/.sqlx/query-c3b5abbf2c9079d597a55f7c63bc83b8b4da98bda204a40f045a62172cfb4ebb.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT tag as \"tag!\", COUNT(*) as \"count!\"\n FROM completed_job\n WHERE started_at > NOW() - make_interval(secs => $1) AND ($2::text IS NULL OR workspace_id = $2)\n GROUP BY tag\n ORDER BY \"count!\" DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "tag!", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "count!", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Float8", - "Text" - ] - }, - "nullable": [ - false, - null - ] - }, - "hash": "c3b5abbf2c9079d597a55f7c63bc83b8b4da98bda204a40f045a62172cfb4ebb" -} diff --git a/backend/.sqlx/query-c458012c39f1327fb2bfd6b087ee6dbe1380eca532f50bab0cb89a91d42131cf.json b/backend/.sqlx/query-c458012c39f1327fb2bfd6b087ee6dbe1380eca532f50bab0cb89a91d42131cf.json new file mode 100644 index 0000000000..86f7f8605e --- /dev/null +++ b/backend/.sqlx/query-c458012c39f1327fb2bfd6b087ee6dbe1380eca532f50bab0cb89a91d42131cf.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "ALTER TABLE v2_job ENABLE ROW LEVEL SECURITY", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "c458012c39f1327fb2bfd6b087ee6dbe1380eca532f50bab0cb89a91d42131cf" +} diff --git a/backend/.sqlx/query-c481e5d63ebf1aa537cc4ce4e84f9a71af5996bc76f328b3ba1cf68a71880462.json b/backend/.sqlx/query-c481e5d63ebf1aa537cc4ce4e84f9a71af5996bc76f328b3ba1cf68a71880462.json new file mode 100644 index 0000000000..b3d20cccea --- /dev/null +++ b/backend/.sqlx/query-c481e5d63ebf1aa537cc4ce4e84f9a71af5996bc76f328b3ba1cf68a71880462.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "create index concurrently if not exists ix_job_root_job_index_by_path_2 ON v2_job (workspace_id, runnable_path, created_at desc) WHERE parent_job IS NULL", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "c481e5d63ebf1aa537cc4ce4e84f9a71af5996bc76f328b3ba1cf68a71880462" +} diff --git a/backend/.sqlx/query-c5063e79aafa70b974276f5bea43ad71135d44b2e84c9efac43a6678f0cf9a18.json b/backend/.sqlx/query-c5063e79aafa70b974276f5bea43ad71135d44b2e84c9efac43a6678f0cf9a18.json new file mode 100644 index 0000000000..9ab57a7e3a --- /dev/null +++ b/backend/.sqlx/query-c5063e79aafa70b974276f5bea43ad71135d44b2e84c9efac43a6678f0cf9a18.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n mqtt_trigger \n SET \n enabled = FALSE, \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "c5063e79aafa70b974276f5bea43ad71135d44b2e84c9efac43a6678f0cf9a18" +} diff --git a/backend/.sqlx/query-c5259e37703c3e48104438bad6e1f3615f4439c090a75e6fde03702a21589b25.json b/backend/.sqlx/query-c5259e37703c3e48104438bad6e1f3615f4439c090a75e6fde03702a21589b25.json deleted file mode 100644 index ee362d8845..0000000000 --- a/backend/.sqlx/query-c5259e37703c3e48104438bad6e1f3615f4439c090a75e6fde03702a21589b25.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id AS \"id!\" FROM v2_job WHERE parent_job = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "c5259e37703c3e48104438bad6e1f3615f4439c090a75e6fde03702a21589b25" -} diff --git a/backend/.sqlx/query-f367a1c8f80dd414dcbcd949374eeb5770796f00b5b3d547163bcfdaed65d8ae.json b/backend/.sqlx/query-c53e1c7133c8ae187656eef5999509fae17fb0ba43e084327accbb5b24c3dfbd.json similarity index 53% rename from backend/.sqlx/query-f367a1c8f80dd414dcbcd949374eeb5770796f00b5b3d547163bcfdaed65d8ae.json rename to backend/.sqlx/query-c53e1c7133c8ae187656eef5999509fae17fb0ba43e084327accbb5b24c3dfbd.json index 776443782e..e343b8b863 100644 --- a/backend/.sqlx/query-f367a1c8f80dd414dcbcd949374eeb5770796f00b5b3d547163bcfdaed65d8ae.json +++ b/backend/.sqlx/query-c53e1c7133c8ae187656eef5999509fae17fb0ba43e084327accbb5b24c3dfbd.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO outstanding_wait_time(job_id, self_wait_time_ms) VALUES ($1, $2)\n ON CONFLICT (job_id) DO UPDATE SET self_wait_time_ms = EXCLUDED.self_wait_time_ms", + "query": "INSERT INTO outstanding_wait_time(job_id, self_wait_time_ms) VALUES ($1, $2)\n ON CONFLICT (job_id) DO UPDATE SET self_wait_time_ms = EXCLUDED.self_wait_time_ms", "describe": { "columns": [], "parameters": { @@ -11,5 +11,5 @@ }, "nullable": [] }, - "hash": "f367a1c8f80dd414dcbcd949374eeb5770796f00b5b3d547163bcfdaed65d8ae" + "hash": "c53e1c7133c8ae187656eef5999509fae17fb0ba43e084327accbb5b24c3dfbd" } diff --git a/backend/.sqlx/query-3ecb25b05d6c14b499f9b00af42ae74134728899f6b59c68b246979bc5143e30.json b/backend/.sqlx/query-c67b5ad3869bbeb53cc06ca7fb2f1000d512c95f3b74ba9aafee684670aff5f4.json similarity index 51% rename from backend/.sqlx/query-3ecb25b05d6c14b499f9b00af42ae74134728899f6b59c68b246979bc5143e30.json rename to backend/.sqlx/query-c67b5ad3869bbeb53cc06ca7fb2f1000d512c95f3b74ba9aafee684670aff5f4.json index a652b56bad..19476e7ac0 100644 --- a/backend/.sqlx/query-3ecb25b05d6c14b499f9b00af42ae74134728899f6b59c68b246979bc5143e30.json +++ b/backend/.sqlx/query-c67b5ad3869bbeb53cc06ca7fb2f1000d512c95f3b74ba9aafee684670aff5f4.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT COUNT(*) as count, \n MIN(scheduled_for) as oldest_job\n FROM queue \n WHERE tag = $1 \n AND scheduled_for <= NOW() - $2::interval \n AND running = false\n ", + "query": "\n SELECT COUNT(*) as count,\n MIN(scheduled_for) as oldest_job\n FROM v2_job_queue\n WHERE tag = $1\n AND scheduled_for <= NOW() - $2::interval\n AND running = false\n ", "describe": { "columns": [ { @@ -25,5 +25,5 @@ null ] }, - "hash": "3ecb25b05d6c14b499f9b00af42ae74134728899f6b59c68b246979bc5143e30" + "hash": "c67b5ad3869bbeb53cc06ca7fb2f1000d512c95f3b74ba9aafee684670aff5f4" } diff --git a/backend/.sqlx/query-c71e12ec9d0054dd5605a4ea2ef77fa54ce686da9c93790420a91a0735589ac7.json b/backend/.sqlx/query-c71e12ec9d0054dd5605a4ea2ef77fa54ce686da9c93790420a91a0735589ac7.json deleted file mode 100644 index ecc62b1682..0000000000 --- a/backend/.sqlx/query-c71e12ec9d0054dd5605a4ea2ef77fa54ce686da9c93790420a91a0735589ac7.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(JSONB_SET(flow_status, ARRAY['retry'], $1), ARRAY['modules', $3::TEXT, 'failed_retries'], $4)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "c71e12ec9d0054dd5605a4ea2ef77fa54ce686da9c93790420a91a0735589ac7" -} diff --git a/backend/.sqlx/query-c732dcb4df5877560a3e75e0032179140cbe442f0b80ce1172d80903dd8a14fa.json b/backend/.sqlx/query-c732dcb4df5877560a3e75e0032179140cbe442f0b80ce1172d80903dd8a14fa.json deleted file mode 100644 index d19fc9d699..0000000000 --- a/backend/.sqlx/query-c732dcb4df5877560a3e75e0032179140cbe442f0b80ce1172d80903dd8a14fa.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET canceled = true WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "c732dcb4df5877560a3e75e0032179140cbe442f0b80ce1172d80903dd8a14fa" -} diff --git a/backend/.sqlx/query-c75761c9aa900391251596771782039809bc01e4a0aa05701d6607b06769caa5.json b/backend/.sqlx/query-c75761c9aa900391251596771782039809bc01e4a0aa05701d6607b06769caa5.json deleted file mode 100644 index abe61cf520..0000000000 --- a/backend/.sqlx/query-c75761c9aa900391251596771782039809bc01e4a0aa05701d6607b06769caa5.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT id, flow_status, suspend, script_path\n FROM queue\n WHERE id = ( SELECT parent_job FROM queue WHERE id = $1 UNION ALL SELECT parent_job FROM completed_job WHERE id = $1)\n FOR UPDATE\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "flow_status", - "type_info": "Jsonb" - }, - { - "ordinal": 2, - "name": "suspend", - "type_info": "Int4" - }, - { - "ordinal": 3, - "name": "script_path", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false, - true, - false, - true - ] - }, - "hash": "c75761c9aa900391251596771782039809bc01e4a0aa05701d6607b06769caa5" -} diff --git a/backend/.sqlx/query-0355b53b1d45955ca56b2829372ce9c656d7f0ad7b8d0709161047f0d8cdc4f4.json b/backend/.sqlx/query-c92cc71e6d10c41368f7aa75b0799c2e1e9ca0ed33077ade8d7560e7cc21fa06.json similarity index 61% rename from backend/.sqlx/query-0355b53b1d45955ca56b2829372ce9c656d7f0ad7b8d0709161047f0d8cdc4f4.json rename to backend/.sqlx/query-c92cc71e6d10c41368f7aa75b0799c2e1e9ca0ed33077ade8d7560e7cc21fa06.json index 2f2cb27400..8a4b957c2a 100644 --- a/backend/.sqlx/query-0355b53b1d45955ca56b2829372ce9c656d7f0ad7b8d0709161047f0d8cdc4f4.json +++ b/backend/.sqlx/query-c92cc71e6d10c41368f7aa75b0799c2e1e9ca0ed33077ade8d7560e7cc21fa06.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "DELETE FROM queue WHERE workspace_id = $1 AND id = $2 RETURNING 1", + "query": "DELETE FROM v2_job_queue WHERE id = $1 RETURNING 1", "describe": { "columns": [ { @@ -11,7 +11,6 @@ ], "parameters": { "Left": [ - "Text", "Uuid" ] }, @@ -19,5 +18,5 @@ null ] }, - "hash": "0355b53b1d45955ca56b2829372ce9c656d7f0ad7b8d0709161047f0d8cdc4f4" + "hash": "c92cc71e6d10c41368f7aa75b0799c2e1e9ca0ed33077ade8d7560e7cc21fa06" } diff --git a/backend/.sqlx/query-ca3ba808e020c8c7a35eaef842b20cfeee64fd47ded72fce55cc75e0bbb291a8.json b/backend/.sqlx/query-ca3ba808e020c8c7a35eaef842b20cfeee64fd47ded72fce55cc75e0bbb291a8.json deleted file mode 100644 index 6d44fb2840..0000000000 --- a/backend/.sqlx/query-ca3ba808e020c8c7a35eaef842b20cfeee64fd47ded72fce55cc75e0bbb291a8.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO usage (id, is_workspace, month_, usage) \n VALUES ($1, TRUE, EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date), $2) \n ON CONFLICT (id, is_workspace, month_) DO UPDATE SET usage = usage.usage + $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Int4" - ] - }, - "nullable": [] - }, - "hash": "ca3ba808e020c8c7a35eaef842b20cfeee64fd47ded72fce55cc75e0bbb291a8" -} diff --git a/backend/.sqlx/query-cb0eecb1130617f2132b8ec74a401ac3fb932bbeafe3aecc0b14465cce7e192d.json b/backend/.sqlx/query-cb0eecb1130617f2132b8ec74a401ac3fb932bbeafe3aecc0b14465cce7e192d.json deleted file mode 100644 index 128863559f..0000000000 --- a/backend/.sqlx/query-cb0eecb1130617f2132b8ec74a401ac3fb932bbeafe3aecc0b14465cce7e192d.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT 1 FROM queue WHERE id = $1 UNION ALL select 1 FROM completed_job WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "cb0eecb1130617f2132b8ec74a401ac3fb932bbeafe3aecc0b14465cce7e192d" -} diff --git a/backend/.sqlx/query-cbb93da0b7719a27d2ae1ec0f653322cae965dc5d8ebc98f69d5922fa1192561.json b/backend/.sqlx/query-cbb93da0b7719a27d2ae1ec0f653322cae965dc5d8ebc98f69d5922fa1192561.json new file mode 100644 index 0000000000..684e857290 --- /dev/null +++ b/backend/.sqlx/query-cbb93da0b7719a27d2ae1ec0f653322cae965dc5d8ebc98f69d5922fa1192561.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT COALESCE(\n (SELECT COUNT(*) FROM jsonb_object_keys(job_uuids)),\n 0\n )\n FROM concurrency_counter \n WHERE concurrency_id = $1\n FOR UPDATE", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "coalesce", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "cbb93da0b7719a27d2ae1ec0f653322cae965dc5d8ebc98f69d5922fa1192561" +} diff --git a/backend/.sqlx/query-cbf5a2b315a7e89689ee86acf452f40fd82605c456aff479890462f4d0202316.json b/backend/.sqlx/query-cbf5a2b315a7e89689ee86acf452f40fd82605c456aff479890462f4d0202316.json deleted file mode 100644 index 889f2433d7..0000000000 --- a/backend/.sqlx/query-cbf5a2b315a7e89689ee86acf452f40fd82605c456aff479890462f4d0202316.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET workspace_id = $1 WHERE workspace_id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Text" - ] - }, - "nullable": [] - }, - "hash": "cbf5a2b315a7e89689ee86acf452f40fd82605c456aff479890462f4d0202316" -} diff --git a/backend/.sqlx/query-ccb7aea162fa8781675d547fb82f2996dede4de49c5bd9cca2928209dc40b8f1.json b/backend/.sqlx/query-ccb7aea162fa8781675d547fb82f2996dede4de49c5bd9cca2928209dc40b8f1.json deleted file mode 100644 index f4bc3f2174..0000000000 --- a/backend/.sqlx/query-ccb7aea162fa8781675d547fb82f2996dede4de49c5bd9cca2928209dc40b8f1.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE http_trigger \n SET route_path = $1, route_path_key = $2, script_path = $3, path = $4, is_flow = $5, http_method = $6, static_asset_config = $7, edited_by = $8, email = $9, is_async = $10, requires_auth = $11, edited_at = now(), is_static_website = $12\n WHERE workspace_id = $13 AND path = $14", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Bool", - { - "Custom": { - "name": "http_method", - "kind": { - "Enum": [ - "get", - "post", - "put", - "delete", - "patch" - ] - } - } - }, - "Jsonb", - "Varchar", - "Varchar", - "Bool", - "Bool", - "Bool", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "ccb7aea162fa8781675d547fb82f2996dede4de49c5bd9cca2928209dc40b8f1" -} diff --git a/backend/.sqlx/query-15557c0acea71cee03f42516553fb4f5709e0e1a02a0187e88fa5d9e94ffb91a.json b/backend/.sqlx/query-cccdcb7fe7968eadfc04d8957a8e98b2f2d92a6d7f687a9dd5a70edb3d5a63e6.json similarity index 50% rename from backend/.sqlx/query-15557c0acea71cee03f42516553fb4f5709e0e1a02a0187e88fa5d9e94ffb91a.json rename to backend/.sqlx/query-cccdcb7fe7968eadfc04d8957a8e98b2f2d92a6d7f687a9dd5a70edb3d5a63e6.json index dc8718bda6..412b64c480 100644 --- a/backend/.sqlx/query-15557c0acea71cee03f42516553fb4f5709e0e1a02a0187e88fa5d9e94ffb91a.json +++ b/backend/.sqlx/query-cccdcb7fe7968eadfc04d8957a8e98b2f2d92a6d7f687a9dd5a70edb3d5a63e6.json @@ -1,27 +1,21 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO queue\n (workspace_id, id, running, parent_job, created_by, permissioned_as, scheduled_for, \n script_hash, script_path, raw_code, raw_lock, args, job_kind, schedule_path, raw_flow, flow_status, is_flow_step, language, started_at, same_worker, pre_run_error, email, visible_to_owner, root_job, tag, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id, cache_ttl, priority, last_ping)\n VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, now()), $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, CASE WHEN $3 THEN now() END, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, NULL) RETURNING id AS \"id!\"", + "query": "WITH inserted_job AS (\n INSERT INTO v2_job (id, workspace_id, raw_code, raw_lock, raw_flow, tag, parent_job,\n created_by, permissioned_as, runnable_id, runnable_path, args, kind, trigger,\n script_lang, same_worker, pre_run_error, permissioned_as_email, visible_to_owner,\n flow_innermost_root_job, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id,\n cache_ttl, priority, trigger_kind, script_entrypoint_override, preprocessed)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18,\n $19, $20, $21, $22, $23, $24, $25, $26,\n CASE WHEN $14::VARCHAR IS NOT NULL THEN 'schedule'::job_trigger_kind END,\n ($12::JSONB)->>'_ENTRYPOINT_OVERRIDE', $27)\n ),\n inserted_runtime AS (\n INSERT INTO v2_job_runtime (id, ping) VALUES ($1, null)\n ),\n inserted_job_perms AS (\n INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) \n values ($1, $32, $33, $34, $35, $36, $37, $2) \n ON CONFLICT (job_id) DO UPDATE SET email = $32, username = $33, is_admin = $34, is_operator = $35, folders = $36, groups = $37, workspace_id = $2\n )\n INSERT INTO v2_job_queue\n (workspace_id, id, running, scheduled_for, started_at, tag, priority)\n VALUES ($2, $1, $28, COALESCE($29, now()), CASE WHEN $27 THEN now() END, $30, $31)", "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - } - ], + "columns": [], "parameters": { "Left": [ - "Varchar", "Uuid", - "Bool", + "Varchar", + "Text", + "Text", + "Jsonb", + "Varchar", "Uuid", "Varchar", "Varchar", - "Timestamptz", "Int8", "Varchar", - "Text", - "Text", "Jsonb", { "Custom": { @@ -51,9 +45,6 @@ } }, "Varchar", - "Jsonb", - "Jsonb", - "Bool", { "Custom": { "name": "script_lang", @@ -77,7 +68,9 @@ "rust", "ansible", "csharp", - "oracledb" + "oracledb", + "nu", + "java" ] } } @@ -87,18 +80,26 @@ "Varchar", "Bool", "Uuid", - "Varchar", "Int4", "Int4", "Int4", "Varchar", "Int4", - "Int2" + "Int2", + "Bool", + "Bool", + "Timestamptz", + "Varchar", + "Int2", + "Varchar", + "Varchar", + "Bool", + "Bool", + "JsonbArray", + "TextArray" ] }, - "nullable": [ - true - ] + "nullable": [] }, - "hash": "15557c0acea71cee03f42516553fb4f5709e0e1a02a0187e88fa5d9e94ffb91a" + "hash": "cccdcb7fe7968eadfc04d8957a8e98b2f2d92a6d7f687a9dd5a70edb3d5a63e6" } diff --git a/backend/.sqlx/query-cd5f02cf10cbf92dd1df53a54f2110efa11a7731ad0f0e5509f55efabdf535cd.json b/backend/.sqlx/query-cd5f02cf10cbf92dd1df53a54f2110efa11a7731ad0f0e5509f55efabdf535cd.json deleted file mode 100644 index f5eb13f1fd..0000000000 --- a/backend/.sqlx/query-cd5f02cf10cbf92dd1df53a54f2110efa11a7731ad0f0e5509f55efabdf535cd.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT preprocessed FROM v2_job WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "preprocessed", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - true - ] - }, - "hash": "cd5f02cf10cbf92dd1df53a54f2110efa11a7731ad0f0e5509f55efabdf535cd" -} diff --git a/backend/.sqlx/query-cd79f4dc6a426f1c4c19c2a86dc877a6d5bc5771b27d9e208a219a63add200f3.json b/backend/.sqlx/query-cd79f4dc6a426f1c4c19c2a86dc877a6d5bc5771b27d9e208a219a63add200f3.json new file mode 100644 index 0000000000..279ff511bd --- /dev/null +++ b/backend/.sqlx/query-cd79f4dc6a426f1c4c19c2a86dc877a6d5bc5771b27d9e208a219a63add200f3.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['failure_module'], $1),\n ARRAY['step'],\n $2\n )\n WHERE id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "cd79f4dc6a426f1c4c19c2a86dc877a6d5bc5771b27d9e208a219a63add200f3" +} diff --git a/backend/.sqlx/query-cf2a6ad6471a40b6298775cda9300aeecdd75503bed59d80cd62091d1642d1ec.json b/backend/.sqlx/query-cf2a6ad6471a40b6298775cda9300aeecdd75503bed59d80cd62091d1642d1ec.json new file mode 100644 index 0000000000..4e17af3ce2 --- /dev/null +++ b/backend/.sqlx/query-cf2a6ad6471a40b6298775cda9300aeecdd75503bed59d80cd62091d1642d1ec.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT s.hash as hash, dm.deployment_msg as deployment_msg \n FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash\n WHERE s.workspace_id = $1 AND s.path = $2\n ORDER by s.created_at DESC LIMIT 1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "hash", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "deployment_msg", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + true + ] + }, + "hash": "cf2a6ad6471a40b6298775cda9300aeecdd75503bed59d80cd62091d1642d1ec" +} diff --git a/backend/.sqlx/query-cf80f068b6a8906939f7ea0f1a8311fdabf78d6d5bd12e71070b1dae24df2352.json b/backend/.sqlx/query-cf80f068b6a8906939f7ea0f1a8311fdabf78d6d5bd12e71070b1dae24df2352.json deleted file mode 100644 index 7373aec488..0000000000 --- a/backend/.sqlx/query-cf80f068b6a8906939f7ea0f1a8311fdabf78d6d5bd12e71070b1dae24df2352.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'approvers'], $2)\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "cf80f068b6a8906939f7ea0f1a8311fdabf78d6d5bd12e71070b1dae24df2352" -} diff --git a/backend/.sqlx/query-cff764601318bfecb8e0f15b64ff9f430d7b1efe8ba863cbcc4a49bab4951794.json b/backend/.sqlx/query-cff764601318bfecb8e0f15b64ff9f430d7b1efe8ba863cbcc4a49bab4951794.json new file mode 100644 index 0000000000..a9e508d01d --- /dev/null +++ b/backend/.sqlx/query-cff764601318bfecb8e0f15b64ff9f430d7b1efe8ba863cbcc4a49bab4951794.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n last_server_ping = now(), \n error = $1 \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'mqtt' AND \n server_id = $5 AND \n last_client_ping > NOW() - INTERVAL '10 seconds' \n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Bool", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "cff764601318bfecb8e0f15b64ff9f430d7b1efe8ba863cbcc4a49bab4951794" +} diff --git a/backend/.sqlx/query-d0bd4f43cb1feabe7ee9e017a0d6af22d25369a33247cec6e9f2d1a5eb851412.json b/backend/.sqlx/query-d0bd4f43cb1feabe7ee9e017a0d6af22d25369a33247cec6e9f2d1a5eb851412.json new file mode 100644 index 0000000000..4e3ee705a3 --- /dev/null +++ b/backend/.sqlx/query-d0bd4f43cb1feabe7ee9e017a0d6af22d25369a33247cec6e9f2d1a5eb851412.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE workspace_settings\n SET git_app_installations = (\n SELECT jsonb_agg(updated)\n FROM (\n -- For each element, if the account_id matches, replace it\n SELECT\n CASE\n WHEN elem->>'account_id' = ($1::jsonb)->>'account_id' THEN $1::jsonb\n ELSE elem\n END AS updated\n FROM jsonb_array_elements(git_app_installations) AS elem\n UNION ALL\n -- Append new installation if no element with the same account_id exists\n SELECT $1::jsonb\n WHERE NOT EXISTS (\n SELECT 1\n FROM jsonb_array_elements(git_app_installations) AS elem\n WHERE elem->>'account_id' = ($1::jsonb)->>'account_id'\n )\n ) sub\n )\n WHERE workspace_id = $2;\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Text" + ] + }, + "nullable": [] + }, + "hash": "d0bd4f43cb1feabe7ee9e017a0d6af22d25369a33247cec6e9f2d1a5eb851412" +} diff --git a/backend/.sqlx/query-bc00efe12901bc7b49c7892d2d9da675e0eaf5e6cc013149740bc778987a1006.json b/backend/.sqlx/query-d15f02f090b8d1a7e816fe11b2e0867540ab6bb02ac6bf82decc220dce0ab048.json similarity index 84% rename from backend/.sqlx/query-bc00efe12901bc7b49c7892d2d9da675e0eaf5e6cc013149740bc778987a1006.json rename to backend/.sqlx/query-d15f02f090b8d1a7e816fe11b2e0867540ab6bb02ac6bf82decc220dce0ab048.json index 67611f00dc..0a2b8c3a0c 100644 --- a/backend/.sqlx/query-bc00efe12901bc7b49c7892d2d9da675e0eaf5e6cc013149740bc778987a1006.json +++ b/backend/.sqlx/query-d15f02f090b8d1a7e816fe11b2e0867540ab6bb02ac6bf82decc220dce0ab048.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32)", + "query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33)", "describe": { "columns": [], "parameters": { @@ -40,7 +40,9 @@ "rust", "ansible", "csharp", - "oracledb" + "oracledb", + "nu", + "java" ] } } @@ -77,10 +79,11 @@ "Bool", "Varchar", "Bool", - "Text" + "Text", + "Bool" ] }, "nullable": [] }, - "hash": "bc00efe12901bc7b49c7892d2d9da675e0eaf5e6cc013149740bc778987a1006" + "hash": "d15f02f090b8d1a7e816fe11b2e0867540ab6bb02ac6bf82decc220dce0ab048" } diff --git a/backend/.sqlx/query-dc9a906d6c6156a84fccf4e3a2a7c08d8ed4984409b669162f0a1fc1aa48e188.json b/backend/.sqlx/query-d1876c46c0b1aba168efaebd3a056e999c400998eb699d862d718e7ab4c1f427.json similarity index 50% rename from backend/.sqlx/query-dc9a906d6c6156a84fccf4e3a2a7c08d8ed4984409b669162f0a1fc1aa48e188.json rename to backend/.sqlx/query-d1876c46c0b1aba168efaebd3a056e999c400998eb699d862d718e7ab4c1f427.json index 03b0c561e1..841499d3bf 100644 --- a/backend/.sqlx/query-dc9a906d6c6156a84fccf4e3a2a7c08d8ed4984409b669162f0a1fc1aa48e188.json +++ b/backend/.sqlx/query-d1876c46c0b1aba168efaebd3a056e999c400998eb699d862d718e7ab4c1f427.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "DELETE FROM http_trigger WHERE workspace_id = $1 AND path = $2", + "query": "UPDATE flow SET lock_error_logs = NULL WHERE path = $1 AND workspace_id = $2", "describe": { "columns": [], "parameters": { @@ -11,5 +11,5 @@ }, "nullable": [] }, - "hash": "dc9a906d6c6156a84fccf4e3a2a7c08d8ed4984409b669162f0a1fc1aa48e188" + "hash": "d1876c46c0b1aba168efaebd3a056e999c400998eb699d862d718e7ab4c1f427" } diff --git a/backend/.sqlx/query-d1dcc7fc8a1e1bc4dad263ec5163a94fca9dd95cc3b26b33611eab9d2a261141.json b/backend/.sqlx/query-d1dcc7fc8a1e1bc4dad263ec5163a94fca9dd95cc3b26b33611eab9d2a261141.json new file mode 100644 index 0000000000..4a0592701c --- /dev/null +++ b/backend/.sqlx/query-d1dcc7fc8a1e1bc4dad263ec5163a94fca9dd95cc3b26b33611eab9d2a261141.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT args as \"args: sqlx::types::Json>\"\n FROM v2_job\n WHERE id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "args: sqlx::types::Json>", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + true + ] + }, + "hash": "d1dcc7fc8a1e1bc4dad263ec5163a94fca9dd95cc3b26b33611eab9d2a261141" +} diff --git a/backend/.sqlx/query-d1fdfabd4f3bbe93cf9667e8e76bd8f537e68fabceb094fe535693fbd146eff2.json b/backend/.sqlx/query-d1fdfabd4f3bbe93cf9667e8e76bd8f537e68fabceb094fe535693fbd146eff2.json new file mode 100644 index 0000000000..5ebf571855 --- /dev/null +++ b/backend/.sqlx/query-d1fdfabd4f3bbe93cf9667e8e76bd8f537e68fabceb094fe535693fbd146eff2.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COALESCE(\n (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),\n (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.path = jb.runnable_path AND jb.kind = 'flow')\n ) FROM v2_job jb\n WHERE jb.id = $1 AND jb.workspace_id = $2\n GROUP BY jb.kind, jb.runnable_path", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "coalesce", + "type_info": "Json" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "d1fdfabd4f3bbe93cf9667e8e76bd8f537e68fabceb094fe535693fbd146eff2" +} diff --git a/backend/.sqlx/query-d25c58d2722ad3dcd91101ce6f66e1d802dd5d82e1cd5f5ed3a15cbc75eb6745.json b/backend/.sqlx/query-d25c58d2722ad3dcd91101ce6f66e1d802dd5d82e1cd5f5ed3a15cbc75eb6745.json deleted file mode 100644 index 2872c1655b..0000000000 --- a/backend/.sqlx/query-d25c58d2722ad3dcd91101ce6f66e1d802dd5d82e1cd5f5ed3a15cbc75eb6745.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM v2_job_queue WHERE workspace_id = $1 AND id = $2 RETURNING 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Text", - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "d25c58d2722ad3dcd91101ce6f66e1d802dd5d82e1cd5f5ed3a15cbc75eb6745" -} diff --git a/backend/.sqlx/query-d585aa6301c41308b02a1f0fbf068221e732e48dfa6e34d5b025adbbdcbb03e0.json b/backend/.sqlx/query-d585aa6301c41308b02a1f0fbf068221e732e48dfa6e34d5b025adbbdcbb03e0.json new file mode 100644 index 0000000000..72169ec94b --- /dev/null +++ b/backend/.sqlx/query-d585aa6301c41308b02a1f0fbf068221e732e48dfa6e34d5b025adbbdcbb03e0.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "create index concurrently if not exists ix_v2_job_workspace_id_created_at ON v2_job (workspace_id, created_at DESC) where kind in ('script', 'flow', 'singlescriptflow') AND parent_job IS NULL", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "d585aa6301c41308b02a1f0fbf068221e732e48dfa6e34d5b025adbbdcbb03e0" +} diff --git a/backend/.sqlx/query-d6c8f4e49cf7b6db5c979c88e02bd41c7b823186ac056a0a676da85dc5d9a027.json b/backend/.sqlx/query-d6c8f4e49cf7b6db5c979c88e02bd41c7b823186ac056a0a676da85dc5d9a027.json deleted file mode 100644 index d090c25a37..0000000000 --- a/backend/.sqlx/query-d6c8f4e49cf7b6db5c979c88e02bd41c7b823186ac056a0a676da85dc5d9a027.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id AS \"id!\" FROM v2_as_queue WHERE schedule_path = $1 AND workspace_id = $2 AND id != $3 AND running = true", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Uuid" - ] - }, - "nullable": [ - true - ] - }, - "hash": "d6c8f4e49cf7b6db5c979c88e02bd41c7b823186ac056a0a676da85dc5d9a027" -} diff --git a/backend/.sqlx/query-d6db1103fee4bad6831656d77a28254c9a832b4f660ec755f4fa14f6f7bb3353.json b/backend/.sqlx/query-d6db1103fee4bad6831656d77a28254c9a832b4f660ec755f4fa14f6f7bb3353.json new file mode 100644 index 0000000000..09f24968f3 --- /dev/null +++ b/backend/.sqlx/query-d6db1103fee4bad6831656d77a28254c9a832b4f660ec755f4fa14f6f7bb3353.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4),\n ARRAY['modules', $1::TEXT, 'iterator', 'index'],\n ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "int4", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Int4", + "Uuid", + "Text", + "Jsonb" + ] + }, + "nullable": [ + null + ] + }, + "hash": "d6db1103fee4bad6831656d77a28254c9a832b4f660ec755f4fa14f6f7bb3353" +} diff --git a/backend/.sqlx/query-d74d3511d394c7ab2931c413e5ae87df1799a0ea64822449350abab02ab570be.json b/backend/.sqlx/query-d74d3511d394c7ab2931c413e5ae87df1799a0ea64822449350abab02ab570be.json deleted file mode 100644 index e4f5aed798..0000000000 --- a/backend/.sqlx/query-d74d3511d394c7ab2931c413e5ae87df1799a0ea64822449350abab02ab570be.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['approval_conditions'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "d74d3511d394c7ab2931c413e5ae87df1799a0ea64822449350abab02ab570be" -} diff --git a/backend/.sqlx/query-d7ce28c7cbd4974c72969858659a2a5c7448c919ae522e91332fa9a6212f5ddf.json b/backend/.sqlx/query-d7ce28c7cbd4974c72969858659a2a5c7448c919ae522e91332fa9a6212f5ddf.json deleted file mode 100644 index d1b5b0030c..0000000000 --- a/backend/.sqlx/query-d7ce28c7cbd4974c72969858659a2a5c7448c919ae522e91332fa9a6212f5ddf.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['retry'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "d7ce28c7cbd4974c72969858659a2a5c7448c919ae522e91332fa9a6212f5ddf" -} diff --git a/backend/.sqlx/query-d7f1e2920aec0f4eab9238d01370465945acdfa779f16b99cdc1a6b7ef84943e.json b/backend/.sqlx/query-d7f1e2920aec0f4eab9238d01370465945acdfa779f16b99cdc1a6b7ef84943e.json deleted file mode 100644 index dbc893740b..0000000000 --- a/backend/.sqlx/query-d7f1e2920aec0f4eab9238d01370465945acdfa779f16b99cdc1a6b7ef84943e.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT id AS \"id!\", flow_status, suspend AS \"suspend!\", script_path\n FROM queue\n WHERE id = ( SELECT parent_job FROM queue WHERE id = $1 UNION ALL SELECT parent_job FROM completed_job WHERE id = $1)\n FOR UPDATE\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "flow_status", - "type_info": "Jsonb" - }, - { - "ordinal": 2, - "name": "suspend!", - "type_info": "Int4" - }, - { - "ordinal": 3, - "name": "script_path", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - true, - true, - true, - true - ] - }, - "hash": "d7f1e2920aec0f4eab9238d01370465945acdfa779f16b99cdc1a6b7ef84943e" -} diff --git a/backend/.sqlx/query-d91a447f3abcd39559d614ab7d423d0287bd34e463967fbaf0a3d590b59c9865.json b/backend/.sqlx/query-d91a447f3abcd39559d614ab7d423d0287bd34e463967fbaf0a3d590b59c9865.json deleted file mode 100644 index bce712700f..0000000000 --- a/backend/.sqlx/query-d91a447f3abcd39559d614ab7d423d0287bd34e463967fbaf0a3d590b59c9865.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n result AS \"result: sqlx::types::Json>\", success AS \"success!\",\n language AS \"language: ScriptLang\",\n flow_status AS \"flow_status: sqlx::types::Json>\",\n created_by AS \"created_by!\"\n FROM completed_job\n WHERE id = $1 AND workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "result: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 1, - "name": "success!", - "type_info": "Bool" - }, - { - "ordinal": 2, - "name": "language: ScriptLang", - "type_info": { - "Custom": { - "name": "script_lang", - "kind": { - "Enum": [ - "python3", - "deno", - "go", - "bash", - "postgresql", - "nativets", - "bun", - "mysql", - "bigquery", - "snowflake", - "graphql", - "powershell", - "mssql", - "php", - "bunnative", - "rust", - "ansible", - "csharp", - "oracledb" - ] - } - } - } - }, - { - "ordinal": 3, - "name": "flow_status: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 4, - "name": "created_by!", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "TextArray" - ] - }, - "nullable": [ - true, - true, - true, - true, - true - ] - }, - "hash": "d91a447f3abcd39559d614ab7d423d0287bd34e463967fbaf0a3d590b59c9865" -} diff --git a/backend/.sqlx/query-da9114fc6689ebc78422b3572de1ed44050bd28212540db50fe235463c15a900.json b/backend/.sqlx/query-da9114fc6689ebc78422b3572de1ed44050bd28212540db50fe235463c15a900.json deleted file mode 100644 index 0e259af63c..0000000000 --- a/backend/.sqlx/query-da9114fc6689ebc78422b3572de1ed44050bd28212540db50fe235463c15a900.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n queue.job_kind AS \"job_kind: JobKind\",\n queue.script_hash AS \"script_hash: ScriptHash\",\n queue.raw_flow AS \"raw_flow: sqlx::types::Json>\",\n completed_job.parent_job AS \"parent_job: Uuid\",\n completed_job.created_at AS \"created_at: chrono::NaiveDateTime\",\n completed_job.created_by AS \"created_by!\",\n queue.script_path,\n queue.args AS \"args: sqlx::types::Json>\"\n FROM queue\n JOIN completed_job ON completed_job.parent_job = queue.id\n WHERE completed_job.id = $1 AND completed_job.workspace_id = $2\n LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "job_kind: JobKind", - "type_info": { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - } - }, - { - "ordinal": 1, - "name": "script_hash: ScriptHash", - "type_info": "Int8" - }, - { - "ordinal": 2, - "name": "raw_flow: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 3, - "name": "parent_job: Uuid", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "created_at: chrono::NaiveDateTime", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "created_by!", - "type_info": "Varchar" - }, - { - "ordinal": 6, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "args: sqlx::types::Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - false, - true, - true, - true, - false, - false, - true, - true - ] - }, - "hash": "da9114fc6689ebc78422b3572de1ed44050bd28212540db50fe235463c15a900" -} diff --git a/backend/.sqlx/query-db91141ae55b96a3237e05e3f127386339cb3c7a6f88bb2102498ec5d34f9537.json b/backend/.sqlx/query-db91141ae55b96a3237e05e3f127386339cb3c7a6f88bb2102498ec5d34f9537.json deleted file mode 100644 index cc6474870c..0000000000 --- a/backend/.sqlx/query-db91141ae55b96a3237e05e3f127386339cb3c7a6f88bb2102498ec5d34f9537.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2)\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "db91141ae55b96a3237e05e3f127386339cb3c7a6f88bb2102498ec5d34f9537" -} diff --git a/backend/.sqlx/query-dd06bdc09968add6a7c09f124f1f9b717990371d98e0784e96eb31cfdd17885a.json b/backend/.sqlx/query-dd06bdc09968add6a7c09f124f1f9b717990371d98e0784e96eb31cfdd17885a.json new file mode 100644 index 0000000000..0d2df00658 --- /dev/null +++ b/backend/.sqlx/query-dd06bdc09968add6a7c09f124f1f9b717990371d98e0784e96eb31cfdd17885a.json @@ -0,0 +1,56 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n http_trigger \n SET \n workspaced_route = $1,\n wrap_body = $2,\n raw_string = $3,\n authentication_resource_path = $4,\n script_path = $5, \n path = $6, \n is_flow = $7, \n http_method = $8, \n static_asset_config = $9, \n edited_by = $10, \n email = $11, \n is_async = $12, \n authentication_method = $13, \n edited_at = now(), \n is_static_website = $14\n WHERE \n workspace_id = $15 AND \n path = $16\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bool", + "Bool", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Bool", + { + "Custom": { + "name": "http_method", + "kind": { + "Enum": [ + "get", + "post", + "put", + "delete", + "patch" + ] + } + } + }, + "Jsonb", + "Varchar", + "Varchar", + "Bool", + { + "Custom": { + "name": "authentication_method", + "kind": { + "Enum": [ + "none", + "windmill", + "api_key", + "basic_http", + "custom_script", + "signature" + ] + } + } + }, + "Bool", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "dd06bdc09968add6a7c09f124f1f9b717990371d98e0784e96eb31cfdd17885a" +} diff --git a/backend/.sqlx/query-de2a213bc5a08d08bcd52ad630559cc0f16d7286c3f07c27d962258022f0e04d.json b/backend/.sqlx/query-de2a213bc5a08d08bcd52ad630559cc0f16d7286c3f07c27d962258022f0e04d.json new file mode 100644 index 0000000000..f3ef9ff937 --- /dev/null +++ b/backend/.sqlx/query-de2a213bc5a08d08bcd52ad630559cc0f16d7286c3f07c27d962258022f0e04d.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(JSONB_SET(flow_status, ARRAY['retry'], $1), ARRAY['modules', $3::TEXT, 'failed_retries'], $4)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid", + "Text", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "de2a213bc5a08d08bcd52ad630559cc0f16d7286c3f07c27d962258022f0e04d" +} diff --git a/backend/.sqlx/query-dea056c89313f4facd62cbbc9fa33ba30fa85efcc83fafe4dd7b4e535b96a8d8.json b/backend/.sqlx/query-dea056c89313f4facd62cbbc9fa33ba30fa85efcc83fafe4dd7b4e535b96a8d8.json new file mode 100644 index 0000000000..12e4b6d81b --- /dev/null +++ b/backend/.sqlx/query-dea056c89313f4facd62cbbc9fa33ba30fa85efcc83fafe4dd7b4e535b96a8d8.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE \n FROM \n sqs_trigger \n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "dea056c89313f4facd62cbbc9fa33ba30fa85efcc83fafe4dd7b4e535b96a8d8" +} diff --git a/backend/.sqlx/query-dee32ce9c4010ae407b4d1ba2ecf2062c36ee5dab3d4894ad4b2aa8b2f5a0db9.json b/backend/.sqlx/query-dee32ce9c4010ae407b4d1ba2ecf2062c36ee5dab3d4894ad4b2aa8b2f5a0db9.json deleted file mode 100644 index bb8d339e55..0000000000 --- a/backend/.sqlx/query-dee32ce9c4010ae407b4d1ba2ecf2062c36ee5dab3d4894ad4b2aa8b2f5a0db9.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT EXISTS(SELECT 1 FROM log_file WHERE hostname = $1 AND log_ts = $2)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text", - "Timestamp" - ] - }, - "nullable": [ - null - ] - }, - "hash": "dee32ce9c4010ae407b4d1ba2ecf2062c36ee5dab3d4894ad4b2aa8b2f5a0db9" -} diff --git a/backend/.sqlx/query-defd99dd2427cdc54bb662d1ba3a1aea7f410ef204ec3465f4fb6c9acd256c95.json b/backend/.sqlx/query-defd99dd2427cdc54bb662d1ba3a1aea7f410ef204ec3465f4fb6c9acd256c95.json deleted file mode 100644 index 6aab7bc049..0000000000 --- a/backend/.sqlx/query-defd99dd2427cdc54bb662d1ba3a1aea7f410ef204ec3465f4fb6c9acd256c95.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_queue SET\n suspend = $1,\n suspend_until = now() + interval '14 day',\n running = true\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int4", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "defd99dd2427cdc54bb662d1ba3a1aea7f410ef204ec3465f4fb6c9acd256c95" -} diff --git a/backend/.sqlx/query-df0454f75e819d7d3d03fef0a7d5606940a089f6179e5443090afa9aba7c5b24.json b/backend/.sqlx/query-df0454f75e819d7d3d03fef0a7d5606940a089f6179e5443090afa9aba7c5b24.json deleted file mode 100644 index 166dc4a5d8..0000000000 --- a/backend/.sqlx/query-df0454f75e819d7d3d03fef0a7d5606940a089f6179e5443090afa9aba7c5b24.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT EXISTS (\n SELECT 1 FROM completed_job \n WHERE workspace_id = $2 \n AND (job_kind = 'appscript' OR job_kind = 'preview')\n AND created_by = 'anonymous' \n AND started_at > now() - interval '3 hours'\n AND script_path LIKE $3 || '/%' \n AND result @> ('{\"s3\":\"' || $1 || '\"}')::jsonb \n )", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "df0454f75e819d7d3d03fef0a7d5606940a089f6179e5443090afa9aba7c5b24" -} diff --git a/backend/.sqlx/query-df1f1d15d442789a5b9c81cdddf44d88d5748499cc48865023ddc1ff1587d0f6.json b/backend/.sqlx/query-df1f1d15d442789a5b9c81cdddf44d88d5748499cc48865023ddc1ff1587d0f6.json deleted file mode 100644 index 7fe6da3923..0000000000 --- a/backend/.sqlx/query-df1f1d15d442789a5b9c81cdddf44d88d5748499cc48865023ddc1ff1587d0f6.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT version()", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "version", - "type_info": "Text" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - null - ] - }, - "hash": "df1f1d15d442789a5b9c81cdddf44d88d5748499cc48865023ddc1ff1587d0f6" -} diff --git a/backend/.sqlx/query-df2a426658f0a36683cc2163fe70912d0221d191b8ab091601cb53cdd932ad58.json b/backend/.sqlx/query-df2a426658f0a36683cc2163fe70912d0221d191b8ab091601cb53cdd932ad58.json new file mode 100644 index 0000000000..f98dd01404 --- /dev/null +++ b/backend/.sqlx/query-df2a426658f0a36683cc2163fe70912d0221d191b8ab091601cb53cdd932ad58.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n gcp_trigger\n SET \n last_server_ping = now(),\n error = $1\n WHERE\n workspace_id = $2\n AND path = $3\n AND server_id = $4 \n AND enabled IS TRUE\n RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "df2a426658f0a36683cc2163fe70912d0221d191b8ab091601cb53cdd932ad58" +} diff --git a/backend/.sqlx/query-df533f1988e409b70a3e0966825d01993cd52e8e85943440081b8dbd3b9ae5a4.json b/backend/.sqlx/query-df533f1988e409b70a3e0966825d01993cd52e8e85943440081b8dbd3b9ae5a4.json deleted file mode 100644 index a4f3b96782..0000000000 --- a/backend/.sqlx/query-df533f1988e409b70a3e0966825d01993cd52e8e85943440081b8dbd3b9ae5a4.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = flow_status - 'retry'\n WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "df533f1988e409b70a3e0966825d01993cd52e8e85943440081b8dbd3b9ae5a4" -} diff --git a/backend/.sqlx/query-e04a8a9f1e9cc3bb5c990194585e08f4c248e1af1c6580bdf8f2735ae1388981.json b/backend/.sqlx/query-e04a8a9f1e9cc3bb5c990194585e08f4c248e1af1c6580bdf8f2735ae1388981.json new file mode 100644 index 0000000000..926e5c2474 --- /dev/null +++ b/backend/.sqlx/query-e04a8a9f1e9cc3bb5c990194585e08f4c248e1af1c6580bdf8f2735ae1388981.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM \n capture\n WHERE \n workspace_id = $1\n AND created_at <= (\n SELECT \n created_at\n FROM \n capture\n WHERE \n workspace_id = $1\n ORDER BY \n created_at DESC\n OFFSET $2\n LIMIT 1\n )\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "e04a8a9f1e9cc3bb5c990194585e08f4c248e1af1c6580bdf8f2735ae1388981" +} diff --git a/backend/.sqlx/query-e1923bc755bd6b8cc871ae9381a97d3c63a0fce3dd57c93f48a2136f7395fa3a.json b/backend/.sqlx/query-e1923bc755bd6b8cc871ae9381a97d3c63a0fce3dd57c93f48a2136f7395fa3a.json deleted file mode 100644 index a477ebf141..0000000000 --- a/backend/.sqlx/query-e1923bc755bd6b8cc871ae9381a97d3c63a0fce3dd57c93f48a2136f7395fa3a.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT raw_flow->'failure_module' != 'null'::jsonb FROM job WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "e1923bc755bd6b8cc871ae9381a97d3c63a0fce3dd57c93f48a2136f7395fa3a" -} diff --git a/backend/.sqlx/query-e19fab3d594c8b9d38f9caa8f71b16ffcb2bc8d94a8240143a913ad125ad6eb8.json b/backend/.sqlx/query-e19fab3d594c8b9d38f9caa8f71b16ffcb2bc8d94a8240143a913ad125ad6eb8.json new file mode 100644 index 0000000000..f75e6eaa56 --- /dev/null +++ b/backend/.sqlx/query-e19fab3d594c8b9d38f9caa8f71b16ffcb2bc8d94a8240143a913ad125ad6eb8.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE workspace_runnable_dependencies SET workspace_id = $1 WHERE workspace_id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Text" + ] + }, + "nullable": [] + }, + "hash": "e19fab3d594c8b9d38f9caa8f71b16ffcb2bc8d94a8240143a913ad125ad6eb8" +} diff --git a/backend/.sqlx/query-e207bbcdd478758562e3082330836d6efe1dd6c4f719bc415222eba78d5e63fc.json b/backend/.sqlx/query-e207bbcdd478758562e3082330836d6efe1dd6c4f719bc415222eba78d5e63fc.json deleted file mode 100644 index ac91e4f1d1..0000000000 --- a/backend/.sqlx/query-e207bbcdd478758562e3082330836d6efe1dd6c4f719bc415222eba78d5e63fc.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id FROM queue", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false - ] - }, - "hash": "e207bbcdd478758562e3082330836d6efe1dd6c4f719bc415222eba78d5e63fc" -} diff --git a/backend/.sqlx/query-ca5f42cb0e368d0817461600152aabefe718829fb6eae2f681c4c8328acc94b2.json b/backend/.sqlx/query-e3c8219420bb859de3f1c322978c5859c924cffa61074562ac6d33106d02d7d6.json similarity index 66% rename from backend/.sqlx/query-ca5f42cb0e368d0817461600152aabefe718829fb6eae2f681c4c8328acc94b2.json rename to backend/.sqlx/query-e3c8219420bb859de3f1c322978c5859c924cffa61074562ac6d33106d02d7d6.json index eb5c3a8bf0..8ee4e7890e 100644 --- a/backend/.sqlx/query-ca5f42cb0e368d0817461600152aabefe718829fb6eae2f681c4c8328acc94b2.json +++ b/backend/.sqlx/query-e3c8219420bb859de3f1c322978c5859c924cffa61074562ac6d33106d02d7d6.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT g_.workspace_id, name, summary, extra_perms, array_agg(u2g.usr) filter (where u2g.usr is not null) as members \n FROM usr u\n JOIN usr_to_group u2g ON u2g.usr = u.username AND u2g.workspace_id = u.workspace_id\n RIGHT JOIN group_ g_ ON g_.workspace_id = u.workspace_id AND g_.name = u2g.group_\n WHERE g_.workspace_id = $1 AND g_.name != 'all'\n GROUP BY g_.workspace_id, name, summary, extra_perms", + "query": "SELECT g_.workspace_id, name, summary, extra_perms, array_agg(u2g.usr) filter (where u2g.usr is not null) as members\n FROM usr u\n JOIN usr_to_group u2g ON u2g.usr = u.username AND u2g.workspace_id = u.workspace_id\n RIGHT JOIN group_ g_ ON g_.workspace_id = u.workspace_id AND g_.name = u2g.group_\n WHERE g_.workspace_id = $1 AND g_.name != 'all'\n GROUP BY g_.workspace_id, name, summary, extra_perms", "describe": { "columns": [ { @@ -42,5 +42,5 @@ null ] }, - "hash": "ca5f42cb0e368d0817461600152aabefe718829fb6eae2f681c4c8328acc94b2" + "hash": "e3c8219420bb859de3f1c322978c5859c924cffa61074562ac6d33106d02d7d6" } diff --git a/backend/.sqlx/query-e3f545460bf317c3e2f34f4cbd12740141eb8b5ed07c090ae9356a970caeadd0.json b/backend/.sqlx/query-e3f545460bf317c3e2f34f4cbd12740141eb8b5ed07c090ae9356a970caeadd0.json new file mode 100644 index 0000000000..34df271cc8 --- /dev/null +++ b/backend/.sqlx/query-e3f545460bf317c3e2f34f4cbd12740141eb8b5ed07c090ae9356a970caeadd0.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH suspend AS (\n UPDATE v2_job_queue SET suspend = $2, suspend_until = now() + $3\n WHERE id = $4\n RETURNING id\n ) UPDATE v2_job_status SET flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', flow_status->>'step'::TEXT],\n $1\n ) WHERE id = (SELECT id FROM suspend)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Int4", + "Interval", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "e3f545460bf317c3e2f34f4cbd12740141eb8b5ed07c090ae9356a970caeadd0" +} diff --git a/backend/.sqlx/query-e4e87539ae18f7e5c6bd9a28d16b7527ececad87d218182ff723e6a6c43ecd50.json b/backend/.sqlx/query-e4e87539ae18f7e5c6bd9a28d16b7527ececad87d218182ff723e6a6c43ecd50.json deleted file mode 100644 index 3148f59bb4..0000000000 --- a/backend/.sqlx/query-e4e87539ae18f7e5c6bd9a28d16b7527ececad87d218182ff723e6a6c43ecd50.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT \n flow_status->>'step' = '0' \n AND (\n jsonb_array_length(flow_status->'modules') = 0 \n OR flow_status->'modules'->0->>'type' = 'WaitingForPriorSteps' \n OR (\n flow_status->'modules'->0->>'type' = 'Failure' \n AND flow_status->'modules'->0->>'job' = $1\n )\n )\n FROM completed_job WHERE id = $2 AND workspace_id = $3", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text", - "Uuid", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "e4e87539ae18f7e5c6bd9a28d16b7527ececad87d218182ff723e6a6c43ecd50" -} diff --git a/backend/.sqlx/query-4205d237c123d8d1c9ff2d61118027a80ccc8cd75e9703cb1d014b45f57c2be6.json b/backend/.sqlx/query-e4f1ee1568ce3c186b569421c6c8a3039f73d04fc53c67c70e67371f06416ef3.json similarity index 54% rename from backend/.sqlx/query-4205d237c123d8d1c9ff2d61118027a80ccc8cd75e9703cb1d014b45f57c2be6.json rename to backend/.sqlx/query-e4f1ee1568ce3c186b569421c6c8a3039f73d04fc53c67c70e67371f06416ef3.json index 6d4744325c..3e61e808ce 100644 --- a/backend/.sqlx/query-4205d237c123d8d1c9ff2d61118027a80ccc8cd75e9703cb1d014b45f57c2be6.json +++ b/backend/.sqlx/query-e4f1ee1568ce3c186b569421c6c8a3039f73d04fc53c67c70e67371f06416ef3.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO flow (workspace_id, path, summary, description, dependency_job, draft_only, tag, dedicated_worker, visible_to_runner_only, on_behalf_of_email, value, schema, edited_by, edited_at) \n VALUES ($1, $2, $3, $4, NULL, $5, $6, $7, $8, $9, $10, $11::text::json, $12, now())", + "query": "INSERT INTO flow (workspace_id, path, summary, description, dependency_job, lock_error_logs, draft_only, tag, dedicated_worker, visible_to_runner_only, on_behalf_of_email, value, schema, edited_by, edited_at) \n VALUES ($1, $2, $3, $4, NULL, '', $5, $6, $7, $8, $9, $10, $11::text::json, $12, now())", "describe": { "columns": [], "parameters": { @@ -21,5 +21,5 @@ }, "nullable": [] }, - "hash": "4205d237c123d8d1c9ff2d61118027a80ccc8cd75e9703cb1d014b45f57c2be6" + "hash": "e4f1ee1568ce3c186b569421c6c8a3039f73d04fc53c67c70e67371f06416ef3" } diff --git a/backend/.sqlx/query-e58cf2e3deb9aa1e9f37313a33e5d44fcfdb40a4764e1dc34896bd85b3d007f9.json b/backend/.sqlx/query-e58cf2e3deb9aa1e9f37313a33e5d44fcfdb40a4764e1dc34896bd85b3d007f9.json deleted file mode 100644 index ab1bd399ea..0000000000 --- a/backend/.sqlx/query-e58cf2e3deb9aa1e9f37313a33e5d44fcfdb40a4764e1dc34896bd85b3d007f9.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT success FROM completed_job WHERE id = ANY($1)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "success", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "UuidArray" - ] - }, - "nullable": [ - false - ] - }, - "hash": "e58cf2e3deb9aa1e9f37313a33e5d44fcfdb40a4764e1dc34896bd85b3d007f9" -} diff --git a/backend/.sqlx/query-82b16e771b6e21c4587b5ebf059e312f43b3e5a48f7599133831dbd65886f5d8.json b/backend/.sqlx/query-e5f1e1e74daeabf410991e2484c1fb565f04539fc7eb141ebfaa957456016841.json similarity index 50% rename from backend/.sqlx/query-82b16e771b6e21c4587b5ebf059e312f43b3e5a48f7599133831dbd65886f5d8.json rename to backend/.sqlx/query-e5f1e1e74daeabf410991e2484c1fb565f04539fc7eb141ebfaa957456016841.json index 880e79a957..ddd216acc1 100644 --- a/backend/.sqlx/query-82b16e771b6e21c4587b5ebf059e312f43b3e5a48f7599133831dbd65886f5d8.json +++ b/backend/.sqlx/query-e5f1e1e74daeabf410991e2484c1fb565f04539fc7eb141ebfaa957456016841.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT usage.usage FROM usage \n WHERE is_workspace = true \n AND month_ = EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date)\n AND id = $1", + "query": "\n SELECT usage.usage FROM usage\n WHERE is_workspace = true\n AND month_ = EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date)\n AND id = $1", "describe": { "columns": [ { @@ -18,5 +18,5 @@ false ] }, - "hash": "82b16e771b6e21c4587b5ebf059e312f43b3e5a48f7599133831dbd65886f5d8" + "hash": "e5f1e1e74daeabf410991e2484c1fb565f04539fc7eb141ebfaa957456016841" } diff --git a/backend/.sqlx/query-f96dd1dd944506c07ad58d178e9b6a09259fedca329d790c34e58703bf26f34c.json b/backend/.sqlx/query-e5fb3531f8bc7ef1f7484524f8c3bc9c48f71a44827ba0d01ac5588dc31082a2.json similarity index 87% rename from backend/.sqlx/query-f96dd1dd944506c07ad58d178e9b6a09259fedca329d790c34e58703bf26f34c.json rename to backend/.sqlx/query-e5fb3531f8bc7ef1f7484524f8c3bc9c48f71a44827ba0d01ac5588dc31082a2.json index 81c15c15cf..f70cbfde21 100644 --- a/backend/.sqlx/query-f96dd1dd944506c07ad58d178e9b6a09259fedca329d790c34e58703bf26f34c.json +++ b/backend/.sqlx/query-e5fb3531f8bc7ef1f7484524f8c3bc9c48f71a44827ba0d01ac5588dc31082a2.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT * FROM usr\n WHERE workspace_id = $1", + "query": "SELECT * FROM usr\n WHERE workspace_id = $1", "describe": { "columns": [ { @@ -60,5 +60,5 @@ true ] }, - "hash": "f96dd1dd944506c07ad58d178e9b6a09259fedca329d790c34e58703bf26f34c" + "hash": "e5fb3531f8bc7ef1f7484524f8c3bc9c48f71a44827ba0d01ac5588dc31082a2" } diff --git a/backend/.sqlx/query-e653d36b607a16c0dfc0324690942ab25883b53a81ebb581fe019af2ec5eb567.json b/backend/.sqlx/query-e653d36b607a16c0dfc0324690942ab25883b53a81ebb581fe019af2ec5eb567.json deleted file mode 100644 index 284cf3338f..0000000000 --- a/backend/.sqlx/query-e653d36b607a16c0dfc0324690942ab25883b53a81ebb581fe019af2ec5eb567.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id AS \"id!\", workspace_id AS \"workspace_id!\", parent_job, is_flow_step,\n flow_status AS \"flow_status: Box\", last_ping, same_worker\n FROM v2_as_queue\n WHERE running = true AND suspend = 0 AND suspend_until IS null AND scheduled_for <= now()\n AND (job_kind = 'flow' OR job_kind = 'flowpreview' OR job_kind = 'flownode')\n AND last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval\n AND canceled = false\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "workspace_id!", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "parent_job", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "is_flow_step", - "type_info": "Bool" - }, - { - "ordinal": 4, - "name": "flow_status: Box", - "type_info": "Jsonb" - }, - { - "ordinal": 5, - "name": "last_ping", - "type_info": "Timestamptz" - }, - { - "ordinal": 6, - "name": "same_worker", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - true, - true, - true, - true, - true, - true, - true - ] - }, - "hash": "e653d36b607a16c0dfc0324690942ab25883b53a81ebb581fe019af2ec5eb567" -} diff --git a/backend/.sqlx/query-e6a8ddfd74ebab55ede5989fd7d09b246b6f84e49c659bc8e2c7b66cfec6d976.json b/backend/.sqlx/query-e6a8ddfd74ebab55ede5989fd7d09b246b6f84e49c659bc8e2c7b66cfec6d976.json new file mode 100644 index 0000000000..a1b52e81fd --- /dev/null +++ b/backend/.sqlx/query-e6a8ddfd74ebab55ede5989fd7d09b246b6f84e49c659bc8e2c7b66cfec6d976.json @@ -0,0 +1,38 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT status = 'success' AS \"success!\",\n result AS \"result: Json>\",\n started_at AS \"started_at!\"FROM v2_job j JOIN v2_job_completed USING (id)\n WHERE j.workspace_id = $1 AND trigger_kind = 'schedule' AND trigger = $2\n AND parent_job IS NULL\n AND runnable_path = $3\n AND j.id != $4\n ORDER BY created_at DESC\n LIMIT $5", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "success!", + "type_info": "Bool" + }, + { + "ordinal": 1, + "name": "result: Json>", + "type_info": "Jsonb" + }, + { + "ordinal": 2, + "name": "started_at!", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Uuid", + "Int8" + ] + }, + "nullable": [ + null, + true, + true + ] + }, + "hash": "e6a8ddfd74ebab55ede5989fd7d09b246b6f84e49c659bc8e2c7b66cfec6d976" +} diff --git a/backend/.sqlx/query-e7a1c2b5d79e72f557181782419a9d8d1a502796842f185374d2d0f69043086b.json b/backend/.sqlx/query-e7a1c2b5d79e72f557181782419a9d8d1a502796842f185374d2d0f69043086b.json deleted file mode 100644 index 829d4f6768..0000000000 --- a/backend/.sqlx/query-e7a1c2b5d79e72f557181782419a9d8d1a502796842f185374d2d0f69043086b.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT COUNT(id) FROM queue WHERE workspace_id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "e7a1c2b5d79e72f557181782419a9d8d1a502796842f185374d2d0f69043086b" -} diff --git a/backend/.sqlx/query-e9d9edc030061c5ccf2fb4294acbbae160e97faede6934d90f4882d806c14813.json b/backend/.sqlx/query-e9d9edc030061c5ccf2fb4294acbbae160e97faede6934d90f4882d806c14813.json deleted file mode 100644 index 8b79600c50..0000000000 --- a/backend/.sqlx/query-e9d9edc030061c5ccf2fb4294acbbae160e97faede6934d90f4882d806c14813.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE completed_job SET canceled_by = $1 WHERE canceled_by = $2 AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "e9d9edc030061c5ccf2fb4294acbbae160e97faede6934d90f4882d806c14813" -} diff --git a/backend/.sqlx/query-ec7836df5f9056ec70015800b7f4feaeb1b671120f5f8c98fca8c89c6587fc35.json b/backend/.sqlx/query-ec7836df5f9056ec70015800b7f4feaeb1b671120f5f8c98fca8c89c6587fc35.json index 81093c5f12..73ac1919c1 100644 --- a/backend/.sqlx/query-ec7836df5f9056ec70015800b7f4feaeb1b671120f5f8c98fca8c89c6587fc35.json +++ b/backend/.sqlx/query-ec7836df5f9056ec70015800b7f4feaeb1b671120f5f8c98fca8c89c6587fc35.json @@ -54,7 +54,9 @@ "rust", "ansible", "csharp", - "oracledb" + "oracledb", + "nu", + "java" ] } } diff --git a/backend/.sqlx/query-ec9980f80a8bfa4b09225035e8a1f78d7d61fbf83dda9b39bb22e0f9584d221b.json b/backend/.sqlx/query-ec9980f80a8bfa4b09225035e8a1f78d7d61fbf83dda9b39bb22e0f9584d221b.json new file mode 100644 index 0000000000..d87e680abe --- /dev/null +++ b/backend/.sqlx/query-ec9980f80a8bfa4b09225035e8a1f78d7d61fbf83dda9b39bb22e0f9584d221b.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT EXISTS (\n SELECT 1 FROM v2_as_completed_job\n WHERE workspace_id = $2\n AND (job_kind = 'appscript' OR job_kind = 'preview')\n AND created_by = 'anonymous'\n AND started_at > now() - interval '3 hours'\n AND script_path LIKE $3 || '/%'\n AND result @> ('{\"s3\":\"' || $1 || '\"}')::jsonb\n )", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "ec9980f80a8bfa4b09225035e8a1f78d7d61fbf83dda9b39bb22e0f9584d221b" +} diff --git a/backend/.sqlx/query-ecd62c48fe2fba2fc2582e9e7ae5590d5dea8c67f6ae7b14743ac4f265dd89a3.json b/backend/.sqlx/query-ecd62c48fe2fba2fc2582e9e7ae5590d5dea8c67f6ae7b14743ac4f265dd89a3.json deleted file mode 100644 index 7431f3e1fb..0000000000 --- a/backend/.sqlx/query-ecd62c48fe2fba2fc2582e9e7ae5590d5dea8c67f6ae7b14743ac4f265dd89a3.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM v2_job WHERE id = ANY($1)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "UuidArray" - ] - }, - "nullable": [] - }, - "hash": "ecd62c48fe2fba2fc2582e9e7ae5590d5dea8c67f6ae7b14743ac4f265dd89a3" -} diff --git a/backend/.sqlx/query-ed318070b26861fda2d591a4356fdbeb6c7fdc965be43bddb010fd8299af1286.json b/backend/.sqlx/query-ed318070b26861fda2d591a4356fdbeb6c7fdc965be43bddb010fd8299af1286.json deleted file mode 100644 index e18818dc3a..0000000000 --- a/backend/.sqlx/query-ed318070b26861fda2d591a4356fdbeb6c7fdc965be43bddb010fd8299af1286.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO metrics (id, value) \n VALUES ('no_uv_usage_py', $1)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "ed318070b26861fda2d591a4356fdbeb6c7fdc965be43bddb010fd8299af1286" -} diff --git a/backend/.sqlx/query-eed16e356f3f36183c3db13fcc1950295e0d0fbdabb38434534fb3430eeddc25.json b/backend/.sqlx/query-eed16e356f3f36183c3db13fcc1950295e0d0fbdabb38434534fb3430eeddc25.json deleted file mode 100644 index 5e0817ae84..0000000000 --- a/backend/.sqlx/query-eed16e356f3f36183c3db13fcc1950295e0d0fbdabb38434534fb3430eeddc25.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT workspace.id, workspace.name, workspace.owner, workspace.deleted, workspace.premium, workspace_settings.color\n FROM workspace\n LEFT JOIN workspace_settings ON workspace.id = workspace_settings.workspace_id\n LIMIT $1 OFFSET $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "owner", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "deleted", - "type_info": "Bool" - }, - { - "ordinal": 4, - "name": "premium", - "type_info": "Bool" - }, - { - "ordinal": 5, - "name": "color", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Int8", - "Int8" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - true - ] - }, - "hash": "eed16e356f3f36183c3db13fcc1950295e0d0fbdabb38434534fb3430eeddc25" -} diff --git a/backend/.sqlx/query-ef6795d93423f98eea82eb18e6332580dc7f7a9e5a67026f8c0b3077f371fc62.json b/backend/.sqlx/query-ef6795d93423f98eea82eb18e6332580dc7f7a9e5a67026f8c0b3077f371fc62.json deleted file mode 100644 index 08c5851944..0000000000 --- a/backend/.sqlx/query-ef6795d93423f98eea82eb18e6332580dc7f7a9e5a67026f8c0b3077f371fc62.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT hash FROM script WHERE path = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "hash", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "ef6795d93423f98eea82eb18e6332580dc7f7a9e5a67026f8c0b3077f371fc62" -} diff --git a/backend/.sqlx/query-ef8868893643a1a71531c1113d5cb38c5c204b3bc34c921b2f653c738af556a9.json b/backend/.sqlx/query-ef8868893643a1a71531c1113d5cb38c5c204b3bc34c921b2f653c738af556a9.json deleted file mode 100644 index 055944bd70..0000000000 --- a/backend/.sqlx/query-ef8868893643a1a71531c1113d5cb38c5c204b3bc34c921b2f653c738af556a9.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id AS \"id!\", flow_status AS \"flow_status!: Json\"\n FROM completed_job WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "flow_status!: Json", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true - ] - }, - "hash": "ef8868893643a1a71531c1113d5cb38c5c204b3bc34c921b2f653c738af556a9" -} diff --git a/backend/.sqlx/query-f04fa0262091c5a4abf7dddafeec1b34ed806dda70592b0030067aed46a104d9.json b/backend/.sqlx/query-f04fa0262091c5a4abf7dddafeec1b34ed806dda70592b0030067aed46a104d9.json deleted file mode 100644 index 58fec1a0d0..0000000000 --- a/backend/.sqlx/query-f04fa0262091c5a4abf7dddafeec1b34ed806dda70592b0030067aed46a104d9.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT created_by, CONCAT(coalesce(completed_job.logs, ''), coalesce(job_logs.logs, '')) as logs, job_logs.log_offset, job_logs.log_file_index\n FROM completed_job\n LEFT JOIN job_logs ON job_logs.job_id = completed_job.id\n WHERE completed_job.id = $1 AND completed_job.workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_by", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "logs", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "log_offset", - "type_info": "Int4" - }, - { - "ordinal": 3, - "name": "log_file_index", - "type_info": "TextArray" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - false, - null, - false, - true - ] - }, - "hash": "f04fa0262091c5a4abf7dddafeec1b34ed806dda70592b0030067aed46a104d9" -} diff --git a/backend/.sqlx/query-f050be8da0d99aa1af64f5d32a8df01c5a59b036b2669878cc557537efb492c8.json b/backend/.sqlx/query-f050be8da0d99aa1af64f5d32a8df01c5a59b036b2669878cc557537efb492c8.json new file mode 100644 index 0000000000..7334c70fb4 --- /dev/null +++ b/backend/.sqlx/query-f050be8da0d99aa1af64f5d32a8df01c5a59b036b2669878cc557537efb492c8.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'mqtt'\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "f050be8da0d99aa1af64f5d32a8df01c5a59b036b2669878cc557537efb492c8" +} diff --git a/backend/.sqlx/query-f33dd637181439ba7fa441dbd7d7430c1ccc0f410c377cde71c70c9211f9c1df.json b/backend/.sqlx/query-f33dd637181439ba7fa441dbd7d7430c1ccc0f410c377cde71c70c9211f9c1df.json deleted file mode 100644 index 536a8116f4..0000000000 --- a/backend/.sqlx/query-f33dd637181439ba7fa441dbd7d7430c1ccc0f410c377cde71c70c9211f9c1df.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT parent_job FROM queue WHERE id = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "parent_job", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "f33dd637181439ba7fa441dbd7d7430c1ccc0f410c377cde71c70c9211f9c1df" -} diff --git a/backend/.sqlx/query-f3c78cb67379f9407f1f32ce3387184d7f4fcb04c07a8e2f07c5effc10f8fd5c.json b/backend/.sqlx/query-f3c78cb67379f9407f1f32ce3387184d7f4fcb04c07a8e2f07c5effc10f8fd5c.json deleted file mode 100644 index 0cf878b15c..0000000000 --- a/backend/.sqlx/query-f3c78cb67379f9407f1f32ce3387184d7f4fcb04c07a8e2f07c5effc10f8fd5c.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'approvers'], $2)\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "f3c78cb67379f9407f1f32ce3387184d7f4fcb04c07a8e2f07c5effc10f8fd5c" -} diff --git a/backend/.sqlx/query-f3d20b0fa17836538ec93b84b08ffc6b555371d6eeeadfc54f5bf0bcbe93d8b4.json b/backend/.sqlx/query-f3d20b0fa17836538ec93b84b08ffc6b555371d6eeeadfc54f5bf0bcbe93d8b4.json deleted file mode 100644 index 2cfac3a744..0000000000 --- a/backend/.sqlx/query-f3d20b0fa17836538ec93b84b08ffc6b555371d6eeeadfc54f5bf0bcbe93d8b4.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT COALESCE((SELECT MIN(started_at) as min_started_at\n FROM queue\n WHERE script_path = $1 AND job_kind != 'dependencies' AND running = true AND workspace_id = $2 AND canceled = false AND concurrent_limit > 0), $3) as min_started_at, now() AS now", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "min_started_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 1, - "name": "now", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Timestamptz" - ] - }, - "nullable": [ - null, - null - ] - }, - "hash": "f3d20b0fa17836538ec93b84b08ffc6b555371d6eeeadfc54f5bf0bcbe93d8b4" -} diff --git a/backend/.sqlx/query-f3f1b312bff773fe04a5dbfc2c03963042fa19d5eb15cfb3e0c291a29482aa5b.json b/backend/.sqlx/query-f3f1b312bff773fe04a5dbfc2c03963042fa19d5eb15cfb3e0c291a29482aa5b.json deleted file mode 100644 index 99d4d7075c..0000000000 --- a/backend/.sqlx/query-f3f1b312bff773fe04a5dbfc2c03963042fa19d5eb15cfb3e0c291a29482aa5b.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE job_logs\n SET logs = '##DELETED##'\n WHERE job_id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "f3f1b312bff773fe04a5dbfc2c03963042fa19d5eb15cfb3e0c291a29482aa5b" -} diff --git a/backend/.sqlx/query-f4849960aff7387cb6b130d2bc62dbfce45209fbbccf3b4b9b13019e0cd55ddb.json b/backend/.sqlx/query-f4849960aff7387cb6b130d2bc62dbfce45209fbbccf3b4b9b13019e0cd55ddb.json deleted file mode 100644 index f97b5d1da4..0000000000 --- a/backend/.sqlx/query-f4849960aff7387cb6b130d2bc62dbfce45209fbbccf3b4b9b13019e0cd55ddb.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT flow_status->'failure_module'->>'parent_module' FROM queue WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "f4849960aff7387cb6b130d2bc62dbfce45209fbbccf3b4b9b13019e0cd55ddb" -} diff --git a/backend/.sqlx/query-f4a792eca82e9974d13d0731e7862e3c75dbee326e0856944539b2c5574cb6d3.json b/backend/.sqlx/query-f4a792eca82e9974d13d0731e7862e3c75dbee326e0856944539b2c5574cb6d3.json deleted file mode 100644 index a0ec3da44b..0000000000 --- a/backend/.sqlx/query-f4a792eca82e9974d13d0731e7862e3c75dbee326e0856944539b2c5574cb6d3.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET flow_status = jsonb_set(jsonb_set(COALESCE(flow_status, '{}'::jsonb), array[$1], COALESCE(flow_status->$1, '{}'::jsonb)), array[$1, 'duration_ms'], to_jsonb($2::bigint)) WHERE id = $3 AND workspace_id = $4", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Int8", - "Uuid", - "Text" - ] - }, - "nullable": [] - }, - "hash": "f4a792eca82e9974d13d0731e7862e3c75dbee326e0856944539b2c5574cb6d3" -} diff --git a/backend/.sqlx/query-f5c6d52f69b99dab6d7ec3aad2ec090a07fe82a2ccb17a1b3d903de499c2e7c8.json b/backend/.sqlx/query-f5c6d52f69b99dab6d7ec3aad2ec090a07fe82a2ccb17a1b3d903de499c2e7c8.json deleted file mode 100644 index 19b691bbe5..0000000000 --- a/backend/.sqlx/query-f5c6d52f69b99dab6d7ec3aad2ec090a07fe82a2ccb17a1b3d903de499c2e7c8.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job SET\n args = '{\"reason\":\"PREPROCESSOR_ARGS_ARE_DISCARDED\"}'::jsonb\n WHERE id = $1 AND args->'wm_trigger' IS NOT NULL", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "f5c6d52f69b99dab6d7ec3aad2ec090a07fe82a2ccb17a1b3d903de499c2e7c8" -} diff --git a/backend/.sqlx/query-f632ca2e17a3952fc45bd40a055a9442c35453dff95140d2f252c4fe6a14c6a4.json b/backend/.sqlx/query-f632ca2e17a3952fc45bd40a055a9442c35453dff95140d2f252c4fe6a14c6a4.json deleted file mode 100644 index 5145efa595..0000000000 --- a/backend/.sqlx/query-f632ca2e17a3952fc45bd40a055a9442c35453dff95140d2f252c4fe6a14c6a4.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT EXISTS (SELECT 1 \n FROM workspace_settings \n WHERE workspace_id <> $1 \n AND slack_command_script IS NOT NULL\n AND slack_team_id IS NOT NULL \n AND slack_team_id = (SELECT slack_team_id FROM workspace_settings WHERE workspace_id = $1))\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "f632ca2e17a3952fc45bd40a055a9442c35453dff95140d2f252c4fe6a14c6a4" -} diff --git a/backend/.sqlx/query-f632d08a8d3df691fff9f57fdf926f787287c3cd181a6853056077edba10473d.json b/backend/.sqlx/query-f632d08a8d3df691fff9f57fdf926f787287c3cd181a6853056077edba10473d.json new file mode 100644 index 0000000000..8fae751d85 --- /dev/null +++ b/backend/.sqlx/query-f632d08a8d3df691fff9f57fdf926f787287c3cd181a6853056077edba10473d.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT EXISTS(\n SELECT 1 \n FROM \n http_trigger \n WHERE \n workspace_id = $1 AND \n path = $2\n )\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "f632d08a8d3df691fff9f57fdf926f787287c3cd181a6853056077edba10473d" +} diff --git a/backend/.sqlx/query-f699cc3644aeb35a0588bbb3a6bf2dc0746d9f3a1bea104123188fe2921bc886.json b/backend/.sqlx/query-f699cc3644aeb35a0588bbb3a6bf2dc0746d9f3a1bea104123188fe2921bc886.json new file mode 100644 index 0000000000..d352d3d69d --- /dev/null +++ b/backend/.sqlx/query-f699cc3644aeb35a0588bbb3a6bf2dc0746d9f3a1bea104123188fe2921bc886.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE workspace_runnable_dependencies SET app_path = REGEXP_REPLACE(app_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE app_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "f699cc3644aeb35a0588bbb3a6bf2dc0746d9f3a1bea104123188fe2921bc886" +} diff --git a/backend/.sqlx/query-f6a275ad8bc7dfec7f9a6b60c669f6f2ff93ce57b5afadabd618e1fb52951fef.json b/backend/.sqlx/query-f6a275ad8bc7dfec7f9a6b60c669f6f2ff93ce57b5afadabd618e1fb52951fef.json deleted file mode 100644 index fc5dce48f3..0000000000 --- a/backend/.sqlx/query-f6a275ad8bc7dfec7f9a6b60c669f6f2ff93ce57b5afadabd618e1fb52951fef.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT usr.email, usage.executions\n FROM usr\n , LATERAL (\n SELECT COALESCE(SUM(duration_ms + 1000)/1000 , 0)::BIGINT executions\n FROM completed_job\n WHERE workspace_id = $1\n AND job_kind NOT IN ('flow', 'flowpreview', 'flownode')\n AND email = usr.email\n AND now() - '1 week'::interval < created_at \n ) usage\n WHERE workspace_id = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "email", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "executions", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - null - ] - }, - "hash": "f6a275ad8bc7dfec7f9a6b60c669f6f2ff93ce57b5afadabd618e1fb52951fef" -} diff --git a/backend/.sqlx/query-5ba4b87528ad49f17d72b53c3db30f5ca4b3b0b0afbd5d9721c8b5d692af601b.json b/backend/.sqlx/query-f6e8812a4479bccdd713a88ed2670ed81601acbc97bdfe4f7d4ac13b9a905b26.json similarity index 53% rename from backend/.sqlx/query-5ba4b87528ad49f17d72b53c3db30f5ca4b3b0b0afbd5d9721c8b5d692af601b.json rename to backend/.sqlx/query-f6e8812a4479bccdd713a88ed2670ed81601acbc97bdfe4f7d4ac13b9a905b26.json index f8b5b54892..f89761726e 100644 --- a/backend/.sqlx/query-5ba4b87528ad49f17d72b53c3db30f5ca4b3b0b0afbd5d9721c8b5d692af601b.json +++ b/backend/.sqlx/query-f6e8812a4479bccdd713a88ed2670ed81601acbc97bdfe4f7d4ac13b9a905b26.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT COUNT(id) FROM queue WHERE email = $1", + "query": "SELECT count(*)\n FROM worker_ping\n WHERE worker_group LIKE $1 AND ping_at > now() - INTERVAL '2 minutes'", "describe": { "columns": [ { @@ -18,5 +18,5 @@ null ] }, - "hash": "5ba4b87528ad49f17d72b53c3db30f5ca4b3b0b0afbd5d9721c8b5d692af601b" + "hash": "f6e8812a4479bccdd713a88ed2670ed81601acbc97bdfe4f7d4ac13b9a905b26" } diff --git a/backend/.sqlx/query-f790a016c4a1333e3d4d1ce468a1679ba5e61ea88a5c65be9696312d2f455508.json b/backend/.sqlx/query-f790a016c4a1333e3d4d1ce468a1679ba5e61ea88a5c65be9696312d2f455508.json deleted file mode 100644 index d64348ef46..0000000000 --- a/backend/.sqlx/query-f790a016c4a1333e3d4d1ce468a1679ba5e61ea88a5c65be9696312d2f455508.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n DELETE\n FROM parallel_monitor_lock\n WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval \n RETURNING parent_flow_id, job_id, last_ping\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "parent_flow_id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "job_id", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "last_ping", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - true - ] - }, - "hash": "f790a016c4a1333e3d4d1ce468a1679ba5e61ea88a5c65be9696312d2f455508" -} diff --git a/backend/.sqlx/query-f822df86bf511fe0d3044b69525a6ff2524167877929886adca9b0fa3d3fee45.json b/backend/.sqlx/query-f822df86bf511fe0d3044b69525a6ff2524167877929886adca9b0fa3d3fee45.json deleted file mode 100644 index 3adc1f8cd5..0000000000 --- a/backend/.sqlx/query-f822df86bf511fe0d3044b69525a6ff2524167877929886adca9b0fa3d3fee45.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n DELETE\n FROM parallel_monitor_lock\n WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval \n RETURNING parent_flow_id, job_id, last_ping, (SELECT workspace_id FROM v2_job_queue q\n WHERE q.id = parent_flow_id AND q.running = true AND q.canceled_by IS NULL\n ) AS workspace_id\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "parent_flow_id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "job_id", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "last_ping", - "type_info": "Timestamptz" - }, - { - "ordinal": 3, - "name": "workspace_id", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - true, - null - ] - }, - "hash": "f822df86bf511fe0d3044b69525a6ff2524167877929886adca9b0fa3d3fee45" -} diff --git a/backend/.sqlx/query-f82974abc7da71fac397f754fe3b3c9c84b6b438b2d9f8cbba61192e999971f4.json b/backend/.sqlx/query-f82974abc7da71fac397f754fe3b3c9c84b6b438b2d9f8cbba61192e999971f4.json new file mode 100644 index 0000000000..db7c69c456 --- /dev/null +++ b/backend/.sqlx/query-f82974abc7da71fac397f754fe3b3c9c84b6b438b2d9f8cbba61192e999971f4.json @@ -0,0 +1,36 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO mqtt_trigger (\n mqtt_resource_path,\n subscribe_topics,\n client_version,\n client_id,\n v3_config,\n v5_config,\n workspace_id,\n path, \n script_path, \n is_flow, \n email, \n enabled, \n edited_by\n ) \n VALUES (\n $1, \n $2, \n $3, \n $4, \n $5, \n $6, \n $7,\n $8,\n $9,\n $10,\n $11,\n $12,\n $13\n )", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "JsonbArray", + { + "Custom": { + "name": "mqtt_client_version", + "kind": { + "Enum": [ + "v3", + "v5" + ] + } + } + }, + "Varchar", + "Jsonb", + "Jsonb", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Bool", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "f82974abc7da71fac397f754fe3b3c9c84b6b438b2d9f8cbba61192e999971f4" +} diff --git a/backend/.sqlx/query-f8f25948ae14fcb71c666cdc5e51d888e1f22fb2300a78bbeafebf64e82658db.json b/backend/.sqlx/query-f8f25948ae14fcb71c666cdc5e51d888e1f22fb2300a78bbeafebf64e82658db.json deleted file mode 100644 index 5167523212..0000000000 --- a/backend/.sqlx/query-f8f25948ae14fcb71c666cdc5e51d888e1f22fb2300a78bbeafebf64e82658db.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id AS \"id!\" FROM queue WHERE parent_job = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "f8f25948ae14fcb71c666cdc5e51d888e1f22fb2300a78bbeafebf64e82658db" -} diff --git a/backend/.sqlx/query-f916ec232837ece9323675e5f5142e7285f4266a1471e5ffdefadf421a67e44b.json b/backend/.sqlx/query-f916ec232837ece9323675e5f5142e7285f4266a1471e5ffdefadf421a67e44b.json deleted file mode 100644 index 2cf1fb31e9..0000000000 --- a/backend/.sqlx/query-f916ec232837ece9323675e5f5142e7285f4266a1471e5ffdefadf421a67e44b.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4),\n ARRAY['modules', $1::TEXT, 'iterator', 'index'],\n ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb\n ),\n last_ping = NULL\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "int4", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int4", - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [ - null - ] - }, - "hash": "f916ec232837ece9323675e5f5142e7285f4266a1471e5ffdefadf421a67e44b" -} diff --git a/backend/.sqlx/query-f955a01779f5441efc6aa9364b24c79b3cbc6413046c3b6099d19f675d8a395b.json b/backend/.sqlx/query-f955a01779f5441efc6aa9364b24c79b3cbc6413046c3b6099d19f675d8a395b.json new file mode 100644 index 0000000000..925d8c39f1 --- /dev/null +++ b/backend/.sqlx/query-f955a01779f5441efc6aa9364b24c79b3cbc6413046c3b6099d19f675d8a395b.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT EXISTS (SELECT 1\n FROM workspace_settings\n WHERE workspace_id <> $1\n AND slack_command_script IS NOT NULL\n AND slack_team_id = $2\n AND (SELECT slack_command_script IS NOT NULL FROM workspace_settings WHERE workspace_id = $1))\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "f955a01779f5441efc6aa9364b24c79b3cbc6413046c3b6099d19f675d8a395b" +} diff --git a/backend/.sqlx/query-f9e0e35b4789a4da89f7bb21fa6b250fd64ee0e0b299471979db5ff8ee906929.json b/backend/.sqlx/query-f9e0e35b4789a4da89f7bb21fa6b250fd64ee0e0b299471979db5ff8ee906929.json deleted file mode 100644 index 39c96b8989..0000000000 --- a/backend/.sqlx/query-f9e0e35b4789a4da89f7bb21fa6b250fd64ee0e0b299471979db5ff8ee906929.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT (flow_status->'step')::integer as step, jsonb_array_length(flow_status->'modules') as len FROM queue WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "step", - "type_info": "Int4" - }, - { - "ordinal": 1, - "name": "len", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - null, - null - ] - }, - "hash": "f9e0e35b4789a4da89f7bb21fa6b250fd64ee0e0b299471979db5ff8ee906929" -} diff --git a/backend/.sqlx/query-07da723ce5c9ee2d7c236e8eabe254c783fc34b617c8a9a95a0eb0cda535dab5.json b/backend/.sqlx/query-f9f7f31390f8ea1f4facd8e6a888886ea136f2327169bf93f126e2ef130d0946.json similarity index 56% rename from backend/.sqlx/query-07da723ce5c9ee2d7c236e8eabe254c783fc34b617c8a9a95a0eb0cda535dab5.json rename to backend/.sqlx/query-f9f7f31390f8ea1f4facd8e6a888886ea136f2327169bf93f126e2ef130d0946.json index a3bc7c1ef6..a97a131baa 100644 --- a/backend/.sqlx/query-07da723ce5c9ee2d7c236e8eabe254c783fc34b617c8a9a95a0eb0cda535dab5.json +++ b/backend/.sqlx/query-f9f7f31390f8ea1f4facd8e6a888886ea136f2327169bf93f126e2ef130d0946.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO capture (workspace_id, path, is_flow, trigger_kind, payload, trigger_extra, created_by)\n VALUES ($1, $2, $3, $4, $5, $6, $7)", + "query": "\n INSERT INTO \n capture (\n workspace_id, path, is_flow, trigger_kind, payload, trigger_extra, created_by\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7\n )\n ", "describe": { "columns": [], "parameters": { @@ -18,7 +18,11 @@ "websocket", "kafka", "email", - "nats" + "nats", + "postgres", + "sqs", + "mqtt", + "gcp" ] } } @@ -30,5 +34,5 @@ }, "nullable": [] }, - "hash": "07da723ce5c9ee2d7c236e8eabe254c783fc34b617c8a9a95a0eb0cda535dab5" + "hash": "f9f7f31390f8ea1f4facd8e6a888886ea136f2327169bf93f126e2ef130d0946" } diff --git a/backend/.sqlx/query-f9fc0084fe086ef80005bb64a8bb6b493e53583017c18e2ab44f44125c52d548.json b/backend/.sqlx/query-f9fc0084fe086ef80005bb64a8bb6b493e53583017c18e2ab44f44125c52d548.json deleted file mode 100644 index bad609fb21..0000000000 --- a/backend/.sqlx/query-f9fc0084fe086ef80005bb64a8bb6b493e53583017c18e2ab44f44125c52d548.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO completed_job AS cj\n ( workspace_id\n , id\n , parent_job\n , created_by\n , created_at\n , started_at\n , duration_ms\n , success\n , script_hash\n , script_path\n , args\n , result\n , raw_code\n , raw_lock\n , canceled\n , canceled_by\n , canceled_reason\n , job_kind\n , schedule_path\n , permissioned_as\n , flow_status\n , raw_flow\n , is_flow_step\n , is_skipped\n , language\n , email\n , visible_to_owner\n , mem_peak\n , tag\n , priority\n )\n VALUES ($1, $2, $3, $4, $5, COALESCE($6, now()), COALESCE($30::bigint, (EXTRACT('epoch' FROM (now())) - EXTRACT('epoch' FROM (COALESCE($6, now()))))*1000), $7, $8, $9,$10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29)\n ON CONFLICT (id) DO UPDATE SET success = $7, result = $11 RETURNING duration_ms", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "duration_ms", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Uuid", - "Uuid", - "Varchar", - "Timestamptz", - "Timestamptz", - "Bool", - "Int8", - "Varchar", - "Jsonb", - "Jsonb", - "Text", - "Text", - "Bool", - "Varchar", - "Text", - { - "Custom": { - "name": "job_kind", - "kind": { - "Enum": [ - "script", - "preview", - "flow", - "dependencies", - "flowpreview", - "script_hub", - "identity", - "flowdependencies", - "http", - "graphql", - "postgresql", - "noop", - "appdependencies", - "deploymentcallback", - "singlescriptflow", - "flowscript", - "flownode", - "appscript" - ] - } - } - }, - "Varchar", - "Varchar", - "Jsonb", - "Jsonb", - "Bool", - "Bool", - { - "Custom": { - "name": "script_lang", - "kind": { - "Enum": [ - "python3", - "deno", - "go", - "bash", - "postgresql", - "nativets", - "bun", - "mysql", - "bigquery", - "snowflake", - "graphql", - "powershell", - "mssql", - "php", - "bunnative", - "rust", - "ansible", - "csharp", - "oracledb" - ] - } - } - }, - "Varchar", - "Bool", - "Int4", - "Varchar", - "Int2", - "Int8" - ] - }, - "nullable": [ - false - ] - }, - "hash": "f9fc0084fe086ef80005bb64a8bb6b493e53583017c18e2ab44f44125c52d548" -} diff --git a/backend/.sqlx/query-fa59674af1d1a4ceb696fc883005ef114772f7d2ee0f60cb1358cdb7f0b5cd0c.json b/backend/.sqlx/query-fa59674af1d1a4ceb696fc883005ef114772f7d2ee0f60cb1358cdb7f0b5cd0c.json deleted file mode 100644 index 917a0910cc..0000000000 --- a/backend/.sqlx/query-fa59674af1d1a4ceb696fc883005ef114772f7d2ee0f60cb1358cdb7f0b5cd0c.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT EXISTS (SELECT 1 \n FROM workspace_settings \n WHERE workspace_id <> $1 \n AND slack_command_script IS NOT NULL\n AND slack_team_id = $2\n AND (SELECT slack_command_script IS NOT NULL FROM workspace_settings WHERE workspace_id = $1))\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "fa59674af1d1a4ceb696fc883005ef114772f7d2ee0f60cb1358cdb7f0b5cd0c" -} diff --git a/backend/.sqlx/query-faa0e401e6beebde6c3fef06151d3e73a5806f61cae4a53b5bdc888ec7164395.json b/backend/.sqlx/query-faa0e401e6beebde6c3fef06151d3e73a5806f61cae4a53b5bdc888ec7164395.json new file mode 100644 index 0000000000..2824316ed3 --- /dev/null +++ b/backend/.sqlx/query-faa0e401e6beebde6c3fef06151d3e73a5806f61cae4a53b5bdc888ec7164395.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DROP INDEX CONCURRENTLY IF EXISTS ix_job_workspace_id_created_at_new_6", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "faa0e401e6beebde6c3fef06151d3e73a5806f61cae4a53b5bdc888ec7164395" +} diff --git a/backend/.sqlx/query-e03b8e0360ed7c282742b9b8657abdeecb3fec75bf10773544339bd025fc45bb.json b/backend/.sqlx/query-faf2c77242e0ab39b33886edf3b742531bf1351d0be1c3631bde0adfe375497a.json similarity index 55% rename from backend/.sqlx/query-e03b8e0360ed7c282742b9b8657abdeecb3fec75bf10773544339bd025fc45bb.json rename to backend/.sqlx/query-faf2c77242e0ab39b33886edf3b742531bf1351d0be1c3631bde0adfe375497a.json index e17fd3f203..ea798bab76 100644 --- a/backend/.sqlx/query-e03b8e0360ed7c282742b9b8657abdeecb3fec75bf10773544339bd025fc45bb.json +++ b/backend/.sqlx/query-faf2c77242e0ab39b33886edf3b742531bf1351d0be1c3631bde0adfe375497a.json @@ -1,11 +1,11 @@ { "db_name": "PostgreSQL", - "query": "SELECT script_path FROM completed_job WHERE id = $1", + "query": "SELECT tag FROM v2_job WHERE id = $1", "describe": { "columns": [ { "ordinal": 0, - "name": "script_path", + "name": "tag", "type_info": "Varchar" } ], @@ -15,8 +15,8 @@ ] }, "nullable": [ - true + false ] }, - "hash": "e03b8e0360ed7c282742b9b8657abdeecb3fec75bf10773544339bd025fc45bb" + "hash": "faf2c77242e0ab39b33886edf3b742531bf1351d0be1c3631bde0adfe375497a" } diff --git a/backend/.sqlx/query-fb1a32318b35ec5c8129eb3660b79eb5e6d1e01fcf01cc05d3c7ddf47295c2f5.json b/backend/.sqlx/query-fb1a32318b35ec5c8129eb3660b79eb5e6d1e01fcf01cc05d3c7ddf47295c2f5.json deleted file mode 100644 index 6997a5e2f4..0000000000 --- a/backend/.sqlx/query-fb1a32318b35ec5c8129eb3660b79eb5e6d1e01fcf01cc05d3c7ddf47295c2f5.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT raw_code, raw_lock, raw_flow AS \"raw_flow: Json>\"\n FROM job WHERE id = $1 AND workspace_id = $2 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "raw_code", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "raw_lock", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "raw_flow: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true, - true - ] - }, - "hash": "fb1a32318b35ec5c8129eb3660b79eb5e6d1e01fcf01cc05d3c7ddf47295c2f5" -} diff --git a/backend/.sqlx/query-fc932ecf697b921fe9143c36872e9cc4e9aca7f9129b0466aacae51334a3db96.json b/backend/.sqlx/query-fc932ecf697b921fe9143c36872e9cc4e9aca7f9129b0466aacae51334a3db96.json new file mode 100644 index 0000000000..091d205b40 --- /dev/null +++ b/backend/.sqlx/query-fc932ecf697b921fe9143c36872e9cc4e9aca7f9129b0466aacae51334a3db96.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n capture_config \n SET \n server_id = $1,\n last_server_ping = now(), \n error = 'Connecting...' \n WHERE \n last_client_ping > NOW() - INTERVAL '10 seconds' AND \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'mqtt' AND \n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') \n RETURNING true\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [ + null + ] + }, + "hash": "fc932ecf697b921fe9143c36872e9cc4e9aca7f9129b0466aacae51334a3db96" +} diff --git a/backend/.sqlx/query-fce269376a0f08cb39359a3cb86be6b9823af022195b80db8ccbc3737576462c.json b/backend/.sqlx/query-fce269376a0f08cb39359a3cb86be6b9823af022195b80db8ccbc3737576462c.json deleted file mode 100644 index 367f01ba75..0000000000 --- a/backend/.sqlx/query-fce269376a0f08cb39359a3cb86be6b9823af022195b80db8ccbc3737576462c.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO v2_job_completed AS cj\n ( workspace_id\n , id\n , started_at\n , duration_ms\n , result\n , result_columns\n , canceled_by\n , canceled_reason\n , flow_status\n , memory_peak\n , status\n )\n VALUES ($1, $2, $3, COALESCE($12::bigint, (EXTRACT('epoch' FROM (now())) - EXTRACT('epoch' FROM (COALESCE($3, now()))))*1000), $5, $13, $7, $8, $9,$11, CASE WHEN $6::BOOL THEN 'canceled'::job_status\n WHEN $10::BOOL THEN 'skipped'::job_status\n WHEN $4::BOOL THEN 'success'::job_status\n ELSE 'failure'::job_status END)\n ON CONFLICT (id) DO UPDATE SET status = EXCLUDED.status, result = $5 RETURNING duration_ms AS \"duration_ms!\"", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "duration_ms!", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Uuid", - "Timestamptz", - "Bool", - "Jsonb", - "Bool", - "Varchar", - "Text", - "Jsonb", - "Bool", - "Int4", - "Int8", - "TextArray" - ] - }, - "nullable": [ - false - ] - }, - "hash": "fce269376a0f08cb39359a3cb86be6b9823af022195b80db8ccbc3737576462c" -} diff --git a/backend/.sqlx/query-fd0a0ca4a107dc813240ad71f372ab7e0bf26431158fbfb5f0023ed847ca7dc5.json b/backend/.sqlx/query-fd0a0ca4a107dc813240ad71f372ab7e0bf26431158fbfb5f0023ed847ca7dc5.json new file mode 100644 index 0000000000..d47bb3fd68 --- /dev/null +++ b/backend/.sqlx/query-fd0a0ca4a107dc813240ad71f372ab7e0bf26431158fbfb5f0023ed847ca7dc5.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n gcp_trigger \n SET \n enabled = FALSE, \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "fd0a0ca4a107dc813240ad71f372ab7e0bf26431158fbfb5f0023ed847ca7dc5" +} diff --git a/backend/.sqlx/query-fd5754fe3c6346ae28818a9d60d144a40f8884f47e5bbdd2824e939dafd8f154.json b/backend/.sqlx/query-fd5754fe3c6346ae28818a9d60d144a40f8884f47e5bbdd2824e939dafd8f154.json new file mode 100644 index 0000000000..e797ab8805 --- /dev/null +++ b/backend/.sqlx/query-fd5754fe3c6346ae28818a9d60d144a40f8884f47e5bbdd2824e939dafd8f154.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n schemaname AS schema_name,\n tablename AS table_name,\n CASE\n WHEN array_length(attnames, 1) = (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = pg_publication_tables.schemaname AND table_name = pg_publication_tables.tablename)\n THEN NULL\n ELSE attnames\n END AS columns,\n rowfilter AS where_clause\n FROM\n pg_publication_tables\n WHERE\n pubname = $1;\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "schema_name", + "type_info": "Name" + }, + { + "ordinal": 1, + "name": "table_name", + "type_info": "Name" + }, + { + "ordinal": 2, + "name": "columns", + "type_info": "NameArray" + }, + { + "ordinal": 3, + "name": "where_clause", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Name" + ] + }, + "nullable": [ + true, + true, + null, + true + ] + }, + "hash": "fd5754fe3c6346ae28818a9d60d144a40f8884f47e5bbdd2824e939dafd8f154" +} diff --git a/backend/.sqlx/query-fe5e941310ffdbbe7cc5f3e2beb213999ff64bbe4f8e2532a7aa48184cb008e7.json b/backend/.sqlx/query-fe5e941310ffdbbe7cc5f3e2beb213999ff64bbe4f8e2532a7aa48184cb008e7.json deleted file mode 100644 index ca0ef918e9..0000000000 --- a/backend/.sqlx/query-fe5e941310ffdbbe7cc5f3e2beb213999ff64bbe4f8e2532a7aa48184cb008e7.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE completed_job SET schedule_path = REGEXP_REPLACE(schedule_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE schedule_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "fe5e941310ffdbbe7cc5f3e2beb213999ff64bbe4f8e2532a7aa48184cb008e7" -} diff --git a/backend/.sqlx/query-fe7221651a982861dede4116bc71fe2dce615ff76a53f72cb5386dc17e4e07aa.json b/backend/.sqlx/query-fe7221651a982861dede4116bc71fe2dce615ff76a53f72cb5386dc17e4e07aa.json new file mode 100644 index 0000000000..1ca60067d0 --- /dev/null +++ b/backend/.sqlx/query-fe7221651a982861dede4116bc71fe2dce615ff76a53f72cb5386dc17e4e07aa.json @@ -0,0 +1,21 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) \n SELECT unnest($1::uuid[]), $2, $3, $4, $5, $6, $7, $8", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "UuidArray", + "Varchar", + "Varchar", + "Bool", + "Bool", + "JsonbArray", + "TextArray", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "fe7221651a982861dede4116bc71fe2dce615ff76a53f72cb5386dc17e4e07aa" +} diff --git a/backend/.sqlx/query-0db43daf8072957b980ac01a56e0e9179b466296f33cd753f58986aee445e2f7.json b/backend/.sqlx/query-fec6d5674dc6b5a6a0ece419c40508835affcb7679a48f2a443777e829bd1e74.json similarity index 78% rename from backend/.sqlx/query-0db43daf8072957b980ac01a56e0e9179b466296f33cd753f58986aee445e2f7.json rename to backend/.sqlx/query-fec6d5674dc6b5a6a0ece419c40508835affcb7679a48f2a443777e829bd1e74.json index 49aaa3f941..de2e819af2 100644 --- a/backend/.sqlx/query-0db43daf8072957b980ac01a56e0e9179b466296f33cd753f58986aee445e2f7.json +++ b/backend/.sqlx/query-fec6d5674dc6b5a6a0ece419c40508835affcb7679a48f2a443777e829bd1e74.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n workspace.id AS \"id!\",\n workspace.name AS \"name!\",\n workspace.owner AS \"owner!\",\n workspace.deleted AS \"deleted!\",\n workspace.premium AS \"premium!\",\n workspace_settings.color AS \"color!\"\n FROM workspace\n LEFT JOIN workspace_settings ON workspace.id = workspace_settings.workspace_id\n LIMIT $1 OFFSET $2", + "query": "SELECT\n workspace.id AS \"id!\",\n workspace.name AS \"name!\",\n workspace.owner AS \"owner!\",\n workspace.deleted AS \"deleted!\",\n workspace.premium AS \"premium!\",\n workspace_settings.color AS \"color\"\n FROM workspace\n LEFT JOIN workspace_settings ON workspace.id = workspace_settings.workspace_id\n LIMIT $1 OFFSET $2", "describe": { "columns": [ { @@ -30,7 +30,7 @@ }, { "ordinal": 5, - "name": "color!", + "name": "color", "type_info": "Varchar" } ], @@ -49,5 +49,5 @@ true ] }, - "hash": "0db43daf8072957b980ac01a56e0e9179b466296f33cd753f58986aee445e2f7" + "hash": "fec6d5674dc6b5a6a0ece419c40508835affcb7679a48f2a443777e829bd1e74" } diff --git a/backend/.sqlx/query-ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238.json b/backend/.sqlx/query-ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238.json index 6a4a3b3d75..c7644fa79a 100644 --- a/backend/.sqlx/query-ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238.json +++ b/backend/.sqlx/query-ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238.json @@ -63,7 +63,9 @@ "rust", "ansible", "csharp", - "oracledb" + "oracledb", + "nu", + "java" ] } } diff --git a/backend/.sqlx/query-ff14230469026418966ec79b77f549b2fb27c90556484f3914666d5ad7f8f107.json b/backend/.sqlx/query-ff14230469026418966ec79b77f549b2fb27c90556484f3914666d5ad7f8f107.json deleted file mode 100644 index 1744aa7167..0000000000 --- a/backend/.sqlx/query-ff14230469026418966ec79b77f549b2fb27c90556484f3914666d5ad7f8f107.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n result #> $3 AS \"result: sqlx::types::Json>\",\n flow_status AS \"flow_status: sqlx::types::Json>\",\n language AS \"language: ScriptLang\",\n created_by AS \"created_by!\"\n FROM completed_job\n WHERE id = $1 AND workspace_id = $2 AND ($4::text[] IS NULL OR tag = ANY($4))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "result: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 1, - "name": "flow_status: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 2, - "name": "language: ScriptLang", - "type_info": { - "Custom": { - "name": "script_lang", - "kind": { - "Enum": [ - "python3", - "deno", - "go", - "bash", - "postgresql", - "nativets", - "bun", - "mysql", - "bigquery", - "snowflake", - "graphql", - "powershell", - "mssql", - "php", - "bunnative", - "rust", - "ansible", - "csharp", - "oracledb" - ] - } - } - } - }, - { - "ordinal": 3, - "name": "created_by!", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "TextArray", - "TextArray" - ] - }, - "nullable": [ - null, - true, - true, - true - ] - }, - "hash": "ff14230469026418966ec79b77f549b2fb27c90556484f3914666d5ad7f8f107" -} diff --git a/backend/.vscode/settings.json b/backend/.vscode/settings.json index ab8340f5c8..6ebd36cf07 100644 --- a/backend/.vscode/settings.json +++ b/backend/.vscode/settings.json @@ -11,5 +11,5 @@ "remote.autoForwardPorts": true, "conventionalCommits.scopes": [ "restructring triggers, decoding trigger message on work" - ], + ] } diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 13c8c06a15..9e6d454605 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -33,6 +33,30 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aead-gcm-stream" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70c8dec860340effb00f6945c49c0daaa6dac963602750db862eabb74bf7886" +dependencies = [ + "aead", + "aes 0.8.3", + "cipher 0.4.4", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "aes" version = "0.7.5" @@ -40,18 +64,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ "cfg-if", - "cipher", + "cipher 0.3.0", "cpufeatures", "opaque-debug", ] +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher 0.4.4", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes 0.8.3", + "cipher 0.4.4", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aes-kw" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" +dependencies = [ + "aes 0.8.3", +] + [[package]] name = "ahash" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "version_check", ] @@ -64,7 +122,7 @@ checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "const-random", - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "version_check", "zerocopy 0.7.35", @@ -167,9 +225,18 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] [[package]] name = "arc-swap" @@ -215,12 +282,15 @@ name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] [[package]] name = "arrow" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05048a8932648b63f21c37d88b552ccc8a65afb6dfe9fc9f30ce79174c2e7a85" +checksum = "3095aaf545942ff5abd46654534f15b03a90fba78299d661e045e5d587222f0d" dependencies = [ "arrow-arith", "arrow-array", @@ -239,41 +309,40 @@ dependencies = [ [[package]] name = "arrow-arith" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d8a57966e43bfe9a3277984a14c24ec617ad874e4c0e1d2a1b083a39cfbf22c" +checksum = "00752064ff47cee746e816ddb8450520c3a52cbad1e256f6fa861a35f86c45e7" dependencies = [ "arrow-array", "arrow-buffer", "arrow-data", "arrow-schema", "chrono", - "half", "num", ] [[package]] name = "arrow-array" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f4a9468c882dc66862cef4e1fd8423d47e67972377d85d80e022786427768c" +checksum = "cebfe926794fbc1f49ddd0cdaf898956ca9f6e79541efce62dabccfd81380472" dependencies = [ "ahash 0.8.11", "arrow-buffer", "arrow-data", "arrow-schema", "chrono", - "chrono-tz 0.9.0", + "chrono-tz", "half", - "hashbrown 0.14.5", + "hashbrown 0.15.3", "num", ] [[package]] name = "arrow-buffer" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c975484888fc95ec4a632cdc98be39c085b1bb518531b0c80c5d462063e5daa1" +checksum = "0303c7ec4cf1a2c60310fc4d6bbc3350cd051a17bf9e9c0a8e47b4db79277824" dependencies = [ "bytes", "half", @@ -282,9 +351,9 @@ dependencies = [ [[package]] name = "arrow-cast" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da26719e76b81d8bc3faad1d4dbdc1bcc10d14704e63dc17fc9f3e7e1e567c8e" +checksum = "335f769c5a218ea823d3760a743feba1ef7857cba114c01399a891c2fff34285" dependencies = [ "arrow-array", "arrow-buffer", @@ -303,28 +372,25 @@ dependencies = [ [[package]] name = "arrow-csv" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13c36dc5ddf8c128df19bab27898eea64bf9da2b555ec1cd17a8ff57fba9ec2" +checksum = "510db7dfbb4d5761826516cc611d97b3a68835d0ece95b034a052601109c0b1b" dependencies = [ "arrow-array", - "arrow-buffer", "arrow-cast", - "arrow-data", "arrow-schema", "chrono", "csv", "csv-core", "lazy_static", - "lexical-core", "regex", ] [[package]] name = "arrow-data" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd9d6f18c65ef7a2573ab498c374d8ae364b4a4edf67105357491c031f716ca5" +checksum = "e8affacf3351a24039ea24adab06f316ded523b6f8c3dbe28fbac5f18743451b" dependencies = [ "arrow-buffer", "arrow-schema", @@ -334,13 +400,12 @@ dependencies = [ [[package]] name = "arrow-ipc" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e786e1cdd952205d9a8afc69397b317cfbb6e0095e445c69cda7e8da5c1eeb0f" +checksum = "69880a9e6934d9cba2b8630dd08a3463a91db8693b16b499d54026b6137af284" dependencies = [ "arrow-array", "arrow-buffer", - "arrow-cast", "arrow-data", "arrow-schema", "flatbuffers", @@ -349,9 +414,9 @@ dependencies = [ [[package]] name = "arrow-json" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb22284c5a2a01d73cebfd88a33511a3234ab45d66086b2ca2d1228c3498e445" +checksum = "d8dafd17a05449e31e0114d740530e0ada7379d7cb9c338fd65b09a8130960b0" dependencies = [ "arrow-array", "arrow-buffer", @@ -360,35 +425,34 @@ dependencies = [ "arrow-schema", "chrono", "half", - "indexmap 2.7.1", + "indexmap 2.9.0", "lexical-core", + "memchr", "num", "serde", "serde_json", + "simdutf8", ] [[package]] name = "arrow-ord" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42745f86b1ab99ef96d1c0bcf49180848a64fe2c7a7a0d945bc64fa2b21ba9bc" +checksum = "895644523af4e17502d42c3cb6b27cb820f0cb77954c22d75c23a85247c849e1" dependencies = [ "arrow-array", "arrow-buffer", "arrow-data", "arrow-schema", "arrow-select", - "half", - "num", ] [[package]] name = "arrow-row" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd09a518c602a55bd406bcc291a967b284cfa7a63edfbf8b897ea4748aad23c" +checksum = "9be8a2a4e5e7d9c822b2b8095ecd77010576d824f654d347817640acfc97d229" dependencies = [ - "ahash 0.8.11", "arrow-array", "arrow-buffer", "arrow-data", @@ -398,15 +462,15 @@ dependencies = [ [[package]] name = "arrow-schema" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e972cd1ff4a4ccd22f86d3e53e835c2ed92e0eea6a3e8eadb72b4f1ac802cf8" +checksum = "7450c76ab7c5a6805be3440dc2e2096010da58f7cab301fdc996a4ee3ee74e49" [[package]] name = "arrow-select" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600bae05d43483d216fb3494f8c32fdbefd8aa4e1de237e790dbb3d9f44690a3" +checksum = "aa5f5a93c75f46ef48e4001535e7b6c922eeb0aa20b73cf58d09e13d057490d8" dependencies = [ "ahash 0.8.11", "arrow-array", @@ -418,9 +482,9 @@ dependencies = [ [[package]] name = "arrow-string" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc1985b67cb45f6606a248ac2b4a288849f196bab8c657ea5589f47cdd55e6" +checksum = "6e7005d858d84b56428ba2a98a107fe88c0132c61793cf6b8232a1f9bfc0452b" dependencies = [ "arrow-array", "arrow-buffer", @@ -433,6 +497,54 @@ dependencies = [ "regex-syntax 0.8.5", ] +[[package]] +name = "ash" +version = "0.37.3+1.3.251" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +dependencies = [ + "libloading 0.7.4", +] + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom 7.1.3", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure 0.12.6", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ast_node" version = "0.9.9" @@ -442,7 +554,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -458,9 +570,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +checksum = "06575e6a9673580f52661c92107baabffbf41e2141373441cbcdc47cb733003c" dependencies = [ "brotli 7.0.0", "bzip2", @@ -492,7 +604,7 @@ dependencies = [ "portable-atomic", "rand 0.8.5", "regex", - "ring 0.17.8", + "ring 0.17.14", "rustls-native-certs 0.7.3", "rustls-pemfile 2.2.0", "rustls-webpki 0.102.8", @@ -503,7 +615,7 @@ dependencies = [ "thiserror 1.0.69", "time", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tokio-util", "tokio-websockets", "tracing", @@ -519,17 +631,23 @@ checksum = "021cf450e9574793e45e1044a5d3d94bba7dbaa0802e6122e9c10eb8c4dd12dc" dependencies = [ "base64 0.22.1", "bytes", - "http 1.2.0", + "http 1.3.1", "rand 0.8.5", - "reqwest 0.12.9", + "reqwest 0.12.15", "serde", "serde-aux", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "thiserror 1.0.69", "url", ] +[[package]] +name = "async-once-cell" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" + [[package]] name = "async-recursion" version = "1.1.1" @@ -538,7 +656,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -560,43 +678,18 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", -] - -[[package]] -name = "async-stripe" -version = "0.39.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58d670cf4d47a1b8ffef54286a5625382e360a34ee76902fd93ad8c7032a0c30" -dependencies = [ - "chrono", - "futures-util", - "hex", - "hmac", - "http-types", - "hyper 0.14.32", - "hyper-tls 0.5.0", - "serde", - "serde_json", - "serde_path_to_error", - "serde_qs 0.10.1", - "sha2 0.10.8", - "smart-default", - "smol_str", - "thiserror 1.0.69", - "tokio", - "uuid 0.8.2", + "syn 2.0.101", ] [[package]] name = "async-trait" -version = "0.1.86" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -608,7 +701,7 @@ dependencies = [ "async-compression", "chrono", "crc32fast", - "futures-lite 2.6.0", + "futures-lite", "pin-project", "thiserror 1.0.69", "tokio", @@ -651,9 +744,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-config" -version = "1.5.16" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50236e4d60fe8458de90a71c0922c761e41755adf091b1b03de1cef537179915" +checksum = "b6fcc63c9860579e4cb396239570e979376e70aab79e496621748a09913f8b36" dependencies = [ "aws-credential-types", "aws-runtime", @@ -668,10 +761,10 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.3.0", + "fastrand", "hex", - "http 0.2.12", - "ring 0.17.8", + "http 1.3.1", + "ring 0.17.14", "time", "tokio", "tracing", @@ -681,9 +774,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" +checksum = "687bc16bc431a8533fe0097c7f0182874767f920989d7260950172ae8e3c4465" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -692,10 +785,33 @@ dependencies = [ ] [[package]] -name = "aws-runtime" -version = "1.5.5" +name = "aws-lc-rs" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76dd04d39cc12844c0994f2c9c5a6f5184c22e9188ec1ff723de41910a21dcad" +checksum = "19b756939cb2f8dc900aa6dcd505e6e2428e9cae7ff7b028c49e3946efa70878" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa9b6986f250236c27e5a204062434a773a13243d2ffc2955f37bdba4c5c6a1" +dependencies = [ + "bindgen 0.69.5", + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "aws-runtime" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c4063282c69991e57faab9e5cb21ae557e59f5b0fb285c196335243df8dc25c" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -706,21 +822,20 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.3.0", + "fastrand", "http 0.2.12", "http-body 0.4.6", - "once_cell", "percent-encoding", "pin-project-lite", "tracing", - "uuid 1.13.1", + "uuid", ] [[package]] -name = "aws-sdk-sso" -version = "1.58.0" +name = "aws-sdk-sqs" +version = "1.66.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ff718c9ee45cc1ebd4774a0e086bb80a6ab752b4902edf1c9f56b86ee1f770" +checksum = "0e4ebe6d9cfbb2cdb6641ce776ca3bea870c12d5480ffeb6b3c3653bf9c07959" dependencies = [ "aws-credential-types", "aws-runtime", @@ -732,6 +847,30 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", + "fastrand", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.66.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "858007b14d0f1ade2e0124473c2126b24d334dc9486ad12eb7c0ed14757be464" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", "http 0.2.12", "once_cell", "regex-lite", @@ -740,9 +879,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.59.0" +version = "1.67.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5183e088715cc135d8d396fdd3bc02f018f0da4c511f53cb8d795b6a31c55809" +checksum = "b83abf3ae8bd10a014933cc2383964a12ca5a3ebbe1948ad26b1b808e7d0d1f2" dependencies = [ "aws-credential-types", "aws-runtime", @@ -754,6 +893,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", + "fastrand", "http 0.2.12", "once_cell", "regex-lite", @@ -762,9 +902,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.59.0" +version = "1.67.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9f944ef032717596639cea4a2118a3a457268ef51bbb5fde9637e54c465da00" +checksum = "74e8e9ac4a837859c8f1d747054172e1e55933f02ed34728b0b34dea0591ec84" dependencies = [ "aws-credential-types", "aws-runtime", @@ -777,6 +917,7 @@ dependencies = [ "aws-smithy-types", "aws-smithy-xml", "aws-types", + "fastrand", "http 0.2.12", "once_cell", "regex-lite", @@ -785,9 +926,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.8" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bc5bbd1e4a2648fd8c5982af03935972c24a2f9846b396de661d351ee3ce837" +checksum = "3503af839bd8751d0bdc5a46b9cac93a003a353e635b0c12cf2376b5b53e41ea" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -798,19 +939,18 @@ dependencies = [ "hex", "hmac", "http 0.2.12", - "http 1.2.0", - "once_cell", + "http 1.3.1", "percent-encoding", - "sha2 0.10.8", + "sha2 0.10.9", "time", "tracing", ] [[package]] name = "aws-smithy-async" -version = "1.2.4" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa59d1327d8b5053c54bf2eaae63bf629ba9e904434d0835a28ed3c0ed0a614e" +checksum = "1e190749ea56f8c42bf15dd76c65e14f8f765233e6df9b0506d9d934ebef867c" dependencies = [ "futures-util", "pin-project-lite", @@ -819,9 +959,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.60.12" +version = "0.62.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7809c27ad8da6a6a68c454e651d4962479e81472aa19ae99e59f9aba1f9713cc" +checksum = "99335bec6cdc50a346fda1437f9fefe33abf8c99060739a546a16457f2862ca9" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -829,8 +969,8 @@ dependencies = [ "bytes-utils", "futures-core", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", - "once_cell", "percent-encoding", "pin-project-lite", "pin-utils", @@ -838,14 +978,51 @@ dependencies = [ ] [[package]] -name = "aws-smithy-json" -version = "0.61.2" +name = "aws-smithy-http-client" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "623a51127f24c30776c8b374295f2df78d92517386f77ba30773f15a30ce1422" +checksum = "8aff1159006441d02e57204bf57a1b890ba68bedb6904ffd2873c1c4c11c546b" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "h2 0.4.10", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper 1.6.0", + "hyper-rustls 0.24.2", + "hyper-rustls 0.27.5", + "hyper-util", + "pin-project-lite", + "rustls 0.21.12", + "rustls 0.23.27", + "rustls-native-certs 0.8.1", + "rustls-pki-types", + "tokio", + "tower 0.5.2", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92144e45819cae7dc62af23eac5a038a58aa544432d2102609654376a900bd07" dependencies = [ "aws-smithy-types", ] +[[package]] +name = "aws-smithy-observability" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9364d5989ac4dd918e5cc4c4bdcc61c9be17dcd2586ea7f69e348fc7c6cab393" +dependencies = [ + "aws-smithy-runtime-api", +] + [[package]] name = "aws-smithy-query" version = "0.60.7" @@ -858,42 +1035,39 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.8" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d526a12d9ed61fadefda24abe2e682892ba288c2018bcb38b1b4c111d13f6d92" +checksum = "14302f06d1d5b7d333fd819943075b13d27c7700b414f574c3c35859bfb55d5e" dependencies = [ "aws-smithy-async", "aws-smithy-http", + "aws-smithy-http-client", + "aws-smithy-observability", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", - "fastrand 2.3.0", - "h2 0.3.26", + "fastrand", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", - "httparse", - "hyper 0.14.32", - "hyper-rustls 0.24.2", - "once_cell", "pin-project-lite", "pin-utils", - "rustls 0.21.12", "tokio", "tracing", ] [[package]] name = "aws-smithy-runtime-api" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92165296a47a812b267b4f41032ff8069ab7ff783696d217f0994a0d7ab585cd" +checksum = "a1e5d9e3a80a18afa109391fb5ad09c3daf887b516c6fd805a157c6ea7994a57" dependencies = [ "aws-smithy-async", "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.2.0", + "http 1.3.1", "pin-project-lite", "tokio", "tracing", @@ -902,16 +1076,16 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.13" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7b8a53819e42f10d0821f56da995e1470b199686a1809168db6ca485665f042" +checksum = "40076bd09fadbc12d5e026ae080d0930defa606856186e31d83ccc6a255eeaf3" dependencies = [ "base64-simd 0.8.0", "bytes", "bytes-utils", "futures-core", "http 0.2.12", - "http 1.2.0", + "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -937,9 +1111,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.5" +version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbd0a668309ec1f66c0f6bda4840dd6d4796ae26d699ebc266d7cc95c6d040f" +checksum = "8a322fec39e4df22777ed3ad8ea868ac2f94cd15e1a55f6ee8d8d6305057689a" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -959,7 +1133,7 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "hyper 1.6.0", @@ -993,7 +1167,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "mime", @@ -1006,26 +1180,32 @@ dependencies = [ ] [[package]] -name = "backon" -version = "1.3.0" +name = "az" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5289ec98f68f28dd809fd601059e6aa908bb8f6108620930828283d4ee23d7" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + +[[package]] +name = "backon" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd0b50b1b78dbadd44ab18b3c794e496f3a139abb9fbc27d9c94c4eebbb96496" dependencies = [ - "fastrand 2.3.0", + "fastrand", "gloo-timers", "tokio", ] [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", "libc", - "miniz_oxide 0.8.3", + "miniz_oxide 0.8.8", "object", "rustc-demangle", "windows-targets 0.52.6", @@ -1082,9 +1262,9 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "better_scoped_tls" @@ -1097,9 +1277,9 @@ dependencies = [ [[package]] name = "bigdecimal" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f31f3af01c5c65a07985c804d3366560e6fa7883d640a122819b14ec327482c" +checksum = "1a22f228ab7a1b23027ccc6c350b72868017af7ea8356fbdf19f8d991c690013" dependencies = [ "autocfg", "libm", @@ -1123,20 +1303,20 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools 0.12.1", "lazy_static", "lazycell", "log", - "prettyplease 0.2.29", + "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.98", + "syn 2.0.101", "which 4.4.2", ] @@ -1146,18 +1326,18 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools 0.13.0", "log", - "prettyplease 0.2.29", + "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1166,7 +1346,16 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "bit-vec", + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", ] [[package]] @@ -1175,6 +1364,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -1183,9 +1378,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" dependencies = [ "serde", ] @@ -1222,9 +1417,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.5" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ "arrayref", "arrayvec", @@ -1233,13 +1428,19 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + [[package]] name = "block-buffer" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding", + "block-padding 0.2.1", "generic-array", ] @@ -1258,8 +1459,8 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" dependencies = [ - "block-padding", - "cipher", + "block-padding 0.2.1", + "cipher 0.3.0", ] [[package]] @@ -1268,6 +1469,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + [[package]] name = "bollard" version = "0.18.1" @@ -1280,7 +1490,7 @@ dependencies = [ "futures-core", "futures-util", "hex", - "http 1.2.0", + "http 1.3.1", "http-body-util", "hyper 1.6.0", "hyper-named-pipe", @@ -1293,7 +1503,7 @@ dependencies = [ "serde_json", "serde_repr", "serde_urlencoded", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tokio-util", "tower-service", @@ -1314,25 +1524,35 @@ dependencies = [ [[package]] name = "borsh" -version = "1.5.5" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5430e3be710b68d984d1391c854eb431a9d548640711faa54eecb1df93db91cc" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" dependencies = [ "borsh-derive", - "cfg_aliases", + "cfg_aliases 0.2.1", ] [[package]] name = "borsh-derive" -version = "1.5.5" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b668d39970baad5356d7c83a86fee3a539e6f93bf6764c97368243e17a0487" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "boxed_error" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" +dependencies = [ + "quote", + "syn 2.0.101", ] [[package]] @@ -1359,14 +1579,24 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.2" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", ] +[[package]] +name = "bstr" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "btoi" version = "0.4.3" @@ -1376,16 +1606,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "built" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b99c4cdc7b2c2364182331055623bdf45254fcb679fea565c40c3c11c101889a" -dependencies = [ - "cargo-lock", - "git2", -] - [[package]] name = "bumpalo" version = "3.17.0" @@ -1395,6 +1615,17 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "byte-unit" +version = "5.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cd29c3c585209b0cbc7309bfe3ed7efd8c84c21b7af29c8bfae908f8777174" +dependencies = [ + "rust_decimal", + "serde", + "utf8-width", +] + [[package]] name = "bytecheck" version = "0.6.12" @@ -1419,22 +1650,22 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.21.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.8.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" +checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1445,9 +1676,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] @@ -1463,52 +1694,63 @@ dependencies = [ ] [[package]] -name = "bzip2" -version = "0.4.4" +name = "bytesize" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" + +[[package]] +name = "bzip2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" dependencies = [ "bzip2-sys", - "libc", ] [[package]] name = "bzip2-sys" -version = "0.1.11+1.0.8" +version = "0.1.13+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ "cc", - "libc", "pkg-config", ] [[package]] -name = "candle-core" -version = "0.3.3" +name = "cache_control" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db8659ea87ee8197d2fc627348916cce0561330ee7ae3874e771691d3cecb2f" +checksum = "1bf2a5fb3207c12b5d208ebc145f967fea5cac41a021c37417ccc31ba40f39ee" + +[[package]] +name = "candle-core" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9f51e2ecf6efe9737af8f993433c839f956d2b6ed4fd2dd4a7c6d8b0fa667ff" dependencies = [ "byteorder", - "gemm", + "gemm 0.17.1", "half", - "memmap2", + "memmap2 0.9.5", "num-traits", "num_cpus", - "rand 0.8.5", - "rand_distr", + "rand 0.9.0", + "rand_distr 0.5.1", "rayon", "safetensors", "thiserror 1.0.69", + "ug", "yoke", "zip", ] [[package]] name = "candle-nn" -version = "0.3.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ddce8312032760a6791d6adc9c56dc54fd7c1be38d85dcc4862f1c75228bbc7" +checksum = "c1980d53280c8f9e2c6cbe1785855d7ff8010208b46e21252b978badf13ad69d" dependencies = [ "candle-core", "half", @@ -1521,40 +1763,68 @@ dependencies = [ [[package]] name = "candle-transformers" -version = "0.3.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68834a0cacb7e002d1f4abfe26a7cd1237e2ba342fddcf2e30913c4edb96409d" +checksum = "186cb80045dbe47e0b387ea6d3e906f02fb3056297080d9922984c90e90a72b0" dependencies = [ "byteorder", "candle-core", "candle-nn", + "fancy-regex 0.13.0", "num-traits", - "rand 0.8.5", + "rand 0.9.0", "rayon", "serde", "serde_json", "serde_plain", "tracing", - "wav", ] [[package]] -name = "cargo-lock" -version = "9.0.0" +name = "capacity_builder" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11c675378efb449ed3ce8de78d75d0d80542fc98487c26aba28eb3b82feac72" +checksum = "58ec49028cb308564429cd8fac4ef21290067a0afe8f5955330a8d487d0d790c" dependencies = [ - "semver 1.0.25", - "serde", - "toml 0.7.8", - "url", + "itoa", +] + +[[package]] +name = "capacity_builder" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f2d24a6dcf0cd402a21b65d35340f3a49ff3475dc5fdac91d22d2733e6641c6" +dependencies = [ + "capacity_builder_macros", + "ecow", + "hipstr", + "itoa", +] + +[[package]] +name = "capacity_builder_macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" +dependencies = [ + "quote", + "syn 2.0.101", +] + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher 0.4.4", ] [[package]] name = "cc" -version = "1.2.12" +version = "1.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" dependencies = [ "jobserver", "libc", @@ -1573,7 +1843,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -1582,6 +1852,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "cfg_aliases" version = "0.2.1" @@ -1590,57 +1866,45 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", + "pure-rust-locales", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", +] + +[[package]] +name = "chrono-humanize" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b" +dependencies = [ + "chrono", ] [[package]] name = "chrono-tz" -version = "0.9.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" +checksum = "efdce149c370f133a071ca8ef6ea340b7b88748ab0810097a9e2976eaa34b4f3" dependencies = [ "chrono", - "chrono-tz-build 0.3.0", - "phf", -] - -[[package]] -name = "chrono-tz" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c6ac4f2c0bf0f44e9161aec9675e1050aa4a530663c4a9e37e108fa948bca9f" -dependencies = [ - "chrono", - "chrono-tz-build 0.4.0", + "chrono-tz-build", "phf", ] [[package]] name = "chrono-tz-build" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" -dependencies = [ - "parse-zoneinfo", - "phf", - "phf_codegen", -] - -[[package]] -name = "chrono-tz-build" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7" +checksum = "8f10f8c9340e31fc120ff885fcdb54a0b48e474bbd77cab557f0c30a3e569402" dependencies = [ "parse-zoneinfo", "phf_codegen", @@ -1655,6 +1919,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -1663,14 +1937,14 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading", + "libloading 0.8.6", ] [[package]] name = "clap" -version = "4.5.28" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", "clap_derive", @@ -1678,9 +1952,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.27" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstream", "anstyle", @@ -1690,14 +1964,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1707,14 +1981,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] -name = "cmake" -version = "0.1.53" +name = "clipboard-win" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24a03c8b52922d68a1589ad61032f2c1aa5a8158d2aa0d93c6e9534944bbad6" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +dependencies = [ + "error-code", +] + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" dependencies = [ "cc", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width 0.1.14", +] + +[[package]] +name = "color-print" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aa954171903797d5623e047d9ab69d91b493657917bdfb8c2c80ecaf9cdb6f4" +dependencies = [ + "color-print-proc-macro", +] + +[[package]] +name = "color-print-proc-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" +dependencies = [ + "nom 7.1.3", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.3" @@ -1723,12 +2043,11 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "comfy-table" -version = "7.1.3" +version = "7.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f165e7b643266ea80cb858aed492ad9280e3e05ce24d4a99d7d7b889b6a4d9" +checksum = "4a65ebfec4fb190b6f90e944a817d60499ee0744e582530e2c9900a22e591d9a" dependencies = [ - "strum 0.26.3", - "strum_macros 0.26.4", + "unicode-segmentation", "unicode-width 0.2.0", ] @@ -1749,9 +2068,9 @@ checksum = "510ca239cf13b7f8d16a2b48f263de7b4f8c566f0af58d901031473c76afb1e3" [[package]] name = "console" -version = "0.15.10" +version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" dependencies = [ "encode_unicode", "libc", @@ -1760,16 +2079,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -1791,7 +2100,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "tiny-keccak", ] @@ -1891,6 +2200,17 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -1953,24 +2273,11 @@ dependencies = [ "chrono", ] -[[package]] -name = "crossbeam" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -2009,6 +2316,15 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "crunchy" version = "0.2.3" @@ -2034,6 +2350,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] @@ -2051,13 +2368,22 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" dependencies = [ "memchr", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher 0.4.4", +] + [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -2068,7 +2394,7 @@ dependencies = [ "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", - "fiat-crypto", + "fiat-crypto 0.2.9", "rustc_version 0.4.1", "subtle", "zeroize", @@ -2082,7 +2408,18 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "d3d12" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813" +dependencies = [ + "bitflags 2.9.0", + "libloading 0.8.6", + "winapi", ] [[package]] @@ -2107,12 +2444,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core 0.20.10", - "darling_macro 0.20.10", + "darling_core 0.20.11", + "darling_macro 0.20.11", ] [[package]] @@ -2145,16 +2482,16 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -2181,13 +2518,13 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core 0.20.10", + "darling_core 0.20.11", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -2200,122 +2537,311 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core 0.9.10", ] [[package]] name = "data-encoding" -version = "2.7.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "data-url" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b319d1b62ffbd002e057f36bebd1f42b9f97927c9577461d855f3513c4289f" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "datafusion" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f92d2d7a9cba4580900b32b009848d9eb35f1028ac84cdd6ddcf97612cd0068" +checksum = "ffe060b978f74ab446be722adb8a274e052e005bf6dfd171caadc3abaad10080" dependencies = [ - "ahash 0.8.11", "arrow", - "arrow-array", "arrow-ipc", "arrow-schema", - "async-compression", "async-trait", "bytes", "bzip2", "chrono", - "dashmap", + "datafusion-catalog", + "datafusion-catalog-listing", "datafusion-common", "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-datasource-csv", + "datafusion-datasource-json", + "datafusion-datasource-parquet", "datafusion-execution", "datafusion-expr", + "datafusion-expr-common", "datafusion-functions", "datafusion-functions-aggregate", - "datafusion-functions-array", + "datafusion-functions-nested", + "datafusion-functions-table", + "datafusion-functions-window", + "datafusion-macros", "datafusion-optimizer", "datafusion-physical-expr", "datafusion-physical-expr-common", + "datafusion-physical-optimizer", "datafusion-physical-plan", + "datafusion-session", "datafusion-sql", "flate2", "futures", - "glob", - "half", - "hashbrown 0.14.5", - "indexmap 2.7.1", - "itertools 0.12.1", + "itertools 0.14.0", "log", - "num_cpus", "object_store", - "parking_lot", + "parking_lot 0.12.3", "parquet", - "paste", - "pin-project-lite", "rand 0.8.5", + "regex", "sqlparser", "tempfile", "tokio", - "tokio-util", "url", - "uuid 1.13.1", + "uuid", "xz2", "zstd", ] [[package]] -name = "datafusion-common" -version = "39.0.0" +name = "datafusion-catalog" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effed030d2c1667eb1e11df5372d4981eaf5d11a521be32220b3985ae5ba6971" +checksum = "61fe34f401bd03724a1f96d12108144f8cd495a3cdda2bf5e091822fb80b7e66" dependencies = [ - "ahash 0.8.11", "arrow", - "arrow-array", - "arrow-buffer", - "arrow-schema", - "chrono", - "half", - "hashbrown 0.14.5", - "instant", - "libc", - "num_cpus", + "async-trait", + "dashmap 6.1.0", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-plan", + "datafusion-session", + "datafusion-sql", + "futures", + "itertools 0.14.0", + "log", "object_store", - "parquet", - "sqlparser", -] - -[[package]] -name = "datafusion-common-runtime" -version = "39.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0091318129dad1359f08e4c6c71f855163c35bba05d1dbf983196f727857894" -dependencies = [ + "parking_lot 0.12.3", "tokio", ] [[package]] -name = "datafusion-execution" -version = "39.0.0" +name = "datafusion-catalog-listing" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8385aba84fc4a06d3ebccfbcbf9b4f985e80c762fac634b49079f7cc14933fb1" +checksum = "a4411b8e3bce5e0fc7521e44f201def2e2d5d1b5f176fb56e8cdc9942c890f00" dependencies = [ "arrow", + "async-trait", + "datafusion-catalog", + "datafusion-common", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "log", + "object_store", + "tokio", +] + +[[package]] +name = "datafusion-common" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0734015d81c8375eb5d4869b7f7ecccc2ee8d6cb81948ef737cd0e7b743bd69c" +dependencies = [ + "ahash 0.8.11", + "arrow", + "arrow-ipc", + "base64 0.22.1", + "half", + "hashbrown 0.14.5", + "indexmap 2.9.0", + "libc", + "log", + "object_store", + "parquet", + "paste", + "recursive", + "sqlparser", + "tokio", + "web-time", +] + +[[package]] +name = "datafusion-common-runtime" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5167bb1d2ccbb87c6bc36c295274d7a0519b14afcfdaf401d53cbcaa4ef4968b" +dependencies = [ + "futures", + "log", + "tokio", +] + +[[package]] +name = "datafusion-datasource" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e602dcdf2f50c2abf297cc2203c73531e6f48b29516af7695d338cf2a778b1" +dependencies = [ + "arrow", + "async-compression", + "async-trait", + "bytes", + "bzip2", "chrono", - "dashmap", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "flate2", + "futures", + "glob", + "itertools 0.14.0", + "log", + "object_store", + "parquet", + "rand 0.8.5", + "tempfile", + "tokio", + "tokio-util", + "url", + "xz2", + "zstd", +] + +[[package]] +name = "datafusion-datasource-csv" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bb2253952dc32296ed5b84077cb2e0257fea4be6373e1c376426e17ead4ef6" +dependencies = [ + "arrow", + "async-trait", + "bytes", + "datafusion-catalog", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "object_store", + "regex", + "tokio", +] + +[[package]] +name = "datafusion-datasource-json" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8c7f47a5d2fe03bfa521ec9bafdb8a5c82de8377f60967c3663f00c8790352" +dependencies = [ + "arrow", + "async-trait", + "bytes", + "datafusion-catalog", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "object_store", + "serde_json", + "tokio", +] + +[[package]] +name = "datafusion-datasource-parquet" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27d15868ea39ed2dc266728b554f6304acd473de2142281ecfa1294bb7415923" +dependencies = [ + "arrow", + "async-trait", + "bytes", + "datafusion-catalog", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-functions-aggregate", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-optimizer", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "itertools 0.14.0", + "log", + "object_store", + "parking_lot 0.12.3", + "parquet", + "rand 0.8.5", + "tokio", +] + +[[package]] +name = "datafusion-doc" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91f8c2c5788ef32f48ff56c68e5b545527b744822a284373ac79bba1ba47292" + +[[package]] +name = "datafusion-execution" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06f004d100f49a3658c9da6fb0c3a9b760062d96cd4ad82ccc3b7b69a9fb2f84" +dependencies = [ + "arrow", + "dashmap 6.1.0", "datafusion-common", "datafusion-expr", "futures", - "hashbrown 0.14.5", "log", "object_store", - "parking_lot", + "parking_lot 0.12.3", "rand 0.8.5", "tempfile", "url", @@ -2323,160 +2849,258 @@ dependencies = [ [[package]] name = "datafusion-expr" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebb192f0055d2ce64e38ac100abc18e4e6ae9734d3c28eee522bbbd6a32108a3" +checksum = "7a4e4ce3802609be38eeb607ee72f6fe86c3091460de9dbfae9e18db423b3964" dependencies = [ - "ahash 0.8.11", "arrow", - "arrow-array", - "arrow-buffer", "chrono", "datafusion-common", + "datafusion-doc", + "datafusion-expr-common", + "datafusion-functions-aggregate-common", + "datafusion-functions-window-common", + "datafusion-physical-expr-common", + "indexmap 2.9.0", "paste", + "recursive", "serde_json", "sqlparser", - "strum 0.26.3", - "strum_macros 0.26.4", +] + +[[package]] +name = "datafusion-expr-common" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422ac9cf3b22bbbae8cdf8ceb33039107fde1b5492693168f13bd566b1bcc839" +dependencies = [ + "arrow", + "datafusion-common", + "indexmap 2.9.0", + "itertools 0.14.0", + "paste", ] [[package]] name = "datafusion-functions" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c081ae5b7edd712b92767fb8ed5c0e32755682f8075707666cd70835807c0b" +checksum = "2ddf0a0a2db5d2918349c978d42d80926c6aa2459cd8a3c533a84ec4bb63479e" dependencies = [ "arrow", + "arrow-buffer", "base64 0.22.1", "blake2", "blake3", "chrono", "datafusion-common", + "datafusion-doc", "datafusion-execution", "datafusion-expr", - "datafusion-physical-expr", - "hashbrown 0.14.5", + "datafusion-expr-common", + "datafusion-macros", "hex", - "itertools 0.12.1", + "itertools 0.14.0", "log", "md-5 0.10.6", "rand 0.8.5", "regex", - "sha2 0.10.8", + "sha2 0.10.9", "unicode-segmentation", - "uuid 1.13.1", + "uuid", ] [[package]] name = "datafusion-functions-aggregate" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb28a4ea52c28a26990646986a27c4052829a2a2572386258679e19263f8b78" +checksum = "408a05dafdc70d05a38a29005b8b15e21b0238734dab1e98483fcb58038c5aba" dependencies = [ "ahash 0.8.11", "arrow", - "arrow-schema", "datafusion-common", + "datafusion-doc", "datafusion-execution", "datafusion-expr", + "datafusion-functions-aggregate-common", + "datafusion-macros", + "datafusion-physical-expr", "datafusion-physical-expr-common", + "half", "log", "paste", - "sqlparser", ] [[package]] -name = "datafusion-functions-array" -version = "39.0.0" +name = "datafusion-functions-aggregate-common" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b17c02a74cdc87380a56758ec27e7d417356bf806f33062700908929aedb8a" +checksum = "756d21da2dd6c9bef97af1504970ff56cbf35d03fbd4ffd62827f02f4d2279d4" +dependencies = [ + "ahash 0.8.11", + "arrow", + "datafusion-common", + "datafusion-expr-common", + "datafusion-physical-expr-common", +] + +[[package]] +name = "datafusion-functions-nested" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d8d50f6334b378930d992d801a10ac5b3e93b846b39e4a05085742572844537" dependencies = [ "arrow", - "arrow-array", - "arrow-buffer", "arrow-ord", - "arrow-schema", "datafusion-common", + "datafusion-doc", "datafusion-execution", "datafusion-expr", "datafusion-functions", - "itertools 0.12.1", + "datafusion-functions-aggregate", + "datafusion-macros", + "datafusion-physical-expr-common", + "itertools 0.14.0", "log", "paste", ] +[[package]] +name = "datafusion-functions-table" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9a97220736c8fff1446e936be90d57216c06f28969f9ffd3b72ac93c958c8a" +dependencies = [ + "arrow", + "async-trait", + "datafusion-catalog", + "datafusion-common", + "datafusion-expr", + "datafusion-physical-plan", + "parking_lot 0.12.3", + "paste", +] + +[[package]] +name = "datafusion-functions-window" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefc2d77646e1aadd1d6a9c40088937aedec04e68c5f0465939912e1291f8193" +dependencies = [ + "datafusion-common", + "datafusion-doc", + "datafusion-expr", + "datafusion-functions-window-common", + "datafusion-macros", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "log", + "paste", +] + +[[package]] +name = "datafusion-functions-window-common" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4aff082c42fa6da99ce0698c85addd5252928c908eb087ca3cfa64ff16b313" +dependencies = [ + "datafusion-common", + "datafusion-physical-expr-common", +] + +[[package]] +name = "datafusion-macros" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df6f88d7ee27daf8b108ba910f9015176b36fbc72902b1ca5c2a5f1d1717e1a1" +dependencies = [ + "datafusion-expr", + "quote", + "syn 2.0.101", +] + [[package]] name = "datafusion-optimizer" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12172f2a6c9eb4992a51e62d709eeba5dedaa3b5369cce37ff6c2260e100ba76" +checksum = "084d9f979c4b155346d3c34b18f4256e6904ded508e9554d90fed416415c3515" dependencies = [ "arrow", - "async-trait", "chrono", "datafusion-common", "datafusion-expr", "datafusion-physical-expr", - "hashbrown 0.14.5", - "indexmap 2.7.1", - "itertools 0.12.1", + "indexmap 2.9.0", + "itertools 0.14.0", "log", + "recursive", + "regex", "regex-syntax 0.8.5", ] [[package]] name = "datafusion-physical-expr" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a3fce531b623e94180f6cd33d620ef01530405751b6ddd2fd96250cdbd78e2e" +checksum = "64c536062b0076f4e30084065d805f389f9fe38af0ca75bcbac86bc5e9fbab65" dependencies = [ "ahash 0.8.11", "arrow", - "arrow-array", - "arrow-buffer", - "arrow-ord", - "arrow-schema", - "arrow-string", - "base64 0.22.1", - "chrono", "datafusion-common", - "datafusion-execution", "datafusion-expr", - "datafusion-functions-aggregate", + "datafusion-expr-common", + "datafusion-functions-aggregate-common", "datafusion-physical-expr-common", "half", "hashbrown 0.14.5", - "hex", - "indexmap 2.7.1", - "itertools 0.12.1", + "indexmap 2.9.0", + "itertools 0.14.0", "log", "paste", "petgraph", - "regex", ] [[package]] name = "datafusion-physical-expr-common" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046400b6a2cc3ed57a7c576f5ae6aecc77804ac8e0186926b278b189305b2a77" +checksum = "f8a92b53b3193fac1916a1c5b8e3f4347c526f6822e56b71faa5fb372327a863" +dependencies = [ + "ahash 0.8.11", + "arrow", + "datafusion-common", + "datafusion-expr-common", + "hashbrown 0.14.5", + "itertools 0.14.0", +] + +[[package]] +name = "datafusion-physical-optimizer" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa0a5ac94c7cf3da97bedabd69d6bbca12aef84b9b37e6e9e8c25286511b5e2" dependencies = [ "arrow", "datafusion-common", + "datafusion-execution", "datafusion-expr", - "rand 0.8.5", + "datafusion-expr-common", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "itertools 0.14.0", + "log", + "recursive", ] [[package]] name = "datafusion-physical-plan" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aed47f5a2ad8766260befb375b201592e86a08b260256e168ae4311426a2bff" +checksum = "690c615db468c2e5fe5085b232d8b1c088299a6c63d87fd960a354a71f7acb55" dependencies = [ "ahash 0.8.11", "arrow", - "arrow-array", - "arrow-buffer", "arrow-ord", "arrow-schema", "async-trait", @@ -2485,37 +3109,59 @@ dependencies = [ "datafusion-common-runtime", "datafusion-execution", "datafusion-expr", - "datafusion-functions-aggregate", + "datafusion-functions-window-common", "datafusion-physical-expr", "datafusion-physical-expr-common", "futures", "half", "hashbrown 0.14.5", - "indexmap 2.7.1", - "itertools 0.12.1", + "indexmap 2.9.0", + "itertools 0.14.0", "log", - "once_cell", - "parking_lot", + "parking_lot 0.12.3", "pin-project-lite", - "rand 0.8.5", + "tokio", +] + +[[package]] +name = "datafusion-session" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad229a134c7406c057ece00c8743c0c34b97f4e72f78b475fe17b66c5e14fa4f" +dependencies = [ + "arrow", + "async-trait", + "dashmap 6.1.0", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-plan", + "datafusion-sql", + "futures", + "itertools 0.14.0", + "log", + "object_store", + "parking_lot 0.12.3", "tokio", ] [[package]] name = "datafusion-sql" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fa92bb1fd15e46ce5fb6f1c85f3ac054592560f294429a28e392b5f9cd4255e" +checksum = "64f6ab28b72b664c21a27b22a2ff815fd390ed224c26e89a93b5a8154a4e8607" dependencies = [ "arrow", - "arrow-array", - "arrow-schema", + "bigdecimal", "datafusion-common", "datafusion-expr", + "indexmap 2.9.0", "log", + "recursive", "regex", "sqlparser", - "strum 0.26.3", ] [[package]] @@ -2531,23 +3177,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ "serde", - "uuid 1.13.1", + "uuid", ] [[package]] name = "deno_ast" -version = "0.43.3" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d00b724e06d2081a141ec1155756a0b465d413d8e2a7515221f61d482eb2ee" +checksum = "eebc7aaabfdb3ddcad32aee1b62d250149dc8b35dfbdccbb125df2bdc62da952" dependencies = [ "base64 0.21.7", + "deno_error", "deno_media_type", - "deno_terminal 0.1.1", + "deno_terminal", "dprint-swc-ext", "once_cell", "percent-encoding", "serde", - "sourcemap 9.1.2", + "sourcemap 9.2.0", "swc_atoms", "swc_common", "swc_config", @@ -2570,40 +3217,144 @@ dependencies = [ "swc_visit", "swc_visit_macros", "text_lines", - "thiserror 1.0.69", + "thiserror 2.0.12", "unicode-width 0.1.14", "url", ] [[package]] -name = "deno_console" -version = "0.179.0" +name = "deno_broadcast_channel" +version = "0.184.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e09f2bbb2d842329b602da25dbab5cd4a342f9a8adcb7c02509fc322f796e79" +checksum = "33db5dacb54c6fda4c5ea4103c5687b76a51202343379af8b21120ba9d20f3c2" +dependencies = [ + "async-trait", + "deno_core", + "deno_error", + "thiserror 2.0.12", + "tokio", + "uuid", +] + +[[package]] +name = "deno_cache" +version = "0.122.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0daca6ec4e6142a994d38e7bc587dda7948fa00e6194b671a5d4340f5a918a3" +dependencies = [ + "async-trait", + "deno_core", + "deno_error", + "rusqlite", + "serde", + "sha2 0.10.9", + "thiserror 2.0.12", + "tokio", +] + +[[package]] +name = "deno_cache_dir" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27429da4d0e601baaa41415a43468d49a586645d13497f12e8a9346f9f6b1347" +dependencies = [ + "async-trait", + "base32", + "base64 0.21.7", + "boxed_error", + "cache_control", + "chrono", + "data-url", + "deno_error", + "deno_media_type", + "deno_path_util", + "http 1.3.1", + "indexmap 2.9.0", + "log", + "once_cell", + "parking_lot 0.12.3", + "serde", + "serde_json", + "sha2 0.10.9", + "sys_traits", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "deno_canvas" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ca8f93d60d96d6f6cb0da632303afb98567accf07d9b6f8d2ef88617589d9e" +dependencies = [ + "deno_core", + "deno_error", + "deno_webgpu", + "image", + "serde", + "thiserror 2.0.12", +] + +[[package]] +name = "deno_config" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08fe512a72c4300bd997c6849450a1f050da0c909a2a4fbdc44891647392bacf" +dependencies = [ + "boxed_error", + "capacity_builder 0.5.0", + "deno_error", + "deno_package_json", + "deno_path_util", + "deno_semver", + "glob", + "ignore", + "import_map", + "indexmap 2.9.0", + "jsonc-parser", + "log", + "percent-encoding", + "phf", + "serde", + "serde_json", + "sys_traits", + "thiserror 2.0.12", + "url", +] + +[[package]] +name = "deno_console" +version = "0.190.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94352b8d75c288a26ef748ad0ddae07e181109374a02c547850f96eef76b5389" dependencies = [ "deno_core", ] [[package]] name = "deno_core" -version = "0.321.0" +version = "0.336.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd2a54cda74cdc187d5fc2d23370a45cf09f912caf566dd1cd24a50157d809c7" +checksum = "fdd50476c4325d5fa52bb906804a1e35b127d2a1dcf674e3447b53dcf25525bf" dependencies = [ "anyhow", + "az", "bincode", - "bit-set", - "bit-vec", + "bit-set 0.5.3", + "bit-vec 0.6.3", "bytes", + "capacity_builder 0.1.3", "cooked-waker", "deno_core_icudata", + "deno_error", "deno_ops", + "deno_path_util", "deno_unsync", "futures", - "indexmap 2.7.1", + "indexmap 2.9.0", "libc", "memoffset", - "parking_lot", + "parking_lot 0.12.3", "percent-encoding", "pin-project", "serde", @@ -2612,6 +3363,7 @@ dependencies = [ "smallvec", "sourcemap 8.0.1", "static_assertions", + "thiserror 2.0.12", "tokio", "url", "v8", @@ -2625,21 +3377,106 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695" [[package]] -name = "deno_fetch" -version = "0.203.0" +name = "deno_cron" +version = "0.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18e66bd3bf786e24a8b8bdc97049fa82957b095a5fd1e142545c5a7cdd2272a" +checksum = "e8ec283bef14bcf655b209619766bdeab67f2a5e093991cca73f5d502f7bf6e8" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "deno_core", + "deno_error", + "saffron", + "thiserror 2.0.12", + "tokio", +] + +[[package]] +name = "deno_crypto" +version = "0.204.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f0493142a437e49b46aa8e08d715942076ff48c3cb776f0b015b4224ed0d37a" +dependencies = [ + "aes 0.8.3", + "aes-gcm", + "aes-kw", + "base64 0.21.7", + "cbc", + "const-oid", + "ctr", + "curve25519-dalek", + "deno_core", + "deno_error", + "deno_web", + "ed448-goldilocks", + "elliptic-curve", + "num-traits", + "once_cell", + "p256", + "p384", + "p521", + "rand 0.8.5", + "ring 0.17.14", + "rsa", + "sec1", + "serde", + "serde_bytes", + "sha1", + "sha2 0.10.9", + "signature", + "spki", + "thiserror 2.0.12", + "tokio", + "uuid", + "x25519-dalek", +] + +[[package]] +name = "deno_error" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c23dbc46d5804814b08b4675838f9884e3a52916987ec5105af36d42f9911b5" +dependencies = [ + "deno_error_macro", + "libc", + "serde", + "serde_json", + "tokio", + "url", +] + +[[package]] +name = "deno_error_macro" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "babccedee31ce7e57c3e6dff2cb3ab8d68c49d0df8222fe0d11d628e65192790" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "deno_fetch" +version = "0.214.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df032ca1f7f06a5cc459189b960793f64d415ddc9f59f262e0ad5059865002d" dependencies = [ "base64 0.21.7", "bytes", "data-url", "deno_core", + "deno_error", + "deno_fs", + "deno_path_util", "deno_permissions", "deno_tls", "dyn-clone", "error_reporter", + "h2 0.4.10", "hickory-resolver", - "http 1.2.0", + "http 1.3.1", "http-body-util", "hyper 1.6.0", "hyper-rustls 0.27.5", @@ -2649,16 +3486,175 @@ dependencies = [ "rustls-webpki 0.102.8", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tokio-socks", "tokio-util", - "tower 0.4.13", + "tower 0.5.2", "tower-http", "tower-service", ] +[[package]] +name = "deno_ffi" +version = "0.177.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfbdc4e55c79ec1bc8a3ac72313e6f70d76340222f1e50c5c91e050296f83544" +dependencies = [ + "deno_core", + "deno_error", + "deno_permissions", + "dlopen2 0.6.1", + "dynasmrt", + "libffi", + "libffi-sys", + "log", + "num-bigint", + "serde", + "serde-value", + "serde_json", + "thiserror 2.0.12", + "tokio", + "winapi", +] + +[[package]] +name = "deno_fs" +version = "0.100.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82f79b71403b93b248727a89746026104b3a5ac1d82e79a02af6fa8e8487666" +dependencies = [ + "async-trait", + "base32", + "boxed_error", + "deno_core", + "deno_error", + "deno_io", + "deno_path_util", + "deno_permissions", + "filetime", + "junction", + "libc", + "nix 0.27.1", + "rand 0.8.5", + "rayon", + "serde", + "thiserror 2.0.12", + "winapi", + "windows-sys 0.59.0", +] + +[[package]] +name = "deno_http" +version = "0.188.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0b7e7a3bcac31ebd4677a96318003a98f0fda4613f6ba6d7f5ba57928727191" +dependencies = [ + "async-compression", + "async-trait", + "base64 0.21.7", + "brotli 6.0.0", + "bytes", + "cache_control", + "deno_core", + "deno_error", + "deno_net", + "deno_websocket", + "flate2", + "http 0.2.12", + "http 1.3.1", + "httparse", + "hyper 0.14.32", + "hyper 1.6.0", + "hyper-util", + "itertools 0.10.5", + "memmem", + "mime", + "once_cell", + "percent-encoding", + "phf", + "pin-project", + "ring 0.17.14", + "scopeguard", + "serde", + "smallvec", + "thiserror 2.0.12", + "tokio", + "tokio-util", +] + +[[package]] +name = "deno_io" +version = "0.100.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e72489fe0dcada08047611d1ab92db1baebf7b606ab7c78790f622ecb30e22b" +dependencies = [ + "async-trait", + "deno_core", + "deno_error", + "filetime", + "fs3", + "libc", + "log", + "once_cell", + "os_pipe", + "parking_lot 0.12.3", + "pin-project", + "rand 0.8.5", + "tokio", + "uuid", + "winapi", + "windows-sys 0.59.0", +] + +[[package]] +name = "deno_kv" +version = "0.98.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0e3930d0195a3350c05eb9a4bc619ec598ea84ad8bdb9b6c3e4bef798e7cf34" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.21.7", + "boxed_error", + "bytes", + "chrono", + "deno_core", + "deno_error", + "deno_fetch", + "deno_path_util", + "deno_permissions", + "deno_tls", + "denokv_proto", + "denokv_remote", + "denokv_sqlite", + "faster-hex", + "http 1.3.1", + "http-body-util", + "log", + "num-bigint", + "prost", + "prost-build", + "rand 0.8.5", + "rusqlite", + "serde", + "thiserror 2.0.12", + "url", +] + +[[package]] +name = "deno_lockfile" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632e835a53ed667d62fdd766c5780fe8361c831d3e3fbf1a760a0b7896657587" +dependencies = [ + "deno_semver", + "serde", + "serde_json", + "thiserror 2.0.12", +] + [[package]] name = "deno_media_type" version = "0.2.5" @@ -2670,13 +3666,30 @@ dependencies = [ "url", ] +[[package]] +name = "deno_napi" +version = "0.121.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13f30bf147cc46dba87e3088d037cf99a2845ead1138033d3b346178cb781558" +dependencies = [ + "deno_core", + "deno_error", + "deno_permissions", + "libc", + "libloading 0.7.4", + "log", + "napi_sym", + "thiserror 2.0.12", + "windows-sys 0.59.0", +] + [[package]] name = "deno_native_certs" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86bc737e098a45aa5742d51ce694ac7236a1e69fb0d9df8c862e9b4c9583c5f9" dependencies = [ - "dlopen2", + "dlopen2 0.7.0", "dlopen2_derive", "once_cell", "rustls-native-certs 0.7.3", @@ -2685,85 +3698,413 @@ dependencies = [ [[package]] name = "deno_net" -version = "0.171.0" +version = "0.182.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b3a51f7b4d5d64d17a7bc6f7495498f20d809930979d21a059d75e850cdea6" +checksum = "ab869063cbfe428a707511835865d55247886eb175659e538af4d5096c3d4d9d" dependencies = [ "deno_core", + "deno_error", "deno_permissions", "deno_tls", "hickory-proto", "hickory-resolver", "pin-project", + "quinn", "rustls-tokio-stream", "serde", "socket2", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", ] [[package]] -name = "deno_ops" -version = "0.197.0" +name = "deno_node" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a8825d92301cf445727c43f17fee2a20fcdf4370004339965156ae7c56c97e" +checksum = "9638e803a668b0a5793ff94c9b2e82c54a05d9fc510901e9f3093d2d63dbdaab" dependencies = [ + "aead-gcm-stream", + "aes 0.8.3", + "async-trait", + "base64 0.21.7", + "blake2", + "boxed_error", + "brotli 6.0.0", + "bytes", + "cbc", + "const-oid", + "ctr", + "data-encoding", + "deno_core", + "deno_error", + "deno_fetch", + "deno_fs", + "deno_io", + "deno_net", + "deno_package_json", + "deno_path_util", + "deno_permissions", + "deno_process", + "deno_whoami", + "der", + "digest 0.10.7", + "dsa", + "ecb", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "errno", + "faster-hex", + "h2 0.4.10", + "hkdf", + "http 1.3.1", + "http-body-util", + "hyper 1.6.0", + "hyper-util", + "idna", + "indexmap 2.9.0", + "ipnetwork", + "k256", + "lazy-regex", + "libc", + "libz-sys", + "md-5 0.10.6", + "md4", + "memchr", + "node_resolver", + "num-bigint", + "num-bigint-dig", + "num-integer", + "num-traits", + "once_cell", + "p224", + "p256", + "p384", + "path-clean", + "pbkdf2", + "pkcs8", + "rand 0.8.5", + "regex", + "ring 0.17.14", + "ripemd", + "rsa", + "scrypt", + "sec1", + "serde", + "sha1", + "sha2 0.10.9", + "sha3", + "signature", + "simd-json", + "sm3", + "spki", + "stable_deref_trait", + "sys_traits", + "thiserror 2.0.12", + "tokio", + "tokio-eld", + "url", + "webpki-root-certs 0.26.11", + "winapi", + "windows-sys 0.59.0", + "x25519-dalek", + "x509-parser", + "yoke", +] + +[[package]] +name = "deno_npm" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4adceb4c34f10e837d0e3ae76e88dddefb13e83c05c1ef1699fa5519241c9d27" +dependencies = [ + "async-trait", + "capacity_builder 0.5.0", + "deno_error", + "deno_lockfile", + "deno_semver", + "futures", + "log", + "monch", + "serde", + "serde_json", + "thiserror 2.0.12", + "url", +] + +[[package]] +name = "deno_ops" +version = "0.212.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d328067139909aa81522a5d90f119368b541fbddd73ab630e4d9f777865f0d" +dependencies = [ + "indexmap 2.9.0", "proc-macro-rules", "proc-macro2", "quote", "stringcase", - "strum 0.25.0", - "strum_macros 0.25.3", - "syn 2.0.98", - "thiserror 1.0.69", + "strum", + "strum_macros", + "syn 2.0.101", + "thiserror 2.0.12", +] + +[[package]] +name = "deno_os" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8371d206f6265c4e0b74116c1a58cc8c464c45da0b43c1e8c19a911e88feb2b" +dependencies = [ + "deno_core", + "deno_error", + "deno_path_util", + "deno_permissions", + "deno_telemetry", + "libc", + "netif", + "ntapi", + "once_cell", + "serde", + "signal-hook", + "signal-hook-registry", + "thiserror 2.0.12", + "tokio", + "winapi", +] + +[[package]] +name = "deno_package_json" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07d26dbfcc01e636aef86f9baff7faf5338398e74d283d8fe01e39068f48049" +dependencies = [ + "boxed_error", + "deno_error", + "deno_path_util", + "deno_semver", + "indexmap 2.9.0", + "serde", + "serde_json", + "sys_traits", + "thiserror 2.0.12", + "url", ] [[package]] name = "deno_path_util" -version = "0.2.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff25f6e08e7a0214bbacdd6f7195c7f1ebcd850c87a624e4ff06326b68b42d99" +checksum = "c87b8996966ae1b13ee9c20219b1d10fc53905b9570faae6adfa34614fd15224" dependencies = [ + "deno_error", "percent-encoding", - "thiserror 1.0.69", + "sys_traits", + "thiserror 2.0.12", "url", ] [[package]] name = "deno_permissions" -version = "0.39.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e822f98185ab3ddf06104b2407681e0008af52361af32f1cd171b7eda5aa59" +checksum = "abf879dff0b3de4dbcb78d6dda3a55e711369d5b9f479270a82853ef106c4176" dependencies = [ + "capacity_builder 0.5.0", "deno_core", + "deno_error", "deno_path_util", - "deno_terminal 0.2.0", + "deno_terminal", "fqdn", "libc", "log", "once_cell", "percent-encoding", "serde", - "thiserror 1.0.69", - "which 4.4.2", + "thiserror 2.0.12", + "which 6.0.3", "winapi", ] [[package]] -name = "deno_terminal" -version = "0.1.1" +name = "deno_process" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e6337d4e7f375f8b986409a76fbeecfa4bd8a1343e63355729ae4befa058eaf" +checksum = "700f8a2c9d369e7035e693f26a671489a73724450cbbc0a1e32f3966ef2f21fb" dependencies = [ + "deno_core", + "deno_error", + "deno_fs", + "deno_io", + "deno_os", + "deno_path_util", + "deno_permissions", + "libc", + "log", + "memchr", + "nix 0.27.1", + "pin-project-lite", + "rand 0.8.5", + "serde", + "simd-json", + "tempfile", + "thiserror 2.0.12", + "tokio", + "which 6.0.3", + "winapi", + "windows-sys 0.59.0", +] + +[[package]] +name = "deno_resolver" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c4ceec7b6e22344047b8a5577bb8239dc0a99884c25c1fa7d8611f4c3ed28b" +dependencies = [ + "anyhow", + "async-once-cell", + "async-trait", + "base32", + "boxed_error", + "dashmap 5.5.3", + "deno_cache_dir", + "deno_config", + "deno_error", + "deno_media_type", + "deno_npm", + "deno_package_json", + "deno_path_util", + "deno_semver", + "deno_terminal", + "futures", + "log", + "node_resolver", "once_cell", - "termcolor", + "parking_lot 0.12.3", + "sys_traits", + "thiserror 2.0.12", + "url", +] + +[[package]] +name = "deno_runtime" +version = "0.198.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a54d54ca920e5256c1e910c7574787d009a34afd41b20ad250278bb1aea290" +dependencies = [ + "color-print", + "deno_ast", + "deno_broadcast_channel", + "deno_cache", + "deno_canvas", + "deno_console", + "deno_core", + "deno_cron", + "deno_crypto", + "deno_error", + "deno_fetch", + "deno_ffi", + "deno_fs", + "deno_http", + "deno_io", + "deno_kv", + "deno_napi", + "deno_net", + "deno_node", + "deno_os", + "deno_path_util", + "deno_permissions", + "deno_process", + "deno_resolver", + "deno_telemetry", + "deno_terminal", + "deno_tls", + "deno_url", + "deno_web", + "deno_webgpu", + "deno_webidl", + "deno_websocket", + "deno_webstorage", + "dlopen2 0.6.1", + "encoding_rs", + "fastwebsockets", + "http 1.3.1", + "http-body-util", + "hyper 0.14.32", + "hyper 1.6.0", + "hyper-util", + "libc", + "log", + "nix 0.27.1", + "node_resolver", + "notify", + "ntapi", + "once_cell", + "percent-encoding", + "regex", + "rustyline", + "same-file", + "serde", + "sys_traits", + "tempfile", + "thiserror 2.0.12", + "tokio", + "tokio-metrics", + "twox-hash 1.6.3", + "uuid", + "which 6.0.3", + "winapi", + "windows-sys 0.59.0", +] + +[[package]] +name = "deno_semver" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4775271f9b5602482698f76d24ea9ed8ba27af7f587a7e9a876916300c542435" +dependencies = [ + "capacity_builder 0.5.0", + "deno_error", + "ecow", + "hipstr", + "monch", + "once_cell", + "serde", + "thiserror 2.0.12", + "url", +] + +[[package]] +name = "deno_telemetry" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d73802ee27361bbb6c0e3c04a799b39f458afbed1972c4aff0867420d1c36fdb" +dependencies = [ + "async-trait", + "deno_core", + "deno_error", + "deno_tls", + "http-body-util", + "hyper 1.6.0", + "hyper-rustls 0.27.5", + "hyper-util", + "log", + "once_cell", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", + "pin-project", + "serde", + "thiserror 2.0.12", + "tokio", ] [[package]] name = "deno_terminal" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daef12499e89ee99e51ad6000a91f600d3937fb028ad4918af76810c5bc9e0d5" +checksum = "23f71c27009e0141dedd315f1dfa3ebb0a6ca4acce7c080fac576ea415a465f6" dependencies = [ "once_cell", "termcolor", @@ -2771,20 +4112,21 @@ dependencies = [ [[package]] name = "deno_tls" -version = "0.166.0" +version = "0.177.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688175eed35e7b3053ec114227894ef24786855405d8844058a48bffa997d85a" +checksum = "a1e3ceb2be448150d8214e8fc454c947e0ea94f6ce16556544f05a67ad5a16b8" dependencies = [ "deno_core", + "deno_error", "deno_native_certs", - "rustls 0.23.22", + "rustls 0.23.27", "rustls-pemfile 2.2.0", "rustls-tokio-stream", "rustls-webpki 0.102.8", "serde", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -2794,71 +4136,241 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d774fd83f26b24f0805a6ab8b26834a0d06ceac0db517b769b1e4633c96a2057" dependencies = [ "futures", - "parking_lot", + "parking_lot 0.12.3", "tokio", ] [[package]] name = "deno_url" -version = "0.179.0" +version = "0.190.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9a108794e505f2b07665e19ff336c1bcba6adcf7182c90c1d3a6c741d7fcd0" +checksum = "d79e743ad841f7826d46c6944580f5ba665fe9ab4c31a68c4eed8b5a78225da3" dependencies = [ "deno_core", - "thiserror 1.0.69", + "deno_error", + "thiserror 2.0.12", "urlpattern", ] [[package]] name = "deno_web" -version = "0.210.0" +version = "0.221.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7679087bcc41f7ae3385f8c12d43bc81cfc54cb9b1ef73983d20f5e39fa4e0da" +checksum = "8041ba73bb2f238c61b5e4ed341d2fe1f9464a71115a240ab3390480b3c10e12" dependencies = [ "async-trait", "base64-simd 0.8.0", "bytes", "deno_core", + "deno_error", "deno_permissions", "encoding_rs", "flate2", "futures", "serde", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", - "uuid 1.13.1", + "uuid", +] + +[[package]] +name = "deno_webgpu" +version = "0.157.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4077584c0ccfde0737e576c396bbf1645f25ed0ebf4f44543e0ad13729285cf3" +dependencies = [ + "deno_core", + "deno_error", + "raw-window-handle", + "serde", + "thiserror 2.0.12", + "tokio", + "wgpu-core", + "wgpu-types", ] [[package]] name = "deno_webidl" -version = "0.179.0" +version = "0.190.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b55d845e3d64f8de7eff67aaa4b6fe1b23bbc2efe967c984f8c64c8dd85fad4" +checksum = "c4ff81a990196bf3a80fe5d339b4eb8b411ef17634d60d399a63bae6e71a37c9" dependencies = [ "deno_core", ] [[package]] -name = "der" -version = "0.7.9" +name = "deno_websocket" +version = "0.195.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "2ad15c3856dd1748f9a36102e90b4e345e40d7d64a9bf6672d584201e1fded28" +dependencies = [ + "bytes", + "deno_core", + "deno_error", + "deno_net", + "deno_permissions", + "deno_tls", + "fastwebsockets", + "h2 0.4.10", + "http 1.3.1", + "http-body-util", + "hyper 1.6.0", + "hyper-util", + "once_cell", + "rustls-tokio-stream", + "serde", + "thiserror 2.0.12", + "tokio", +] + +[[package]] +name = "deno_webstorage" +version = "0.185.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "079dc4f6ce91f53bb848bad8d743dc20d16ca44cbed3425531cf5d922b1a45bc" +dependencies = [ + "deno_core", + "deno_error", + "deno_web", + "rusqlite", + "thiserror 2.0.12", +] + +[[package]] +name = "deno_whoami" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75e4caa92b98a27f09c671d1399aee0f5970aa491b9a598523aac000a2192e3" +dependencies = [ + "libc", + "whoami", +] + +[[package]] +name = "denokv_proto" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5b77de4d3b9215e14624d4f4eb16cb38c0810e3f5860ba3b3fc47d0537f9a4d" +dependencies = [ + "async-trait", + "chrono", + "deno_error", + "futures", + "num-bigint", + "prost", + "serde", + "uuid", +] + +[[package]] +name = "denokv_remote" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6497c28eec268ed99f1e8664f0842935f02d1508529c67d94c57ca5d893d743" +dependencies = [ + "async-stream", + "async-trait", + "bytes", + "chrono", + "deno_error", + "denokv_proto", + "futures", + "http 1.3.1", + "log", + "prost", + "rand 0.8.5", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tokio-util", + "url", + "uuid", +] + +[[package]] +name = "denokv_sqlite" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0f21a450a35eb85760761401fddf9bfff9840127be07a6ca5c31863127913d" +dependencies = [ + "async-stream", + "async-trait", + "chrono", + "deno_error", + "denokv_proto", + "futures", + "hex", + "log", + "num-bigint", + "rand 0.8.5", + "rusqlite", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "uuid", + "v8_valueserializer", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", + "der_derive", "pem-rfc7468", "zeroize", ] [[package]] -name = "deranged" -version = "0.3.11" +name = "der-parser" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom 7.1.3", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "derive_builder" version = "0.12.0" @@ -2892,15 +4404,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.19" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -2910,7 +4422,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac41dd49fb554432020d52c875fc290e110113f864c6b1b525cd62c7e7747a5d" dependencies = [ "byteorder", - "cipher", + "cipher 0.3.0", "opaque-debug", ] @@ -3005,7 +4517,19 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "dlopen2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bc2c7ed06fd72a8513ded8d0d2f6fd2655a85d6885c48cae8625d80faf28c03" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", ] [[package]] @@ -3028,14 +4552,17 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] -name = "doc-comment" -version = "0.3.3" +name = "document-features" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", +] [[package]] name = "dotenv" @@ -3071,10 +4598,32 @@ dependencies = [ ] [[package]] -name = "dyn-clone" -version = "1.0.18" +name = "dsa" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35" +checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" +dependencies = [ + "digest 0.10.7", + "num-bigint-dig", + "num-traits", + "pkcs8", + "rfc6979", + "sha2 0.10.9", + "signature", + "zeroize", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" [[package]] name = "dyn-iter" @@ -3092,6 +4641,50 @@ dependencies = [ "reborrow", ] +[[package]] +name = "dyn-stack" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490bd48eb68fffcfed519b4edbfd82c69cbe741d175b84f0e0cbe8c57cbe0bdd" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "dynasm" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dynasmrt" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" +dependencies = [ + "byteorder", + "dynasm", + "memmap2 0.5.10", +] + +[[package]] +name = "ecb" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7" +dependencies = [ + "cipher 0.4.4", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -3106,6 +4699,15 @@ dependencies = [ "spki", ] +[[package]] +name = "ecow" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b92b481eb5d59fd8e80e92ff11d057d1ca8d144b2cd8c66cc8d5bd177a3c0dc5" +dependencies = [ + "serde", +] + [[package]] name = "ed25519" version = "2.2.3" @@ -3124,18 +4726,31 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", + "rand_core 0.6.4", "serde", - "sha2 0.10.8", + "sha2 0.10.9", "signature", "subtle", "zeroize", ] [[package]] -name = "either" -version = "1.13.0" +name = "ed448-goldilocks" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "06924531e9e90130842b012e447f85bdaf9161bc8a0f8092be8cb70b01ebe092" +dependencies = [ + "fiat-crypto 0.1.20", + "hex", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" dependencies = [ "serde", ] @@ -3147,6 +4762,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", + "base64ct", "crypto-bigint", "digest 0.10.7", "ff", @@ -3157,6 +4773,8 @@ dependencies = [ "pkcs8", "rand_core 0.6.4", "sec1", + "serde_json", + "serdect", "subtle", "zeroize", ] @@ -3176,6 +4794,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "enum-as-inner" version = "0.6.1" @@ -3185,7 +4809,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -3205,25 +4829,41 @@ checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +dependencies = [ + "serde", + "typeid", +] [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", ] +[[package]] +name = "error-code" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" + [[package]] name = "error_reporter" version = "1.0.0" @@ -3273,6 +4913,40 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fancy-regex" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2" +dependencies = [ + "bit-set 0.5.3", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "fancy-regex" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" +dependencies = [ + "bit-set 0.8.0", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + [[package]] name = "fastdivide" version = "0.4.2" @@ -3280,12 +4954,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afc2bd4d5a73106dd53d10d73d3401c2f32730ba2c0b93ddb888a8983680471" [[package]] -name = "fastrand" -version = "1.9.0" +name = "faster-hex" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" dependencies = [ - "instant", + "serde", ] [[package]] @@ -3295,15 +4969,61 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "ff" -version = "0.13.0" +name = "fastwebsockets" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "26da0c7b5cef45c521a6f9cdfffdfeb6c9f5804fbac332deb5ae254634c7a6be" +dependencies = [ + "base64 0.21.7", + "bytes", + "http-body-util", + "hyper 1.6.0", + "hyper-util", + "pin-project", + "rand 0.8.5", + "sha1", + "simdutf8", + "thiserror 1.0.69", + "tokio", + "utf-8", +] + +[[package]] +name = "fd-lock" +version = "4.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" +dependencies = [ + "cfg-if", + "rustix 1.0.7", + "windows-sys 0.59.0", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ "rand_core 0.6.4", "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + [[package]] name = "fiat-crypto" version = "0.2.9" @@ -3324,29 +5044,39 @@ dependencies = [ [[package]] name = "fixedbitset" -version = "0.4.2" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flatbuffers" -version = "24.12.23" +version = "25.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1baf0dbf96932ec9a3038d57900329c015b0bfb7b63d904f3bc27e2b02a096" +checksum = "1045398c1bfd89168b5fd3f1fc11f6e70b34f6f66300c87d44d3de849463abf1" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "rustc_version 0.4.1", ] [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", + "libz-rs-sys", "libz-sys", - "miniz_oxide 0.8.3", + "miniz_oxide 0.8.8", +] + +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", ] [[package]] @@ -3357,6 +5087,7 @@ checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", + "nanorand", "spin 0.9.8", ] @@ -3368,9 +5099,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "foreign-types" @@ -3378,7 +5109,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] @@ -3387,6 +5139,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -3410,7 +5168,18 @@ checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" dependencies = [ "proc-macro2", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "fs3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb17cf6ed704f72485332f6ab65257460c4f9f3083934cf402bf9f5b3b600a90" +dependencies = [ + "libc", + "rustc_version 0.2.3", + "winapi", ] [[package]] @@ -3419,10 +5188,25 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7e180ac76c23b45e767bd7ae9579bc0bb458618c4bc71835926e098e61d15f8" dependencies = [ - "rustix", + "rustix 0.38.44", "windows-sys 0.52.0", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "fslock" version = "0.2.1" @@ -3489,7 +5273,7 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot", + "parking_lot 0.12.3", ] [[package]] @@ -3498,28 +5282,13 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-lite" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ - "fastrand 2.3.0", + "fastrand", "futures-core", "futures-io", "parking", @@ -3534,7 +5303,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -3598,17 +5367,37 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ab24cc62135b40090e31a76a9b2766a501979f3070fa27f689c27ec04377d32" dependencies = [ - "dyn-stack", - "gemm-c32", - "gemm-c64", - "gemm-common", - "gemm-f16", - "gemm-f32", - "gemm-f64", + "dyn-stack 0.10.0", + "gemm-c32 0.17.1", + "gemm-c64 0.17.1", + "gemm-common 0.17.1", + "gemm-f16 0.17.1", + "gemm-f32 0.17.1", + "gemm-f64 0.17.1", "num-complex", "num-traits", "paste", - "raw-cpuid", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab96b703d31950f1aeddded248bc95543c9efc7ac9c4a21fda8703a83ee35451" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-c32 0.18.2", + "gemm-c64 0.18.2", + "gemm-common 0.18.2", + "gemm-f16 0.18.2", + "gemm-f32 0.18.2", + "gemm-f64 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.5.0", "seq-macro", ] @@ -3618,12 +5407,27 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9c030d0b983d1e34a546b86e08f600c11696fde16199f971cd46c12e67512c0" dependencies = [ - "dyn-stack", - "gemm-common", + "dyn-stack 0.10.0", + "gemm-common 0.17.1", "num-complex", "num-traits", "paste", - "raw-cpuid", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm-c32" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6db9fd9f40421d00eea9dd0770045a5603b8d684654816637732463f4073847" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.5.0", "seq-macro", ] @@ -3633,12 +5437,27 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbb5f2e79fefb9693d18e1066a557b4546cd334b226beadc68b11a8f9431852a" dependencies = [ - "dyn-stack", - "gemm-common", + "dyn-stack 0.10.0", + "gemm-common 0.17.1", "num-complex", "num-traits", "paste", - "raw-cpuid", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm-c64" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfcad8a3d35a43758330b635d02edad980c1e143dc2f21e6fd25f9e4eada8edf" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.5.0", "seq-macro", ] @@ -3649,17 +5468,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2e7ea062c987abcd8db95db917b4ffb4ecdfd0668471d8dc54734fdff2354e8" dependencies = [ "bytemuck", - "dyn-stack", + "dyn-stack 0.10.0", "half", "num-complex", "num-traits", "once_cell", "paste", - "pulp", - "raw-cpuid", + "pulp 0.18.22", + "raw-cpuid 10.7.0", "rayon", "seq-macro", - "sysctl", + "sysctl 0.5.5", +] + +[[package]] +name = "gemm-common" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a352d4a69cbe938b9e2a9cb7a3a63b7e72f9349174a2752a558a8a563510d0f3" +dependencies = [ + "bytemuck", + "dyn-stack 0.13.0", + "half", + "libm", + "num-complex", + "num-traits", + "once_cell", + "paste", + "pulp 0.21.5", + "raw-cpuid 11.5.0", + "rayon", + "seq-macro", + "sysctl 0.6.0", ] [[package]] @@ -3668,14 +5508,32 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ca4c06b9b11952071d317604acb332e924e817bd891bec8dfb494168c7cedd4" dependencies = [ - "dyn-stack", - "gemm-common", - "gemm-f32", + "dyn-stack 0.10.0", + "gemm-common 0.17.1", + "gemm-f32 0.17.1", "half", "num-complex", "num-traits", "paste", - "raw-cpuid", + "raw-cpuid 10.7.0", + "rayon", + "seq-macro", +] + +[[package]] +name = "gemm-f16" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff95ae3259432f3c3410eaa919033cd03791d81cebd18018393dc147952e109" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "gemm-f32 0.18.2", + "half", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.5.0", "rayon", "seq-macro", ] @@ -3686,12 +5544,27 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9a69f51aaefbd9cf12d18faf273d3e982d9d711f60775645ed5c8047b4ae113" dependencies = [ - "dyn-stack", - "gemm-common", + "dyn-stack 0.10.0", + "gemm-common 0.17.1", "num-complex", "num-traits", "paste", - "raw-cpuid", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm-f32" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc8d3d4385393304f407392f754cd2dc4b315d05063f62cf09f47b58de276864" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.5.0", "seq-macro", ] @@ -3701,15 +5574,43 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa397a48544fadf0b81ec8741e5c0fba0043008113f71f2034def1935645d2b0" dependencies = [ - "dyn-stack", - "gemm-common", + "dyn-stack 0.10.0", + "gemm-common 0.17.1", "num-complex", "num-traits", "paste", - "raw-cpuid", + "raw-cpuid 10.7.0", "seq-macro", ] +[[package]] +name = "gemm-f64" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b2a4f76ce4b8b16eadc11ccf2e083252d8237c1b589558a49b0183545015bd" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.5.0", + "seq-macro", +] + +[[package]] +name = "generator" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" +dependencies = [ + "cfg-if", + "libc", + "log", + "rustversion", + "windows 0.58.0", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -3742,20 +5643,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.16" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", @@ -3766,14 +5656,26 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.6", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", ] [[package]] @@ -3799,20 +5701,18 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] -name = "git2" -version = "0.17.2" +name = "gl_generator" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b989d6a7ca95a362cf2cfc5ad688b3a467be1f87e480b8dad07fee8c79b0044" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" dependencies = [ - "bitflags 1.3.2", - "libc", - "libgit2-sys", + "khronos_api", "log", - "url", + "xml-rs", ] [[package]] @@ -3821,6 +5721,19 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[package]] +name = "globset" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + [[package]] name = "gloo-timers" version = "0.3.0" @@ -3833,6 +5746,115 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "glow" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "google-cloud-auth" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57a13fbacc5e9c41ded3ad8d0373175a6b7a6ad430d99e89d314ac121b7ab06" +dependencies = [ + "async-trait", + "base64 0.21.7", + "google-cloud-metadata", + "google-cloud-token", + "home", + "jsonwebtoken 9.3.1", + "reqwest 0.12.15", + "serde", + "serde_json", + "thiserror 1.0.69", + "time", + "tokio", + "tracing", + "urlencoding", +] + +[[package]] +name = "google-cloud-gax" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de13e62d7e0ffc3eb40a0113ddf753cf6ec741be739164442b08893db4f9bfca" +dependencies = [ + "google-cloud-token", + "http 1.3.1", + "thiserror 1.0.69", + "tokio", + "tokio-retry2", + "tonic", + "tower 0.4.13", + "tracing", +] + +[[package]] +name = "google-cloud-googleapis" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "886aa8ec755382a1fdf4651f6e6ec01f2f3bf49f2cb0f068b9a74cafd574a715" +dependencies = [ + "prost", + "prost-types", + "tonic", +] + +[[package]] +name = "google-cloud-metadata" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d901aeb453fd80e51d64df4ee005014f6cf39f2d736dd64f7239c132d9d39a6a" +dependencies = [ + "reqwest 0.12.15", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "google-cloud-pubsub" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebc6e7327e49a66ffb40508c673b7643191bd6b509530193bda97f09272cdcf" +dependencies = [ + "async-channel", + "async-stream", + "google-cloud-auth", + "google-cloud-gax", + "google-cloud-googleapis", + "google-cloud-token", + "prost-types", + "thiserror 1.0.69", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "google-cloud-token" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c12ba8b21d128a2ce8585955246977fbce4415f680ebf9199b6f9d6d725f" +dependencies = [ + "async-trait", +] + [[package]] name = "gosyn" version = "0.2.9" @@ -3840,11 +5862,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eb37859fda6792e95231aef1c5838f4043ec0ee352d8313421e311c606df612" dependencies = [ "anyhow", - "strum 0.25.0", + "strum", "thiserror 1.0.69", "unic-ucd-category", ] +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.9.0", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" +dependencies = [ + "bitflags 2.9.0", + "gpu-descriptor-types", + "hashbrown 0.15.3", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "group" version = "0.13.0" @@ -3877,7 +5938,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.7.1", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -3886,17 +5947,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.2.0", - "indexmap 2.7.1", + "http 1.3.1", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -3905,16 +5966,26 @@ dependencies = [ [[package]] name = "half" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "bytemuck", "cfg-if", "crunchy", "num-traits", - "rand 0.8.5", - "rand_distr", + "rand 0.9.0", + "rand_distr 0.5.1", +] + +[[package]] +name = "halfbrown" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8588661a8607108a5ca69cab034063441a0413a0b041c13618a7dd348021ef6f" +dependencies = [ + "hashbrown 0.14.5", + "serde", ] [[package]] @@ -3926,15 +5997,6 @@ dependencies = [ "ahash 0.7.8", ] -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.11", -] - [[package]] name = "hashbrown" version = "0.14.5" @@ -3947,9 +6009,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "allocator-api2", "equivalent", @@ -3964,7 +6026,16 @@ checksum = "f208758247e68e239acaa059e72e4ce1f30f2a4b6523f19c1b923d25b7e9cceb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", ] [[package]] @@ -3973,7 +6044,21 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", +] + +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "base64 0.21.7", + "byteorder", + "crossbeam-channel", + "flate2", + "nom 7.1.3", + "num-traits", ] [[package]] @@ -4000,6 +6085,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + [[package]] name = "hf-hub" version = "0.3.2" @@ -4019,10 +6110,11 @@ dependencies = [ [[package]] name = "hickory-proto" -version = "0.24.2" +version = "0.25.0-alpha.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" +checksum = "1d00147af6310f4392a31680db52a3ed45a2e0f68eb18e8c3fe5537ecc96d9e2" dependencies = [ + "async-recursion", "async-trait", "cfg-if", "data-encoding", @@ -4033,9 +6125,9 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.8.5", + "rand 0.9.0", "serde", - "thiserror 1.0.69", + "thiserror 2.0.12", "tinyvec", "tokio", "tracing", @@ -4044,26 +6136,37 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.2" +version = "0.25.0-alpha.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" +checksum = "5762f69ebdbd4ddb2e975cd24690bf21fe6b2604039189c26acddbc427f12887" dependencies = [ "cfg-if", "futures-util", "hickory-proto", "ipconfig", - "lru-cache", + "moka", "once_cell", - "parking_lot", - "rand 0.8.5", + "parking_lot 0.12.3", + "rand 0.9.0", "resolv-conf", "serde", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tracing", ] +[[package]] +name = "hipstr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97971ffc85d4c98de12e2608e992a43f5294ebb625fdb045b27c731b64c4c6d6" +dependencies = [ + "serde", + "serde_bytes", + "sptr", +] + [[package]] name = "hkdf" version = "0.12.4" @@ -4091,17 +6194,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - [[package]] name = "hstr" version = "0.2.17" @@ -4135,9 +6227,9 @@ dependencies = [ [[package]] name = "http" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -4162,48 +6254,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.2.0", + "http 1.3.1", ] [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", - "http 1.2.0", + "futures-core", + "http 1.3.1", "http-body 1.0.1", "pin-project-lite", ] -[[package]] -name = "http-types" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" -dependencies = [ - "anyhow", - "async-channel", - "base64 0.13.1", - "futures-lite 1.13.0", - "http 0.2.12", - "infer", - "pin-project-lite", - "rand 0.7.3", - "serde", - "serde_json", - "serde_qs 0.8.5", - "serde_urlencoded", - "url", -] - [[package]] name = "httparse" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -4213,9 +6284,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" [[package]] name = "hyper" @@ -4250,8 +6321,8 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.7", - "http 1.2.0", + "h2 0.4.10", + "http 1.3.1", "http-body 1.0.1", "httparse", "httpdate", @@ -4300,23 +6371,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 1.2.0", + "http 1.3.1", "hyper 1.6.0", "hyper-util", - "rustls 0.23.22", + "rustls 0.23.27", "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tower-service", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] name = "hyper-timeout" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ "hyper 1.6.0", "hyper-util", @@ -4356,20 +6427,20 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", "hyper 1.6.0", + "libc", "pin-project-lite", "socket2", "tokio", - "tower 0.4.13", "tower-service", "tracing", ] @@ -4391,16 +6462,17 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "windows-core", + "windows-core 0.61.0", ] [[package]] @@ -4453,9 +6525,9 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" @@ -4477,9 +6549,9 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" @@ -4498,9 +6570,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" @@ -4527,7 +6599,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4563,6 +6635,52 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.9", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-traits", + "png", +] + +[[package]] +name = "import_map" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1215d4d92511fbbdaea50e750e91f2429598ef817f02b579158e92803b52c00a" +dependencies = [ + "boxed_error", + "deno_error", + "indexmap 2.9.0", + "log", + "percent-encoding", + "serde", + "serde_json", + "thiserror 2.0.12", + "url", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -4576,12 +6694,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "serde", ] @@ -4599,10 +6717,34 @@ dependencies = [ ] [[package]] -name = "infer" -version = "0.2.3" +name = "inotify" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding 0.3.3", + "generic-array", +] [[package]] name = "instant" @@ -4622,6 +6764,15 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" +[[package]] +name = "inventory" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab08d7cd2c5897f2c949e5383ea7c7db03fb19130ffcfbf7eda795137ae3cb83" +dependencies = [ + "rustversion", +] + [[package]] name = "ipconfig" version = "0.3.2" @@ -4640,6 +6791,15 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "ipnetwork" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" +dependencies = [ + "serde", +] + [[package]] name = "is-macro" version = "0.3.7" @@ -4649,9 +6809,15 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] +[[package]] +name = "is_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -4705,28 +6871,45 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.2", "libc", ] [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] +[[package]] +name = "jsonc-parser" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b558af6b49fd918e970471374e7a798b2c9bbcda624a210ffa3901ee5614bc8e" +dependencies = [ + "serde_json", +] + [[package]] name = "jsonwebtoken" version = "8.3.0" @@ -4741,15 +6924,80 @@ dependencies = [ "simple_asn1", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64 0.22.1", + "js-sys", + "pem 3.0.5", + "ring 0.17.14", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "junction" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72bbdfd737a243da3dfc1f99ee8d6e166480f17ab4ac84d7c34aacd73fc7bd16" +dependencies = [ + "scopeguard", + "windows-sys 0.52.0", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.9", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "keyed_priority_queue" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee7893dab2e44ae5f9d0173f26ff4aa327c10b01b06a72b52dd9405b628640d" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", ] +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading 0.8.6", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + [[package]] name = "konst" version = "0.2.19" @@ -4765,12 +7013,55 @@ version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" +[[package]] +name = "kqueue" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lalrpop-util" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +[[package]] +name = "lazy-regex" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] + +[[package]] +name = "lazy-regex-proc_macros" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.101", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -4794,9 +7085,9 @@ checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25" [[package]] name = "lexical-core" -version = "0.8.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" +checksum = "b765c31809609075565a70b4b71402281283aeda7ecaf4818ac14a7b2ade8958" dependencies = [ "lexical-parse-float", "lexical-parse-integer", @@ -4807,9 +7098,9 @@ dependencies = [ [[package]] name = "lexical-parse-float" -version = "0.8.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" +checksum = "de6f9cb01fb0b08060209a057c048fcbab8717b4c1ecd2eac66ebfe39a65b0f2" dependencies = [ "lexical-parse-integer", "lexical-util", @@ -4818,9 +7109,9 @@ dependencies = [ [[package]] name = "lexical-parse-integer" -version = "0.8.6" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" +checksum = "72207aae22fc0a121ba7b6d479e42cbfea549af1479c3f3a4f12c70dd66df12e" dependencies = [ "lexical-util", "static_assertions", @@ -4828,18 +7119,18 @@ dependencies = [ [[package]] name = "lexical-util" -version = "0.8.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" +checksum = "5a82e24bf537fd24c177ffbbdc6ebcc8d54732c35b50a3f28cc3f4e4c949a0b3" dependencies = [ "static_assertions", ] [[package]] name = "lexical-write-float" -version = "0.8.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" +checksum = "c5afc668a27f460fb45a81a757b6bf2f43c2d7e30cb5a2dcd3abf294c78d62bd" dependencies = [ "lexical-util", "lexical-write-integer", @@ -4848,9 +7139,9 @@ dependencies = [ [[package]] name = "lexical-write-integer" -version = "0.8.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" +checksum = "629ddff1a914a836fb245616a7888b62903aae58fa771e1d83943035efa0f978" dependencies = [ "lexical-util", "static_assertions", @@ -4858,20 +7149,37 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] -name = "libgit2-sys" -version = "0.15.2+1.6.4" +name = "libffi" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a80df2e11fb4a61f4ba2ab42dbe7f74468da143f1a75c74e11dee7c813f694fa" +checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2" +dependencies = [ + "libc", + "libffi-sys", +] + +[[package]] +name = "libffi-sys" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36115160c57e8529781b4183c2bb51fdc1f6d6d1ed345591d84be7703befb3c" dependencies = [ "cc", - "libc", - "libz-sys", - "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", ] [[package]] @@ -4886,9 +7194,20 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libproc" +version = "0.14.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78a09b56be5adbcad5aa1197371688dc6bb249a26da3bca2011ee2fb987ebfb" +dependencies = [ + "bindgen 0.70.1", + "errno", + "libc", +] [[package]] name = "libredox" @@ -4896,9 +7215,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.12", ] [[package]] @@ -4907,6 +7226,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ + "cc", "pkg-config", "vcpkg", ] @@ -4923,10 +7243,19 @@ dependencies = [ ] [[package]] -name = "libz-sys" -version = "1.1.21" +name = "libz-rs-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa" +checksum = "6489ca9bd760fe9642d7644e827b0c9add07df89857b0416ee15c1cc1a3b8c5a" +dependencies = [ + "zlib-rs", +] + +[[package]] +name = "libz-sys" +version = "1.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" dependencies = [ "cc", "libc", @@ -4947,10 +7276,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] -name = "litemap" -version = "0.7.4" +name = "linux-raw-sys" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" @@ -4964,9 +7305,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "loki-api" @@ -4978,22 +7319,44 @@ dependencies = [ "prost-types", ] +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + [[package]] name = "lru" version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] -name = "lru-cache" -version = "0.1.2" +name = "lru" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198" dependencies = [ - "linked-hash-map", + "hashbrown 0.15.3", +] + +[[package]] +name = "lscolors" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53304fff6ab1e597661eee37e42ea8c47a146fca280af902bb76bff8a896e523" +dependencies = [ + "nu-ansi-term 0.50.1", ] [[package]] @@ -5016,6 +7379,15 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + [[package]] name = "macro_rules_attribute" version = "0.2.0" @@ -5038,7 +7410,7 @@ version = "3.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c42f95f9d296f2dcb50665f507ed5a68a171453142663ce44d77a4eb217b053" dependencies = [ - "aes", + "aes 0.7.5", "base64 0.21.7", "block-modes", "crc-any", @@ -5078,12 +7450,12 @@ dependencies = [ "base64 0.22.1", "gethostname", "mail-builder", - "rustls 0.23.22", + "rustls 0.23.27", "rustls-pki-types", "smtp-proto", "tokio", - "tokio-rustls 0.26.1", - "webpki-roots", + "tokio-rustls 0.26.2", + "webpki-roots 0.26.11", ] [[package]] @@ -5144,18 +7516,21 @@ dependencies = [ "malachite-nz", ] +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + [[package]] name = "mappable-rc" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "204651f31b0a6a7b2128d2b92c372cd94607b210c3a6b6e542c57a8cfd4db996" -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matchers" version = "0.1.0" @@ -5198,6 +7573,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "md4" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da5ac363534dce5fabf69949225e174fbf111a498bf0ff794c8ea1fba9f3dda" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "measure_time" version = "0.8.3" @@ -5214,6 +7598,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + [[package]] name = "memmap2" version = "0.9.5" @@ -5224,6 +7617,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "memmem" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" + [[package]] name = "memoffset" version = "0.9.1" @@ -5233,6 +7632,49 @@ dependencies = [ "autocfg", ] +[[package]] +name = "metal" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" +dependencies = [ + "bitflags 2.9.0", + "block", + "core-graphics-types", + "foreign-types 0.5.0", + "log", + "objc", + "paste", +] + +[[package]] +name = "miette" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" +dependencies = [ + "cfg-if", + "miette-derive", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap", + "unicode-width 0.1.14", +] + +[[package]] +name = "miette-derive" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "mime" version = "0.3.17" @@ -5249,6 +7691,16 @@ dependencies = [ "unicase", ] +[[package]] +name = "minicov" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +dependencies = [ + "cc", + "walkdir", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -5266,11 +7718,24 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", ] [[package]] @@ -5285,10 +7750,35 @@ dependencies = [ ] [[package]] -name = "monostate" -version = "0.1.13" +name = "moka" +version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d208407d7552cd041d8cdb69a1bc3303e029c598738177a3d87082004dc0e1e" +checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" +dependencies = [ + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "loom", + "parking_lot 0.12.3", + "portable-atomic", + "rustc_version 0.4.1", + "smallvec", + "tagptr", + "thiserror 1.0.69", + "uuid", +] + +[[package]] +name = "monch" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b52c1b33ff98142aecea13138bd399b68aa7ab5d9546c300988c345004001eea" + +[[package]] +name = "monostate" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aafe1be9d0c75642e3e50fedc7ecadf1ef1cbce6eb66462153fc44245343fbee" dependencies = [ "monostate-impl", "serde", @@ -5296,13 +7786,13 @@ dependencies = [ [[package]] name = "monostate-impl" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ce64b975ed4f123575d11afd9491f2e37bbd5813fbfbc0f09ae1fbddea74e0" +checksum = "c402a4092d5e204f32c9e155431046831fa712637043c58cb73bc6bc6c9663b5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -5314,7 +7804,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.2.0", + "http 1.3.1", "httparse", "memchr", "mime", @@ -5322,6 +7812,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + [[package]] name = "murmurhash32" version = "0.3.1" @@ -5330,46 +7826,45 @@ checksum = "2195bf6aa996a481483b29d62a7663eed3fe39600c460e323f8ff41e90bdd89b" [[package]] name = "mysql-common-derive" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb6d9ff4094f6d58d3f892fc558e60048476213dd17dcf904b62202e9029da6" +checksum = "66f62cad7623a9cb6f8f64037f0c4f69c8db8e82914334a83c9788201c2c1bfa" dependencies = [ - "darling 0.20.10", + "darling 0.20.11", "heck 0.5.0", "num-bigint", "proc-macro-crate", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "termcolor", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] name = "mysql_async" -version = "0.35.1" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d14cf024116ba8fef4a7fec5abf0bd5de89b9fb29a7e55818a119ac5ec745077" +checksum = "277ce2f2459b2af4cc6d0a0b7892381f80800832f57c533f03e2845f4ea331ea" dependencies = [ "bytes", - "crossbeam", + "crossbeam-queue", "flate2", "futures-core", "futures-sink", "futures-util", "keyed_priority_queue", - "lru", + "lru 0.14.0", "mysql_common", "native-tls", - "pem 3.0.4", + "pem 3.0.5", "percent-encoding", - "pin-project", - "rand 0.8.5", + "rand 0.9.0", "serde", "serde_json", "socket2", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tokio-native-tls", "tokio-util", @@ -5379,42 +7874,80 @@ dependencies = [ [[package]] name = "mysql_common" -version = "0.34.1" +version = "0.35.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34a9141e735d5bb02414a7ac03add09522466d4db65bdd827069f76ae0850e58" +checksum = "6e0ec195e788c95f36b7cf88127d538465fc2f7773e6e47af01834738eab0aee" dependencies = [ "base64 0.22.1", - "bitflags 2.8.0", + "bitflags 2.9.0", "btoi", "byteorder", "bytes", - "cc", - "cmake", "crc32fast", "flate2", - "lazy_static", + "getrandom 0.3.2", "mysql-common-derive", "num-bigint", "num-traits", - "rand 0.8.5", "regex", "rust_decimal", "saturating", "serde", "serde_json", "sha1", - "sha2 0.10.8", - "subprocess", + "sha2 0.10.9", + "thiserror 2.0.12", + "uuid", +] + +[[package]] +name = "naga" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" +dependencies = [ + "arrayvec", + "bit-set 0.5.3", + "bitflags 2.9.0", + "codespan-reporting", + "hexf-parse", + "indexmap 2.9.0", + "log", + "num-traits", + "rustc-hash 1.1.0", + "serde", + "spirv", + "termcolor", "thiserror 1.0.69", - "uuid 1.13.1", - "zstd", + "unicode-xid", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "napi_sym" +version = "0.120.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33a55ec137cebb7f4a594edd16157a5b9d9addf7ebd29c88198ec4e0cff2e93e" +dependencies = [ + "quote", + "serde", + "serde_json", + "syn 2.0.101", ] [[package]] name = "native-tls" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", @@ -5427,23 +7960,63 @@ dependencies = [ "tempfile", ] +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "netif" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29a01b9f018d6b7b277fef6c79fdbd9bf17bb2d1e298238055cafab49baa5ee" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + [[package]] name = "nix" version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", "libc", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "cfg_aliases 0.2.1", + "libc", +] + [[package]] name = "nkeys" version = "0.4.4" @@ -5453,12 +8026,48 @@ dependencies = [ "data-encoding", "ed25519", "ed25519-dalek", - "getrandom 0.2.15", + "getrandom 0.2.16", "log", "rand 0.8.5", "signatory", ] +[[package]] +name = "node_resolver" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808426e80ce77a311b24ac080caf18c23c632e035d797edb217ce74cdf6a0e71" +dependencies = [ + "anyhow", + "async-trait", + "boxed_error", + "dashmap 5.5.3", + "deno_error", + "deno_media_type", + "deno_package_json", + "deno_path_util", + "futures", + "lazy-regex", + "once_cell", + "path-clean", + "regex", + "serde", + "serde_json", + "sys_traits", + "thiserror 2.0.12", + "url", +] + +[[package]] +name = "nom" +version = "5.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" +dependencies = [ + "memchr", + "version_check", +] + [[package]] name = "nom" version = "7.1.3" @@ -5469,6 +8078,34 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.9.0", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio 0.8.11", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -5479,6 +8116,145 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "nu-derive-value" +version = "0.101.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71f7c8ed6ba88a567ec6f7c4cad4a7a8465ab93b8cdaf89d3dc72347a83c2d1f" +dependencies = [ + "heck 0.5.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "nu-engine" +version = "0.101.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6619583ed281060a9ea0a3f4532eea918370c94e703b903065f35e5aa49b14" +dependencies = [ + "log", + "nu-glob", + "nu-path", + "nu-protocol", + "nu-utils", + "terminal_size", +] + +[[package]] +name = "nu-glob" +version = "0.101.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd0a9fe69412acdc8501f5ef19031f9cac119d93823cb957b14ddfe1cb97660" + +[[package]] +name = "nu-parser" +version = "0.101.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2adc2876bd7bc83be15786cedf2cb08a81a9d70fa4b8df569b3f1cbec1e0b58d" +dependencies = [ + "bytesize", + "chrono", + "itertools 0.13.0", + "log", + "nu-engine", + "nu-path", + "nu-protocol", + "nu-utils", + "serde_json", +] + +[[package]] +name = "nu-path" +version = "0.101.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ccd1bbaf370d79118bd1a807abb07d8d1386751d0ae9266baafa91bd0b5523f" +dependencies = [ + "dirs 5.0.1", + "omnipath", + "pwd", +] + +[[package]] +name = "nu-protocol" +version = "0.101.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f49a395b632530d7f46fd24183c7f42423677f70afb3cb4726e3abfe92273b" +dependencies = [ + "byte-unit", + "bytes", + "chrono", + "chrono-humanize", + "dirs 5.0.1", + "dirs-sys 0.4.1", + "fancy-regex 0.14.0", + "heck 0.5.0", + "indexmap 2.9.0", + "log", + "lru 0.12.5", + "miette", + "nix 0.29.0", + "nu-derive-value", + "nu-path", + "nu-system", + "nu-utils", + "num-format", + "serde", + "serde_json", + "thiserror 2.0.12", + "typetag", + "windows-sys 0.48.0", +] + +[[package]] +name = "nu-system" +version = "0.101.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81182f7e64bd5dd16ab844d8e40f78e389d06d95f5a0c419f4701fb8fc163077" +dependencies = [ + "chrono", + "itertools 0.13.0", + "libc", + "libproc", + "log", + "mach2", + "nix 0.29.0", + "ntapi", + "procfs", + "sysinfo", + "windows 0.56.0", +] + +[[package]] +name = "nu-utils" +version = "0.101.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d1468fa8e6e12d9d53c90b44f3d11a37d87502d7a30d145f122341c5b33745" +dependencies = [ + "crossterm_winapi", + "fancy-regex 0.14.0", + "log", + "lscolors", + "nix 0.29.0", + "num-format", + "serde", + "serde_json", + "strip-ansi-escapes", + "sys-locale", + "unicase", +] + [[package]] name = "nuid" version = "0.5.0" @@ -5527,6 +8303,7 @@ dependencies = [ "num-iter", "num-traits", "rand 0.8.5", + "serde", "smallvec", "zeroize", ] @@ -5547,6 +8324,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -5616,7 +8403,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -5633,18 +8420,27 @@ checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" dependencies = [ "base64 0.22.1", "chrono", - "getrandom 0.2.15", - "http 1.2.0", + "getrandom 0.2.16", + "http 1.3.1", "rand 0.8.5", - "reqwest 0.12.9", + "reqwest 0.12.15", "serde", "serde_json", "serde_path_to_error", - "sha2 0.10.8", + "sha2 0.10.9", "thiserror 1.0.69", "url", ] +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + [[package]] name = "object" version = "0.36.7" @@ -5656,32 +8452,38 @@ dependencies = [ [[package]] name = "object_store" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6da452820c715ce78221e8202ccc599b4a52f3e1eb3eedb487b680c81a8e3f3" +version = "0.12.0" +source = "git+https://github.com/apache/arrow-rs-object-store?rev=36752c975d4f29e20b57c91f81a10872dcd48ae7#36752c975d4f29e20b57c91f81a10872dcd48ae7" dependencies = [ "async-trait", "base64 0.22.1", "bytes", "chrono", + "form_urlencoded", "futures", + "http 1.3.1", + "http-body-util", + "httparse", "humantime", "hyper 1.6.0", - "itertools 0.13.0", + "itertools 0.14.0", "md-5 0.10.6", - "parking_lot", + "parking_lot 0.12.3", "percent-encoding", - "quick-xml 0.36.2", - "rand 0.8.5", - "reqwest 0.12.9", - "ring 0.17.8", + "quick-xml 0.37.5", + "rand 0.9.0", + "reqwest 0.12.15", + "ring 0.17.14", "serde", "serde_json", - "snafu", + "serde_urlencoded", + "thiserror 2.0.12", "tokio", "tracing", "url", "walkdir", + "wasm-bindgen-futures", + "web-time", ] [[package]] @@ -5694,16 +8496,31 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.20.3" +name = "oid-registry" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "omnipath" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80adb31078122c880307e9cdfd4e3361e6545c319f9b9dcafcb03acd3b51a575" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oneshot" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79d72a7c0f743d2ebb0a2ad1d219db75fdc799092ed3a884c9144c42a31225bd" +checksum = "b4ce411919553d3f9fa53a0880544cda985a112117a0444d5ff1e870a893d6ea" [[package]] name = "onig" @@ -5755,7 +8572,7 @@ dependencies = [ "dyn-clone", "ed25519-dalek", "hmac", - "http 1.2.0", + "http 1.3.1", "itertools 0.10.5", "log", "oauth2", @@ -5769,7 +8586,7 @@ dependencies = [ "serde_path_to_error", "serde_plain", "serde_with", - "sha2 0.10.8", + "sha2 0.10.9", "subtle", "thiserror 1.0.69", "url", @@ -5777,13 +8594,13 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.70" +version = "0.10.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", - "foreign-types", + "foreign-types 0.3.2", "libc", "once_cell", "openssl-macros", @@ -5798,7 +8615,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -5809,18 +8626,18 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.4.1+3.4.0" +version = "300.5.0+3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" +checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.105" +version = "0.9.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" +checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" dependencies = [ "cc", "libc", @@ -5855,6 +8672,18 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "opentelemetry-http" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a8a7f5f6ba7c1b286c2fbca0454eaba116f63bbe69ed250b642d36fbb04d80" +dependencies = [ + "async-trait", + "bytes", + "http 1.3.1", + "opentelemetry", +] + [[package]] name = "opentelemetry-otlp" version = "0.27.0" @@ -5863,11 +8692,13 @@ checksum = "91cf61a1868dacc576bf2b2a1c3e9ab150af7272909e80085c3173384fe11f76" dependencies = [ "async-trait", "futures-core", - "http 1.2.0", + "http 1.3.1", "opentelemetry", + "opentelemetry-http", "opentelemetry-proto", "opentelemetry_sdk", "prost", + "serde_json", "thiserror 1.0.69", "tokio", "tonic", @@ -5880,9 +8711,11 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6e05acbfada5ec79023c85368af14abd0b307c015e9064d249b2a950ef459a6" dependencies = [ + "hex", "opentelemetry", "opentelemetry_sdk", "prost", + "serde", "tonic", ] @@ -5955,6 +8788,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_pipe" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "outref" version = "0.1.0" @@ -5982,6 +8825,24 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "owo-colors" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" + +[[package]] +name = "p224" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c06436d66652bc2f01ade021592c80a2aad401570a18aa18b82e440d2b9aa1" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + [[package]] name = "p256" version = "0.13.2" @@ -5991,7 +8852,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -6003,7 +8864,21 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.8", + "sha2 0.10.9", +] + +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primeorder", + "rand_core 0.6.4", + "sha2 0.10.9", ] [[package]] @@ -6012,6 +8887,17 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -6019,7 +8905,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", ] [[package]] @@ -6030,16 +8930,16 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.12", "smallvec", "windows-targets 0.52.6", ] [[package]] name = "parquet" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e977b9066b4d3b03555c22bdc442f3fadebd96a39111249113087d0edb2691cd" +checksum = "cd31a8290ac5b19f09ad77ee7a1e6a541f1be7674ad410547d5f1eef6eef4a9c" dependencies = [ "ahash 0.8.11", "arrow-array", @@ -6050,25 +8950,25 @@ dependencies = [ "arrow-schema", "arrow-select", "base64 0.22.1", - "brotli 6.0.0", + "brotli 7.0.0", "bytes", "chrono", "flate2", "futures", "half", - "hashbrown 0.14.5", + "hashbrown 0.15.3", "lz4_flex", "num", "num-bigint", "object_store", "paste", "seq-macro", + "simdutf8", "snap", "thrift", "tokio", - "twox-hash 1.6.3", + "twox-hash 2.1.0", "zstd", - "zstd-sys", ] [[package]] @@ -6097,12 +8997,28 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "path-clean" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" + [[package]] name = "pathdiff" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac", +] + [[package]] name = "pem" version = "1.1.1" @@ -6114,9 +9030,9 @@ dependencies = [ [[package]] name = "pem" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ "base64 0.22.1", "serde", @@ -6139,12 +9055,12 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" -version = "0.6.5" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap 2.7.1", + "indexmap 2.9.0", ] [[package]] @@ -6196,7 +9112,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -6222,22 +9138,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -6263,6 +9179,21 @@ dependencies = [ "spki", ] +[[package]] +name = "pkcs5" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" +dependencies = [ + "aes 0.8.3", + "cbc", + "der", + "pbkdf2", + "scrypt", + "sha2 0.10.9", + "spki", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -6270,20 +9201,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", + "pkcs5", + "rand_core 0.6.4", "spki", ] [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.8.8", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] [[package]] name = "portable-atomic" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" [[package]] name = "postgres-native-tls" @@ -6316,12 +9274,12 @@ dependencies = [ "base64 0.22.1", "byteorder", "bytes", - "fallible-iterator", + "fallible-iterator 0.2.0", "hmac", "md-5 0.10.6", "memchr", "rand 0.8.5", - "sha2 0.10.8", + "sha2 0.10.9", "stringprep", ] @@ -6334,12 +9292,12 @@ dependencies = [ "base64 0.22.1", "byteorder", "bytes", - "fallible-iterator", + "fallible-iterator 0.2.0", "hmac", "md-5 0.10.6", "memchr", "rand 0.9.0", - "sha2 0.10.8", + "sha2 0.10.9", "stringprep", ] @@ -6349,7 +9307,7 @@ version = "0.2.7" source = "git+https://github.com/imor/rust-postgres?rev=20265ef38e32a06f76b6f9b678e2077fc2211f6b#20265ef38e32a06f76b6f9b678e2077fc2211f6b" dependencies = [ "bytes", - "fallible-iterator", + "fallible-iterator 0.2.0", "postgres-protocol 0.6.7", ] @@ -6360,14 +9318,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" dependencies = [ "array-init", - "bit-vec", + "bit-vec 0.6.3", "bytes", "chrono", - "fallible-iterator", + "fallible-iterator 0.2.0", "postgres-protocol 0.6.8", "serde", "serde_json", - "uuid 1.13.1", + "uuid", ] [[package]] @@ -6378,11 +9336,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.7.35", + "zerocopy 0.8.25", ] [[package]] @@ -6393,22 +9351,12 @@ checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" [[package]] name = "prettyplease" -version = "0.1.25" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", - "syn 1.0.109", -] - -[[package]] -name = "prettyplease" -version = "0.2.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" -dependencies = [ - "proc-macro2", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -6422,11 +9370,35 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit 0.22.23", + "toml_edit 0.22.26", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", ] [[package]] @@ -6448,7 +9420,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -6459,7 +9431,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", "proc-macro2", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -6471,37 +9443,49 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] -name = "progenitor" -version = "0.3.0" -source = "git+https://github.com/oxidecomputer/progenitor?rev=3d96016ae8d422e90513b2d34fb5b63eeab30b01#3d96016ae8d422e90513b2d34fb5b63eeab30b01" +name = "procfs" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" dependencies = [ - "anyhow", - "built", - "clap", - "openapiv3", - "progenitor-client", - "progenitor-impl", - "progenitor-macro", - "project-root", - "rustfmt-wrapper", - "serde", - "serde_json", - "serde_yaml", + "bitflags 2.9.0", + "chrono", + "flate2", + "hex", + "procfs-core", + "rustix 0.38.44", ] +[[package]] +name = "procfs-core" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" +dependencies = [ + "bitflags 2.9.0", + "chrono", + "hex", +] + +[[package]] +name = "profiling" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" + [[package]] name = "progenitor-client" version = "0.3.0" @@ -6516,102 +9500,77 @@ dependencies = [ "serde_urlencoded", ] -[[package]] -name = "progenitor-impl" -version = "0.3.0" -source = "git+https://github.com/oxidecomputer/progenitor?rev=3d96016ae8d422e90513b2d34fb5b63eeab30b01#3d96016ae8d422e90513b2d34fb5b63eeab30b01" -dependencies = [ - "getopts", - "heck 0.4.1", - "http 0.2.12", - "indexmap 1.9.3", - "openapiv3", - "proc-macro2", - "quote", - "regex", - "schemars", - "serde", - "serde_json", - "syn 2.0.98", - "thiserror 1.0.69", - "typify", - "unicode-ident", -] - -[[package]] -name = "progenitor-macro" -version = "0.3.0" -source = "git+https://github.com/oxidecomputer/progenitor?rev=3d96016ae8d422e90513b2d34fb5b63eeab30b01#3d96016ae8d422e90513b2d34fb5b63eeab30b01" -dependencies = [ - "openapiv3", - "proc-macro2", - "progenitor-impl", - "quote", - "schemars", - "serde", - "serde_json", - "serde_tokenstream", - "serde_yaml", - "syn 2.0.98", -] - -[[package]] -name = "project-root" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bccbff07d5ed689c4087d20d7307a52ab6141edeedf487c3876a55b86cf63df" - [[package]] name = "prometheus" -version = "0.13.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" +checksum = "3ca5326d8d0b950a9acd87e6a3f94745394f62e4dae1b1ee22b2bc0c394af43a" dependencies = [ "cfg-if", "fnv", "lazy_static", "memchr", - "parking_lot", - "thiserror 1.0.69", + "parking_lot 0.12.3", + "thiserror 2.0.12", ] [[package]] name = "prost" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", "prost-derive", ] [[package]] -name = "prost-derive" -version = "0.13.4" +name = "prost-build" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +dependencies = [ + "heck 0.5.0", + "itertools 0.14.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.101", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "prost-types" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" dependencies = [ "prost", ] [[package]] name = "psm" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" +checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" dependencies = [ "cc", ] @@ -6642,7 +9601,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "getopts", "memchr", "unicase", @@ -6661,10 +9620,34 @@ dependencies = [ ] [[package]] -name = "quick-error" -version = "1.2.3" +name = "pulp" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +checksum = "96b86df24f0a7ddd5e4b95c94fc9ed8a98f1ca94d3b01bdce2824097e7835907" +dependencies = [ + "bytemuck", + "cfg-if", + "libm", + "num-complex", + "reborrow", + "version_check", +] + +[[package]] +name = "pure-rust-locales" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a" + +[[package]] +name = "pwd" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c71c0c79b9701efe4e1e4b563b2016dd4ee789eb99badcb09d61ac4b92e4a2" +dependencies = [ + "libc", + "thiserror 1.0.69", +] [[package]] name = "quick-xml" @@ -6678,9 +9661,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.36.2" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", "serde", @@ -6688,49 +9671,51 @@ dependencies = [ [[package]] name = "quick_cache" -version = "0.6.10" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f67cfc9c723c39f3615eb0840b00c4cb9e2b068d2fa761a30d845ec91730a59" +checksum = "287e56aac5a2b4fb25a6fb050961d157635924c8696305a5c937a76f29841a0f" dependencies = [ "ahash 0.8.11", "equivalent", - "hashbrown 0.14.5", - "parking_lot", + "hashbrown 0.15.3", + "parking_lot 0.12.3", ] [[package]] name = "quinn" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" dependencies = [ "bytes", + "cfg_aliases 0.2.1", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash 2.1.1", - "rustls 0.23.22", + "rustls 0.23.27", "socket2", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tracing", + "web-time", ] [[package]] name = "quinn-proto" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" +checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b" dependencies = [ "bytes", - "getrandom 0.2.15", - "rand 0.8.5", - "ring 0.17.8", + "getrandom 0.3.2", + "rand 0.9.0", + "ring 0.17.14", "rustc-hash 2.1.1", - "rustls 0.23.22", + "rustls 0.23.27", "rustls-pki-types", "slab", - "thiserror 2.0.11", + "thiserror 2.0.12", "tinyvec", "tracing", "web-time", @@ -6738,11 +9723,11 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.9" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" +checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" dependencies = [ - "cfg_aliases", + "cfg_aliases 0.2.1", "libc", "once_cell", "socket2", @@ -6752,13 +9737,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radium" version = "0.7.0" @@ -6766,16 +9757,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] -name = "rand" -version = "0.7.3" +name = "radix_trie" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", + "endian-type", + "nibble_vec", ] [[package]] @@ -6796,18 +9784,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.0", - "zerocopy 0.8.17", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_core 0.9.3", + "zerocopy 0.8.25", ] [[package]] @@ -6827,16 +9805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.0", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core 0.9.3", ] [[package]] @@ -6845,17 +9814,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] name = "rand_core" -version = "0.9.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.1", - "zerocopy 0.8.17", + "getrandom 0.3.2", ] [[package]] @@ -6869,14 +9837,21 @@ dependencies = [ ] [[package]] -name = "rand_hc" -version = "0.2.0" +name = "rand_distr" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" dependencies = [ - "rand_core 0.5.1", + "num-traits", + "rand 0.9.0", ] +[[package]] +name = "range-alloc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" + [[package]] name = "raw-cpuid" version = "10.7.0" @@ -6886,6 +9861,21 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "raw-cpuid" +version = "11.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + [[package]] name = "rayon" version = "1.10.0" @@ -6955,6 +9945,35 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" +[[package]] +name = "recursive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0786a43debb760f491b1bc0269fe5e84155353c67482b9e60d0cfb596054b43e" +dependencies = [ + "recursive-proc-macro-impl", + "stacker", +] + +[[package]] +name = "recursive-proc-macro-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b" +dependencies = [ + "quote", + "syn 2.0.101", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -6966,11 +9985,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -6979,11 +9998,31 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", "thiserror 1.0.69", ] +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "regex" version = "1.11.1" @@ -7040,16 +10079,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "regress" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a9ecfa0cb04d0b04dddb99b8ccf4f66bc8dfd23df694b398570bd8ae3a50fb" -dependencies = [ - "hashbrown 0.13.2", - "memchr", -] - [[package]] name = "rend" version = "0.4.2" @@ -7103,9 +10132,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "async-compression", "base64 0.22.1", @@ -7113,8 +10142,8 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.4.7", - "http 1.2.0", + "h2 0.4.10", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "hyper 1.6.0", @@ -7130,7 +10159,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.22", + "rustls 0.23.27", "rustls-native-certs 0.8.1", "rustls-pemfile 2.2.0", "rustls-pki-types", @@ -7141,26 +10170,69 @@ dependencies = [ "system-configuration 0.6.1", "tokio", "tokio-native-tls", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tokio-util", + "tower 0.5.2", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", + "webpki-roots 0.26.11", "windows-registry", ] [[package]] -name = "resolv-conf" +name = "reqwest-middleware" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f17d28a6e6acfe1733fe24bcd30774d13bffa4b8a22535b4c8c98423088d4e" +dependencies = [ + "anyhow", + "async-trait", + "http 1.3.1", + "reqwest 0.12.15", + "serde", + "thiserror 1.0.69", + "tower-service", +] + +[[package]] +name = "reqwest-retry" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +checksum = "29c73e4195a6bfbcb174b790d9b3407ab90646976c55de58a6515da25d851178" dependencies = [ - "hostname", - "quick-error", + "anyhow", + "async-trait", + "futures", + "getrandom 0.2.16", + "http 1.3.1", + "hyper 1.6.0", + "parking_lot 0.11.2", + "reqwest 0.12.15", + "reqwest-middleware", + "retry-policies", + "thiserror 1.0.69", + "tokio", + "tracing", + "wasm-timer", +] + +[[package]] +name = "resolv-conf" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7c8f7f733062b66dc1c63f9db168ac0b97a9210e247fa90fdc9ad08f51b302" + +[[package]] +name = "retry-policies" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5875471e6cab2871bc150ecb8c727db5113c9338cc3354dc5ee3425b6aa40a1c" +dependencies = [ + "rand 0.8.5", ] [[package]] @@ -7173,12 +10245,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "riff" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b1a3d5f46d53f4a3478e2be4a5a5ce5108ea58b100dcd139830eae7f79a3a1" - [[package]] name = "ring" version = "0.16.20" @@ -7196,19 +10262,27 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", - "spin 0.9.8", "untrusted 0.9.0", "windows-sys 0.52.0", ] +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "rkyv" version = "0.7.45" @@ -7224,7 +10298,7 @@ dependencies = [ "rkyv_derive", "seahash", "tinyvec", - "uuid 1.13.1", + "uuid", ] [[package]] @@ -7239,10 +10313,56 @@ dependencies = [ ] [[package]] -name = "rsa" -version = "0.9.7" +name = "rmcp" +version = "0.1.5" +source = "git+https://github.com/windmill-labs/rust-sdk#9142b40202e49ca0b6530fa49a3abc8bd1b2fcf0" +dependencies = [ + "async-stream", + "axum", + "base64 0.21.7", + "chrono", + "futures", + "paste", + "pin-project-lite", + "rand 0.9.0", + "rmcp-macros", + "schemars", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "rmcp-macros" +version = "0.1.5" +source = "git+https://github.com/windmill-labs/rust-sdk#9142b40202e49ca0b6530fa49a3abc8bd1b2fcf0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "ron" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64 0.21.7", + "bitflags 2.9.0", + "serde", + "serde_derive", +] + +[[package]] +name = "rsa" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" dependencies = [ "const-oid", "digest 0.10.7", @@ -7258,6 +10378,40 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rumqttc" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1568e15fab2d546f940ed3a21f48bbbd1c494c90c99c4481339364a497f94a9" +dependencies = [ + "bytes", + "flume", + "futures-util", + "log", + "native-tls", + "rustls-native-certs 0.7.3", + "rustls-pemfile 2.2.0", + "rustls-webpki 0.102.8", + "thiserror 1.0.69", + "tokio", + "tokio-native-tls", + "tokio-rustls 0.25.0", +] + +[[package]] +name = "rusqlite" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags 2.9.0", + "fallible-iterator 0.3.0", + "fallible-streaming-iterator", + "hashlink 0.9.1", + "libsqlite3-sys", + "smallvec", +] + [[package]] name = "rust-embed" version = "6.8.1" @@ -7279,7 +10433,7 @@ dependencies = [ "quote", "rust-embed-utils", "shellexpand", - "syn 2.0.98", + "syn 2.0.101", "walkdir", ] @@ -7289,7 +10443,7 @@ version = "7.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74" dependencies = [ - "sha2 0.10.8", + "sha2 0.10.9", "walkdir", ] @@ -7305,9 +10459,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.36.0" +version = "1.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" +checksum = "faa7de2ba56ac291bd90c6b9bece784a52ae1411f9506544b3eae36dd2356d50" dependencies = [ "arrayvec", "borsh", @@ -7353,20 +10507,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.25", + "semver 1.0.26", ] [[package]] -name = "rustfmt-wrapper" -version = "0.2.1" +name = "rusticata-macros" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1adc9dfed5cc999077978cc7163b9282c5751c8d39827c4ea8c8c220ca5a440" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" dependencies = [ - "serde", - "tempfile", - "thiserror 1.0.69", - "toml 0.8.20", - "toolchain_find", + "nom 7.1.3", ] [[package]] @@ -7375,10 +10525,23 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] @@ -7389,26 +10552,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.17.8", + "ring 0.17.14", "rustls-webpki 0.101.7", "sct", ] [[package]] name = "rustls" -version = "0.23.22" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "once_cell", - "ring 0.17.8", + "ring 0.17.14", "rustls-pki-types", "rustls-webpki 0.102.8", "subtle", "zeroize", ] +[[package]] +name = "rustls" +version = "0.23.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring 0.17.14", + "rustls-pki-types", + "rustls-webpki 0.103.2", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -7480,7 +10658,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22557157d7395bc30727745b365d923f1ecc230c4c80b176545f3f4f08c46e33" dependencies = [ "futures", - "rustls 0.23.22", + "rustls 0.23.27", "socket2", "tokio", ] @@ -7491,7 +10669,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.8", + "ring 0.17.14", "untrusted 0.9.0", ] @@ -7501,7 +10679,19 @@ version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ - "ring 0.17.8", + "ring 0.17.14", + "rustls-pki-types", + "untrusted 0.9.0", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7149975849f1abb3832b246010ef62ccc80d3a76169517ada7188252b9cfb437" +dependencies = [ + "aws-lc-rs", + "ring 0.17.14", "rustls-pki-types", "untrusted 0.9.0", ] @@ -7565,21 +10755,43 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "rustyline" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix 0.27.1", + "radix_trie", + "unicode-segmentation", + "unicode-width 0.1.14", + "utf8parse", + "winapi", +] [[package]] name = "ryu" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "ryu-js" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5" +checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15" [[package]] name = "safetensors" @@ -7591,6 +10803,25 @@ dependencies = [ "serde_json", ] +[[package]] +name = "saffron" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03fb9a628596fc7590eb7edbf7b0613287be78df107f5f97b118aad59fb2eea9" +dependencies = [ + "chrono", + "nom 5.1.3", +] + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher 0.4.4", +] + [[package]] name = "samael" version = "0.0.14" @@ -7615,7 +10846,7 @@ dependencies = [ "serde", "thiserror 1.0.69", "url", - "uuid 1.13.1", + "uuid", ] [[package]] @@ -7644,28 +10875,26 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" dependencies = [ - "chrono", "dyn-clone", "schemars_derive", "serde", "serde_json", - "uuid 1.13.1", ] [[package]] name = "schemars_derive" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7680,13 +10909,25 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "password-hash", + "pbkdf2", + "salsa20", + "sha2 0.10.9", +] + [[package]] name = "sct" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.8", + "ring 0.17.14", "untrusted 0.9.0", ] @@ -7706,6 +10947,7 @@ dependencies = [ "der", "generic-array", "pkcs8", + "serdect", "subtle", "zeroize", ] @@ -7716,7 +10958,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -7729,7 +10971,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "core-foundation 0.10.0", "core-foundation-sys", "libc", @@ -7757,12 +10999,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" -dependencies = [ - "serde", -] +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "semver-parser" @@ -7772,27 +11011,28 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "seq-macro" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde-aux" -version = "4.5.0" +version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d2e8bfba469d06512e11e3311d4d051a4a387a5b42d010404fecf3200321c95" +checksum = "207f67b28fe90fb596503a9bf0bf1ea5e831e21307658e177c5dfcdfc3ab8a0a" dependencies = [ "chrono", "serde", + "serde-value", "serde_json", ] @@ -7818,14 +11058,23 @@ dependencies = [ ] [[package]] -name = "serde_derive" -version = "1.0.217" +name = "serde_bytes" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7836,16 +11085,16 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "itoa", "memchr", "ryu", @@ -7863,9 +11112,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" dependencies = [ "itoa", "serde", @@ -7880,37 +11129,15 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_qs" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" -dependencies = [ - "percent-encoding", - "serde", - "thiserror 1.0.69", -] - -[[package]] -name = "serde_qs" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cac3f1e2ca2fe333923a1ae72caca910b98ed0630bb35ef6f8c8517d6e81afa" -dependencies = [ - "percent-encoding", - "serde", - "thiserror 1.0.69", -] - [[package]] name = "serde_repr" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7922,18 +11149,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_tokenstream" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64060d864397305347a78851c51588fd283767e7e7589829e8121d65512340f1" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn 2.0.98", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -7948,14 +11163,15 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.230.0" +version = "0.245.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a783242d2af51d6955cc04bf2b64adb643ab588b61e9573c908a69dabf8c2f" +checksum = "945f93c91e0c7e4799b5fefff076756141aae92e262c4dc4833310dd3d2d845e" dependencies = [ + "deno_error", "num-bigint", "serde", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.12", "v8", ] @@ -7969,7 +11185,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.7.1", + "indexmap 2.9.0", "serde", "serde_derive", "serde_json", @@ -7983,23 +11199,20 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ - "darling 0.20.10", + "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" +name = "serdect" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" dependencies = [ - "indexmap 2.7.1", - "itoa", - "ryu", + "base16ct", "serde", - "unsafe-libyaml", ] [[package]] @@ -8028,15 +11241,25 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", "digest 0.10.7", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -8062,10 +11285,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "signal-hook-registry" -version = "1.4.2" +name = "signal-hook" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -8101,6 +11334,27 @@ dependencies = [ "outref 0.1.0", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd-json" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2bcf6c6e164e81bc7a5d49fc6988b3d515d9e8c07457d7b74ffb9324b9cd40" +dependencies = [ + "getrandom 0.2.16", + "halfbrown", + "ref-cast", + "serde", + "serde_json", + "simdutf8", + "value-trait", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -8115,7 +11369,7 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", ] @@ -8131,6 +11385,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +[[package]] +name = "size" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6709c7b6754dca1311b3c73e79fcce40dd414c782c66d88e8823030093b02b" + [[package]] name = "sketches-ddsketch" version = "0.2.2" @@ -8150,23 +11410,30 @@ dependencies = [ ] [[package]] -name = "smallvec" -version = "1.13.2" +name = "slotmap" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" dependencies = [ - "serde", + "version_check", ] [[package]] -name = "smart-default" -version = "0.6.0" +name = "sm3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" +checksum = "ebb9a3b702d0a7e33bc4d85a14456633d2b165c2ad839c5fd9a8417c1ab15860" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "digest 0.10.7", +] + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +dependencies = [ + "serde", ] [[package]] @@ -8180,42 +11447,11 @@ dependencies = [ "version_check", ] -[[package]] -name = "smol_str" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad6c857cbab2627dcf01ec85a623ca4e7dcb5691cbaa3d7fb7653671f0d09c9" -dependencies = [ - "serde", -] - [[package]] name = "smtp-proto" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b8ad3dd187f0d4debab02ad65405a9919d6a4f7bce25bd64a258781063a53a" - -[[package]] -name = "snafu" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" -dependencies = [ - "doc-comment", - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] +checksum = "b7d3950ab75b03c52f2f13fd52aab91c9d62698b231b67240e85c3ef5301e63e" [[package]] name = "snap" @@ -8225,9 +11461,9 @@ checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -8254,17 +11490,16 @@ dependencies = [ [[package]] name = "sourcemap" -version = "9.1.2" +version = "9.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c4ea7042fd1a155ad95335b5d505ab00d5124ea0332a06c8390d200bb1a76a" +checksum = "dd430118acc9fdd838557649b9b43fd0a78e3834d84a283b466f8e84720d6101" dependencies = [ - "base64-simd 0.7.0", + "base64-simd 0.8.0", "bitvec", "data-encoding", "debugid", "if_chain", - "rustc-hash 1.1.0", - "rustc_version 0.2.3", + "rustc-hash 2.1.1", "serde", "serde_json", "unicode-id-start", @@ -8286,6 +11521,15 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "spki" version = "0.7.3" @@ -8303,11 +11547,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5851699c4033c63636f7ea4cf7b7c1f1bf06d0cc03cfb42e711de5a5c46cf326" dependencies = [ "base64 0.13.1", - "nom", + "nom 7.1.3", "serde", "unicode-segmentation", ] +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + [[package]] name = "sql-builder" version = "3.1.1" @@ -8320,30 +11570,31 @@ dependencies = [ [[package]] name = "sqlparser" -version = "0.47.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "295e9930cd7a97e58ca2a070541a3ca502b17f5d1fa7157376d0fabd85324f25" +checksum = "c4521174166bac1ff04fe16ef4524c70144cd29682a45978978ca3d7f4e0be11" dependencies = [ "log", + "recursive", "sqlparser_derive", ] [[package]] name = "sqlparser_derive" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554" +checksum = "da5fc6819faabb412da764b99d3b713bb55083c11e7e0c00144d386cd6a1939c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "sqlx" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f" +checksum = "f3c3a85280daca669cfd3bcb68a337882a8bc57ec882f72c5d13a430613a738e" dependencies = [ "sqlx-core", "sqlx-macros", @@ -8354,10 +11605,11 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0" +checksum = "f743f2a3cea30a58cd479013f75550e879009e3a02f616f18ca699335aa248c3" dependencies = [ + "base64 0.22.1", "bigdecimal", "bytes", "chrono", @@ -8369,46 +11621,45 @@ dependencies = [ "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.15.2", - "hashlink", - "indexmap 2.7.1", + "hashbrown 0.15.3", + "hashlink 0.10.0", + "indexmap 2.9.0", "log", "memchr", "once_cell", "percent-encoding", - "rustls 0.23.22", - "rustls-pemfile 2.2.0", + "rustls 0.23.27", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "smallvec", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tokio-stream", "tracing", "url", - "uuid 1.13.1", - "webpki-roots", + "uuid", + "webpki-roots 0.26.11", ] [[package]] name = "sqlx-macros" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310" +checksum = "7f4200e0fde19834956d4252347c12a083bdcb237d7a1a1446bffd8768417dce" dependencies = [ "proc-macro2", "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "sqlx-macros-core" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad" +checksum = "882ceaa29cade31beca7129b6beeb05737f44f82dbe2a9806ecea5a7093d00b7" dependencies = [ "dotenvy", "either", @@ -8419,12 +11670,12 @@ dependencies = [ "quote", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "sqlx-core", "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.98", + "syn 2.0.101", "tempfile", "tokio", "url", @@ -8432,14 +11683,14 @@ dependencies = [ [[package]] name = "sqlx-mysql" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233" +checksum = "0afdd3aa7a629683c2d750c2df343025545087081ab5942593a5288855b1b7a7" dependencies = [ "atoi", "base64 0.22.1", "bigdecimal", - "bitflags 2.8.0", + "bitflags 2.9.0", "byteorder", "bytes", "chrono", @@ -8465,26 +11716,26 @@ dependencies = [ "rsa", "serde", "sha1", - "sha2 0.10.8", + "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.11", + "thiserror 2.0.12", "tracing", - "uuid 1.13.1", + "uuid", "whoami", ] [[package]] name = "sqlx-postgres" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613" +checksum = "a0bedbe1bbb5e2615ef347a5e9d8cd7680fb63e77d9dafc0f29be15e53f1ebe6" dependencies = [ "atoi", "base64 0.22.1", "bigdecimal", - "bitflags 2.8.0", + "bitflags 2.9.0", "byteorder", "chrono", "crc", @@ -8506,21 +11757,21 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.11", + "thiserror 2.0.12", "tracing", - "uuid 1.13.1", + "uuid", "whoami", ] [[package]] name = "sqlx-sqlite" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540" +checksum = "c26083e9a520e8eb87a06b12347679b142dc2ea29e6e409f805644a7a979a5bc" dependencies = [ "atoi", "chrono", @@ -8536,9 +11787,10 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", + "thiserror 2.0.12", "tracing", "url", - "uuid 1.13.1", + "uuid", ] [[package]] @@ -8549,9 +11801,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stacker" -version = "0.1.17" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" +checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" dependencies = [ "cc", "cfg-if", @@ -8575,7 +11827,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -8595,6 +11847,15 @@ dependencies = [ "unicode-properties", ] +[[package]] +name = "strip-ansi-escapes" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025" +dependencies = [ + "vte", +] + [[package]] name = "strsim" version = "0.10.0" @@ -8613,16 +11874,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" dependencies = [ - "strum_macros 0.25.3", -] - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros 0.26.4", + "strum_macros", ] [[package]] @@ -8635,30 +11887,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.98", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.98", -] - -[[package]] -name = "subprocess" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" -dependencies = [ - "libc", - "winapi", + "syn 2.0.101", ] [[package]] @@ -8667,6 +11896,27 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "supports-color" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" +dependencies = [ + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f44ed3c63152de6a9f90acbea1a110441de43006ea51bcce8f436196a288b" + +[[package]] +name = "supports-unicode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" + [[package]] name = "swc_allocator" version = "0.1.10" @@ -8700,7 +11950,7 @@ checksum = "83406221c501860fce9c27444f44125eafe9e598b8b81be7563d7036784cd05c" dependencies = [ "ahash 0.8.11", "anyhow", - "dashmap", + "dashmap 5.5.3", "once_cell", "regex", "serde", @@ -8723,7 +11973,7 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "siphasher 0.3.11", - "sourcemap 9.1.2", + "sourcemap 9.2.0", "swc_allocator", "swc_atoms", "swc_eq_ignore_macros", @@ -8740,7 +11990,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4740e53eaf68b101203c1df0937d5161a29f3c13bceed0836ddfe245b72dd000" dependencies = [ "anyhow", - "indexmap 2.7.1", + "indexmap 2.9.0", "serde", "serde_json", "swc_cached", @@ -8756,7 +12006,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -8765,7 +12015,7 @@ version = "0.118.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6f866d12e4d519052b92a0a86d1ac7ff17570da1272ca0c89b3d6f802cd79df" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "is-macro", "num-bigint", "phf", @@ -8787,7 +12037,7 @@ dependencies = [ "num-bigint", "once_cell", "serde", - "sourcemap 9.1.2", + "sourcemap 9.2.0", "swc_allocator", "swc_atoms", "swc_common", @@ -8805,7 +12055,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -8851,8 +12101,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65f21494e75d0bd8ef42010b47cabab9caaed8f2207570e809f6f4eb51a710d1" dependencies = [ "better_scoped_tls", - "bitflags 2.8.0", - "indexmap 2.7.1", + "bitflags 2.9.0", + "indexmap 2.9.0", "once_cell", "phf", "rustc-hash 1.1.0", @@ -8890,7 +12140,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -8920,8 +12170,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76c76d8b9792ce51401d38da0fa62158d61f6d80d16d68fe5b03ce4bf5fba383" dependencies = [ "base64 0.21.7", - "dashmap", - "indexmap 2.7.1", + "dashmap 5.5.3", + "indexmap 2.9.0", "once_cell", "serde", "sha1", @@ -8961,7 +12211,7 @@ version = "0.134.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029eec7dd485923a75b5a45befd04510288870250270292fc2c1b3a9e7547408" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "num_cpus", "once_cell", "rustc-hash 1.1.0", @@ -8997,7 +12247,7 @@ checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -9008,7 +12258,7 @@ checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -9031,7 +12281,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -9047,9 +12297,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.98" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -9073,13 +12323,44 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "sys_traits" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b46ac05dfbe9fd3a9703eff20e17f5b31e7b6a54daf27a421dcd56c7a27ecdd" +dependencies = [ + "libc", + "windows-sys 0.59.0", ] [[package]] @@ -9088,7 +12369,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7dddc5f0fee506baf8b9fdb989e242f17e4b11c61dfbb0635b705217199eea" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "byteorder", "enum-as-inner", "libc", @@ -9096,6 +12377,34 @@ dependencies = [ "walkdir", ] +[[package]] +name = "sysctl" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01198a2debb237c62b6826ec7081082d951f46dbb64b0e8c7649a452230d1dfc" +dependencies = [ + "bitflags 2.9.0", + "byteorder", + "enum-as-inner", + "libc", + "thiserror 1.0.69", + "walkdir", +] + +[[package]] +name = "sysinfo" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c33cd241af0f2e9e3b5c32163b873b29956890b5342e6745b917ce9d490f4af" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "rayon", + "windows 0.57.0", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -9113,7 +12422,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "core-foundation 0.9.4", "system-configuration-sys 0.6.0", ] @@ -9138,6 +12447,26 @@ dependencies = [ "libc", ] +[[package]] +name = "systemstat" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668a4db78b439df482c238f559e4ea869017f9e62ef0a059c8bfcd841a4df544" +dependencies = [ + "bytesize", + "lazy_static", + "libc", + "nom 7.1.3", + "time", + "winapi", +] + +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tantivy" version = "0.22.0" @@ -9160,10 +12489,10 @@ dependencies = [ "itertools 0.12.1", "levenshtein_automata", "log", - "lru", + "lru 0.12.5", "lz4_flex", "measure_time", - "memmap2", + "memmap2 0.9.5", "num_cpus", "once_cell", "oneshot", @@ -9185,7 +12514,7 @@ dependencies = [ "tempfile", "thiserror 1.0.69", "time", - "uuid 1.13.1", + "uuid", "winapi", ] @@ -9244,7 +12573,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "847434d4af57b32e309f4ab1b4f1707a6c566656264caa427ff4285c4d9d0b82" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -9266,7 +12595,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c56d6ff5591fc332739b3ce7035b57995a3ce29a93ffd6012660e0949c956ea8" dependencies = [ "murmurhash32", - "rand_distr", + "rand_distr 0.4.3", "tantivy-common", ] @@ -9287,9 +12616,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", @@ -9298,15 +12627,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.16.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "cfg-if", - "fastrand 2.3.0", - "getrandom 0.3.1", + "fastrand", + "getrandom 0.3.2", "once_cell", - "rustix", + "rustix 1.0.7", "windows-sys 0.59.0", ] @@ -9319,6 +12647,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" +dependencies = [ + "rustix 1.0.7", + "windows-sys 0.59.0", +] + [[package]] name = "text_lines" version = "0.6.0" @@ -9328,6 +12666,16 @@ dependencies = [ "serde", ] +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "unicode-linebreak", + "unicode-width 0.2.0", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -9339,11 +12687,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.11", + "thiserror-impl 2.0.12", ] [[package]] @@ -9354,18 +12702,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -9415,7 +12763,7 @@ dependencies = [ "tokio-rustls 0.24.1", "tokio-util", "tracing", - "uuid 1.13.1", + "uuid", ] [[package]] @@ -9462,9 +12810,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.37" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -9477,15 +12825,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -9512,9 +12860,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -9550,7 +12898,7 @@ dependencies = [ "clap", "derive_builder", "esaxx-rs", - "getrandom 0.2.15", + "getrandom 0.2.16", "indicatif", "itertools 0.11.0", "lazy_static", @@ -9575,15 +12923,15 @@ dependencies = [ [[package]] name = "tokio" -version = "1.43.0" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", "libc", - "mio", - "parking_lot", + "mio 1.0.3", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", "socket2", @@ -9592,6 +12940,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "tokio-eld" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9166030f05d6bc5642bdb8f8c2be31eb3c02cd465d662bcdc2df82d4aa41a584" +dependencies = [ + "hdrhistogram", + "tokio", +] + [[package]] name = "tokio-macros" version = "2.5.0" @@ -9600,7 +12958,19 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "tokio-metrics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eace09241d62c98b7eeb1107d4c5c64ca3bd7da92e8c218c153ab3a78f9be112" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", + "tokio-stream", ] [[package]] @@ -9621,11 +12991,11 @@ dependencies = [ "async-trait", "byteorder", "bytes", - "fallible-iterator", + "fallible-iterator 0.2.0", "futures-channel", "futures-util", "log", - "parking_lot", + "parking_lot 0.12.3", "percent-encoding", "phf", "pin-project-lite", @@ -9647,11 +13017,11 @@ dependencies = [ "async-trait", "byteorder", "bytes", - "fallible-iterator", + "fallible-iterator 0.2.0", "futures-channel", "futures-util", "log", - "parking_lot", + "parking_lot 0.12.3", "percent-encoding", "phf", "pin-project-lite", @@ -9664,6 +13034,16 @@ dependencies = [ "whoami", ] +[[package]] +name = "tokio-retry2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1264d076dd34560544a2799e40e457bd07c43d30f4a845686b031bcd8455c84f" +dependencies = [ + "pin-project", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" @@ -9676,11 +13056,22 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls 0.23.22", + "rustls 0.22.4", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls 0.23.27", "tokio", ] @@ -9738,15 +13129,18 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", + "futures-util", + "hashbrown 0.15.3", "pin-project-lite", + "slab", "tokio", ] @@ -9760,14 +13154,14 @@ dependencies = [ "bytes", "futures-core", "futures-sink", - "http 1.2.0", + "http 1.3.1", "httparse", "rand 0.8.5", - "ring 0.17.8", + "ring 0.17.14", "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tokio-util", ] @@ -9783,23 +13177,11 @@ dependencies = [ "toml_edit 0.19.15", ] -[[package]] -name = "toml" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.23", -] - [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] @@ -9810,7 +13192,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", @@ -9819,15 +13201,13 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.23" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ - "indexmap 2.7.1", - "serde", - "serde_spanned", + "indexmap 2.9.0", "toml_datetime", - "winnow 0.7.1", + "winnow 0.7.10", ] [[package]] @@ -9841,8 +13221,9 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2 0.4.7", - "http 1.2.0", + "flate2", + "h2 0.4.10", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "hyper 1.6.0", @@ -9855,25 +13236,13 @@ dependencies = [ "rustls-pemfile 2.2.0", "socket2", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tokio-stream", "tower 0.4.13", "tower-layer", "tower-service", "tracing", -] - -[[package]] -name = "toolchain_find" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc8c9a7f0a2966e1acdaf0461023d0b01471eeead645370cf4c3f5cff153f2a" -dependencies = [ - "home", - "once_cell", - "regex", - "semver 1.0.25", - "walkdir", + "webpki-roots 0.26.11", ] [[package]] @@ -9922,8 +13291,8 @@ dependencies = [ "axum-core", "cookie 0.18.1", "futures-util", - "http 1.2.0", - "parking_lot", + "http 1.3.1", + "parking_lot 0.12.3", "pin-project-lite", "tower-layer", "tower-service", @@ -9936,10 +13305,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ "async-compression", - "bitflags 2.8.0", + "bitflags 2.9.0", "bytes", "futures-core", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "pin-project-lite", @@ -9994,7 +13363,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -10035,7 +13404,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3beec919fbdf99d719de8eda6adae3281f8a5b71ae40431f44dc7423053d34" dependencies = [ "loki-api", - "reqwest 0.12.9", + "reqwest 0.12.15", "serde", "serde_json", "snap", @@ -10084,7 +13453,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", - "nu-ansi-term", + "nu-ansi-term 0.46.0", "once_cell", "regex", "serde", @@ -10121,10 +13490,20 @@ dependencies = [ ] [[package]] -name = "tree-sitter-language" -version = "0.1.4" +name = "tree-sitter-java" +version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eee4db33814de3d004de9d8d825627ed3320d0989cce0dea30efaf5be4736c" +checksum = "0aa6cbcdc8c679b214e616fd3300da67da0e492e066df01bcf5a5921a71e90d6" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-language" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4013970217383f67b18aef68f6fb2e8d409bc5755227092d32efb0422ba24b8" [[package]] name = "triomphe" @@ -10162,7 +13541,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.2.0", + "http 1.3.1", "httparse", "log", "native-tls", @@ -10179,6 +13558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", + "rand 0.8.5", "static_assertions", ] @@ -10194,64 +13574,71 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] -name = "typify" -version = "0.0.12" +name = "typetag" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6658d09e71bfe59e7987dc95ee7f71809fdb5793ab0cdc1503cc0073990484d" +checksum = "73f22b40dd7bfe8c14230cf9702081366421890435b2d625fa92b4acc4c3de6f" dependencies = [ - "typify-impl", - "typify-macro", -] - -[[package]] -name = "typify-impl" -version = "0.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d3bb47587b13edf526d6ed02bf360ecefe083ab47a4ef29fc43112828b2bef" -dependencies = [ - "heck 0.4.1", - "log", - "proc-macro2", - "quote", - "regress", - "schemars", - "serde_json", - "syn 2.0.98", - "thiserror 1.0.69", - "unicode-ident", -] - -[[package]] -name = "typify-macro" -version = "0.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3f7e627c18be12d53bc1f261830b9c2763437b6a86ac57293b9085af2d32ffe" -dependencies = [ - "proc-macro2", - "quote", - "schemars", + "erased-serde", + "inventory", + "once_cell", "serde", - "serde_json", - "serde_tokenstream", - "syn 2.0.98", - "typify-impl", + "typetag-impl", +] + +[[package]] +name = "typetag-impl" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35f5380909ffc31b4de4f4bdf96b877175a016aa2ca98cee39fcfd8c4d53d952" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "ug" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b70b37e9074642bc5f60bb23247fd072a84314ca9e71cdf8527593406a0dd3" +dependencies = [ + "gemm 0.18.2", + "half", + "libloading 0.8.6", + "memmap2 0.9.5", + "num", + "num-traits", + "num_cpus", + "rayon", + "safetensors", + "serde", + "thiserror 1.0.69", + "tracing", + "yoke", ] [[package]] name = "ulid" -version = "1.1.4" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f294bff79170ed1c5633812aff1e565c35d993a36e757f9bc0accf5eec4e6045" +checksum = "470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe" dependencies = [ - "rand 0.8.5", - "uuid 1.13.1", + "rand 0.9.0", + "uuid", "web-time", ] @@ -10345,9 +13732,15 @@ checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-normalization" @@ -10426,10 +13819,14 @@ dependencies = [ ] [[package]] -name = "unsafe-libyaml" -version = "0.2.11" +name = "universal-hash" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] [[package]] name = "untrusted" @@ -10454,12 +13851,12 @@ dependencies = [ "log", "native-tls", "once_cell", - "rustls 0.23.22", + "rustls 0.23.27", "rustls-pki-types", "serde", "serde_json", "url", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -10510,6 +13907,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" +[[package]] +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -10524,21 +13927,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "0.8.2" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.2.15", -] - -[[package]] -name = "uuid" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" -dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.2", + "js-sys", "serde", + "wasm-bindgen", ] [[package]] @@ -10548,7 +13944,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a511192602f7b435b0a241c1947aa743eb7717f20a9195f4b5e8ed1952e01db1" dependencies = [ "bindgen 0.70.1", - "bitflags 2.8.0", + "bitflags 2.9.0", "fslock", "gzip-header", "home", @@ -10558,12 +13954,39 @@ dependencies = [ "which 6.0.3", ] +[[package]] +name = "v8_valueserializer" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97599c400fc79925922b58303e98fcb8fa88f573379a08ddb652e72cbd2e70f6" +dependencies = [ + "bitflags 2.9.0", + "encoding_rs", + "indexmap 2.9.0", + "num-bigint", + "serde", + "thiserror 1.0.69", + "wtf8", +] + [[package]] name = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "value-trait" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9170e001f458781e92711d2ad666110f153e4e50bfd5cbd02db6547625714187" +dependencies = [ + "float-cmp", + "halfbrown", + "itoa", + "ryu", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -10583,10 +14006,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" [[package]] -name = "waker-fn" -version = "1.2.0" +name = "vte" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" +checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077" +dependencies = [ + "memchr", +] [[package]] name = "walkdir" @@ -10607,12 +14033,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -10621,9 +14041,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -10636,46 +14056,48 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -10683,32 +14105,34 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-bindgen-test" -version = "0.3.42" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" dependencies = [ - "console_error_panic_hook", "js-sys", - "scoped-tls", + "minicov", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -10716,20 +14140,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.42" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "wasm-streams" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -10739,28 +14163,35 @@ dependencies = [ ] [[package]] -name = "wasm_dep_analyzer" -version = "0.1.0" +name = "wasm-timer" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f270206a91783fd90625c8bb0d8fbd459d0b1d1bf209b656f713f01ae7c04b8" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" dependencies = [ - "thiserror 1.0.69", + "futures", + "js-sys", + "parking_lot 0.11.2", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] -name = "wav" -version = "1.0.1" +name = "wasm_dep_analyzer" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d97402f69875b579ec37f2aa52d1f455a1d6224251edba32e8c18a5da2698d" +checksum = "2eeee3bdea6257cc36d756fa745a70f9d393571e47d69e0ed97581676a5369ca" dependencies = [ - "riff", + "deno_error", + "thiserror 2.0.12", ] [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -10777,14 +14208,124 @@ dependencies = [ ] [[package]] -name = "webpki-roots" -version = "0.26.8" +name = "webpki-root-certs" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +dependencies = [ + "webpki-root-certs 1.0.0", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a83f7e1a9f8712695c03eabe9ed3fbca0feff0152f33f12593e5a6303cb1a4" dependencies = [ "rustls-pki-types", ] +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.0", +] + +[[package]] +name = "webpki-roots" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "wgpu-core" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50819ab545b867d8a454d1d756b90cd5f15da1f2943334ca314af10583c9d39" +dependencies = [ + "arrayvec", + "bit-vec 0.6.3", + "bitflags 2.9.0", + "cfg_aliases 0.1.1", + "codespan-reporting", + "document-features", + "indexmap 2.9.0", + "log", + "naga", + "once_cell", + "parking_lot 0.12.3", + "profiling", + "raw-window-handle", + "ron", + "rustc-hash 1.1.0", + "serde", + "smallvec", + "thiserror 1.0.69", + "web-sys", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172e490a87295564f3fcc0f165798d87386f6231b04d4548bca458cbbfd63222" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set 0.5.3", + "bitflags 2.9.0", + "block", + "cfg_aliases 0.1.1", + "core-graphics-types", + "d3d12", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-descriptor", + "js-sys", + "khronos-egl", + "libc", + "libloading 0.8.6", + "log", + "metal", + "naga", + "ndk-sys", + "objc", + "once_cell", + "parking_lot 0.12.3", + "profiling", + "range-alloc", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 1.0.69", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "winapi", +] + +[[package]] +name = "wgpu-types" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef" +dependencies = [ + "bitflags 2.9.0", + "js-sys", + "serde", + "web-sys", +] + [[package]] name = "which" version = "4.4.2" @@ -10794,7 +14335,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix", + "rustix 0.38.44", ] [[package]] @@ -10805,26 +14346,26 @@ checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" dependencies = [ "either", "home", - "rustix", + "rustix 0.38.44", "winsafe", ] [[package]] name = "whoami" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" dependencies = [ - "redox_syscall 0.5.8", + "redox_syscall 0.5.12", "wasite", "web-sys", ] [[package]] name = "widestring" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" +checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" [[package]] name = "winapi" @@ -10859,12 +14400,13 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "axum", "base64 0.22.1", "chrono", + "constant_time_eq", "deno_core", "dotenv", "futures", @@ -10877,18 +14419,23 @@ dependencies = [ "prometheus", "quote", "rand 0.9.0", - "reqwest 0.12.9", + "reqwest 0.12.15", + "rustls 0.23.27", "serde", "serde_json", - "sha2 0.10.8", + "sha1", + "sha2 0.10.9", + "size", "sqlx", + "systemstat", "tikv-jemalloc-ctl", "tikv-jemalloc-sys", "tikv-jemallocator", "tokio", + "tokio-stream", "tracing", "url", - "uuid 1.13.1", + "uuid", "v8", "windmill-api", "windmill-api-client", @@ -10902,7 +14449,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "argon2", @@ -10910,8 +14457,10 @@ dependencies = [ "async-oauth2", "async-recursion", "async-stream", - "async-stripe", "async_zip", + "aws-config", + "aws-sdk-sqs", + "aws-sdk-sts", "axum", "base32", "base64 0.22.1", @@ -10921,21 +14470,27 @@ dependencies = [ "candle-nn", "candle-transformers", "chrono", - "chrono-tz 0.10.1", + "chrono-tz", "const_format", + "constant_time_eq", "cookie 0.17.0", "cron", "datafusion", + "deno_core", + "deno_error", "futures", "git-version", + "google-cloud-googleapis", + "google-cloud-pubsub", "hex", "hf-hub", "hmac", - "http 1.2.0", + "http 1.3.1", "hyper 1.6.0", "itertools 0.14.0", - "jsonwebtoken", + "jsonwebtoken 8.3.0", "lazy_static", + "libxml", "magic-crypt", "mail-parser", "matchit", @@ -10953,28 +14508,33 @@ dependencies = [ "rand 0.9.0", "rdkafka", "regex", - "reqwest 0.12.9", + "reqwest 0.12.15", + "rmcp", "rsa", + "rumqttc", "rust-embed", "rust_decimal", "samael", "serde", "serde_json", "serde_urlencoded", - "sha2 0.10.8", + "sha1", + "sha2 0.10.9", "sql-builder", "sqlx", "tempfile", - "thiserror 2.0.11", + "thiserror 2.0.12", "time", "tinyvector", "tokenizers", "tokio", "tokio-native-tls", "tokio-postgres 0.7.11", + "tokio-stream", "tokio-tar", "tokio-tungstenite", "tokio-util", + "tonic", "tower 0.5.2", "tower-cookies", "tower-http", @@ -10983,37 +14543,37 @@ dependencies = [ "ulid", "url", "urlencoding", - "uuid 1.13.1", + "uuid", "windmill-audit", "windmill-common", "windmill-git-sync", "windmill-indexer", "windmill-parser", + "windmill-parser-py", + "windmill-parser-py-imports", "windmill-parser-ts", "windmill-queue", + "windmill-worker", ] [[package]] name = "windmill-api-client" -version = "1.458.1" +version = "1.488.0" dependencies = [ "base64 0.22.1", "chrono", "openapiv3", - "prettyplease 0.1.25", - "progenitor", "progenitor-client", "rand 0.9.0", "reqwest 0.11.27", "serde", "serde_json", - "syn 1.0.109", - "uuid 1.13.1", + "uuid", ] [[package]] name = "windmill-audit" -version = "1.458.1" +version = "1.488.0" dependencies = [ "chrono", "serde", @@ -11026,30 +14586,31 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "serde", "serde_json", "sqlx", "tracing", - "uuid 1.13.1", + "uuid", "windmill-common", "windmill-queue", ] [[package]] name = "windmill-common" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "async-stream", "aws-config", "aws-sdk-sts", "axum", + "backon", "bytes", "chrono", - "chrono-tz 0.10.1", + "chrono-tz", "const_format", "crc", "cron", @@ -11061,8 +14622,9 @@ dependencies = [ "hex", "hmac", "hyper 1.6.0", - "indexmap 2.7.1", + "indexmap 2.9.0", "itertools 0.14.0", + "jsonwebtoken 8.3.0", "lazy_static", "magic-crypt", "mail-send", @@ -11077,14 +14639,19 @@ dependencies = [ "quick_cache", "rand 0.9.0", "regex", - "reqwest 0.12.9", - "semver 1.0.25", + "reqwest 0.12.15", + "reqwest-middleware", + "reqwest-retry", + "semver 1.0.26", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", + "size", "sqlx", + "systemstat", + "tar", "tempfile", - "thiserror 2.0.11", + "thiserror 2.0.12", "tikv-jemalloc-ctl", "tokio", "tonic", @@ -11093,27 +14660,27 @@ dependencies = [ "tracing-loki", "tracing-opentelemetry", "tracing-subscriber", - "uuid 1.13.1", + "uuid", "windmill-macros", ] [[package]] name = "windmill-git-sync" -version = "1.458.1" +version = "1.488.0" dependencies = [ "regex", "serde", "serde_json", "sqlx", "tracing", - "uuid 1.13.1", + "uuid", "windmill-common", "windmill-queue", ] [[package]] name = "windmill-indexer" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "bytes", @@ -11130,25 +14697,25 @@ dependencies = [ "tokio", "tokio-tar", "tracing", - "uuid 1.13.1", + "uuid", "windmill-common", ] [[package]] name = "windmill-macros" -version = "1.458.1" +version = "1.488.0" dependencies = [ "itertools 0.14.0", "lazy_static", "proc-macro2", "quote", "regex", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "windmill-parser" -version = "1.458.1" +version = "1.488.0" dependencies = [ "convert_case 0.6.0", "serde", @@ -11157,7 +14724,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "lazy_static", @@ -11169,7 +14736,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "serde_json", @@ -11181,7 +14748,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "gosyn", @@ -11193,7 +14760,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "lazy_static", @@ -11203,9 +14770,32 @@ dependencies = [ "windmill-parser", ] +[[package]] +name = "windmill-parser-java" +version = "1.488.0" +dependencies = [ + "anyhow", + "serde_json", + "tree-sitter", + "tree-sitter-java", + "wasm-bindgen", + "windmill-parser", +] + +[[package]] +name = "windmill-parser-nu" +version = "1.488.0" +dependencies = [ + "anyhow", + "nu-parser", + "serde_json", + "wasm-bindgen", + "windmill-parser", +] + [[package]] name = "windmill-parser-php" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -11216,7 +14806,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -11227,7 +14817,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "async-recursion", @@ -11247,7 +14837,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -11257,14 +14847,14 @@ dependencies = [ "quote", "regex", "serde_json", - "syn 2.0.98", - "toml 0.7.8", + "syn 2.0.101", + "toml", "windmill-parser", ] [[package]] name = "windmill-parser-sql" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "lazy_static", @@ -11276,7 +14866,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "lazy_static", @@ -11294,10 +14884,10 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", - "getrandom 0.2.15", + "getrandom 0.2.16", "serde_json", "wasm-bindgen", "wasm-bindgen-test", @@ -11306,6 +14896,8 @@ dependencies = [ "windmill-parser-csharp", "windmill-parser-go", "windmill-parser-graphql", + "windmill-parser-java", + "windmill-parser-nu", "windmill-parser-php", "windmill-parser-py", "windmill-parser-rust", @@ -11316,7 +14908,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "serde_json", @@ -11326,14 +14918,14 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "async-recursion", "axum", "backon", "chrono", - "chrono-tz 0.10.1", + "chrono-tz", "cron", "futures", "futures-core", @@ -11343,7 +14935,7 @@ dependencies = [ "lazy_static", "prometheus", "regex", - "reqwest 0.12.9", + "reqwest 0.12.15", "serde", "serde_json", "serde_urlencoded", @@ -11352,14 +14944,14 @@ dependencies = [ "tokio", "tracing", "ulid", - "uuid 1.13.1", + "uuid", "windmill-audit", "windmill-common", ] [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.458.1" +version = "1.488.0" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -11369,13 +14961,13 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.458.1" +version = "1.488.0" dependencies = [ "anyhow", "async-recursion", "backon", "base64 0.22.1", - "bit-vec", + "bit-vec 0.6.3", "bollard", "bytes", "chrono", @@ -11384,40 +14976,46 @@ dependencies = [ "deno_ast", "deno_console", "deno_core", + "deno_error", "deno_fetch", + "deno_io", "deno_net", "deno_permissions", + "deno_runtime", + "deno_telemetry", "deno_tls", "deno_url", "deno_web", "deno_webidl", "dotenv", "dyn-iter", + "flume", "futures", "gcp_auth", "git-version", "hex", "itertools 0.14.0", - "jsonwebtoken", + "jsonwebtoken 8.3.0", "lazy_static", "mappable-rc", "mysql_async", "native-tls", - "nix", + "nix 0.27.1", "object_store", "once_cell", "opentelemetry", "oracle", - "pem 3.0.4", + "pem 3.0.5", "postgres-native-tls 0.5.1", "prometheus", "rand 0.9.0", "regex", - "reqwest 0.12.9", + "reqwest 0.12.15", + "reqwest-middleware", "rust_decimal", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "sqlx", "tar", "tiberius", @@ -11425,16 +15023,21 @@ dependencies = [ "tokio-postgres 0.7.13", "tokio-util", "tracing", + "url", "urlencoding", - "uuid 1.13.1", + "uuid", + "winapi", "windmill-audit", "windmill-common", "windmill-git-sync", + "windmill-macros", "windmill-parser", "windmill-parser-bash", "windmill-parser-csharp", "windmill-parser-go", "windmill-parser-graphql", + "windmill-parser-java", + "windmill-parser-nu", "windmill-parser-php", "windmill-parser-py", "windmill-parser-py-imports", @@ -11447,22 +15050,196 @@ dependencies = [ ] [[package]] -name = "windows-core" -version = "0.52.0" +name = "windows" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" dependencies = [ + "windows-core 0.56.0", "windows-targets 0.52.6", ] [[package]] -name = "windows-registry" -version = "0.2.0" +name = "windows" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" +dependencies = [ + "windows-implement 0.56.0", + "windows-interface 0.56.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.2", + "windows-strings 0.4.0", +] + +[[package]] +name = "windows-implement" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result 0.3.2", + "windows-strings 0.3.1", + "windows-targets 0.53.0", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ - "windows-result", - "windows-strings", "windows-targets 0.52.6", ] @@ -11475,16 +15252,43 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -11536,13 +15340,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -11555,6 +15375,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -11567,6 +15393,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -11579,12 +15411,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -11597,6 +15441,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -11609,6 +15459,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -11621,6 +15477,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -11633,6 +15495,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.5.40" @@ -11653,9 +15521,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.1" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -11678,11 +15546,11 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -11697,6 +15565,12 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "wtf8" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c01ae8492c38f52376efd3a17d0994b6bcf3df1e39c0226d458b7d81670b2a06" + [[package]] name = "wyz" version = "0.5.1" @@ -11707,16 +15581,50 @@ dependencies = [ ] [[package]] -name = "xattr" -version = "1.4.0" +name = "x25519-dalek" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom 7.1.3", + "oid-registry", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "xattr" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" dependencies = [ "libc", - "linux-raw-sys", - "rustix", + "rustix 1.0.7", ] +[[package]] +name = "xml-rs" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" + [[package]] name = "xmlparser" version = "0.13.6" @@ -11767,8 +15675,8 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", - "synstructure", + "syn 2.0.101", + "synstructure 0.13.2", ] [[package]] @@ -11777,17 +15685,16 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", "zerocopy-derive 0.7.35", ] [[package]] name = "zerocopy" -version = "0.8.17" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "zerocopy-derive 0.8.17", + "zerocopy-derive 0.8.25", ] [[package]] @@ -11798,39 +15705,39 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "zerocopy-derive" -version = "0.8.17" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", - "synstructure", + "syn 2.0.101", + "synstructure 0.13.2", ] [[package]] @@ -11838,6 +15745,20 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] [[package]] name = "zerovec" @@ -11858,43 +15779,53 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "zip" -version = "0.6.6" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "9cc23c04387f4da0374be4533ad1208cbb091d5c11d070dfef13676ad6497164" dependencies = [ - "byteorder", + "arbitrary", "crc32fast", "crossbeam-utils", + "displaydoc", + "indexmap 2.9.0", + "num_enum", + "thiserror 1.0.69", ] [[package]] -name = "zstd" -version = "0.13.2" +name = "zlib-rs" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +checksum = "868b928d7949e09af2f6086dfc1e01936064cc7a819253bce650d4e2a2d63ba8" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.2.1" +version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" +version = "2.0.15+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" dependencies = [ "cc", "pkg-config", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 45f45b1caf..ffe4a8ee1b 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.458.1" +version = "1.488.0" authors.workspace = true edition.workspace = true @@ -22,15 +22,17 @@ members = [ "./parsers/windmill-parser-go", "./parsers/windmill-parser-rust", "./parsers/windmill-parser-csharp", + "./parsers/windmill-parser-nu", + "./parsers/windmill-parser-java", "./parsers/windmill-parser-bash", "./parsers/windmill-parser-py", "./parsers/windmill-parser-py-imports", "./parsers/windmill-sql-datatype-parser-wasm", - "./parsers/windmill-parser-yaml", "windmill-macros", + "./parsers/windmill-parser-yaml", "windmill-macros", "parsers/windmill-parser-nu", ] [workspace.package] -version = "1.458.1" +version = "1.488.0" authors = ["Ruben Fiszel "] edition = "2021" @@ -47,9 +49,10 @@ lto = "thin" [features] default = [] +agent_worker_server = ["windmill-api/agent_worker_server"] enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise", "dep:windmill-autoscaling", "windmill-autoscaling/enterprise", "windmill-git-sync/enterprise", "windmill-common/prometheus", "windmill-common/enterprise"] enterprise_saml = ["windmill-api/enterprise_saml", "oauth2"] -stripe = ["windmill-api/stripe", "enterprise"] +stripe = ["windmill-api/stripe"] benchmark = ["windmill-api/benchmark", "windmill-worker/benchmark", "windmill-queue/benchmark", "windmill-common/benchmark"] loki = ["windmill-common/loki"] embedding = ["windmill-api/embedding"] @@ -57,37 +60,49 @@ parquet = ["windmill-api/parquet", "windmill-common/parquet", "windmill-worker/p prometheus = ["windmill-common/prometheus", "windmill-api/prometheus", "windmill-worker/prometheus", "windmill-queue/prometheus", "dep:prometheus"] flow_testing = ["windmill-worker/flow_testing"] openidconnect = ["windmill-api/openidconnect"] -cloud = ["windmill-queue/cloud", "windmill-worker/cloud"] +cloud = ["windmill-queue/cloud", "windmill-worker/cloud", "windmill-common/cloud", "windmill-api/cloud"] jemalloc = ["windmill-common/jemalloc", "dep:tikv-jemallocator", "dep:tikv-jemalloc-sys", "dep:tikv-jemalloc-ctl"] -tantivy = ["dep:windmill-indexer", "windmill-api/tantivy", "windmill-indexer/enterprise", "windmill-indexer/parquet", "enterprise", "parquet"] +tantivy = ["dep:windmill-indexer", "windmill-api/tantivy", "windmill-indexer/enterprise", "windmill-indexer/parquet", "windmill-common/tantivy", "enterprise", "parquet"] sqlx = ["windmill-worker/sqlx"] -deno_core = ["windmill-worker/deno_core", "dep:deno_core", "dep:v8"] +deno_core = ["windmill-worker/deno_core", "windmill-api/deno_core", "dep:deno_core", "dep:v8"] kafka = ["windmill-api/kafka"] nats = ["windmill-api/nats"] otel = ["windmill-common/otel", "windmill-worker/otel"] dind = ["windmill-worker/dind"] -php = ["windmill-worker/php"] +websocket = ["windmill-api/websocket"] +http_trigger = ["windmill-api/http_trigger"] +postgres_trigger = ["windmill-api/postgres_trigger"] +mcp = ["windmill-api/mcp"] +mqtt_trigger = ["windmill-api/mqtt_trigger"] +sqs_trigger = ["windmill-api/sqs_trigger", "windmill-common/aws_auth", "windmill-api/openidconnect"] +gcp_trigger = ["windmill-api/gcp_trigger"] +smtp = ["windmill-api/smtp", "windmill-common/smtp"] +license = ["windmill-api/license"] +oauth2 = ["windmill-api/oauth2"] +zip = ["windmill-api/zip"] +static_frontend = ["windmill-api/static_frontend"] +scoped_cache = ["windmill-common/scoped_cache"] +# Languages +python = ["windmill-worker/python"] rust = ["windmill-worker/rust"] mysql = ["windmill-worker/mysql"] oracledb = ["windmill-worker/oracledb"] mssql = ["windmill-worker/mssql"] bigquery = ["windmill-worker/bigquery"] -websocket = ["windmill-api/websocket"] -postgres_trigger = ["windmill-api/postgres_trigger"] -python = ["windmill-worker/python"] -smtp = ["windmill-api/smtp", "windmill-common/smtp"] +php = ["windmill-worker/php"] csharp = ["windmill-worker/csharp"] -license = ["windmill-api/license"] -oauth2 = ["windmill-api/oauth2"] -http_trigger = ["windmill-api/http_trigger"] -zip = ["windmill-api/zip"] -static_frontend = ["windmill-api/static_frontend"] -scoped_cache = ["windmill-common/scoped_cache"] +nu = ["windmill-worker/nu"] +java = ["windmill-worker/java"] +all_languages = [ "python", "deno_core", "rust", "mysql", "oracledb", "mssql", "bigquery", "csharp", "nu", "php", "java"] +[patch.crates-io] +object_store = { git = "https://github.com/apache/arrow-rs-object-store", rev = "36752c975d4f29e20b57c91f81a10872dcd48ae7" } + [dependencies] anyhow.workspace = true tokio.workspace = true +tokio-stream.workspace = true dotenv.workspace = true windmill-queue.workspace = true windmill-common = { workspace = true, default-features = false } @@ -114,10 +129,14 @@ serde_json.workspace = true serde.workspace = true deno_core = { workspace = true, optional = true } object_store = { workspace = true, optional = true } +sha1 = { workspace = true, optional = true } +constant_time_eq = { workspace = true, optional = true } quote.workspace = true memchr.workspace = true v8 = { workspace = true, optional = true } - +rustls.workspace = true +systemstat.workspace = true +size.workspace = true [target.'cfg(not(target_env = "msvc"))'.dependencies] tikv-jemallocator = { optional = true, workspace = true } @@ -134,7 +153,6 @@ windmill-api-client.workspace = true deno_core = { workspace = true, features = ["include_js_files_for_snapshotting", "unsafe_use_unprotected_platform"] } - [workspace.dependencies] windmill-api = { path = "./windmill-api", default-features = false } windmill-queue = { path = "./windmill-queue" } @@ -153,18 +171,24 @@ windmill-parser-go = { path = "./parsers/windmill-parser-go" } windmill-parser-rust = { path = "./parsers/windmill-parser-rust" } windmill-parser-yaml = { path = "./parsers/windmill-parser-yaml" } windmill-parser-csharp = { path = "./parsers/windmill-parser-csharp" } +windmill-parser-java = { path = "./parsers/windmill-parser-java" } +windmill-parser-nu = { path = "./parsers/windmill-parser-nu" } windmill-parser-bash = { path = "./parsers/windmill-parser-bash" } windmill-parser-sql = { path = "./parsers/windmill-parser-sql" } windmill-parser-graphql = { path = "./parsers/windmill-parser-graphql" } windmill-parser-php = { path = "./parsers/windmill-parser-php" } windmill-api-client = { path = "./windmill-api-client" } -v8 = "=130.0.7" # Exact version +reqwest-retry = "^0" +reqwest-middleware = { version = "^0", features = ["json"] } + +rustls = "0.23.0" memchr = "2.7.4" axum = { version = "^0.7", features = ["multipart"] } headers = "^0" hyper = { version = "^1", features = ["full"] } -tokio = { version = "^1.42.0", features = ["full", "tracing"] } +tokio = { version = "^1.42.0", features = ["full", "tracing", "time"] } +tokio-stream = { version = "0.1.17" } tower = "^0" tower-http = { version = "^0.6", features = ["trace", "cors"] } tower-cookies = "^0.10" @@ -173,8 +197,8 @@ serde_json = { version = "^1", features = ["preserve_order", "raw_value"] } uuid = { version = "^1", features = ["serde", "v4"] } thiserror = "^2" anyhow = "^1" -chrono = { version = "0.4.35", features = ["serde"] } -chrono-tz = "^0" +chrono = { version = "^0.4", features = ["serde"] } +chrono-tz = "^0.10.1" tracing = "^0" tracing-subscriber = { version = "^0", features = ["env-filter", "json"] } tracing-appender = "^0" @@ -187,7 +211,7 @@ hex = "^0" sql-builder = "^3" argon2 = "^0" quick_cache = "^0" -rand = "^0" +rand = "=0.9.0" rand_core = { version = "^0", features = ["std"] } magic-crypt = "^3" git-version = "^0" @@ -211,28 +235,40 @@ itertools = "^0" regex = "^1" semver = "^1" -deno_fetch = "0.203.0" -deno_tls = "0.166.0" -deno_console = "0.179.0" -deno_url = "0.179.0" -deno_webidl = "0.179.0" -deno_web = "0.210.0" -deno_net = "0.171.0" -deno_core = "0.321.0" -deno_ast = { version = "=0.43.3", features = ["transpiling"] } -deno_permissions = "0.39.0" +v8 = "=130.0.7" # Exact version +deno_fetch = "0.214.0" +deno_tls = "0.177.0" +deno_console = "0.190.0" +deno_url = "0.190.0" +deno_webidl = "0.190.0" +deno_web = "0.221.0" +deno_io = "0.100.0" +deno_net = "0.182.0" +deno_core = "0.336.0" +deno_ast = { version = "=0.44.0", features = ["transpiling"] } +deno_permissions = "0.49.0" +deno_runtime = { version = "0.198.0", features = ["transpile"] } +deno_telemetry = "0.12.0" +deno_error = "=0.5.5" + +google-cloud-pubsub = "0.30.0" +google-cloud-googleapis = {version = "0.16.1", features = ["pubsub"]} +# TODO: remove once deno fixes the issue on their end +# https://github.com/denoland/deno/issues/28557 +winapi = { version = "0.3.9", features = ["sysinfoapi"] } swc_common = "=0.37.5" swc_ecma_parser = "=0.149.1" swc_ecma_ast = "=0.118.2" swc_ecma_visit = "=0.104.8" -async-recursion = "^1" +async-recursion = "^1" base64 = "^0" base32 = "^0" hmac = "0.12.1" sha2 = "0.10.6" +sha1 = "0.10.6" sqlx = { version = "0.8.0", features = [ "macros", "migrate", @@ -251,21 +287,17 @@ futures-core = "^0" lazy_static = "1.4.0" serde_derive = "1.0.147" const_format = { version = "0.2", features = ["rust_1_64", "rust_1_51"] } +constant_time_eq = "0.3.1" dyn-iter = "0.2.0" rsa = "^0" -async-stripe = { version = "0.39.1", features = [ - "runtime-tokio-hyper", - "checkout", - "billing", -] } async_zip = { version = "0.0.17", features = ["tokio", "tokio-fs", "deflate", "chrono"] } once_cell = "1.17.1" gosyn = "0.2.6" bytes = "1.4.0" gethostname = "0.4.3" -wasm-bindgen = "=0.2.92" -serde-wasm-bindgen = "0.6.5" -wasm-bindgen-test = "0.3.42" +wasm-bindgen = "^0" +serde-wasm-bindgen = "^0" +wasm-bindgen-test = "^0" convert_case = "0.6.0" getrandom = "0.2" tokio-postgres = {version = "^0.7", features = ["array-impls", "with-serde_json-1", "with-chrono-0_4", "with-uuid-1", "with-bit-vec-0_6"]} @@ -278,6 +310,7 @@ postgres-native-tls = "^0" native-tls = "^0" # samael will break compilation on MacOS. Use this fork instead to make it work # samael = { git="https://github.com/njaremko/samael", rev="464d015e3ae393e4b5dd00b4d6baa1b617de0dd6", features = ["xmlsec"] } +libxml = { version = "=0.3.3" } samael = { version="0.0.14", features = ["xmlsec"] } gcp_auth = "0.9.0" rust_decimal = { version = "^1", features = ["db-postgres", "serde-float"]} @@ -287,9 +320,9 @@ nix = { version = "0.27.1", features = ["process", "signal"] } tinyvector = { git = "https://github.com/windmill-labs/tinyvector", rev = "20823b94c20f2b9093f318badd24026cf54dcc85" } hf-hub = "0.3.2" tokenizers = "0.14.1" -candle-core = "0.3.0" -candle-transformers = "0.3.0" -candle-nn = "0.3.0" +candle-core = "0.9.1" +candle-transformers = "0.9.1" +candle-nn = "0.9.1" tiberius = { version = "0.12.3", default-features = false, features = ["rustls", "tds73", "chrono", "sql-browser-tokio"]} pin-project = "1" indexmap = { version = "2.2.5", features = ["serde"]} @@ -301,11 +334,13 @@ rdkafka = { version = "0.36.2", features = ["cmake-build", "ssl-vendored"] } pg_escape = "0.1.1" async-nats = "0.38.0" nkeys = "0.4.4" +nu-parser = { version = "0.101.0", default-features = false } -datafusion = "39.0.0" -object_store = { version = "0.10.0", features = ["aws", "azure"] } +datafusion = "47.0.0" +object_store = { git = "https://github.com/apache/arrow-rs-object-store", rev = "36752c975d4f29e20b57c91f81a10872dcd48ae7", features = ["aws", "azure"] } openidconnect = { version = "4.0.0-rc.1" } aws-config = "^1" +aws-sdk-sqs = "1.57.0" aws-sdk-sts = "^1" crc = "^3" @@ -315,14 +350,14 @@ async-stream = "^0" opentelemetry = "0.27.0" tracing-opentelemetry = "0.28.0" -opentelemetry_sdk = { version = "*", features = ["rt-tokio"] } +opentelemetry_sdk = { version = "0.27.1", features = ["rt-tokio"] } opentelemetry-otlp = { version = "0.27.0", features = ["grpc-tonic", "tls"] } opentelemetry-appender-tracing = "0.27.0" -opentelemetry-semantic-conventions = { version = "*", features = ["semconv_experimental"] } +opentelemetry-semantic-conventions = { version = "0.27.0", features = ["semconv_experimental"] } bollard = "0.18.1" -tonic = { version = "^0", features = ["tls-native-roots"] } +tonic = { version = "=0.12.3", features = ["tls-native-roots"] } byteorder = "1.5.0" tikv-jemallocator = { version = "0.5" } @@ -335,6 +370,10 @@ pin-project-lite = "^0" tantivy = "0.22.0" backon = "1.3.0" +systemstat = "0.2.4" +size = "0.5.0" + +flume = { version = "0.11.1", features = ["async"] } # Macro-related proc-macro2 = "1.0" @@ -345,6 +384,8 @@ quote = "1.0.36" regex-lite = "0.1.6" yaml-rust = "0.4.5" tokio-tungstenite = { version = "0.24.0", features = ["native-tls"] } -tree-sitter = {version = "0.23.0", features = []} +tree-sitter = { version = "0.23.0", features = [] } tree-sitter-c-sharp = "0.23.0" +tree-sitter-java = "0.23.0" oracle = { version = "0.6.3", features = ["chrono"] } +rumqttc = { version = "0.24.0", features = ["use-native-tls"]} diff --git a/backend/custom_migrations/grant_all_current_schema.sql b/backend/custom_migrations/grant_all_current_schema.sql new file mode 100644 index 0000000000..4a2860cf29 --- /dev/null +++ b/backend/custom_migrations/grant_all_current_schema.sql @@ -0,0 +1,27 @@ +DO +$$ +DECLARE + tbl_name text; + policy_exists boolean; + current_sch text; + tbl_names text[] := ARRAY['account', 'app', 'audit', 'capture', 'completed_job', 'flow', 'folder', 'http_trigger', 'queue', 'raw_app', 'resource', 'schedule', 'script', 'usr_to_group', 'variable']; +BEGIN + -- Get the current schema + SELECT current_schema() INTO current_sch; + + FOR tbl_name IN SELECT unnest(tbl_names) + LOOP + SELECT EXISTS ( + SELECT 1 + FROM pg_policies + WHERE schemaname = current_sch + AND tablename = tbl_name + AND policyname = 'admin_policy' + ) INTO policy_exists; + + IF NOT policy_exists THEN + EXECUTE format('CREATE POLICY admin_policy ON %I.%I TO windmill_admin USING (true);', current_sch, tbl_name); + END IF; + END LOOP; +END; +$$; diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 77cb564474..8e0364f544 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -3dab441fe1eb5c9a042a6a4d9933ddea29c1bf4e \ No newline at end of file +868ccad87afb804fe22818ecd3d5a091199bcdbf \ No newline at end of file diff --git a/backend/migrations/20241223155748_raw_apps_v2.down.sql b/backend/migrations/20241223155748_raw_apps_v2.down.sql new file mode 100644 index 0000000000..7e7dbecf84 --- /dev/null +++ b/backend/migrations/20241223155748_raw_apps_v2.down.sql @@ -0,0 +1,2 @@ +-- Add down migration script here +ALTER TABLE app_version DROP COLUMN IF EXISTS raw_app; \ No newline at end of file diff --git a/backend/migrations/20241223155748_raw_apps_v2.up.sql b/backend/migrations/20241223155748_raw_apps_v2.up.sql new file mode 100644 index 0000000000..02b1124957 --- /dev/null +++ b/backend/migrations/20241223155748_raw_apps_v2.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TABLE app_version ADD COLUMN IF NOT EXISTS raw_app BOOLEAN NOT NULL DEFAULT FALSE; \ No newline at end of file diff --git a/backend/migrations/20250122094704_add_nu_lang.down.sql b/backend/migrations/20250122094704_add_nu_lang.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250122094704_add_nu_lang.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250122094704_add_nu_lang.up.sql b/backend/migrations/20250122094704_add_nu_lang.up.sql new file mode 100644 index 0000000000..5317efa87f --- /dev/null +++ b/backend/migrations/20250122094704_add_nu_lang.up.sql @@ -0,0 +1,2 @@ +ALTER TYPE SCRIPT_LANG ADD VALUE IF NOT EXISTS 'nu'; +UPDATE config set config = jsonb_set(config, '{worker_tags}', config->'worker_tags' || '["nu"]'::jsonb) where name = 'worker__default' and config @> '{"worker_tags": ["deno", "python3", "go", "bash", "powershell", "dependency", "flow", "hub", "other", "bun", "php", "rust", "ansible", "csharp"]}'::jsonb AND NOT config->'worker_tags' @> '"nu"'::jsonb; diff --git a/backend/migrations/20250130184358_sqs_trigger.down.sql b/backend/migrations/20250130184358_sqs_trigger.down.sql new file mode 100644 index 0000000000..5b91b856ad --- /dev/null +++ b/backend/migrations/20250130184358_sqs_trigger.down.sql @@ -0,0 +1,2 @@ +-- Add down migration script here +DROP TABLE IF EXISTS sqs_trigger; \ No newline at end of file diff --git a/backend/migrations/20250130184358_sqs_trigger.up.sql b/backend/migrations/20250130184358_sqs_trigger.up.sql new file mode 100644 index 0000000000..aaa219b1de --- /dev/null +++ b/backend/migrations/20250130184358_sqs_trigger.up.sql @@ -0,0 +1,69 @@ +-- Add up migration script here +CREATE TABLE sqs_trigger( + path VARCHAR(255) NOT NULL, + queue_url VARCHAR(255) NOT NULL, + aws_resource_path VARCHAR(255) NOT NULL, + message_attributes TEXT[], + script_path VARCHAR(255) NOT NULL, + is_flow BOOLEAN NOT NULL, + workspace_id VARCHAR(50) NOT NULL, + edited_by VARCHAR(50) NOT NULL, + email VARCHAR(255) NOT NULL, + edited_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + extra_perms JSONB NULL, + error TEXT NULL, + server_id VARCHAR(50) NULL, + last_server_ping TIMESTAMPTZ NULL, + enabled BOOLEAN NOT NULL, + CONSTRAINT PK_sqs_trigger PRIMARY KEY (path,workspace_id), + CONSTRAINT fk_sqs_trigger_workspace FOREIGN KEY (workspace_id) + REFERENCES workspace(id) ON DELETE CASCADE +); + +GRANT ALL ON sqs_trigger TO windmill_user; +GRANT ALL ON sqs_trigger TO windmill_admin; + +ALTER TABLE sqs_trigger ENABLE ROW LEVEL SECURITY; + +CREATE POLICY admin_policy ON sqs_trigger FOR ALL TO windmill_admin USING (true); + +CREATE POLICY see_folder_extra_perms_user_select ON sqs_trigger FOR SELECT TO windmill_user +USING (SPLIT_PART(sqs_trigger.path, '/', 1) = 'f' AND SPLIT_PART(sqs_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_read'), ',')::text[])); +CREATE POLICY see_folder_extra_perms_user_insert ON sqs_trigger FOR INSERT TO windmill_user +WITH CHECK (SPLIT_PART(sqs_trigger.path, '/', 1) = 'f' AND SPLIT_PART(sqs_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[])); +CREATE POLICY see_folder_extra_perms_user_update ON sqs_trigger FOR UPDATE TO windmill_user +USING (SPLIT_PART(sqs_trigger.path, '/', 1) = 'f' AND SPLIT_PART(sqs_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[])); +CREATE POLICY see_folder_extra_perms_user_delete ON sqs_trigger FOR DELETE TO windmill_user +USING (SPLIT_PART(sqs_trigger.path, '/', 1) = 'f' AND SPLIT_PART(sqs_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[])); + +CREATE POLICY see_own ON sqs_trigger FOR ALL TO windmill_user +USING (SPLIT_PART(sqs_trigger.path, '/', 1) = 'u' AND SPLIT_PART(sqs_trigger.path, '/', 2) = current_setting('session.user')); +CREATE POLICY see_member ON sqs_trigger FOR ALL TO windmill_user +USING (SPLIT_PART(sqs_trigger.path, '/', 1) = 'g' AND SPLIT_PART(sqs_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.groups'), ',')::text[])); + +CREATE POLICY see_extra_perms_user_select ON sqs_trigger FOR SELECT TO windmill_user +USING (extra_perms ? CONCAT('u/', current_setting('session.user'))); +CREATE POLICY see_extra_perms_user_insert ON sqs_trigger FOR INSERT TO windmill_user +WITH CHECK ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean); +CREATE POLICY see_extra_perms_user_update ON sqs_trigger FOR UPDATE TO windmill_user +USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean); +CREATE POLICY see_extra_perms_user_delete ON sqs_trigger FOR DELETE TO windmill_user +USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean); + +CREATE POLICY see_extra_perms_groups_select ON sqs_trigger FOR SELECT TO windmill_user +USING (extra_perms ?| regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]); +CREATE POLICY see_extra_perms_groups_insert ON sqs_trigger FOR INSERT TO windmill_user +WITH CHECK (exists( + SELECT key, value FROM jsonb_each_text(extra_perms) + WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]) + AND value::boolean)); +CREATE POLICY see_extra_perms_groups_update ON sqs_trigger FOR UPDATE TO windmill_user +USING (exists( + SELECT key, value FROM jsonb_each_text(extra_perms) + WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]) + AND value::boolean)); +CREATE POLICY see_extra_perms_groups_delete ON sqs_trigger FOR DELETE TO windmill_user +USING (exists( + SELECT key, value FROM jsonb_each_text(extra_perms) + WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]) + AND value::boolean)); \ No newline at end of file diff --git a/frontend/src/lib/components/BoundedInputNumber b/backend/migrations/20250204192651_add_postgres_type_value_to_trigger_kind_type.down.sql similarity index 100% rename from frontend/src/lib/components/BoundedInputNumber rename to backend/migrations/20250204192651_add_postgres_type_value_to_trigger_kind_type.down.sql diff --git a/backend/migrations/20250204192651_add_postgres_type_value_to_trigger_kind_type.up.sql b/backend/migrations/20250204192651_add_postgres_type_value_to_trigger_kind_type.up.sql new file mode 100644 index 0000000000..35c37f75ce --- /dev/null +++ b/backend/migrations/20250204192651_add_postgres_type_value_to_trigger_kind_type.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TYPE TRIGGER_KIND ADD VALUE IF NOT EXISTS 'postgres'; \ No newline at end of file diff --git a/backend/migrations/20250205003539_add_sqs_type_value_to_trigger_kind_type.down.sql b/backend/migrations/20250205003539_add_sqs_type_value_to_trigger_kind_type.down.sql new file mode 100644 index 0000000000..0197d4e7b1 --- /dev/null +++ b/backend/migrations/20250205003539_add_sqs_type_value_to_trigger_kind_type.down.sql @@ -0,0 +1 @@ +-- Add down migration script here \ No newline at end of file diff --git a/backend/migrations/20250205003539_add_sqs_type_value_to_trigger_kind_type.up.sql b/backend/migrations/20250205003539_add_sqs_type_value_to_trigger_kind_type.up.sql new file mode 100644 index 0000000000..2281aeba2a --- /dev/null +++ b/backend/migrations/20250205003539_add_sqs_type_value_to_trigger_kind_type.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TYPE TRIGGER_KIND ADD VALUE IF NOT EXISTS 'sqs'; \ No newline at end of file diff --git a/backend/migrations/20250205131516_v2_grant.down.sql b/backend/migrations/20250205131516_v2_grant.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250205131516_v2_grant.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250205131516_v2_grant.up.sql b/backend/migrations/20250205131516_v2_grant.up.sql new file mode 100644 index 0000000000..3d58575b41 --- /dev/null +++ b/backend/migrations/20250205131516_v2_grant.up.sql @@ -0,0 +1,6 @@ +-- Add up migration script here +GRANT ALL ON v2_as_queue TO windmill_admin; +GRANT ALL ON v2_as_queue TO windmill_user; + +GRANT ALL ON v2_as_completed_job TO windmill_admin; +GRANT ALL ON v2_as_completed_job TO windmill_user; diff --git a/backend/migrations/20250205131517_v2_skipped_is_success.down.sql b/backend/migrations/20250205131517_v2_skipped_is_success.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250205131517_v2_skipped_is_success.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250205131517_v2_skipped_is_success.up.sql b/backend/migrations/20250205131517_v2_skipped_is_success.up.sql new file mode 100644 index 0000000000..86506fe255 --- /dev/null +++ b/backend/migrations/20250205131517_v2_skipped_is_success.up.sql @@ -0,0 +1,42 @@ +-- Add up migration script here +CREATE OR REPLACE VIEW v2_as_completed_job AS +SELECT + j.id, + j.workspace_id, + j.parent_job, + j.created_by, + j.created_at, + c.duration_ms, + c.status = 'success' OR c.status = 'skipped' AS success, + j.runnable_id AS script_hash, + j.runnable_path AS script_path, + j.args, + c.result, + FALSE AS deleted, + j.raw_code, + c.status = 'canceled' AS canceled, + c.canceled_by, + c.canceled_reason, + j.kind AS job_kind, + CASE WHEN j.trigger_kind = 'schedule'::job_trigger_kind THEN j.trigger END + AS schedule_path, + j.permissioned_as, + COALESCE(c.flow_status, c.workflow_as_code_status) AS flow_status, + j.raw_flow, + j.flow_step_id IS NOT NULL AS is_flow_step, + j.script_lang AS language, + c.started_at, + c.status = 'skipped' AS is_skipped, + j.raw_lock, + j.permissioned_as_email AS email, + j.visible_to_owner, + c.memory_peak AS mem_peak, + j.tag, + j.priority, + NULL::TEXT AS logs, + c.result_columns, + j.script_entrypoint_override, + j.preprocessed +FROM v2_job_completed c + JOIN v2_job j USING (id) +; diff --git a/backend/migrations/20250205131518_see_own_fix_job.down.sql b/backend/migrations/20250205131518_see_own_fix_job.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250205131518_see_own_fix_job.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250205131518_see_own_fix_job.up.sql b/backend/migrations/20250205131518_see_own_fix_job.up.sql new file mode 100644 index 0000000000..554cf5647b --- /dev/null +++ b/backend/migrations/20250205131518_see_own_fix_job.up.sql @@ -0,0 +1,8 @@ +-- Add up migration script here +DROP POLICY IF EXISTS see_own ON v2_job; +CREATE POLICY see_own ON v2_job + AS PERMISSIVE + FOR ALL + TO windmill_user + USING ((SPLIT_PART((permissioned_as)::TEXT, '/'::TEXT, 1) = 'u'::TEXT) AND + (SPLIT_PART((permissioned_as)::TEXT, '/'::TEXT, 2) = CURRENT_SETTING('session.user'::TEXT))); \ No newline at end of file diff --git a/backend/migrations/20250205131519_windmill_admin_skip_bypassrls_on_v2_job.down.sql b/backend/migrations/20250205131519_windmill_admin_skip_bypassrls_on_v2_job.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250205131519_windmill_admin_skip_bypassrls_on_v2_job.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250205131519_windmill_admin_skip_bypassrls_on_v2_job.up.sql b/backend/migrations/20250205131519_windmill_admin_skip_bypassrls_on_v2_job.up.sql new file mode 100644 index 0000000000..f83ee7f348 --- /dev/null +++ b/backend/migrations/20250205131519_windmill_admin_skip_bypassrls_on_v2_job.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here +DROP POLICY IF EXISTS admin_policy ON v2_job; +CREATE POLICY admin_policy ON v2_job FOR ALL TO windmill_admin USING (true); diff --git a/backend/migrations/20250205131520_add_flow_lock_errors.down.sql b/backend/migrations/20250205131520_add_flow_lock_errors.down.sql new file mode 100644 index 0000000000..39b6121239 --- /dev/null +++ b/backend/migrations/20250205131520_add_flow_lock_errors.down.sql @@ -0,0 +1,2 @@ +-- Add down migration script here + ALTER TABLE flow DROP COLUMN lock_error_logs; \ No newline at end of file diff --git a/backend/migrations/20250205131520_add_flow_lock_errors.up.sql b/backend/migrations/20250205131520_add_flow_lock_errors.up.sql new file mode 100644 index 0000000000..affa494384 --- /dev/null +++ b/backend/migrations/20250205131520_add_flow_lock_errors.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TABLE flow ADD COLUMN lock_error_logs TEXT; \ No newline at end of file diff --git a/backend/migrations/20250205131521_clear_cache_on_webhook_changes.down.sql b/backend/migrations/20250205131521_clear_cache_on_webhook_changes.down.sql new file mode 100644 index 0000000000..179a2cd6de --- /dev/null +++ b/backend/migrations/20250205131521_clear_cache_on_webhook_changes.down.sql @@ -0,0 +1,4 @@ +-- Add down migration script here + +DROP TRIGGER webhook_change_trigger ON workspace_settings; +DROP FUNCTION notify_webhook_change(); diff --git a/backend/migrations/20250205131521_clear_cache_on_webhook_changes.up.sql b/backend/migrations/20250205131521_clear_cache_on_webhook_changes.up.sql new file mode 100644 index 0000000000..a8a13c10cf --- /dev/null +++ b/backend/migrations/20250205131521_clear_cache_on_webhook_changes.up.sql @@ -0,0 +1,15 @@ +-- Add up migration script here + +CREATE OR REPLACE FUNCTION notify_webhook_change() +RETURNS TRIGGER AS $$ +BEGIN + PERFORM pg_notify('notify_webhook_change', NEW.workspace_id); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER webhook_change_trigger +AFTER UPDATE OF webhook ON workspace_settings +FOR EACH ROW +WHEN (OLD.webhook IS DISTINCT FROM NEW.webhook) +EXECUTE FUNCTION notify_webhook_change(); diff --git a/backend/migrations/20250205131522_add_zombie_job_counter.down.sql b/backend/migrations/20250205131522_add_zombie_job_counter.down.sql new file mode 100644 index 0000000000..e8686738c5 --- /dev/null +++ b/backend/migrations/20250205131522_add_zombie_job_counter.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here + +DROP TABLE zombie_job_counter; \ No newline at end of file diff --git a/backend/migrations/20250205131522_add_zombie_job_counter.up.sql b/backend/migrations/20250205131522_add_zombie_job_counter.up.sql new file mode 100644 index 0000000000..cc8c7457e3 --- /dev/null +++ b/backend/migrations/20250205131522_add_zombie_job_counter.up.sql @@ -0,0 +1,7 @@ +-- Add up migration script here + +CREATE TABLE IF NOT EXISTS zombie_job_counter ( + job_id UUID PRIMARY KEY REFERENCES v2_job (id) ON DELETE CASCADE, + counter INTEGER NOT NULL DEFAULT 0 +); + diff --git a/backend/migrations/20250205131523_grant_all_in_current_schema.down.sql b/backend/migrations/20250205131523_grant_all_in_current_schema.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250205131523_grant_all_in_current_schema.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250205131523_grant_all_in_current_schema.up.sql b/backend/migrations/20250205131523_grant_all_in_current_schema.up.sql new file mode 100644 index 0000000000..04047fa2b0 --- /dev/null +++ b/backend/migrations/20250205131523_grant_all_in_current_schema.up.sql @@ -0,0 +1,37 @@ +DO +$do$ +DECLARE + current_schema_name TEXT; +BEGIN + -- Get the current schema for the session + SELECT current_schema() INTO current_schema_name; + + -- Lock the roles table to prevent race conditions + LOCK TABLE pg_catalog.pg_roles; + + + + EXECUTE format('GRANT USAGE ON SCHEMA %I TO windmill_user', current_schema_name); + EXECUTE format('GRANT USAGE ON SCHEMA %I TO windmill_admin', current_schema_name); + + + -- Grant privileges dynamically to the current schema + EXECUTE format('GRANT ALL ON ALL TABLES IN SCHEMA %I TO windmill_user', current_schema_name); + EXECUTE format('GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA %I TO windmill_user', current_schema_name); + + -- Alter default privileges dynamically + EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON TABLES TO windmill_user', current_schema_name); + EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON SEQUENCES TO windmill_user', current_schema_name); + + -- Grant privileges dynamically to the current schema + EXECUTE format('GRANT ALL ON ALL TABLES IN SCHEMA %I TO windmill_admin', current_schema_name); + EXECUTE format('GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA %I TO windmill_admin', current_schema_name); + + -- Alter default privileges dynamically + EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON TABLES TO windmill_admin', current_schema_name); + EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT ALL ON SEQUENCES TO windmill_admin', current_schema_name); + +EXCEPTION WHEN OTHERS THEN + RAISE NOTICE 'Error granting proper permissions to windmill users: %', SQLERRM; +END +$do$; diff --git a/backend/migrations/20250205141517_backend_schema_validation.down.sql b/backend/migrations/20250205141517_backend_schema_validation.down.sql new file mode 100644 index 0000000000..0c2b95e5c6 --- /dev/null +++ b/backend/migrations/20250205141517_backend_schema_validation.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here +ALTER TABLE script +DROP COLUMN schema_validation; diff --git a/backend/migrations/20250205141517_backend_schema_validation.up.sql b/backend/migrations/20250205141517_backend_schema_validation.up.sql new file mode 100644 index 0000000000..61546be0e8 --- /dev/null +++ b/backend/migrations/20250205141517_backend_schema_validation.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here +ALTER TABLE script +ADD COLUMN schema_validation BOOLEAN NOT NULL DEFAULT FALSE; diff --git a/backend/migrations/20250207172929_mqtt_trigger.down.sql b/backend/migrations/20250207172929_mqtt_trigger.down.sql new file mode 100644 index 0000000000..5b2fba771a --- /dev/null +++ b/backend/migrations/20250207172929_mqtt_trigger.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here +DROP TABLE mqtt_trigger; +DROP TYPE MQTT_CLIENT_VERSION; \ No newline at end of file diff --git a/backend/migrations/20250207172929_mqtt_trigger.up.sql b/backend/migrations/20250207172929_mqtt_trigger.up.sql new file mode 100644 index 0000000000..feebd3602c --- /dev/null +++ b/backend/migrations/20250207172929_mqtt_trigger.up.sql @@ -0,0 +1,72 @@ + -- Add up migration script here +CREATE TYPE MQTT_CLIENT_VERSION AS ENUM ('v3', 'v5'); + +CREATE TABLE mqtt_trigger ( + mqtt_resource_path VARCHAR(255) NOT NULL, + subscribe_topics JSONB[] NOT NULL, + client_version MQTT_CLIENT_VERSION DEFAULT 'v5' NOT NULL, + v5_config JSONB NULL, + v3_config JSONB NULL, + client_id VARCHAR(65535) DEFAULT NULL, + path VARCHAR(255) NOT NULL, + script_path VARCHAR(255) NOT NULL, + is_flow BOOLEAN NOT NULL, + workspace_id VARCHAR(50) NOT NULL, + edited_by VARCHAR(50) NOT NULL, + email VARCHAR(255) NOT NULL, + edited_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + extra_perms JSONB NOT NULL DEFAULT '{}', + server_id VARCHAR(50) NULL, + last_server_ping TIMESTAMPTZ NULL, + error TEXT NULL, + enabled BOOLEAN NOT NULL, + PRIMARY KEY (path, workspace_id) +); + +GRANT ALL ON mqtt_trigger TO windmill_user; +GRANT ALL ON mqtt_trigger TO windmill_admin; + +ALTER TABLE mqtt_trigger ENABLE ROW LEVEL SECURITY; + +CREATE POLICY admin_policy ON mqtt_trigger FOR ALL TO windmill_admin USING (true); + +CREATE POLICY see_folder_extra_perms_user_select ON mqtt_trigger FOR SELECT TO windmill_user +USING (SPLIT_PART(mqtt_trigger.path, '/', 1) = 'f' AND SPLIT_PART(mqtt_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_read'), ',')::text[])); +CREATE POLICY see_folder_extra_perms_user_insert ON mqtt_trigger FOR INSERT TO windmill_user +WITH CHECK (SPLIT_PART(mqtt_trigger.path, '/', 1) = 'f' AND SPLIT_PART(mqtt_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[])); +CREATE POLICY see_folder_extra_perms_user_update ON mqtt_trigger FOR UPDATE TO windmill_user +USING (SPLIT_PART(mqtt_trigger.path, '/', 1) = 'f' AND SPLIT_PART(mqtt_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[])); +CREATE POLICY see_folder_extra_perms_user_delete ON mqtt_trigger FOR DELETE TO windmill_user +USING (SPLIT_PART(mqtt_trigger.path, '/', 1) = 'f' AND SPLIT_PART(mqtt_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[])); + +CREATE POLICY see_own ON mqtt_trigger FOR ALL TO windmill_user +USING (SPLIT_PART(mqtt_trigger.path, '/', 1) = 'u' AND SPLIT_PART(mqtt_trigger.path, '/', 2) = current_setting('session.user')); +CREATE POLICY see_member ON mqtt_trigger FOR ALL TO windmill_user +USING (SPLIT_PART(mqtt_trigger.path, '/', 1) = 'g' AND SPLIT_PART(mqtt_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.groups'), ',')::text[])); + +CREATE POLICY see_extra_perms_user_select ON mqtt_trigger FOR SELECT TO windmill_user +USING (extra_perms ? CONCAT('u/', current_setting('session.user'))); +CREATE POLICY see_extra_perms_user_insert ON mqtt_trigger FOR INSERT TO windmill_user +WITH CHECK ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean); +CREATE POLICY see_extra_perms_user_update ON mqtt_trigger FOR UPDATE TO windmill_user +USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean); +CREATE POLICY see_extra_perms_user_delete ON mqtt_trigger FOR DELETE TO windmill_user +USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean); + +CREATE POLICY see_extra_perms_groups_select ON mqtt_trigger FOR SELECT TO windmill_user +USING (extra_perms ?| regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]); +CREATE POLICY see_extra_perms_groups_insert ON mqtt_trigger FOR INSERT TO windmill_user +WITH CHECK (exists( + SELECT key, value FROM jsonb_each_text(extra_perms) + WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]) + AND value::boolean)); +CREATE POLICY see_extra_perms_groups_update ON mqtt_trigger FOR UPDATE TO windmill_user +USING (exists( + SELECT key, value FROM jsonb_each_text(extra_perms) + WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]) + AND value::boolean)); +CREATE POLICY see_extra_perms_groups_delete ON mqtt_trigger FOR DELETE TO windmill_user +USING (exists( + SELECT key, value FROM jsonb_each_text(extra_perms) + WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]) + AND value::boolean)); \ No newline at end of file diff --git a/backend/migrations/20250207173304_add_mqtt_type_value_to_trigger_kind_type.down.sql b/backend/migrations/20250207173304_add_mqtt_type_value_to_trigger_kind_type.down.sql new file mode 100644 index 0000000000..0197d4e7b1 --- /dev/null +++ b/backend/migrations/20250207173304_add_mqtt_type_value_to_trigger_kind_type.down.sql @@ -0,0 +1 @@ +-- Add down migration script here \ No newline at end of file diff --git a/backend/migrations/20250207173304_add_mqtt_type_value_to_trigger_kind_type.up.sql b/backend/migrations/20250207173304_add_mqtt_type_value_to_trigger_kind_type.up.sql new file mode 100644 index 0000000000..62d397bc94 --- /dev/null +++ b/backend/migrations/20250207173304_add_mqtt_type_value_to_trigger_kind_type.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TYPE TRIGGER_KIND ADD VALUE IF NOT EXISTS 'mqtt'; \ No newline at end of file diff --git a/backend/migrations/20250224131521_flow_workspace_runnables.down.sql b/backend/migrations/20250224131521_flow_workspace_runnables.down.sql new file mode 100644 index 0000000000..930245a236 --- /dev/null +++ b/backend/migrations/20250224131521_flow_workspace_runnables.down.sql @@ -0,0 +1 @@ +DROP TABLE flow_workspace_runnables; \ No newline at end of file diff --git a/backend/migrations/20250224131521_flow_workspace_runnables.up.sql b/backend/migrations/20250224131521_flow_workspace_runnables.up.sql new file mode 100644 index 0000000000..fe61497c6d --- /dev/null +++ b/backend/migrations/20250224131521_flow_workspace_runnables.up.sql @@ -0,0 +1,12 @@ +CREATE TABLE flow_workspace_runnables ( + flow_path VARCHAR(255) NOT NULL, + runnable_path VARCHAR(255) NOT NULL, + script_hash BIGINT NULL, + runnable_is_flow BOOLEAN NOT NULL, + workspace_id VARCHAR(50) NOT NULL, + FOREIGN KEY (workspace_id, flow_path) REFERENCES flow (workspace_id, path) ON DELETE CASCADE +); + +CREATE UNIQUE INDEX flow_workspace_without_hash_unique_idx ON flow_workspace_runnables (flow_path, runnable_path, runnable_is_flow, workspace_id) WHERE script_hash IS NULL; +CREATE UNIQUE INDEX flow_workspace_with_hash_unique_idx ON flow_workspace_runnables (flow_path, runnable_path, script_hash, runnable_is_flow, workspace_id) WHERE script_hash IS NOT NULL; +CREATE INDEX flow_workspace_runnable_path_is_flow_idx ON flow_workspace_runnables (runnable_path, runnable_is_flow, workspace_id); diff --git a/backend/migrations/20250304181111_add_java.down.sql b/backend/migrations/20250304181111_add_java.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250304181111_add_java.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250304181111_add_java.up.sql b/backend/migrations/20250304181111_add_java.up.sql new file mode 100644 index 0000000000..57256afe18 --- /dev/null +++ b/backend/migrations/20250304181111_add_java.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here +ALTER TYPE SCRIPT_LANG ADD VALUE IF NOT EXISTS 'java'; +UPDATE config set config = jsonb_set(config, '{worker_tags}', config->'worker_tags' || '["java"]'::jsonb) where name = 'worker__default' and config @> '{"worker_tags": ["deno", "python3", "go", "bash", "powershell", "dependency", "flow", "hub", "other", "bun", "php", "rust", "ansible", "csharp", "nu"]}'::jsonb AND NOT config->'worker_tags' @> '"java"'::jsonb; diff --git a/backend/migrations/20250309165536_add_workspaced_route_column_for_http_trigger.down.sql b/backend/migrations/20250309165536_add_workspaced_route_column_for_http_trigger.down.sql new file mode 100644 index 0000000000..937853bec6 --- /dev/null +++ b/backend/migrations/20250309165536_add_workspaced_route_column_for_http_trigger.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here +ALTER TABLE http_trigger +DROP COLUMN workspaced_route; \ No newline at end of file diff --git a/backend/migrations/20250309165536_add_workspaced_route_column_for_http_trigger.up.sql b/backend/migrations/20250309165536_add_workspaced_route_column_for_http_trigger.up.sql new file mode 100644 index 0000000000..8bb8c8e2ff --- /dev/null +++ b/backend/migrations/20250309165536_add_workspaced_route_column_for_http_trigger.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here +ALTER TABLE http_trigger +ADD COLUMN workspaced_route BOOLEAN NOT NULL DEFAULT false; \ No newline at end of file diff --git a/backend/migrations/20250311162140_add_columns_to_handle_format_response_of_http_routes.down.sql b/backend/migrations/20250311162140_add_columns_to_handle_format_response_of_http_routes.down.sql new file mode 100644 index 0000000000..8e428e5290 --- /dev/null +++ b/backend/migrations/20250311162140_add_columns_to_handle_format_response_of_http_routes.down.sql @@ -0,0 +1,4 @@ +-- Add down migration script here +ALTER TABLE http_trigger +DROP COLUMN wrap_body, +DROP COLUMN raw_string; \ No newline at end of file diff --git a/backend/migrations/20250311162140_add_columns_to_handle_format_response_of_http_routes.up.sql b/backend/migrations/20250311162140_add_columns_to_handle_format_response_of_http_routes.up.sql new file mode 100644 index 0000000000..13b1f4bd58 --- /dev/null +++ b/backend/migrations/20250311162140_add_columns_to_handle_format_response_of_http_routes.up.sql @@ -0,0 +1,4 @@ +-- Add up migration script here +ALTER TABLE http_trigger +ADD COLUMN wrap_body BOOLEAN NOT NULL DEFAULT false, +ADD COLUMN raw_string BOOLEAN NOT NULL DEFAULT false; \ No newline at end of file diff --git a/backend/migrations/20250317145509_more_generic_workspace_runnable_usage.down.sql b/backend/migrations/20250317145509_more_generic_workspace_runnable_usage.down.sql new file mode 100644 index 0000000000..27f65545ae --- /dev/null +++ b/backend/migrations/20250317145509_more_generic_workspace_runnable_usage.down.sql @@ -0,0 +1,24 @@ +DROP VIEW flow_workspace_runnables; + +DELETE FROM workspace_runnable_dependencies WHERE flow_path IS NULL; + +ALTER TABLE workspace_runnable_dependencies +DROP CONSTRAINT flow_workspace_runnables_workspace_id_flow_path_fkey; + +ALTER TABLE workspace_runnable_dependencies +ADD CONSTRAINT flow_workspace_runnables_workspace_id_flow_path_fkey +FOREIGN KEY (flow_path, workspace_id) REFERENCES flow (path, workspace_id) +ON DELETE CASCADE; + +ALTER TABLE workspace_runnable_dependencies +DROP CONSTRAINT workspace_runnable_dependencies_path_exclusive; + +ALTER TABLE workspace_runnable_dependencies +DROP CONSTRAINT fk_workspace_runnable_dependencies_app_path; + +ALTER TABLE workspace_runnable_dependencies DROP COLUMN app_path; + +ALTER TABLE workspace_runnable_dependencies ALTER flow_path SET NOT NULL; + +ALTER TABLE workspace_runnable_dependencies +RENAME TO flow_workspace_runnables; diff --git a/backend/migrations/20250317145509_more_generic_workspace_runnable_usage.up.sql b/backend/migrations/20250317145509_more_generic_workspace_runnable_usage.up.sql new file mode 100644 index 0000000000..4b7b209bb5 --- /dev/null +++ b/backend/migrations/20250317145509_more_generic_workspace_runnable_usage.up.sql @@ -0,0 +1,42 @@ +-- flow_workspace_runnables only stored runnable usages by +-- flows although apps can also use runnables + +ALTER TABLE flow_workspace_runnables +RENAME TO workspace_runnable_dependencies; + +ALTER TABLE workspace_runnable_dependencies ALTER flow_path DROP NOT NULL; + +ALTER TABLE workspace_runnable_dependencies +ADD COLUMN app_path VARCHAR(255); + +ALTER TABLE workspace_runnable_dependencies +ADD CONSTRAINT workspace_runnable_dependencies_path_exclusive CHECK ( + (flow_path IS NOT NULL AND app_path IS NULL) OR + (flow_path IS NULL AND app_path IS NOT NULL) +); + +ALTER TABLE workspace_runnable_dependencies +ADD CONSTRAINT fk_workspace_runnable_dependencies_app_path +FOREIGN KEY (app_path, workspace_id) REFERENCES app (path, workspace_id) +ON DELETE CASCADE +ON UPDATE CASCADE; + + +ALTER TABLE workspace_runnable_dependencies +DROP CONSTRAINT flow_workspace_runnables_workspace_id_flow_path_fkey; + +ALTER TABLE workspace_runnable_dependencies +ADD CONSTRAINT flow_workspace_runnables_workspace_id_flow_path_fkey +FOREIGN KEY (flow_path, workspace_id) REFERENCES flow (path, workspace_id) +ON DELETE CASCADE +ON UPDATE CASCADE; + + +CREATE UNIQUE INDEX app_workspace_without_hash_unique_idx ON workspace_runnable_dependencies (app_path, runnable_path, runnable_is_flow, workspace_id) WHERE script_hash IS NULL; +CREATE UNIQUE INDEX app_workspace_with_hash_unique_idx ON workspace_runnable_dependencies (app_path, runnable_path, script_hash, runnable_is_flow, workspace_id) WHERE script_hash IS NOT NULL; + + +-- This is to maintain compatibility with old workers +CREATE VIEW flow_workspace_runnables AS +SELECT flow_path, runnable_path, script_hash, runnable_is_flow, workspace_id +FROM workspace_runnable_dependencies; \ No newline at end of file diff --git a/backend/migrations/20250318151955_workspace_git_app_settings.down.sql b/backend/migrations/20250318151955_workspace_git_app_settings.down.sql new file mode 100644 index 0000000000..97cc1fafb8 --- /dev/null +++ b/backend/migrations/20250318151955_workspace_git_app_settings.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE workspace_settings +DROP COLUMN IF EXISTS git_app_installations; diff --git a/backend/migrations/20250318151955_workspace_git_app_settings.up.sql b/backend/migrations/20250318151955_workspace_git_app_settings.up.sql new file mode 100644 index 0000000000..0e0fc97fb6 --- /dev/null +++ b/backend/migrations/20250318151955_workspace_git_app_settings.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE workspace_settings +ADD COLUMN IF NOT EXISTS git_app_installations JSONB NOT NULL DEFAULT '[]'; \ No newline at end of file diff --git a/backend/migrations/20250319121050_multiple_ai_providers.down.sql b/backend/migrations/20250319121050_multiple_ai_providers.down.sql new file mode 100644 index 0000000000..b6c6ff23bc --- /dev/null +++ b/backend/migrations/20250319121050_multiple_ai_providers.down.sql @@ -0,0 +1,24 @@ +ALTER TABLE workspace_settings RENAME COLUMN ai_config TO ai_resource; + +ALTER TABLE workspace_settings +ADD COLUMN ai_models VARCHAR(255)[] NOT NULL DEFAULT '{}', +ADD COLUMN code_completion_model VARCHAR(255); + +UPDATE workspace_settings +SET ai_resource = CASE + WHEN ai_resource IS NULL THEN NULL + ELSE jsonb_build_object( + 'provider', + COALESCE((SELECT jsonb_object_keys(COALESCE(ai_resource->>'providers', '{}')::jsonb) LIMIT 1), 'openai'), -- Get the first provider key + 'path', + ai_resource->'providers'->(SELECT jsonb_object_keys(COALESCE(ai_resource->>'providers', '{}')::jsonb) LIMIT 1)->>'resource_path' + ) +END, +ai_models = COALESCE(( + SELECT array_agg(model) + FROM jsonb_array_elements_text( + COALESCE(ai_resource->'providers'->(SELECT jsonb_object_keys(COALESCE(ai_resource->>'providers', '{}')::jsonb) LIMIT 1)->>'models', '[]')::jsonb + ) model + WHERE model IS NOT NULL +), '{}'), +code_completion_model = ai_resource->'code_completion_model'->>'model'; diff --git a/backend/migrations/20250319121050_multiple_ai_providers.up.sql b/backend/migrations/20250319121050_multiple_ai_providers.up.sql new file mode 100644 index 0000000000..40353f7501 --- /dev/null +++ b/backend/migrations/20250319121050_multiple_ai_providers.up.sql @@ -0,0 +1,38 @@ +UPDATE workspace_settings +SET ai_resource = CASE + WHEN ai_resource IS NULL OR ai_resource->>'path' IS NULL OR ai_resource->>'provider' IS NULL THEN NULL + ELSE jsonb_build_object( + 'providers', jsonb_build_object( + ai_resource->>'provider', + jsonb_build_object( + 'resource_path', ai_resource->>'path', + 'models', to_jsonb(ai_models) + ) + ), + 'default_model', + CASE + WHEN array_length(ai_models, 1) > 0 THEN jsonb_build_object( + 'model', ai_models[1], + 'provider', ai_resource->>'provider' + ) + ELSE NULL + END, + 'code_completion_model', + CASE + WHEN code_completion_model IS NULL THEN NULL + ELSE jsonb_build_object( + 'model', code_completion_model, + 'provider', ai_resource->>'provider' + ) + END + ) +END; + +ALTER TABLE workspace_settings +DROP COLUMN code_completion_model, +DROP COLUMN ai_models; + +ALTER TABLE workspace_settings RENAME COLUMN ai_resource TO ai_config; + + +-- { providers: { [provider]: { resource_path: resource_path, models: ai_models}, default_model: ai_models[0], code_completion_model: code_completion_model} diff --git a/backend/migrations/20250319234432_rename_require_auth_column_of_http_trigger.down.sql b/backend/migrations/20250319234432_rename_require_auth_column_of_http_trigger.down.sql new file mode 100644 index 0000000000..f34e8407b8 --- /dev/null +++ b/backend/migrations/20250319234432_rename_require_auth_column_of_http_trigger.down.sql @@ -0,0 +1,15 @@ +ALTER TABLE http_trigger + DROP COLUMN authentication_resource_path, + ALTER COLUMN authentication_method DROP DEFAULT, + ALTER COLUMN authentication_method TYPE boolean + USING CASE + WHEN authentication_method = 'windmill'::AUTHENTICATION_METHOD THEN true + ELSE false + END, + ALTER COLUMN authentication_method SET NOT NULL, + ALTER COLUMN authentication_method SET DEFAULT false; + +ALTER TABLE http_trigger + RENAME COLUMN authentication_method TO requires_auth; + +DROP TYPE AUTHENTICATION_METHOD; \ No newline at end of file diff --git a/backend/migrations/20250319234432_rename_require_auth_column_of_http_trigger.up.sql b/backend/migrations/20250319234432_rename_require_auth_column_of_http_trigger.up.sql new file mode 100644 index 0000000000..713e1b1fe3 --- /dev/null +++ b/backend/migrations/20250319234432_rename_require_auth_column_of_http_trigger.up.sql @@ -0,0 +1,22 @@ +CREATE TYPE AUTHENTICATION_METHOD AS ENUM ( + 'none', + 'windmill', + 'api_key', + 'basic_http', + 'custom_script', + 'signature' +); + +ALTER TABLE http_trigger + RENAME COLUMN requires_auth TO authentication_method; + +ALTER TABLE http_trigger + ADD COLUMN authentication_resource_path VARCHAR(255) DEFAULT NULL, + ALTER COLUMN authentication_method DROP DEFAULT, + ALTER COLUMN authentication_method TYPE AUTHENTICATION_METHOD + USING CASE + WHEN authentication_method = true THEN 'windmill'::AUTHENTICATION_METHOD + ELSE 'none'::AUTHENTICATION_METHOD + END, + ALTER COLUMN authentication_method SET NOT NULL, + ALTER COLUMN authentication_method SET DEFAULT 'none'::AUTHENTICATION_METHOD; diff --git a/backend/migrations/20250320081915_longer_variable_description.down.sql b/backend/migrations/20250320081915_longer_variable_description.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250320081915_longer_variable_description.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250320081915_longer_variable_description.up.sql b/backend/migrations/20250320081915_longer_variable_description.up.sql new file mode 100644 index 0000000000..d7c5ba06f5 --- /dev/null +++ b/backend/migrations/20250320081915_longer_variable_description.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here +ALTER TABLE variable +ALTER COLUMN description TYPE VARCHAR(10000); \ No newline at end of file diff --git a/backend/migrations/20250322171903_workspace_envs_cache.down.sql b/backend/migrations/20250322171903_workspace_envs_cache.down.sql new file mode 100644 index 0000000000..68a678236d --- /dev/null +++ b/backend/migrations/20250322171903_workspace_envs_cache.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here +DROP TRIGGER workspace_envs_change_trigger ON workspace_env; +DROP FUNCTION notify_workspace_envs_change(); diff --git a/backend/migrations/20250322171903_workspace_envs_cache.up.sql b/backend/migrations/20250322171903_workspace_envs_cache.up.sql new file mode 100644 index 0000000000..439a4dd106 --- /dev/null +++ b/backend/migrations/20250322171903_workspace_envs_cache.up.sql @@ -0,0 +1,15 @@ +-- Add up migration script here +-- Add up migration script here + +CREATE OR REPLACE FUNCTION notify_workspace_envs_change() +RETURNS TRIGGER AS $$ +BEGIN + PERFORM pg_notify('notify_workspace_envs_change', NEW.workspace_id); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER workspace_envs_change_trigger +AFTER INSERT OR UPDATE OF name, value OR DELETE ON workspace_env +FOR EACH ROW +EXECUTE FUNCTION notify_workspace_envs_change(); diff --git a/backend/migrations/20250323131258_add-description-and-cast-extra-args-to-jsonb.down.sql b/backend/migrations/20250323131258_add-description-and-cast-extra-args-to-jsonb.down.sql new file mode 100644 index 0000000000..8384b48725 --- /dev/null +++ b/backend/migrations/20250323131258_add-description-and-cast-extra-args-to-jsonb.down.sql @@ -0,0 +1,6 @@ +-- Add down migration script here +ALTER TABLE schedule + DROP COLUMN description, + ALTER COLUMN on_failure_extra_args SET DATA TYPE json USING on_failure_extra_args::json, + ALTER COLUMN on_success_extra_args SET DATA TYPE json USING on_success_extra_args::json, + ALTER COLUMN on_recovery_extra_args SET DATA TYPE json USING on_recovery_extra_args::json; diff --git a/backend/migrations/20250323131258_add-description-and-cast-extra-args-to-jsonb.up.sql b/backend/migrations/20250323131258_add-description-and-cast-extra-args-to-jsonb.up.sql new file mode 100644 index 0000000000..cd50e7a2db --- /dev/null +++ b/backend/migrations/20250323131258_add-description-and-cast-extra-args-to-jsonb.up.sql @@ -0,0 +1,6 @@ +-- Add up migration script here +ALTER TABLE schedule + ADD COLUMN description TEXT NULL, + ALTER COLUMN on_failure_extra_args SET DATA TYPE jsonb USING on_failure_extra_args::jsonb, + ALTER COLUMN on_success_extra_args SET DATA TYPE jsonb USING on_success_extra_args::jsonb, + ALTER COLUMN on_recovery_extra_args SET DATA TYPE jsonb USING on_recovery_extra_args::jsonb; diff --git a/backend/migrations/20250323135044_add-gcp-trigger-table.down.sql b/backend/migrations/20250323135044_add-gcp-trigger-table.down.sql new file mode 100644 index 0000000000..94e32f5962 --- /dev/null +++ b/backend/migrations/20250323135044_add-gcp-trigger-table.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here +DROP TABLE gcp_trigger; +DROP TYPE DELIVERY_MODE; \ No newline at end of file diff --git a/backend/migrations/20250323135044_add-gcp-trigger-table.up.sql b/backend/migrations/20250323135044_add-gcp-trigger-table.up.sql new file mode 100644 index 0000000000..2098d27e69 --- /dev/null +++ b/backend/migrations/20250323135044_add-gcp-trigger-table.up.sql @@ -0,0 +1,80 @@ +-- Add up migration script here + +ALTER TYPE TRIGGER_KIND ADD VALUE IF NOT EXISTS 'gcp'; +CREATE TYPE DELIVERY_MODE AS ENUM ('push', 'pull'); + +CREATE TABLE gcp_trigger ( + gcp_resource_path VARCHAR(255) NOT NULL, + topic_id VARCHAR(255) NOT NULL CHECK ( + CHAR_LENGTH(topic_id) BETWEEN 3 AND 255 + ), + subscription_id VARCHAR(255) NOT NULL CHECK ( + CHAR_LENGTH(subscription_id) BETWEEN 3 AND 255 + ), + delivery_type DELIVERY_MODE NOT NULL, + delivery_config JSONB NULL CHECK (delivery_type != 'push'::DELIVERY_MODE OR (delivery_config IS NOT NULL)), + path VARCHAR(255) NOT NULL, + script_path VARCHAR(255) NOT NULL, + is_flow BOOLEAN NOT NULL, + workspace_id VARCHAR(50) NOT NULL, + edited_by VARCHAR(50) NOT NULL, + email VARCHAR(255) NOT NULL, + edited_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + extra_perms JSONB NOT NULL DEFAULT '{}', + server_id VARCHAR(50), + last_server_ping TIMESTAMPTZ, + error TEXT, + enabled BOOLEAN NOT NULL, + PRIMARY KEY (path, workspace_id) +); + +CREATE UNIQUE INDEX unique_subscription_per_gcp_resource +ON gcp_trigger (subscription_id, gcp_resource_path, workspace_id); + +GRANT ALL ON gcp_trigger TO windmill_user; +GRANT ALL ON gcp_trigger TO windmill_admin; + +ALTER TABLE gcp_trigger ENABLE ROW LEVEL SECURITY; + +CREATE POLICY admin_policy ON gcp_trigger FOR ALL TO windmill_admin USING (true); + +CREATE POLICY see_folder_extra_perms_user_select ON gcp_trigger FOR SELECT TO windmill_user +USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'f' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_read'), ',')::text[])); +CREATE POLICY see_folder_extra_perms_user_insert ON gcp_trigger FOR INSERT TO windmill_user +WITH CHECK (SPLIT_PART(gcp_trigger.path, '/', 1) = 'f' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[])); +CREATE POLICY see_folder_extra_perms_user_update ON gcp_trigger FOR UPDATE TO windmill_user +USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'f' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[])); +CREATE POLICY see_folder_extra_perms_user_delete ON gcp_trigger FOR DELETE TO windmill_user +USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'f' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[])); + +CREATE POLICY see_own ON gcp_trigger FOR ALL TO windmill_user +USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'u' AND SPLIT_PART(gcp_trigger.path, '/', 2) = current_setting('session.user')); +CREATE POLICY see_member ON gcp_trigger FOR ALL TO windmill_user +USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'g' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.groups'), ',')::text[])); + +CREATE POLICY see_extra_perms_user_select ON gcp_trigger FOR SELECT TO windmill_user +USING (extra_perms ? CONCAT('u/', current_setting('session.user'))); +CREATE POLICY see_extra_perms_user_insert ON gcp_trigger FOR INSERT TO windmill_user +WITH CHECK ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean); +CREATE POLICY see_extra_perms_user_update ON gcp_trigger FOR UPDATE TO windmill_user +USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean); +CREATE POLICY see_extra_perms_user_delete ON gcp_trigger FOR DELETE TO windmill_user +USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean); + +CREATE POLICY see_extra_perms_groups_select ON gcp_trigger FOR SELECT TO windmill_user +USING (extra_perms ?| regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]); +CREATE POLICY see_extra_perms_groups_insert ON gcp_trigger FOR INSERT TO windmill_user +WITH CHECK (exists( + SELECT key, value FROM jsonb_each_text(extra_perms) + WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]) + AND value::boolean)); +CREATE POLICY see_extra_perms_groups_update ON gcp_trigger FOR UPDATE TO windmill_user +USING (exists( + SELECT key, value FROM jsonb_each_text(extra_perms) + WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]) + AND value::boolean)); +CREATE POLICY see_extra_perms_groups_delete ON gcp_trigger FOR DELETE TO windmill_user +USING (exists( + SELECT key, value FROM jsonb_each_text(extra_perms) + WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]) + AND value::boolean)); \ No newline at end of file diff --git a/backend/migrations/20250323162033_add-missing-trigger-kind-to-job-trigger-kind-type.down.sql b/backend/migrations/20250323162033_add-missing-trigger-kind-to-job-trigger-kind-type.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250323162033_add-missing-trigger-kind-to-job-trigger-kind-type.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250323162033_add-missing-trigger-kind-to-job-trigger-kind-type.up.sql b/backend/migrations/20250323162033_add-missing-trigger-kind-to-job-trigger-kind-type.up.sql new file mode 100644 index 0000000000..cdf8530b35 --- /dev/null +++ b/backend/migrations/20250323162033_add-missing-trigger-kind-to-job-trigger-kind-type.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here +ALTER TYPE job_trigger_kind ADD VALUE IF NOT EXISTS 'sqs'; +ALTER TYPE job_trigger_kind ADD VALUE IF NOT EXISTS 'gcp'; diff --git a/backend/migrations/20250325003851_workspace_premium_listener.down.sql b/backend/migrations/20250325003851_workspace_premium_listener.down.sql new file mode 100644 index 0000000000..5515b4226d --- /dev/null +++ b/backend/migrations/20250325003851_workspace_premium_listener.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here +DROP TRIGGER workspace_premium_change_trigger ON workspace; +DROP FUNCTION notify_workspace_premium_change(); diff --git a/backend/migrations/20250325003851_workspace_premium_listener.up.sql b/backend/migrations/20250325003851_workspace_premium_listener.up.sql new file mode 100644 index 0000000000..e841dfbe16 --- /dev/null +++ b/backend/migrations/20250325003851_workspace_premium_listener.up.sql @@ -0,0 +1,13 @@ +-- Add up migration script here +CREATE OR REPLACE FUNCTION notify_workspace_premium_change() +RETURNS TRIGGER AS $$ +BEGIN + PERFORM pg_notify('notify_workspace_premium_change', NEW.id); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER workspace_premium_change_trigger +AFTER UPDATE OF premium ON workspace +FOR EACH ROW +EXECUTE FUNCTION notify_workspace_premium_change(); diff --git a/backend/migrations/20250326105126_remove_automatic_billing_col.down.sql b/backend/migrations/20250326105126_remove_automatic_billing_col.down.sql new file mode 100644 index 0000000000..b49aa205d9 --- /dev/null +++ b/backend/migrations/20250326105126_remove_automatic_billing_col.down.sql @@ -0,0 +1 @@ +ALTER TABLE workspace_settings ADD COLUMN automatic_billing BOOLEAN NOT NULL DEFAULT TRUE; \ No newline at end of file diff --git a/backend/migrations/20250326105126_remove_automatic_billing_col.up.sql b/backend/migrations/20250326105126_remove_automatic_billing_col.up.sql new file mode 100644 index 0000000000..776f1da82f --- /dev/null +++ b/backend/migrations/20250326105126_remove_automatic_billing_col.up.sql @@ -0,0 +1 @@ +ALTER TABLE workspace_settings DROP COLUMN automatic_billing; \ No newline at end of file diff --git a/backend/migrations/20250407124204_update_hub_sync_script.down.sql b/backend/migrations/20250407124204_update_hub_sync_script.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250407124204_update_hub_sync_script.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250407124204_update_hub_sync_script.up.sql b/backend/migrations/20250407124204_update_hub_sync_script.up.sql new file mode 100644 index 0000000000..84e50b36c0 --- /dev/null +++ b/backend/migrations/20250407124204_update_hub_sync_script.up.sql @@ -0,0 +1,284 @@ +-- Add up migration script here +-- Add up migration script here +UPDATE script SET content = 'import * as wmill from "windmill-cli@1.481.0" + +export async function main() { + await wmill.hubPull({ workspace: "admins", token: process.env["WM_TOKEN"], baseUrl: globalThis.process.env["BASE_URL"] }); +} +', language = 'bun', +lock = '{ + "dependencies": { + "windmill-cli": "1.481.0" + } +} +//bun.lock +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "dependencies": { + "windmill-cli": "1.481.0", + }, + }, + }, + "packages": { + "@ayonli/jsext": ["@ayonli/jsext@1.6.0", "", { "dependencies": { "iconv-lite": "^0.6.3", "sudo-prompt": "^9.2.1", "ws": "^8.17.0", "zod": "^3.23.8" } }, "sha512-dMQuZJIVadEgQ6xp1Q5hRv2JfwANDwnElH6kZWuSjcnAjhvtCoQQ02CdOipfCd4cyrFpaI+8yBboiT9OkRaVyg=="], + + "@deno/shim-deno": ["@deno/shim-deno@0.18.2", "", { "dependencies": { "@deno/shim-deno-test": "^0.5.0", "which": "^4.0.0" } }, "sha512-oQ0CVmOio63wlhwQF75zA4ioolPvOwAoK0yuzcS5bDC1JUvH3y1GS8xPh8EOpcoDQRU4FTG8OQfxhpR+c6DrzA=="], + + "@deno/shim-deno-test": ["@deno/shim-deno-test@0.5.0", "", {}, "sha512-4nMhecpGlPi0cSzT67L+Tm+GOJqvuk8gqHBziqcUQOarnuIax1z96/gJHCSIz2Z0zhxE6Rzwb3IZXPtFh51j+w=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.2", "", { "os": "android", "cpu": "arm" }, "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.2", "", { "os": "android", "cpu": "arm64" }, "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.2", "", { "os": "android", "cpu": "x64" }, "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.2", "", { "os": "linux", "cpu": "arm" }, "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.2", "", { "os": "linux", "cpu": "none" }, "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.2", "", { "os": "linux", "cpu": "none" }, "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.2", "", { "os": "linux", "cpu": "none" }, "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.2", "", { "os": "linux", "cpu": "x64" }, "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.2", "", { "os": "none", "cpu": "arm64" }, "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.2", "", { "os": "none", "cpu": "x64" }, "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.2", "", { "os": "win32", "cpu": "x64" }, "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA=="], + + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], + + "brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + + "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], + + "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "default-browser": ["default-browser@5.2.1", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg=="], + + "default-browser-id": ["default-browser-id@5.0.0", "", {}, "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA=="], + + "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "diff": ["diff@7.0.0", "", {}, "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-main": ["es-main@1.3.0", "", {}, "sha512-AzORKdz1Zt97TzbYQnIrI3ZiibWpRXUfpo/w0xOJ20GpNYd2bd3MU9m31zS/aJ1TJl6JfLTok83Y8HjNunYT0A=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "esbuild": ["esbuild@0.25.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.2", "@esbuild/android-arm": "0.25.2", "@esbuild/android-arm64": "0.25.2", "@esbuild/android-x64": "0.25.2", "@esbuild/darwin-arm64": "0.25.2", "@esbuild/darwin-x64": "0.25.2", "@esbuild/freebsd-arm64": "0.25.2", "@esbuild/freebsd-x64": "0.25.2", "@esbuild/linux-arm": "0.25.2", "@esbuild/linux-arm64": "0.25.2", "@esbuild/linux-ia32": "0.25.2", "@esbuild/linux-loong64": "0.25.2", "@esbuild/linux-mips64el": "0.25.2", "@esbuild/linux-ppc64": "0.25.2", "@esbuild/linux-riscv64": "0.25.2", "@esbuild/linux-s390x": "0.25.2", "@esbuild/linux-x64": "0.25.2", "@esbuild/netbsd-arm64": "0.25.2", "@esbuild/netbsd-x64": "0.25.2", "@esbuild/openbsd-arm64": "0.25.2", "@esbuild/openbsd-x64": "0.25.2", "@esbuild/sunos-x64": "0.25.2", "@esbuild/win32-arm64": "0.25.2", "@esbuild/win32-ia32": "0.25.2", "@esbuild/win32-x64": "0.25.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], + + "finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="], + + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-port": ["get-port@7.1.0", "", {}, "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], + + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], + + "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], + + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + + "is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="], + + "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + + "isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], + + "jszip": ["jszip@3.7.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "set-immediate-shim": "~1.0.1" } }, "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg=="], + + "lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], + + "minimatch": ["minimatch@10.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "open": ["open@10.1.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "is-wsl": "^3.1.0" } }, "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw=="], + + "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], + + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + + "path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="], + + "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], + + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="], + + "readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + + "run-applescript": ["run-applescript@7.0.0", "", {}, "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="], + + "serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="], + + "set-immediate-shim": ["set-immediate-shim@1.0.1", "", {}, "sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], + + "string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + + "sudo-prompt": ["sudo-prompt@9.2.1", "", {}, "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="], + + "windmill-cli": ["windmill-cli@1.481.0", "", { "dependencies": { "@ayonli/jsext": "*", "@deno/shim-deno": "~0.18.0", "diff": "*", "es-main": "*", "esbuild": "*", "express": "*", "get-port": "7.1.0", "jszip": "3.7.1", "minimatch": "*", "open": "*", "ws": "*" }, "bin": { "wmill": "esm/main.js" } }, "sha512-nIIrt+/+TqeyHlgcDnPMTBH3CZX4TMVwxy2UFD+5lOI5OY9JOtCpWk5UPn6BOs6fw1DG0MIBJG7AhCuPelfSiQ=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="], + + "zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="], + + "readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + + "string_decoder/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + } +}' +WHERE hash = -28028598712388162 AND workspace_id = 'admins'; \ No newline at end of file diff --git a/backend/migrations/20250409093642_add_grant.down.sql b/backend/migrations/20250409093642_add_grant.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250409093642_add_grant.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250409093642_add_grant.up.sql b/backend/migrations/20250409093642_add_grant.up.sql new file mode 100644 index 0000000000..95bae6b21c --- /dev/null +++ b/backend/migrations/20250409093642_add_grant.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here +GRANT ALL on workspace_runnable_dependencies TO windmill_user; +GRANT ALL on workspace_runnable_dependencies TO windmill_admin; \ No newline at end of file diff --git a/backend/migrations/20250412144540_improve_perf_api_role.down.sql b/backend/migrations/20250412144540_improve_perf_api_role.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250412144540_improve_perf_api_role.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250412144540_improve_perf_api_role.up.sql b/backend/migrations/20250412144540_improve_perf_api_role.up.sql new file mode 100644 index 0000000000..8a521849fb --- /dev/null +++ b/backend/migrations/20250412144540_improve_perf_api_role.up.sql @@ -0,0 +1,22 @@ +-- Add up migration script here + CREATE OR REPLACE FUNCTION set_session_context( + admin BOOLEAN, + username TEXT, + groups TEXT, + pgroups TEXT, + folders_read TEXT, + folders_write TEXT +) RETURNS void AS $$ +BEGIN + IF admin THEN + SET LOCAL ROLE windmill_admin; + ELSE + SET LOCAL ROLE windmill_user; + END IF; + PERFORM set_config('session.user', username, true); + PERFORM set_config('session.groups', groups, true); + PERFORM set_config('session.pgroups', pgroups, true); + PERFORM set_config('session.folders_read', folders_read, true); + PERFORM set_config('session.folders_write', folders_write, true); +END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/backend/migrations/20250417132646_add-resource-type-column-to-sqs.down.sql b/backend/migrations/20250417132646_add-resource-type-column-to-sqs.down.sql new file mode 100644 index 0000000000..8a5cb640cc --- /dev/null +++ b/backend/migrations/20250417132646_add-resource-type-column-to-sqs.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here +ALTER TABLE sqs_trigger DROP COLUMN aws_auth_resource_type; +DROP TYPE IF EXISTS AWS_AUTH_RESOURCE_TYPE; \ No newline at end of file diff --git a/backend/migrations/20250417132646_add-resource-type-column-to-sqs.up.sql b/backend/migrations/20250417132646_add-resource-type-column-to-sqs.up.sql new file mode 100644 index 0000000000..50b3a367d8 --- /dev/null +++ b/backend/migrations/20250417132646_add-resource-type-column-to-sqs.up.sql @@ -0,0 +1,4 @@ +-- Add up migration script here +CREATE TYPE AWS_AUTH_RESOURCE_TYPE AS ENUM ('oidc', 'credentials'); +ALTER TABLE sqs_trigger + ADD COLUMN aws_auth_resource_type AWS_AUTH_RESOURCE_TYPE DEFAULT 'credentials'::AWS_AUTH_RESOURCE_TYPE NOT NULL; \ No newline at end of file diff --git a/backend/migrations/20250420144035_add-new-column-to-gcp-trigger.down.sql b/backend/migrations/20250420144035_add-new-column-to-gcp-trigger.down.sql new file mode 100644 index 0000000000..076f1891b6 --- /dev/null +++ b/backend/migrations/20250420144035_add-new-column-to-gcp-trigger.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here +ALTER TABLE gcp_trigger DROP COLUMN subscription_mode; +DROP TYPE GCP_SUBSCRIPTION_MODE; \ No newline at end of file diff --git a/backend/migrations/20250420144035_add-new-column-to-gcp-trigger.up.sql b/backend/migrations/20250420144035_add-new-column-to-gcp-trigger.up.sql new file mode 100644 index 0000000000..529d9580db --- /dev/null +++ b/backend/migrations/20250420144035_add-new-column-to-gcp-trigger.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here +CREATE TYPE GCP_SUBSCRIPTION_MODE AS ENUM ('create_update', 'existing'); +ALTER TABLE gcp_trigger ADD COLUMN subscription_mode GCP_SUBSCRIPTION_MODE NOT NULL DEFAULT 'create_update'::GCP_SUBSCRIPTION_MODE; \ No newline at end of file diff --git a/backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.down.sql b/backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.down.sql new file mode 100644 index 0000000000..18b1b65140 --- /dev/null +++ b/backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here +ALTER TABLE websocket_trigger +ALTER COLUMN url TYPE VARCHAR(255); \ No newline at end of file diff --git a/backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.up.sql b/backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.up.sql new file mode 100644 index 0000000000..51da384e02 --- /dev/null +++ b/backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here +ALTER TABLE websocket_trigger +ALTER COLUMN url TYPE VARCHAR(1000); \ No newline at end of file diff --git a/backend/migrations/20250424144434_critical_alerts_on_db_oversize.down.sql b/backend/migrations/20250424144434_critical_alerts_on_db_oversize.down.sql new file mode 100644 index 0000000000..5eceb2c095 --- /dev/null +++ b/backend/migrations/20250424144434_critical_alerts_on_db_oversize.down.sql @@ -0,0 +1 @@ +DELETE FROM global_settings WHERE name = 'critical_alerts_on_db_oversize'; diff --git a/backend/migrations/20250424144434_critical_alerts_on_db_oversize.up.sql b/backend/migrations/20250424144434_critical_alerts_on_db_oversize.up.sql new file mode 100644 index 0000000000..cb1868dbba --- /dev/null +++ b/backend/migrations/20250424144434_critical_alerts_on_db_oversize.up.sql @@ -0,0 +1,3 @@ +INSERT INTO global_settings (name, value) +VALUES ('critical_alerts_on_db_oversize', '{}') +ON CONFLICT (name) DO NOTHING; diff --git a/backend/migrations/20250428170426_mcp_mode_log.down.sql b/backend/migrations/20250428170426_mcp_mode_log.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250428170426_mcp_mode_log.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250428170426_mcp_mode_log.up.sql b/backend/migrations/20250428170426_mcp_mode_log.up.sql new file mode 100644 index 0000000000..038bb8234a --- /dev/null +++ b/backend/migrations/20250428170426_mcp_mode_log.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TYPE log_mode ADD VALUE 'mcp'; \ No newline at end of file diff --git a/backend/migrations/20250429211554_create_indices_on_queue.down.sql b/backend/migrations/20250429211554_create_indices_on_queue.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250429211554_create_indices_on_queue.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250429211554_create_indices_on_queue.up.sql b/backend/migrations/20250429211554_create_indices_on_queue.up.sql new file mode 100644 index 0000000000..0aafb20d34 --- /dev/null +++ b/backend/migrations/20250429211554_create_indices_on_queue.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +CREATE INDEX IF NOT EXISTS idx_metrics_id_created_at ON public.metrics (id, created_at DESC) WHERE id LIKE 'queue_%'; \ No newline at end of file diff --git a/backend/migrations/20250429214657_create_indices_on_job_stats.down.sql b/backend/migrations/20250429214657_create_indices_on_job_stats.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250429214657_create_indices_on_job_stats.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250429214657_create_indices_on_job_stats.up.sql b/backend/migrations/20250429214657_create_indices_on_job_stats.up.sql new file mode 100644 index 0000000000..b201db5dbe --- /dev/null +++ b/backend/migrations/20250429214657_create_indices_on_job_stats.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +CREATE INDEX IF NOT EXISTS job_stats_id ON job_stats (job_id); \ No newline at end of file diff --git a/backend/migrations/20250506151818_orderby_refactor.down.sql b/backend/migrations/20250506151818_orderby_refactor.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250506151818_orderby_refactor.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250506151818_orderby_refactor.up.sql b/backend/migrations/20250506151818_orderby_refactor.up.sql new file mode 100644 index 0000000000..a983639760 --- /dev/null +++ b/backend/migrations/20250506151818_orderby_refactor.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here +DROP INDEX IF EXISTS index_script_on_path_created_at; +CREATE INDEX IF NOT EXISTS index_script_on_path_created_at ON script (workspace_id, path, created_at DESC); diff --git a/backend/package-lock.json b/backend/package-lock.json deleted file mode 100644 index dfb18f1156..0000000000 --- a/backend/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "backend", - "lockfileVersion": 3, - "requires": true, - "packages": {} -} diff --git a/backend/parsers/windmill-parser-bash/src/lib.rs b/backend/parsers/windmill-parser-bash/src/lib.rs index 39f1d2667b..69d96400dc 100644 --- a/backend/parsers/windmill-parser-bash/src/lib.rs +++ b/backend/parsers/windmill-parser-bash/src/lib.rs @@ -45,7 +45,7 @@ pub fn parse_powershell_sig(code: &str) -> anyhow::Result { } lazy_static::lazy_static! { - static ref RE_BASH: Regex = Regex::new(r#"(?m)^(\w+)="\$(?:(\d+)|\{(\d+):-(.*)\})"(?:[\t ]*)?(?:#.*)?$"#).unwrap(); + static ref RE_BASH: Regex = Regex::new(r#"(?m)^(\w+)="\$(?:(\d+)|\{(\d+)\}|\{(\d+):-(.*)\})"(?:[\t ]*)?(?:#.*)?$"#).unwrap(); pub static ref RE_POWERSHELL_PARAM: Regex = Regex::new(r#"(?m)param[\t ]*\(([^)]*)\)"#).unwrap(); static ref RE_POWERSHELL_ARGS: Regex = Regex::new(r#"(?:\[(\w+)\])?\$(\w+)[\t ]*(?:=[\t ]*(?:(?:(?:"|')([^"\n\r\$]*)(?:"|'))|([\d.]+)))?"#).unwrap(); @@ -57,11 +57,12 @@ fn parse_bash_file(code: &str) -> anyhow::Result>> { hm.insert( cap.get(2) .or(cap.get(3)) + .or(cap.get(4)) .and_then(|x| x.as_str().parse::().ok()) .ok_or_else(|| anyhow!("Impossible to parse arg digit"))?, ( cap[1].to_string(), - cap.get(4).map(|x| x.as_str().to_string()), + cap.get(5).map(|x| x.as_str().to_string()), ), ); } diff --git a/backend/parsers/windmill-parser-java/Cargo.toml b/backend/parsers/windmill-parser-java/Cargo.toml new file mode 100644 index 0000000000..19809e3120 --- /dev/null +++ b/backend/parsers/windmill-parser-java/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "windmill-parser-java" +version.workspace = true +edition.workspace = true +authors.workspace = true + +[lib] +name = "windmill_parser_java" +path = "./src/lib.rs" + +[dependencies] +windmill-parser.workspace = true +tree-sitter.workspace = true +tree-sitter-java.workspace = true +anyhow.workspace = true +wasm-bindgen.workspace = true +serde_json.workspace = true +# convert_case.workspace = true +# lazy_static.workspace = true +# regex.workspace = true + diff --git a/backend/parsers/windmill-parser-java/src/lib.rs b/backend/parsers/windmill-parser-java/src/lib.rs new file mode 100644 index 0000000000..93aa348cec --- /dev/null +++ b/backend/parsers/windmill-parser-java/src/lib.rs @@ -0,0 +1,446 @@ +#![cfg_attr(target_arch = "wasm32", feature(c_variadic))] + +#[cfg(target_arch = "wasm32")] +pub mod wasm_libc; + +use anyhow::anyhow; +use anyhow::bail; +use serde_json::Value; +use tree_sitter::Node; +use windmill_parser::Arg; +use windmill_parser::MainArgSignature; +use windmill_parser::Typ; + +#[derive(Debug)] +pub struct JavaMainSigMeta { + pub is_public: bool, + pub returns_void: bool, + pub class_name: Option, + pub main_sig: MainArgSignature, +} + +pub fn parse_java_sig_meta(code: &str) -> anyhow::Result { + let mut parser = tree_sitter::Parser::new(); + let language = tree_sitter_java::LANGUAGE; + parser + .set_language(&language.into()) + .map_err(|e| anyhow!("Error setting Java as language: {e}"))?; + + // Parse code + let tree = parser + .parse(code, None) + .ok_or(anyhow!("Failed to parse code"))?; + let root_node = tree.root_node(); + + // Traverse the AST to find the Main method signature + let main_sig = find_main_signature(root_node, code); + let no_main_func = Some(main_sig.is_none()); + let mut is_public = false; + let mut returns_void = false; + let mut class_name = None; + + let mut args = vec![]; + if let Some((sig, name)) = main_sig { + class_name = name; + for sig_node in sig.children(&mut sig.walk()) { + if sig_node.kind() == "modifier" && sig_node.utf8_text(code.as_bytes())? == "public" { + is_public = true; + } + } + if let Some(return_type) = sig.child_by_field_name("type") { + let return_type = return_type.utf8_text(code.as_bytes())?; + + if return_type == "void" { + returns_void = true; + } + } + if let Some(param_list) = sig.child_by_field_name("parameters") { + for p_list_node in param_list.children(&mut param_list.walk()) { + if p_list_node.kind() == "formal_parameter" { + let (otyp, typ, name, default) = parse_java_typ(p_list_node, code)?; + args.push(Arg { + name, + otyp, + typ, + has_default: default.is_some(), + default, + oidx: None, + }); + } + } + } + } + + let main_sig = MainArgSignature { + star_args: false, + star_kwargs: false, + args, + has_preprocessor: None, + no_main_func, + }; + + Ok(JavaMainSigMeta { returns_void, class_name, main_sig, is_public }) +} + +pub fn parse_java_signature(code: &str) -> anyhow::Result { + Ok(parse_java_sig_meta(code)?.main_sig) +} + +fn find_typ<'a>(typ_node: Node<'a>, code: &str) -> anyhow::Result<(Typ, Option)> { + let null = Some(serde_json::Value::Null); + let res = match typ_node.kind() { + #[rustfmt::skip] + "type_identifier" => { + match typ_node.utf8_text(code.as_bytes()) { + Ok("String") => (Typ::Str(None), null), + Ok("Byte") => (Typ::Bytes, null), + Ok("Short") => (Typ::Int, null), + Ok("Integer") => (Typ::Int, null), + Ok("Long") => (Typ::Int, null), + Ok("Float") => (Typ::Float, null), + Ok("Double") => (Typ::Float, null), + Ok("Boolean") => (Typ::Bool, null), + Ok("Character") => (Typ::Str(None), null), + Ok("Object") => (Typ::Object(vec![]),null), // TODO: Complete the object type + Ok(s) => bail!("Unknown type `{s}`"), + Err(e) => bail!("Error getting type name: {}", e), + } + } + #[rustfmt::skip] + "integral_type" => { + match typ_node.utf8_text(code.as_bytes()) { + Ok("byte") => (Typ::Bytes, None), + Ok("short") => (Typ::Int, None), + Ok("int") => (Typ::Int, None), + Ok("long") => (Typ::Int, None), + Ok("char") => (Typ::Str(None), None), + Ok(s) => bail!("Unknown type `{s}`"), + Err(e) => bail!("Error getting type name: {}", e), + } + } + "floating_point_type" => { + match typ_node.utf8_text(code.as_bytes()) { + Ok("float") => (Typ::Float, None), + Ok("double") => (Typ::Float, None), + Ok(s) => bail!("Unknown type `{s}`"), + Err(e) => bail!("Error getting type name: {}", e), + } + } + "boolean_type" => { + match typ_node.utf8_text(code.as_bytes()) { + Ok("boolean") => (Typ::Bool, None), + Ok(s) => bail!("Unknown type `{s}`"), + Err(e) => bail!("Error getting type name: {}", e), + } + } + "array_type" => { + let new_typ_node = typ_node + .named_child(0) + .ok_or(anyhow!("Failed to find inner type of array type"))?; + (Typ::List(Box::new(find_typ(new_typ_node, code)?.0)), null) + } + wc => bail!( + "Unexpected Java type node kind: {} for '{}'. This type is not handled by Windmill, please open an issue if this seems to be an error", + wc, + typ_node.utf8_text(code.as_bytes())? + ), + + }; + Ok(res) +} + +fn parse_java_typ<'a>( + param_node: Node<'a>, + code: &str, +) -> anyhow::Result<(Option, Typ, String, Option)> { + let name = param_node + .child_by_field_name("name") + .and_then(|n| n.utf8_text(code.as_bytes()).ok()) + .unwrap_or(""); + let otyp_node = param_node.child_by_field_name("type"); + let otyp = otyp_node + .and_then(|n| n.utf8_text(code.as_bytes()).ok()) + .map(|s| s.to_string()); + + let (typ, default) = find_typ( + otyp_node.ok_or(anyhow!( + "Internal error: Failed to get child by field name 'type'" + ))?, + code, + )?; + + Ok((otyp, typ, name.to_string(), default)) +} + +// Function to find the Main method's signature +fn find_main_signature<'a>(root_node: Node<'a>, code: &str) -> Option<(Node<'a>, Option)> { + let mut cursor = root_node.walk(); + for x in root_node.children(&mut cursor) { + if x.kind() == "class_declaration" { + let class_name = x + .child_by_field_name("name") + .and_then(|n| n.utf8_text(code.as_bytes()).ok().map(|s| s.to_string())); + for c in x.children(&mut x.walk()) { + if c.kind() == "class_body" { + for w in c.children(&mut c.walk()) { + if w.kind() == "method_declaration" { + for child in w.children(&mut w.walk()) { + if child + .utf8_text(code.as_bytes()) + .map(|name| name == "main") + .unwrap_or(false) + { + return Some((w, class_name)); + } + } + } + } + } + } + } + } + return None; +} + +#[cfg(test)] +mod test { + + use serde_json::json; + + use super::*; + #[test] + fn test_parse_java_return_void() { + let code = r#" +class Main { + public static void main() {} +}"#; + let sig_meta = parse_java_sig_meta(code).unwrap(); + + assert_eq!(sig_meta.class_name, Some("Main".to_string())); + + assert!(sig_meta.returns_void); + } + #[test] + fn test_parse_java_return_object() { + let code = r#" +class Main { + public static Object main() {} +}"#; + let sig_meta = parse_java_sig_meta(code).unwrap(); + + assert_eq!(sig_meta.class_name, Some("Main".to_string())); + assert!(!sig_meta.returns_void); + } + #[test] + fn test_parse_java_primitive_types() { + let code = r#" +class Main { + public static string main(byte a, short b, int c, long d, float e, double f, boolean g, char h) {} +}"#; + let sig_meta = parse_java_sig_meta(code).unwrap(); + + assert_eq!(sig_meta.class_name, Some("Main".to_string())); + + let ret = sig_meta.main_sig; + assert_eq!( + ret.args, + vec![ + Arg { + name: "a".into(), + otyp: Some("byte".into()), + typ: Typ::Bytes, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "b".into(), + otyp: Some("short".into()), + typ: Typ::Int, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "c".into(), + otyp: Some("int".into()), + typ: Typ::Int, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "d".into(), + otyp: Some("long".into()), + typ: Typ::Int, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "e".into(), + otyp: Some("float".into()), + typ: Typ::Float, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "f".into(), + otyp: Some("double".into()), + typ: Typ::Float, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "g".into(), + otyp: Some("boolean".into()), + typ: Typ::Bool, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "h".into(), + otyp: Some("char".into()), + typ: Typ::Str(None), + default: None, + has_default: false, + oidx: None + }, + ] + ); + } + + #[test] + fn test_parse_java_objects() { + let code = r#" +class Main { + public static string main(Byte a, Short b, Integer c, Long d, Float e, Double f, Boolean g, Character h, Object i) {} +}"#; + let sig_meta = parse_java_sig_meta(code).unwrap(); + + assert_eq!(sig_meta.class_name, Some("Main".to_string())); + + let ret = sig_meta.main_sig; + assert_eq!( + ret.args, + vec![ + Arg { + name: "a".into(), + otyp: Some("Byte".into()), + typ: Typ::Bytes, + default: Some(json!(null)), + has_default: true, + oidx: None + }, + Arg { + name: "b".into(), + otyp: Some("Short".into()), + typ: Typ::Int, + default: Some(json!(null)), + has_default: true, + oidx: None + }, + Arg { + name: "c".into(), + otyp: Some("Integer".into()), + typ: Typ::Int, + default: Some(json!(null)), + has_default: true, + oidx: None + }, + Arg { + name: "d".into(), + otyp: Some("Long".into()), + typ: Typ::Int, + default: Some(json!(null)), + has_default: true, + oidx: None + }, + Arg { + name: "e".into(), + otyp: Some("Float".into()), + typ: Typ::Float, + default: Some(json!(null)), + has_default: true, + oidx: None + }, + Arg { + name: "f".into(), + otyp: Some("Double".into()), + typ: Typ::Float, + default: Some(json!(null)), + has_default: true, + oidx: None + }, + Arg { + name: "g".into(), + otyp: Some("Boolean".into()), + typ: Typ::Bool, + default: Some(json!(null)), + has_default: true, + oidx: None + }, + Arg { + name: "h".into(), + otyp: Some("Character".into()), + typ: Typ::Str(None), + default: Some(json!(null)), + has_default: true, + oidx: None + }, + Arg { + name: "i".into(), + otyp: Some("Object".into()), + typ: Typ::Object(vec![]), + default: Some(json!(null)), + has_default: true, + oidx: None + }, + ] + ); + } + #[test] + fn test_parse_java_array() { + let code = r#" +class Main { + public static string main(int[] a, Object[] b, String[] c) {} +}"#; + let sig_meta = parse_java_sig_meta(code).unwrap(); + + assert_eq!(sig_meta.class_name, Some("Main".to_string())); + + let ret = sig_meta.main_sig; + assert_eq!( + ret.args, + vec![ + Arg { + name: "a".into(), + otyp: Some("int[]".into()), + typ: Typ::List(Box::new(Typ::Int)), + default: Some(json!(null)), + has_default: true, + oidx: None + }, + Arg { + name: "b".into(), + otyp: Some("Object[]".into()), + typ: Typ::List(Box::new(Typ::Object(vec![]))), + default: Some(json!(null)), + has_default: true, + oidx: None + }, + Arg { + name: "c".into(), + otyp: Some("String[]".into()), + typ: Typ::List(Box::new(Typ::Str(None))), + default: Some(json!(null)), + has_default: true, + oidx: None + }, + ] + ); + } +} diff --git a/backend/parsers/windmill-parser-java/src/wasm_libc.rs b/backend/parsers/windmill-parser-java/src/wasm_libc.rs new file mode 100644 index 0000000000..7d260af2e8 --- /dev/null +++ b/backend/parsers/windmill-parser-java/src/wasm_libc.rs @@ -0,0 +1,207 @@ +use std::collections::BTreeMap; +use std::sync::{Mutex, OnceLock}; +use std::{ + alloc::{self, Layout}, + ffi::{c_char, c_int, c_void}, + mem::align_of, + ptr, +}; +use wasm_bindgen::prelude::*; + +/* -------------------------------- stdlib.h -------------------------------- */ + +#[no_mangle] +pub unsafe extern "C" fn abort() { + panic!("Aborted from C"); +} + +macro_rules! console_log { + ($($t:tt)*) => (unsafe { log(&format_args!($($t)*).to_string()) }) +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace = console)] + fn log(a: &str); +} + +#[no_mangle] +pub unsafe extern "C" fn malloc(size: usize) -> *mut c_void { + if size == 0 { + return ptr::null_mut(); + } + + let (layout, offset_to_data) = layout_for_size_prepended(size); + let buf = alloc::alloc(layout); + store_layout(buf, layout, offset_to_data) +} + +#[no_mangle] +pub unsafe extern "C" fn calloc(count: usize, size: usize) -> *mut c_void { + if count == 0 || size == 0 { + return ptr::null_mut(); + } + + let (layout, offset_to_data) = layout_for_size_prepended(size * count); + let buf = alloc::alloc_zeroed(layout); + store_layout(buf, layout, offset_to_data) +} + +#[no_mangle] +pub unsafe extern "C" fn realloc(buf: *mut c_void, new_size: usize) -> *mut c_void { + if buf.is_null() { + malloc(new_size) + } else if new_size == 0 { + free(buf); + ptr::null_mut() + } else { + let (old_buf, old_layout) = retrieve_layout(buf); + let (new_layout, offset_to_data) = layout_for_size_prepended(new_size); + let new_buf = alloc::realloc(old_buf, old_layout, new_layout.size()); + store_layout(new_buf, new_layout, offset_to_data) + } +} + +#[no_mangle] +pub unsafe extern "C" fn free(buf: *mut c_void) { + if buf.is_null() { + return; + } + let (buf, layout) = retrieve_layout(buf); + alloc::dealloc(buf, layout); +} + +// In all these allocations, we store the layout before the data for later retrieval. +// This is because we need to know the layout when deallocating the memory. +// Here are some helper methods for that: + +/// Given a pointer to the data, retrieve the layout and the pointer to the layout. +unsafe fn retrieve_layout(buf: *mut c_void) -> (*mut u8, Layout) { + let (_, layout_offset) = Layout::new::() + .extend(Layout::from_size_align(0, align_of::<*const u8>() * 2).unwrap()) + .unwrap(); + + let buf = (buf as *mut u8).offset(-(layout_offset as isize)); + let layout = *(buf as *mut Layout); + + (buf, layout) +} + +/// Calculate a layout for a given size with space for storing a layout at the start. +/// Returns the layout and the offset to the data. +fn layout_for_size_prepended(size: usize) -> (Layout, usize) { + Layout::new::() + .extend(Layout::from_size_align(size, align_of::<*const u8>() * 2).unwrap()) + .unwrap() +} + +/// Store a layout in the pointer, returning a pointer to where the data should be stored. +unsafe fn store_layout(buf: *mut u8, layout: Layout, offset_to_data: usize) -> *mut c_void { + *(buf as *mut Layout) = layout; + (buf as *mut u8).offset(offset_to_data as isize) as *mut c_void +} + +/* -------------------------------- string.h -------------------------------- */ + +#[no_mangle] +pub unsafe extern "C" fn strncmp(ptr1: *const c_void, ptr2: *const c_void, n: usize) -> c_int { + let s1 = std::slice::from_raw_parts(ptr1 as *const u8, n); + let s2 = std::slice::from_raw_parts(ptr2 as *const u8, n); + + for (a, b) in s1.iter().zip(s2.iter()) { + if *a != *b || *a == 0 { + return (*a as i32) - (*b as i32); + } + } + + 0 +} + +/* -------------------------------- wctype.h -------------------------------- */ + +#[no_mangle] +pub unsafe extern "C" fn iswspace(c: c_int) -> bool { + char::from_u32(c as u32).map_or(false, |c| c.is_whitespace()) +} + +#[no_mangle] +pub unsafe extern "C" fn iswalnum(c: c_int) -> bool { + char::from_u32(c as u32).map_or(false, |c| c.is_alphanumeric()) +} + +/* --------------------------------- time.h --------------------------------- */ + +#[no_mangle] +pub unsafe extern "C" fn clock() -> u64 { + panic!("clock is not supported"); +} + +/* --------------------------------- ctype.h -------------------------------- */ + +#[no_mangle] +pub unsafe extern "C" fn isprint(c: c_int) -> bool { + c >= 32 && c <= 126 +} + +/* --------------------------------- stdio.h -------------------------------- */ + +#[no_mangle] +pub unsafe extern "C" fn fprintf(_file: *mut c_void, _format: *const c_void, _args: ...) -> c_int { + panic!("fprintf is not supported"); +} + +#[no_mangle] +pub unsafe extern "C" fn fputs(_s: *const c_void, _file: *mut c_void) -> c_int { + panic!("fputs is not supported"); +} + +#[no_mangle] +pub unsafe extern "C" fn fputc(_c: c_int, _file: *mut c_void) -> c_int { + panic!("fputc is not supported"); +} + +#[no_mangle] +pub unsafe extern "C" fn fdopen(_fd: c_int, _mode: *const c_void) -> *mut c_void { + panic!("fdopen is not supported"); +} + +#[no_mangle] +pub unsafe extern "C" fn fclose(_file: *mut c_void) -> c_int { + panic!("fclose is not supported"); +} + +#[no_mangle] +pub unsafe extern "C" fn fwrite( + _ptr: *const c_void, + _size: usize, + _nmemb: usize, + _stream: *mut c_void, +) -> usize { + panic!("fwrite is not supported"); +} + +#[no_mangle] +pub unsafe extern "C" fn vsnprintf( + _buf: *mut c_char, + _size: usize, + _format: *const c_char, + _args: ... +) -> c_int { + panic!("vsnprintf is not supported"); +} + +#[no_mangle] +pub extern "C" fn clock_gettime(ptr: usize, new_size: usize) { + panic!("clock_gettime is not supported"); +} + +// int snprintf( char* restrict buffer, size_t bufsz, const char* restrict format, ... ); +#[no_mangle] +pub extern "C" fn snprintf() { + panic!("snprintf is not supported"); +} + +#[no_mangle] +pub extern "C" fn __assert_fail(_: *const i32, _: *const i32, _: *const i32, _: *const i32) { + panic!("oh no"); +} diff --git a/backend/parsers/windmill-parser-nu/Cargo.toml b/backend/parsers/windmill-parser-nu/Cargo.toml new file mode 100644 index 0000000000..c8c3e0d616 --- /dev/null +++ b/backend/parsers/windmill-parser-nu/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "windmill-parser-nu" +version.workspace = true +authors.workspace = true +edition.workspace = true + +[lib] +name = "windmill_parser_nu" +path = "./src/lib.rs" + +[dependencies] +windmill-parser.workspace = true +anyhow.workspace = true +nu-parser.workspace = true +wasm-bindgen.workspace = true +serde_json.workspace = true diff --git a/backend/parsers/windmill-parser-nu/src/lib.rs b/backend/parsers/windmill-parser-nu/src/lib.rs new file mode 100644 index 0000000000..ddd049f609 --- /dev/null +++ b/backend/parsers/windmill-parser-nu/src/lib.rs @@ -0,0 +1,261 @@ +#![cfg_attr(target_arch = "wasm32", feature(c_variadic))] + +use anyhow::{anyhow, bail}; +use nu_parser::lex; + +use serde_json::{json, Value}; +use windmill_parser::{Arg, MainArgSignature, Typ}; + +pub fn parse_nu_signature(code: &str) -> anyhow::Result { + let (tokens, ..) = lex(code.as_bytes(), 0, &[], &[], true); + let src = code.to_owned(); + #[derive(Debug)] + enum LastToken { + None, + Def, + Main, + Args(String), + } + let mut last_token = LastToken::None; + for token in tokens { + let s = token.span; + let cont = src.get(s.start..s.end).ok_or(anyhow!("Parsing error"))?; + last_token = match last_token { + LastToken::None if cont == "def" => LastToken::Def, + LastToken::Def if cont == "main" => LastToken::Main, + LastToken::Main => { + LastToken::Args(cont.get(1..(cont.len() - 1)).unwrap_or("Error").to_owned()) + } + LastToken::Args(_) => break, + _ => LastToken::None, + }; + } + + let LastToken::Args(args) = last_token else { + bail!("Cannot find main function."); + }; + + let mut sig = MainArgSignature::default(); + sig.no_main_func = Some(false); + + let batches = args + .lines() + .filter_map(|el| { + if el.trim_start().starts_with('#') { + None + } else { + Some( + el.split(',') + .map(|el| el.trim()) + .filter(|el| el != &"") + .collect::>(), + ) + } + }) + .flatten() + .collect::>(); + + let mut compensate_lookahead = 0; + for (i, batch) in batches.iter().enumerate() { + // parse_default can lookahead and if it does we need to compensate + // otherwise we would try to parse data already parsed but not yielded by parse_default + if compensate_lookahead > 0 { + compensate_lookahead -= 1; + continue; + } + + let type_start = batch.find(":"); + let default_start = batch.find("="); + + let (name, typ, default) = match (type_start, default_start) { + (None, None) => (batch.trim(), None, None), + (None, Some(d)) => ( + batch + .get(0..d) + .ok_or(anyhow!("Cannot parse argument ident"))? + .trim(), + None, + Some(parse_default( + &batch + .get(d..) + .ok_or(anyhow!("Cannot parse default value for argument"))?, + &batches, + i, + &mut compensate_lookahead, + )?), + ), + (Some(t), None) => ( + batch + .get(0..t) + .ok_or(anyhow!("Cannot parse argument ident"))? + .trim(), + Some(parse_type( + &batch + .get(t..) + .ok_or(anyhow!("Cannot parse type of argument"))?, + )?), + None, + ), + (Some(t), Some(d)) => { + if t < d { + ( + batch + .get(0..t) + .ok_or(anyhow!("Cannot parse argument ident"))? + .trim(), + Some(parse_type( + &batch + .get(t..d) + .ok_or(anyhow!("Cannot parse type of argument"))?, + )?), + Some(parse_default( + &batch + .get(d..) + .ok_or(anyhow!("Cannot parse default value of argument"))?, + &batches, + i, + &mut compensate_lookahead, + )?), + ) + } else { + bail!("Parsing error `:` should be before `=`\nit likely means you are trying to set default value to record or table which is not supported at the moment.") + } + } + }; + + // Check if it is optional + let optional = { + let Some(element) = name.chars().last() else { + bail!("Internal error, cannot check if argument is optional") + }; + element == '?' + }; + + // Rest parameters are not supported + if matches!(name.get(0..3), Some("...")) { + bail!("Rest (...) parameters are not supported") + } + + // Flags are not supported + if matches!(name.get(0..2), Some("--")) { + bail!("Flags are not supported") + } + + sig.args.push(Arg { + name: if optional { + name.get(..name.len() - 1).unwrap_or("Error").to_owned() + } else { + name.to_owned() + }, + typ: typ.unwrap_or(Typ::Unknown), + otyp: None, + has_default: default.is_some() || optional, + default: default.or_else(|| if optional { Some(json!(null)) } else { None }), + oidx: None, + }); + } + + fn parse_type(content: &str) -> anyhow::Result { + let c = content.replace(":", "").trim().to_owned(); + let typ = match c.as_str() { + "string" => Typ::Str(None), + "int" => Typ::Int, + "float" => Typ::Float, + "number" => Typ::Float, + "record" => Typ::Object(vec![]), + "table" => Typ::List(Box::new(Typ::Object(vec![]))), + "nothing" => Typ::Unknown, + // TODO: needs additional work on literal parsing + // "binary" => Typ::Bytes, + "datetime" => Typ::Datetime, + "any" => Typ::Unknown, + "bool" => Typ::Bool, + // Lists + "list" | "list" | "list" => Typ::List(Box::new(Typ::Unknown)), + "list" => Typ::List(Box::new(Typ::Float)), + "list" => Typ::List(Box::new(Typ::Bool)), + "list" => Typ::List(Box::new(Typ::Str(None))), + // list is not supported + // Records and Tables + // TODO: Support in V1? + s if s.contains("record<") => { + bail!("typed records are not supported, use `ident: record`") + } + s if s.contains("table<") => { + bail!("typed tables are not supported, use `ident: table`") + } + s => bail!("{s} is not supported"), + }; + Ok(typ) + } + fn parse_default( + content: &str, + ctx: &[&str], + i: usize, + skip: &mut usize, + ) -> anyhow::Result { + let mut c = content.replace("=", "").trim().to_owned(); + + fn parse_object_literal( + (open, close): (char, char), + mut c_2: String, + ctx: &[&str], + i: usize, + skip: &mut usize, + ) -> anyhow::Result { + // It is list + // if c.contains("[") { + let mut closed = false; + if c_2 != open.to_string() { + // [a ~ , ~ ... + // Add ^ + c_2 += ","; + } + // else { + // [ + // a < Do not add "," + // ... + // } + let remainder = &ctx + .iter() + .skip(i + 1) + .map_while(|el| { + let el = el.trim(); + + if closed { + None + } else { + if el.chars().last() == Some(close) { + closed = true; + } + *skip += 1; + Some(el) + } + }) + .collect::>() + .join(","); + + if remainder.contains(&['{', '[']) { + bail!("Nesting is not supported") + } + + Ok((c_2 + remainder) + // Remove trailing comma if there is any + .replace(&format!(",{close}"), &close.to_string())) + } + // It is list + if c.contains("[") { + if c.chars().last() != Some(']') { + c = parse_object_literal(('[', ']'), c.clone(), ctx, i, skip)?; + } + } + // It is record + if c.contains("{") { + if c.chars().last() != Some('}') { + c = parse_object_literal(('{', '}'), c.clone(), ctx, i, skip)?; + } + } + Ok(serde_json::from_str(&c)?) + } + Ok(sig) +} diff --git a/backend/parsers/windmill-parser-nu/tests/tests.rs b/backend/parsers/windmill-parser-nu/tests/tests.rs new file mode 100644 index 0000000000..13c54930d6 --- /dev/null +++ b/backend/parsers/windmill-parser-nu/tests/tests.rs @@ -0,0 +1,690 @@ +#[cfg(test)] +mod test { + use serde_json::json; + use windmill_parser::{Arg, MainArgSignature, Typ}; + use windmill_parser_nu::parse_nu_signature; + + #[test] + fn test_nu_no_main_sig() { + assert!(parse_nu_signature("").is_err()); + } + #[test] + fn test_nu_any_sig() { + let sig = parse_nu_signature( + r#" + def main [ a, b , c, d] {} + "#, + ) + .unwrap(); + assert_eq!( + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![ + Arg { + name: "a".into(), + otyp: None, + typ: Typ::Unknown, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "b".into(), + otyp: None, + typ: Typ::Unknown, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "c".into(), + otyp: None, + typ: Typ::Unknown, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "d".into(), + otyp: None, + typ: Typ::Unknown, + default: None, + has_default: false, + oidx: None + } + ], + no_main_func: Some(false), + has_preprocessor: None, + }, + sig + ); + } + #[test] + fn test_nu_optional_sig() { + let sig = parse_nu_signature( + r#" + def main [foo?] {} + "#, + ) + .unwrap(); + + assert_eq!( + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![Arg { + name: "foo".into(), + otyp: None, + typ: Typ::Unknown, + default: Some(serde_json::Value::Null), + has_default: true, + oidx: None + },], + no_main_func: Some(false), + has_preprocessor: None, + }, + sig + ); + } + #[test] + fn test_nu_simple_typed_sig() { + let sig = parse_nu_signature( + r#" + def main [ foo: string, bar: int] {} + "#, + ) + .unwrap(); + + assert_eq!( + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![ + Arg { + name: "foo".into(), + otyp: None, + typ: Typ::Str(None), + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "bar".into(), + otyp: None, + typ: Typ::Int, + default: None, + has_default: false, + oidx: None + }, + ], + no_main_func: Some(false), + has_preprocessor: None, + }, + sig + ); + } + #[test] + fn test_nu_complete_typed_sig() { + let sig = parse_nu_signature( + r#" + def main [ + a1: any, + a2: bool, + a3: int, + a4: float, + a5: datetime, + a6: string, + a7: record, + a8: list, + a9: table, + a10: nothing, + ] {} + "#, + ) + .unwrap(); + + assert_eq!( + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![ + Arg { + name: "a1".into(), + otyp: None, + typ: Typ::Unknown, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "a2".into(), + otyp: None, + typ: Typ::Bool, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "a3".into(), + otyp: None, + typ: Typ::Int, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "a4".into(), + otyp: None, + typ: Typ::Float, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "a5".into(), + otyp: None, + typ: Typ::Datetime, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "a6".into(), + otyp: None, + typ: Typ::Str(None), + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "a7".into(), + otyp: None, + typ: Typ::Object(vec![]), + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "a8".into(), + otyp: None, + typ: Typ::List(Box::new(Typ::Unknown)), + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "a9".into(), + otyp: None, + typ: Typ::List(Box::new(Typ::Object(vec![]))), + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "a10".into(), + otyp: None, + typ: Typ::Unknown, + default: None, + has_default: false, + oidx: None + }, + ], + no_main_func: Some(false), + has_preprocessor: None, + }, + sig + ); + } + #[test] + fn test_nu_default_sig() { + let sig = parse_nu_signature( + r#" + def main [ foo = "Foo", bar: string = "Bar", bazz = 3 ] {} + "#, + ) + .unwrap(); + + println!("{}", serde_json::to_string_pretty(&sig).unwrap()); + + assert_eq!( + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![ + Arg { + name: "foo".into(), + otyp: None, + typ: Typ::Unknown, + default: Some(json!("Foo")), + has_default: true, + oidx: None + }, + Arg { + name: "bar".into(), + otyp: None, + typ: Typ::Str(None), + default: Some(json!("Bar")), + has_default: true, + oidx: None + }, + Arg { + name: "bazz".into(), + otyp: None, + typ: Typ::Unknown, + default: Some(json!(3)), + has_default: true, + oidx: None + }, + ], + no_main_func: Some(false), + has_preprocessor: None, + }, + sig + ); + } + #[test] + fn test_nu_preprocessor_sig() {} + + #[test] + fn test_nu_flags_sig() { + assert!(parse_nu_signature( + r#" + def main [--flag] {} + "#, + ) + .is_err()); + } + #[test] + fn test_nu_rest_sig() { + assert!(parse_nu_signature( + r#" + def main [...foo: string] {} + "#, + ) + .is_err()) + } + + // #[test] + // fn test_nu_dynamically_sized_sig() { + // parse_nu_signature( + // r#" + // def main [] { + + // } + // "#, + // ); + // } + // #[test] + // fn test_nu_record_sig() { + // let sig = parse_nu_signature( + // r#" + // def main [ foo: record ] { } + // "#, + // ) + // .unwrap(); + + // assert_eq!( + // MainArgSignature { + // star_args: false, + // star_kwargs: false, + // args: vec![Arg { + // name: "foo".into(), + // otyp: None, + // typ: Typ::Object(vec![ + // ObjectProperty { key: "a".into(), typ: Box::new(Typ::Str(None)) }, + // ObjectProperty { key: "b".into(), typ: Box::new(Typ::Unknown) }, + // ObjectProperty { key: "c".into(), typ: Box::new(Typ::Float) }, + // ObjectProperty { key: "d".into(), typ: Box::new(Typ::Unknown) }, + // ]), + // default: None, + // has_default: false, + // oidx: None + // },], + // no_main_func: Some(false), + // has_preprocessor: None, + // }, + // sig + // ); + // } + + #[test] + fn test_nu_list_sig() { + let sig = parse_nu_signature( + r#" + def main [ foo: list ] { } + "#, + ) + .unwrap(); + + println!("{}", serde_json::to_string_pretty(&sig).unwrap()); + + assert_eq!( + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![Arg { + name: "foo".into(), + otyp: None, + typ: Typ::List(Box::new(Typ::Float)), + default: None, + has_default: false, + oidx: None + },], + no_main_func: Some(false), + has_preprocessor: None, + }, + sig + ); + } + #[test] + fn test_nu_list_full_sig() { + let sig = parse_nu_signature( + r#" + def main [ a, foo: list = [ 2, 3, 4 ], b ] { } + "#, + ) + .unwrap(); + + println!("{}", serde_json::to_string_pretty(&sig).unwrap()); + + assert_eq!( + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![ + Arg { + name: "a".into(), + otyp: None, + typ: Typ::Unknown, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "foo".into(), + otyp: None, + typ: Typ::List(Box::new(Typ::Float)), + default: Some(json!([2, 3, 4])), + has_default: true, + oidx: None + }, + Arg { + name: "b".into(), + otyp: None, + typ: Typ::Unknown, + default: None, + has_default: false, + oidx: None + }, + ], + no_main_func: Some(false), + has_preprocessor: None, + }, + sig + ); + } + + #[test] + fn test_nu_datetime_sig() { + let sig = parse_nu_signature( + r#" + def main [ foo: datetime ] { } + "#, + ) + .unwrap(); + + assert_eq!( + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![Arg { + name: "foo".into(), + otyp: None, + typ: Typ::Datetime, + default: None, + has_default: false, + oidx: None + },], + no_main_func: Some(false), + has_preprocessor: None, + }, + sig + ); + } + // TODO: Re-enable for V1 + // #[test] + // fn test_nu_table_sig() { + // let sig = parse_nu_signature( + // r#" + // def main [ foo: table] { } + // "#, + // ) + // .unwrap(); + + // assert_eq!( + // MainArgSignature { + // star_args: false, + // star_kwargs: false, + // args: vec![Arg { + // name: "foo".into(), + // otyp: None, + // typ: Typ::List(Box::new(Typ::Object(vec![ + // ObjectProperty { key: "a".into(), typ: Box::new(Typ::Unknown) }, + // ObjectProperty { key: "b".into(), typ: Box::new(Typ::Float) }, + // ObjectProperty { key: "c".into(), typ: Box::new(Typ::Str(None)) }, + // ]))), + // default: None, + // has_default: false, + // oidx: None + // },], + // no_main_func: Some(false), + // has_preprocessor: None, + // }, + // sig + // ); + // } + + #[test] + fn test_nu_wrapup_sig() { + let sig = parse_nu_signature( + r#" + def main [a ,b :int,c? , d: string = "foo", bi?: any] {} + "#, + ) + .unwrap(); + + assert_eq!( + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![ + Arg { + name: "a".into(), + otyp: None, + typ: Typ::Unknown, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "b".into(), + otyp: None, + typ: Typ::Int, + default: None, + has_default: false, + oidx: None + }, + Arg { + name: "c".into(), + otyp: None, + typ: Typ::Unknown, + default: Some(serde_json::Value::Null), + has_default: true, + oidx: None + }, + Arg { + name: "d".into(), + otyp: None, + typ: Typ::Str(None), + default: Some(json!("foo")), + has_default: true, + oidx: None + }, + Arg { + name: "bi".into(), + otyp: None, + typ: Typ::Unknown, + default: Some(serde_json::Value::Null), + has_default: true, + oidx: None + } + ], + no_main_func: Some(false), + has_preprocessor: None, + }, + sig + ); + } + // #[test] + // fn test_nu_wrapup_nested_sig() { + // let sig = parse_nu_signature( + // r#" + // def main [ + // baz: string, + // foo: record, + // d: record> + // = + // { + // a: "a", + // b: 3, + // c: [ 2, 3, 4 ], + // d: { + // a: true, + // b: false, + // c: true + // } + // } + // ] { } + // "#, + // ) + // .unwrap(); + + // println!("{}", serde_json::to_string_pretty(&sig).unwrap()); + + // assert_eq!( + // MainArgSignature { + // star_args: false, + // star_kwargs: false, + // args: vec![ + // Arg { + // name: "baz".into(), + // otyp: None, + // typ: Typ::Str(None), + // default: None, + // has_default: false, + // oidx: None + // }, + // Arg { + // name: "foo".into(), + // otyp: None, + // typ: Typ::Object(vec![ + // ObjectProperty { key: "a".into(), typ: Box::new(Typ::Str(None)) }, + // ObjectProperty { key: "b".into(), typ: Box::new(Typ::Unknown) }, + // ObjectProperty { + // key: "c".into(), + // typ: Box::new(Typ::List(Box::new(Typ::Float))) + // }, + // ObjectProperty { + // key: "d".into(), + // typ: Box::new(Typ::Object(vec![ + // ObjectProperty { + // key: "a1".into(), + // typ: Box::new(Typ::Unknown) + // }, + // ObjectProperty { + // key: "b1".into(), + // typ: Box::new(Typ::Unknown) + // }, + // ObjectProperty { + // key: "c1".into(), + // typ: Box::new(Typ::Unknown) + // } + // ])) + // }, + // ]), + // default: Some(json!({ + // "a": "a", + // "b": 3, + // "c": [ + // 2, + // 3, + // 4 + // ], + // "d": { + // "a": true, + // "b": false, + // "c": true + // } + // })), + // has_default: true, + // oidx: None + // }, + // ], + // no_main_func: Some(false), + // has_preprocessor: None, + // }, + // sig + // ); + // } + #[test] + fn test_nu_nested_extra_types() { + assert_eq!( + parse_nu_signature( + r#" + def main [a: list] {} + "#, + ) + .is_err(), + true + ); + assert_eq!( + parse_nu_signature( + r#" + def main [a: list] {} + "#, + ) + .is_err(), + true + ); + assert_eq!( + parse_nu_signature( + r#" + def main [a: list] {} + "#, + ) + .is_err(), + true + ); + assert_eq!( + parse_nu_signature( + r#" + def main [a: list] {} + "#, + ) + .is_err(), + true + ); + assert_eq!( + parse_nu_signature( + r#" + def main [a: list>] {} + "#, + ) + .is_err(), + true + ); + } +} diff --git a/backend/parsers/windmill-parser-py-imports/src/lib.rs b/backend/parsers/windmill-parser-py-imports/src/lib.rs index 58f857ef08..3b06a491d5 100644 --- a/backend/parsers/windmill-parser-py-imports/src/lib.rs +++ b/backend/parsers/windmill-parser-py-imports/src/lib.rs @@ -6,11 +6,14 @@ * LICENSE-AGPL for a copy of the license. */ +mod mapping; + use async_recursion::async_recursion; use itertools::Itertools; use lazy_static::lazy_static; -use phf::phf_map; +use std::collections::HashMap; +use mapping::{FULL_IMPORTS_MAP, SHORT_IMPORTS_MAP}; #[cfg(not(target_arch = "wasm32"))] use regex::Regex; #[cfg(target_arch = "wasm32")] @@ -18,6 +21,7 @@ use regex_lite::Regex; use rustpython_parser::{ ast::{Stmt, StmtImport, StmtImportFrom, Suite}, + text_size::TextRange, Parse, }; use sqlx::{Pool, Postgres}; @@ -25,61 +29,25 @@ use windmill_common::{error, worker::PythonAnnotations}; const DEF_MAIN: &str = "def main("; -static PYTHON_IMPORTS_REPLACEMENT: phf::Map<&'static str, &'static str> = phf_map! { - "psycopg2" => "psycopg2-binary", - "psycopg" => "psycopg[binary, pool]", - "yaml" => "pyyaml", - "git" => "GitPython", - "shopify" => "ShopifyAPI", - "seleniumwire" => "selenium-wire", - "openbb-terminal" => "openbb[all]", - "riskfolio" => "riskfolio-lib", - "smb" => "pysmb", - "PIL" => "Pillow", - "googleapiclient" => "google-api-python-client", - "googlecloudbigquery" => "google-cloud-bigquery", - "dateutil" => "python-dateutil", - "mailparser" => "mail-parser", - "mailparser-reply" => "mail-parser-reply", - "gitlab" => "python-gitlab", - "smbclient" => "smbprotocol", - "playhouse" => "peewee", - "dns" => "dnspython", - "msoffcrypto" => "msoffcrypto-tool", - "tabula" => "tabula-py", - "shapefile" => "pyshp", - "sklearn" => "scikit-learn", - "umap" => "umap-learn", - "cv2" => "opencv-python", - "atlassian" => "atlassian-python-api", - "mysql" => "mysql-connector-python", - "tenable" => "pytenable", - "ns1" => "ns1-python", - "pymsql" => "PyMySQL", - "haystack" => "haystack-ai", - "github" => "PyGithub", - "ldap" => "python-ldap", - "opensearchpy" => "opensearch-py", - "lokalise" => "python-lokalise-api", - "msgraph" => "msgraph-sdk", - "pythonjsonlogger" => "python-json-logger", - "socks" => "PySocks", - "taiga" => "python-taiga", -}; - fn replace_import(x: String) -> String { - PYTHON_IMPORTS_REPLACEMENT + SHORT_IMPORTS_MAP .get(&x) .map(|x| x.to_owned()) .unwrap_or(&x) .to_string() } -lazy_static! { - static ref RE: Regex = Regex::new(r"^\#\s?(\S+)\s*$").unwrap(); +fn replace_full_import(x: &str) -> Option { + FULL_IMPORTS_MAP.get(x).map(|x| (*x).to_owned()) } -fn process_import(module: Option, path: &str, level: usize) -> Vec { +lazy_static! { + static ref RE: Regex = Regex::new(r"^\#\s?(\S+)\s*$").unwrap(); + static ref PIN_RE: Regex = Regex::new(r"(?:\s*#\s*(pin|repin):\s*)(\S*)").unwrap(); + static ref PKG_RE: Regex = Regex::new(r"^([^!=<>]+)(?:[!=<>]|$)").unwrap(); +} + +fn process_import(module: Option, path: &str, level: usize) -> Vec { if level > 0 { let mut imports = vec![]; let splitted_path = path.split("/"); @@ -88,17 +56,18 @@ fn process_import(module: Option, path: &str, level: usize) -> Vec error::Result Some(path), + _ => None, }) .collect()); } -fn parse_code_for_imports(code: &str, path: &str) -> error::Result> { +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +enum NImport { + // Order matters! First we want to resolve all repins + + // manually repinned requirement + // e.g.: + // import pandas # repin: pandas==x.y.z + Repin { + pin: ImportPin, + key: String, + }, + // manually pinned requirements + // e.g.: + // import pandas # pin: pandas>=x.y.z + // import pandas # pin: pandas<=x.y.z + // + // NOTE: It is possible for multiple pins exist on same import + // That's why we store vector of pins + Pin { + pins: Vec, + key: String, + }, + // Automatically inferred requirement + // e.g.: + // import pandas + Auto { + // Take `x.y.z` for example + // x is going to be the `root` + // and x.y.z is `full` + // + // `full` will be None if it is equal to root + // + // We will use `root` as a requirement name and pass to `uv pip compile` if it was not replaced with any pin + pkg: String, + + // However we still need full, since all pins pin against full import names + key: Option, + }, + // Relative imports + Relative(String), +} +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +enum NImportResolved { + Repin { pin: ImportPin, key: String }, + Pin { pins: Vec, key: String }, + Auto { pkg: String, key: Option }, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +struct ImportPin { + pkg: String, + path: String, +} + +fn parse_code_for_imports(code: &str, path: &str) -> error::Result> { let mut code = code.split(DEF_MAIN).next().unwrap_or("").to_string(); // remove main function decorator from end of file if it exists @@ -140,19 +160,54 @@ fn parse_code_for_imports(code: &str, path: &str) -> error::Result> let ast = Suite::parse(&code, "main.py").map_err(|e| { error::Error::ExecutionErr(format!("Error parsing code for imports: {}", e.to_string())) })?; - let nimports: Vec = ast + + let find_pin = |range: TextRange, key: String| { + let hs = code + .chars() + .skip(range.end().to_usize()) + .take_while(|e| *e != '\n') + .collect::(); + + if hs.trim_start().is_empty() { + return None; + } + + PIN_RE.captures(&hs).and_then(|x| { + x.get(1).zip(x.get(2)).and_then(|(ty_m, pkg_m)| { + let pkg = pkg_m.as_str().to_owned(); + if ty_m.as_str() == "pin" { + Some(vec![NImport::Pin { + pins: vec![ImportPin { pkg, path: path.to_owned() }], + key, + }]) + } else if ty_m.as_str() == "repin" { + Some(vec![NImport::Repin { + pin: ImportPin { pkg, path: path.to_owned() }, + key, + }]) + } else { + None + } + }) + }) + }; + + let mut nimports: Vec = ast .into_iter() .filter_map(|x| match x { - Stmt::Import(StmtImport { names, .. }) => Some( - names - .into_iter() - .map(|x| { - let name = x.name.to_string(); - process_import(Some(name), path, 0) - }) - .flatten() - .collect::>(), - ), + Stmt::Import(StmtImport { names, range }) => names + .get(0) + .and_then(|al| find_pin(range, al.name.to_string())) + .or(Some( + names + .into_iter() + .map(|x| { + let name = x.name.to_string(); + process_import(Some(name), path, 0) + }) + .flatten() + .collect::>(), + )), Stmt::ImportFrom(StmtImportFrom { level: Some(i), module, .. }) if i.to_u32() > 0 => { Some(process_import( module.map(|x| x.to_string()), @@ -160,15 +215,25 @@ fn parse_code_for_imports(code: &str, path: &str) -> error::Result> i.to_usize(), )) } - Stmt::ImportFrom(StmtImportFrom { level: _, module, .. }) => { - Some(process_import(module.map(|x| x.to_string()), path, 0)) - } + Stmt::ImportFrom(StmtImportFrom { level: _, module, range, .. }) => find_pin( + range, + module.clone().map(|x| x.to_string()).unwrap_or_default(), + ) + .or(Some(process_import(module.map(|x| x.to_string()), path, 0))), _ => None, }) .flatten() - .filter(|x| !STDIMPORTS.contains(&x.as_str())) + .filter(|x| { + if let NImport::Auto { ref pkg, .. } = x { + !STDIMPORTS.contains(&(*pkg).as_str()) + } else { + true + } + }) .unique() .collect(); + + nimports.sort(); return Ok(nimports); } @@ -179,8 +244,9 @@ pub async fn parse_python_imports( db: &Pool, already_visited: &mut Vec, annotated_pyv_numeric: &mut Option, -) -> error::Result> { - parse_python_imports_inner( +) -> error::Result<(Vec, Option)> { + let mut compile_error_hint: Option = None; + let mut imports = parse_python_imports_inner( code, w_id, path, @@ -189,7 +255,46 @@ pub async fn parse_python_imports( annotated_pyv_numeric, &mut annotated_pyv_numeric.and_then(|_| Some(path.to_owned())), ) - .await + .await? + .into_values() + .map(|nimport| match nimport { + NImportResolved::Pin { pins, .. } => pins.into_iter().map(|p| { + if let Some(hint) = &mut compile_error_hint{ + hint.push_str(&format!("\n - pin to {} in {}", p.pkg, p.path)); + } else { + compile_error_hint = Some("\n\nMultiple pins can cause problems during lockfile resolution.\nMake sure you checked every pin for conflicts:\n".into()) + }; + Ok(p.pkg) + }).collect_vec(), + NImportResolved::Repin { pin: ImportPin { pkg, .. }, .. } => vec![Ok(pkg)], + NImportResolved::Auto { pkg, key } => vec![ + + if let Some(key) = key { + Ok(format!("{pkg} # (mapped from {key})")) + } else { + Ok(pkg) + } + ], + }) + .flatten() + .collect::>>()? + .into_iter() + .unique() + .collect_vec(); + + imports.sort(); + + compile_error_hint + .as_mut() + .map(|e| e.push_str("\n\nNOTE: You can also `repin` to override all pins")); + Ok((imports, compile_error_hint)) +} + +fn extract_pkg_name(requirement: &str) -> String { + PKG_RE + .captures(requirement) + .map(|x| x.get(1).map(|m| m.as_str().to_string()).unwrap_or_default()) + .unwrap_or_default() } #[async_recursion] @@ -201,7 +306,7 @@ async fn parse_python_imports_inner( already_visited: &mut Vec, annotated_pyv_numeric: &mut Option, path_where_annotated_pyv: &mut Option, -) -> error::Result> { +) -> error::Result> { let PythonAnnotations { py310, py311, py312, py313, .. } = PythonAnnotations::parse(&code); // we pass only if there is none or only one annotation @@ -230,7 +335,6 @@ async fn parse_python_imports_inner( } else { *annotated_pyv_numeric = Some(numeric); } - *path_where_annotated_pyv = Some(path.to_owned()); } Ok(()) @@ -245,79 +349,221 @@ async fn parse_python_imports_inner( .lines() .find_position(|x| x.starts_with("#requirements:") || x.starts_with("# requirements:")); if let Some((pos, _)) = find_requirements { - let lines = code - .lines() + let mut requirements = HashMap::new(); + code.lines() .skip(pos + 1) .map_while(|x| { - RE.captures(x) - .map(|x| x.get(1).unwrap().as_str().to_string()) + RE.captures(x).and_then(|x| { + x.get(1).map(|m| { + let requirement = m.as_str().to_string(); + let key = extract_pkg_name(&requirement); + requirements.insert( + key.clone(), + NImportResolved::Pin { + pins: vec![ImportPin { + pkg: requirement.clone(), + path: Default::default(), + }], + key, + }, + ); + }) + }) }) - .collect(); - Ok(lines) + .collect_vec(); + + Ok(requirements) } else { let find_extra_requirements = code.lines().find_position(|x| { x.starts_with("#extra_requirements:") || x.starts_with("# extra_requirements:") }); - let mut imports: Vec = vec![]; + let mut imports: HashMap = HashMap::new(); if let Some((pos, _)) = find_extra_requirements { - let lines: Vec = code - .lines() + code.lines() .skip(pos + 1) .map_while(|x| { - RE.captures(x) - .map(|x| x.get(1).unwrap().as_str().to_string()) + RE.captures(x).and_then(|x| { + x.get(1).map(|m| { + let requirement = m.as_str().to_string(); + let key = extract_pkg_name(&requirement); + imports.insert( + key.clone(), + NImportResolved::Pin { + pins: vec![ImportPin { + pkg: requirement, + path: Default::default(), + }], + key, + }, + ); + }) + }) }) - .collect(); - imports.extend(lines); + .collect_vec(); } - let nimports = parse_code_for_imports(code, path)?; - for n in nimports.iter() { - let nested = if n.starts_with("relative:") { - let rpath = n.replace("relative:", ""); - let code = sqlx::query_scalar!( - r#" - SELECT content FROM script WHERE path = $1 AND workspace_id = $2 - AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND - workspace_id = $2) - "#, - &rpath, - w_id - ) - .fetch_optional(db) - .await? - .unwrap_or_else(|| "".to_string()); + // Will get unsorted vector of imports found in current script + let mut nimports = parse_code_for_imports(code, path)?; - if already_visited.contains(&rpath) { - vec![] - } else { - already_visited.push(rpath.clone()); - parse_python_imports_inner( - &code, - w_id, + // It is important to note, that sorting is important and will always result in this pattern: + // 1. All Repins go first + // 2. All Pins go second + // 3. All Auto go third + // 4. All relative imports go the last + // + // This way we make sure all repins are resolved before (re)pins inside imported relative scripts. + nimports.sort(); + + for n in nimports.into_iter() { + let mut nested = match n { + NImport::Relative(rpath) => { + let code = sqlx::query_scalar!( + r#" + SELECT content FROM script WHERE path = $1 AND workspace_id = $2 + AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND + workspace_id = $2) + "#, &rpath, - db, - already_visited, - annotated_pyv_numeric, - path_where_annotated_pyv, + w_id ) + .fetch_optional(db) .await? + .unwrap_or_else(|| "".to_string()); + + if already_visited.contains(&rpath) { + vec![] + } else { + already_visited.push(rpath.clone()); + // Because the algo goes depth first, this function will never return relative import + // This why we can safely assume later, that there is no relative imports + parse_python_imports_inner( + &code, + w_id, + &rpath, + db, + already_visited, + annotated_pyv_numeric, + path_where_annotated_pyv, + ) + .await? + .into_values() + .collect_vec() + } } - } else { - vec![replace_import(n.to_string())] + NImport::Repin { pin, key } => vec![NImportResolved::Repin { pin, key }], + NImport::Pin { pins, key } => vec![NImportResolved::Pin { pins, key }], + NImport::Auto { pkg, key } => vec![NImportResolved::Auto { pkg, key }], }; + + // Nested should also be sorted for the same reason + nested.sort(); + + // At this point there should be no NImport::Relative in `nested` for imp in nested { - if !imports.contains(&imp) { - imports.push(imp); + let key = match imp.clone() { + NImportResolved::Pin { key, .. } => key, + NImportResolved::Repin { key, .. } => key, + NImportResolved::Auto { key, pkg } => key.unwrap_or(pkg), + }; + // Handled cases: + // + // 1. + // Error: Imported windmill scripts have different pins + // + // auto + // ├── pin:2 + // └── pin:1 + // + // Fix 1: + // + // auto + // ├── pin:1 + // └── pin:1 + // + // Fix 2: + // + // repin:1 + // ├── pin:2 + // └── pin:1 + // + // 2. + // Error: Imported windmill scripts have different pins + // + // pin:2 + // └── pin:1 + // + // Fix 1: + // + // auto + // └── pin:1 + // + // Fix 2: + // + // repin:2 + // └── pin:1 + // + // 3. repins allowed to be repinned again + // + // repin:2 + // └── repin:1 + // + match imp.clone() { + NImportResolved::Repin { .. } => { + if let Some(existing_import) = imports.get(&key) { + match existing_import { + // replace + p if matches!( + p, + NImportResolved::Pin { .. } | NImportResolved::Auto { .. } + ) => + { + imports.insert(key, imp); + } + // do nothing (older repins have greater precedence) + NImportResolved::Repin { .. } => {} + // Should not be possible + _ => { + return Err(anyhow::anyhow!( + "Internal error: cannot resolve requirement pins", + ) + .into()); + } + } + } else { + imports.insert(key, imp.clone()); + } + } + NImportResolved::Pin { pins: new_pins, .. } => { + if let Some(existing_import) = imports.get_mut(&key) { + match existing_import { + // Check if pin is the same version, if same, do nothing, if not error + NImportResolved::Pin { pins: existing_pins, .. } => { + existing_pins.extend(new_pins) + } + // do nothing + NImportResolved::Repin { .. } => {} + // Replace with new pin + NImportResolved::Auto { .. } => { + imports.insert(key, imp); + } + } + } else { + imports.insert(key, imp.clone()); + } + } + NImportResolved::Auto { .. } => { + if !imports.contains_key(&key) { + imports.insert(key, imp); + } + } } } } - imports.sort(); Ok(imports) } } -const STDIMPORTS: [&str; 302] = [ +const STDIMPORTS: [&str; 303] = [ "--future--", "-abc", "-aix-support", @@ -619,5 +865,6 @@ const STDIMPORTS: [&str; 302] = [ "zipapp", "zipfile", "zipimport", + "zlib", "", ]; diff --git a/backend/parsers/windmill-parser-py-imports/src/mapping.rs b/backend/parsers/windmill-parser-py-imports/src/mapping.rs new file mode 100644 index 0000000000..1bd0688951 --- /dev/null +++ b/backend/parsers/windmill-parser-py-imports/src/mapping.rs @@ -0,0 +1,384 @@ +/* + * 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. + */ + +use phf::phf_map; +type PyMap = phf::Map<&'static str, &'static str>; + +/// In some cases inferring requirement from import is not possible. +/// That's why we need to map import to requirement ident. +/// These two maps allows us to do so. + +/// import x.y.z +/// ^^^^^ replaces entire x.y.z import with [?] +/// +pub static FULL_IMPORTS_MAP: PyMap = phf_map! { + // import => requirement + "google.cloud.bigquery_storage" => "google-cloud-bigquery-storage", + "google.cloud.bigquery" => "google-cloud-bigquery", + "google.cloud.parametermanager" => "google-cloud-parametermanager", + "google.cloud.oracledatabase" => "google-cloud-oracledatabase", + "google.cloud.deploy" => "google-cloud-deploy", + "google.cloud.pubsub" => "google-cloud-pubsub", + "google.cloud.workflows" => "google-cloud-workflows", + "google.cloud.managedkafka" => "google-cloud-managedkafka", + "google.cloud.iam" => "google-cloud-iam", + "google.cloud.documentai" => "google-cloud-documentai", + "google.cloud.dlp" => "google-cloud-dlp", + "google.cloud.compute" => "google-cloud-compute", + "google.cloud.alloydb" => "google-cloud-alloydb", + "google.cloud.aiplatform" => "google-cloud-aiplatform", + "google.cloud.bigtable" => "google-cloud-bigtable", + "google.cloud.workstations" => "google-cloud-workstations", + "google.cloud.websecurityscanner" => "google-cloud-websecurityscanner", + "google.cloud.webrisk" => "google-cloud-webrisk", + "google.cloud.vmwareengine" => "google-cloud-vmwareengine", + "google.cloud.visionai" => "google-cloud-visionai", + "google.cloud.vision" => "google-cloud-vision", + "google.cloud.videointelligence" => "google-cloud-videointelligence", + "google.cloud.translate" => "google-cloud-translate", + "google.cloud.trace" => "google-cloud-trace", + "google.cloud.tpu" => "google-cloud-tpu", + "google.cloud.texttospeech" => "google-cloud-texttospeech", + "google.cloud.telcoautomation" => "google-cloud-telcoautomation", + "google.cloud.tasks" => "google-cloud-tasks", + "google.cloud.talent" => "google-cloud-talent", + "google.cloud.support" => "google-cloud-support", + "google.cloud.storageinsights" => "google-cloud-storageinsights", + "google.cloud.speech" => "google-cloud-speech", + "google.cloud.shell" => "google-cloud-shell", + "google.cloud.servicehealth" => "google-cloud-servicehealth", + "google.cloud.securitycentermanagement" => "google-cloud-securitycentermanagement", + "google.cloud.securitycenter" => "google-cloud-securitycenter", + "google.cloud.securesourcemanager" => "google-cloud-securesourcemanager", + "google.cloud.scheduler" => "google-cloud-scheduler", + "google.cloud.run" => "google-cloud-run", + "google.cloud.retail" => "google-cloud-retail", + "google.cloud.recommender" => "google-cloud-recommender", + "google.cloud.rapidmigrationassessment" => "google-cloud-rapidmigrationassessment", + "google.cloud.privilegedaccessmanager" => "google-cloud-privilegedaccessmanager", + "google.cloud.policytroubleshooter_iam" => "google-cloud-policytroubleshooter-iam", + "google.cloud.policysimulator" => "google-cloud-policysimulator", + "google.cloud.parallelstore" => "google-cloud-parallelstore", + "google.cloud.optimization" => "google-cloud-optimization", + "google.cloud.notebooks" => "google-cloud-notebooks", + "google.cloud.network_services" => "google-cloud-network-services", + "google.cloud.network_security" => "google-cloud-network-security", + "google.cloud.netapp" => "google-cloud-netapp", + "google.cloud.monitoring" => "google-cloud-monitoring", + "google.cloud.modelarmor" => "google-cloud-modelarmor", + "google.cloud.migrationcenter" => "google-cloud-migrationcenter", + "google.cloud.memorystore" => "google-cloud-memorystore", + "google.cloud.memcache" => "google-cloud-memcache", + "google.cloud.language" => "google-cloud-language", + "google.cloud.kms" => "google-cloud-kms", + "google.cloud.kms_inventory" => "google-cloud-kms-inventory", + "google.cloud.ids" => "google-cloud-ids", + "google.cloud.iap" => "google-cloud-iap", + "google.cloud.gsuiteaddons" => "google-cloud-gsuiteaddons", + "google.cloud.gke_multicloud" => "google-cloud-gke-multicloud", + "google.cloud.gke_backup" => "google-cloud-gke-backup", + "google.cloud.gdchardwaremanagement" => "google-cloud-gdchardwaremanagement", + "google.cloud.functions" => "google-cloud-functions", + "google.cloud.financialservices" => "google-cloud-financialservices", + "google.cloud.filestore" => "google-cloud-filestore", + "google.cloud.eventarc" => "google-cloud-eventarc", + "google.cloud.eventarc_publishing" => "google-cloud-eventarc-publishing", + "google.cloud.essential_contacts" => "google-cloud-essential-contacts", + "google.cloud.enterpriseknowledgegraph" => "google-cloud-enterpriseknowledgegraph", + "google.cloud.edgenetwork" => "google-cloud-edgenetwork", + "google.cloud.edgecontainer" => "google-cloud-edgecontainer", + "google.cloud.domains" => "google-cloud-domains", + "google.cloud.discoveryengine" => "google-cloud-discoveryengine", + "google.cloud.dialogflow" => "google-cloud-dialogflow", + "google.cloud.developerconnect" => "google-cloud-developerconnect", + "google.cloud.datastream" => "google-cloud-datastream", + "google.cloud.dataproc" => "google-cloud-dataproc", + "google.cloud.dataplex" => "google-cloud-dataplex", + "google.cloud.datalabeling" => "google-cloud-datalabeling", + "google.cloud.dataform" => "google-cloud-dataform", + "google.cloud.datacatalog" => "google-cloud-datacatalog", + "google.cloud.datacatalog_lineage" => "google-cloud-datacatalog-lineage", + "google.cloud.data_fusion" => "google-cloud-data-fusion", + "google.cloud.contentwarehouse" => "google-cloud-contentwarehouse", + "google.cloud.container" => "google-cloud-container", + "google.cloud.config" => "google-cloud-config", + "google.cloud.confidentialcomputing" => "google-cloud-confidentialcomputing", + "google.cloud.common" => "google-cloud-common", + "google.cloud.cloudcontrolspartner" => "google-cloud-cloudcontrolspartner", + "google.cloud.channel" => "google-cloud-channel", + "google.cloud.certificate_manager" => "google-cloud-certificate-manager", + "google.cloud.billing" => "google-cloud-billing", + "google.cloud.bigquery_migration" => "google-cloud-bigquery-migration", + "google.cloud.bigquery_logging" => "google-cloud-bigquery-logging", + "google.cloud.bigquery_datatransfer" => "google-cloud-bigquery-datatransfer", + "google.cloud.bigquery_datapolicies" => "google-cloud-bigquery-datapolicies", + "google.cloud.bigquery_connection" => "google-cloud-bigquery-connection", + "google.cloud.bigquery_biglake" => "google-cloud-bigquery-biglake", + "google.cloud.bigquery_analyticshub" => "google-cloud-bigquery-analyticshub", + "google.cloud.beyondcorp_clientgateways" => "google-cloud-beyondcorp-clientgateways", + "google.cloud.beyondcorp_clientconnectorservices" => "google-cloud-beyondcorp-clientconnectorservices", + "google.cloud.beyondcorp_appgateways" => "google-cloud-beyondcorp-appgateways", + "google.cloud.beyondcorp_appconnectors" => "google-cloud-beyondcorp-appconnectors", + "google.cloud.beyondcorp_appconnections" => "google-cloud-beyondcorp-appconnections", + "google.cloud.batch" => "google-cloud-batch", + "google.cloud.bare_metal_solution" => "google-cloud-bare-metal-solution", + "google.cloud.backupdr" => "google-cloud-backupdr", + "google.cloud.automl" => "google-cloud-automl", + "google.cloud.asset" => "google-cloud-asset", + "google.cloud.apphub" => "google-cloud-apphub", + "google.cloud.apihub" => "google-cloud-apihub", + "google.cloud.advisorynotifications" => "google-cloud-advisorynotifications", + "google.cloud.spanner" => "google-cloud-spanner", + "google.cloud.storage" => "google-cloud-storage", + "google.cloud.firestore" => "google-cloud-firestore", + "google.cloud.pubsublite" => "google-cloud-pubsublite", + "google.cloud.datastore" => "google-cloud-datastore", + "google.cloud.ndb" => "google-cloud-ndb", + "google.cloud.dns" => "google-cloud-dns", + "google.cloud.runtimeconfig" => "google-cloud-runtimeconfig", + "google.cloud.iot" => "google-cloud-iot", + "google.generativeai" => "google-generativeai", + "google.genai" => "google-genai", + // Azure + "azure.mgmt.hybridkubernetes" => "azure-mgmt-hybridkubernetes", + "azure.mgmt.sql" => "azure-mgmt-sql", + "azure.mgmt.compute" => "azure-mgmt-compute", + "azure.mgmt.eventgrid" => "azure-mgmt-eventgrid", + "azure.mgmt.containerservice" => "azure-mgmt-containerservice", + "azure.mgmt.databox" => "azure-mgmt-databox", + "azure.mgmt.keyvault" => "azure-mgmt-keyvault", + "azure.mgmt.applicationinsights" => "azure-mgmt-applicationinsights", + "azure.mgmt.storage" => "azure-mgmt-storage", + "azure.mgmt.quota" => "azure-mgmt-quota", + "azure.mgmt.nginx" => "azure-mgmt-nginx", + "azure.mgmt.netapp" => "azure-mgmt-netapp", + "azure.mgmt.resource" => "azure-mgmt-resource", + "azure.mgmt.containerregistry" => "azure-mgmt-containerregistry", + "azure.mgmt.databoxedge" => "azure-mgmt-databoxedge", + "azure.mgmt.logz" => "azure-mgmt-logz", + "azure.mgmt.monitor" => "azure-mgmt-monitor", + "azure.mgmt.servicenetworking" => "azure-mgmt-servicenetworking", + "azure.mgmt.kusto" => "azure-mgmt-kusto", + "azure.mgmt.web" => "azure-mgmt-web", + "azure.mgmt.eventhub" => "azure-mgmt-eventhub", + "azure.mgmt.redis" => "azure-mgmt-redis", + "azure.mgmt.cosmosdb" => "azure-mgmt-cosmosdb", + "azure.mgmt.network" => "azure-mgmt-network", + "azure.mgmt.cognitiveservices" => "azure-mgmt-cognitiveservices", + "azure.mgmt.servicefabricmanagedclusters" => "azure-mgmt-servicefabricmanagedclusters", + "azure.mgmt.datafactory" => "azure-mgmt-datafactory", + "azure.mgmt.hybridcompute" => "azure-mgmt-hybridcompute", + "azure.mgmt.servicebus" => "azure-mgmt-servicebus", + "azure.mgmt.marketplaceordering" => "azure-mgmt-marketplaceordering", + "azure.mgmt.managedservices" => "azure-mgmt-managedservices", + "azure.mgmt.managementgroups" => "azure-mgmt-managementgroups", + "azure.mgmt.loganalytics" => "azure-mgmt-loganalytics", + "azure.mgmt.automation" => "azure-mgmt-automation", + "azure.mgmt.devtestlabs" => "azure-mgmt-devtestlabs", + "azure.mgmt.documentdb" => "azure-mgmt-documentdb", + "azure.mgmt.scheduler" => "azure-mgmt-scheduler", + "azure.mgmt.core" => "azure-mgmt-core", + "azure.mgmt.servermanager" => "azure-mgmt-servermanager", + "azure.mgmt.batchai" => "azure-mgmt-batchai", + "azure.mgmt.extendedlocation" => "azure-mgmt-extendedlocation", + "azure.mgmt.digitaltwins" => "azure-mgmt-digitaltwins", + "azure.mgmt.appconfiguration" => "azure-mgmt-appconfiguration", + "azure.mgmt.edgeorder" => "azure-mgmt-edgeorder", + "azure.mgmt.resourcehealth" => "azure-mgmt-resourcehealth", + "azure.mgmt.redhatopenshift" => "azure-mgmt-redhatopenshift", + "azure.mgmt.appplatform" => "azure-mgmt-appplatform", + "azure.mgmt.appcontainers" => "azure-mgmt-appcontainers", + "azure.mgmt.elastic" => "azure-mgmt-elastic", + "azure.mgmt.dnsresolver" => "azure-mgmt-dnsresolver", + "azure.mgmt.containerinstance" => "azure-mgmt-containerinstance", + "azure.mgmt.elasticsan" => "azure-mgmt-elasticsan", + "azure.mgmt.dns" => "azure-mgmt-dns", + "azure.mgmt.redisenterprise" => "azure-mgmt-redisenterprise", + "azure.mgmt.servicelinker" => "azure-mgmt-servicelinker", + "azure.mgmt.rdbms" => "azure-mgmt-rdbms", + "azure.mgmt.batch" => "azure-mgmt-batch", + "azure.mgmt.avs" => "azure-mgmt-avs", + "azure.mgmt.webpubsub" => "azure-mgmt-webpubsub", + "azure.mgmt.desktopvirtualization" => "azure-mgmt-desktopvirtualization", + "azure.mgmt.privatedns" => "azure-mgmt-privatedns", + "azure.mgmt.hdinsight" => "azure-mgmt-hdinsight", + "azure.mgmt.billing" => "azure-mgmt-billing", + "azure.mgmt.azurestackhci" => "azure-mgmt-azurestackhci", + "azure.mgmt.dataprotection" => "azure-mgmt-dataprotection", + "azure.mgmt.search" => "azure-mgmt-search", + "azure.mgmt.appcomplianceautomation" => "azure-mgmt-appcomplianceautomation", + "azure.mgmt.scvmm" => "azure-mgmt-scvmm", + "azure.mgmt.powerbiembedded" => "azure-mgmt-powerbiembedded", + "azure.mgmt.imagebuilder" => "azure-mgmt-imagebuilder", + "azure.mgmt.storagemover" => "azure-mgmt-storagemover", + "azure.mgmt.mobilenetwork" => "azure-mgmt-mobilenetwork", + "azure.mgmt.cdn" => "azure-mgmt-cdn", + "azure.mgmt.storagecache" => "azure-mgmt-storagecache", + "azure.mgmt.maintenance" => "azure-mgmt-maintenance", + "azure.mgmt.security" => "azure-mgmt-security", + "azure.mgmt.devcenter" => "azure-mgmt-devcenter", + "azure.mgmt.support" => "azure-mgmt-support", + "azure.mgmt.recoveryservicesbackup" => "azure-mgmt-recoveryservicesbackup", + "azure.mgmt.recoveryservices" => "azure-mgmt-recoveryservices", + "azure.mgmt.confidentialledger" => "azure-mgmt-confidentialledger", + "azure.mgmt.healthcareapis" => "azure-mgmt-healthcareapis", + "azure.mgmt.frontdoor" => "azure-mgmt-frontdoor", + "azure.mgmt.notificationhubs" => "azure-mgmt-notificationhubs", + "azure.mgmt.quantum" => "azure-mgmt-quantum", + "azure.mgmt.apimanagement" => "azure-mgmt-apimanagement", + "azure.mgmt.communication" => "azure-mgmt-communication", + "azure.mgmt.newrelicobservability" => "azure-mgmt-newrelicobservability", + "azure.mgmt.confluent" => "azure-mgmt-confluent", + "azure.mgmt.chaos" => "azure-mgmt-chaos", + "azure.mgmt.recoveryservicessiterecovery" => "azure-mgmt-recoveryservicessiterecovery", + "azure.mgmt.servicefabric" => "azure-mgmt-servicefabric", + "azure.mgmt.hybridcontainerservice" => "azure-mgmt-hybridcontainerservice", + "azure.mgmt.streamanalytics" => "azure-mgmt-streamanalytics", + "azure.mgmt.deviceupdate" => "azure-mgmt-deviceupdate", + "azure.mgmt.hybridnetwork" => "azure-mgmt-hybridnetwork", + "azure.mgmt.dashboard" => "azure-mgmt-dashboard", + "azure.mgmt.connectedvmware" => "azure-mgmt-connectedvmware", + "azure.mgmt.datadog" => "azure-mgmt-datadog", + "azure.mgmt.baremetalinfrastructure" => "azure-mgmt-baremetalinfrastructure", + "azure.mgmt.signalr" => "azure-mgmt-signalr", + "azure.mgmt.resourcemover" => "azure-mgmt-resourcemover", + "azure.mgmt.kubernetesconfiguration" => "azure-mgmt-kubernetesconfiguration", + "azure.mgmt.iothub" => "azure-mgmt-iothub", + "azure.mgmt.maps" => "azure-mgmt-maps", + "azure.mgmt.devspaces" => "azure-mgmt-devspaces", + "azure.mgmt.dynatrace" => "azure-mgmt-dynatrace", + "azure.mgmt.resourceconnector" => "azure-mgmt-resourceconnector", + "azure.mgmt.authorization" => "azure-mgmt-authorization", + "azure.mgmt.costmanagement" => "azure-mgmt-costmanagement", + "azure.mgmt.databricks" => "azure-mgmt-databricks", + "azure.mgmt.graphservices" => "azure-mgmt-graphservices", + "azure.mgmt.iothubprovisioningservices" => "azure-mgmt-iothubprovisioningservices", + "azure.mgmt.sqlvirtualmachine" => "azure-mgmt-sqlvirtualmachine", + "azure.mgmt.trafficmanager" => "azure-mgmt-trafficmanager", + "azure.mgmt.agfood" => "azure-mgmt-agfood", + "azure.mgmt.azureadb2c" => "azure-mgmt-azureadb2c", + "azure.mgmt.voiceservices" => "azure-mgmt-voiceservices", + "azure.mgmt.machinelearningservices" => "azure-mgmt-machinelearningservices", + "azure.mgmt.workloads" => "azure-mgmt-workloads", + "azure.mgmt.reservations" => "azure-mgmt-reservations", + "azure.mgmt.orbital" => "azure-mgmt-orbital", + "azure.mgmt.defendereasm" => "azure-mgmt-defendereasm", + "azure.mgmt.msi" => "azure-mgmt-msi", + "azure.mgmt.synapse" => "azure-mgmt-synapse", + "azure.mgmt.commerce" => "azure-mgmt-commerce", + "azure.mgmt.loadtesting" => "azure-mgmt-loadtesting", + "azure.mgmt.botservice" => "azure-mgmt-botservice", + "azure.mgmt.media" => "azure-mgmt-media", + "azure.mgmt.securitydevops" => "azure-mgmt-securitydevops", + "azure.mgmt.policyinsights" => "azure-mgmt-policyinsights", + "azure.mgmt.securityinsight" => "azure-mgmt-securityinsight", + "azure.mgmt.subscription" => "azure-mgmt-subscription", + "azure.mgmt.agrifood" => "azure-mgmt-agrifood", + "azure.mgmt.resourcegraph" => "azure-mgmt-resourcegraph", + "azure.mgmt.alertsmanagement" => "azure-mgmt-alertsmanagement", + "azure.mgmt.labservices" => "azure-mgmt-labservices", + "azure.mgmt.fluidrelay" => "azure-mgmt-fluidrelay", + "azure.mgmt.automanage" => "azure-mgmt-automanage", + "azure.mgmt.billingbenefits" => "azure-mgmt-billingbenefits", + "azure.mgmt.education" => "azure-mgmt-education", + "azure.mgmt.consumption" => "azure-mgmt-consumption", + "azure.mgmt.workloadmonitor" => "azure-mgmt-workloadmonitor", + "azure.mgmt.iotcentral" => "azure-mgmt-iotcentral", + "azure.mgmt.datamigration" => "azure-mgmt-datamigration", + "azure.mgmt.azurearcdata" => "azure-mgmt-azurearcdata", + "azure.mgmt.azurestack" => "azure-mgmt-azurestack", + "azure.mgmt.networkfunction" => "azure-mgmt-networkfunction", + "azure.mgmt.oep" => "azure-mgmt-oep", + "azure.mgmt.storagepool" => "azure-mgmt-storagepool", + "azure.mgmt.relay" => "azure-mgmt-relay", + "azure.mgmt.purview" => "azure-mgmt-purview", + "azure.mgmt.vmwarecloudsimple" => "azure-mgmt-vmwarecloudsimple", + "azure.mgmt.guestconfig" => "azure-mgmt-guestconfig", + "azure.mgmt.testbase" => "azure-mgmt-testbase", + "azure.mgmt.logic" => "azure-mgmt-logic", + "azure.mgmt.storageimportexport" => "azure-mgmt-storageimportexport", + "azure.mgmt.managementpartner" => "azure-mgmt-managementpartner", + "azure.mgmt.serialconsole" => "azure-mgmt-serialconsole", + "azure.mgmt.portal" => "azure-mgmt-portal", + "azure.mgmt.deploymentmanager" => "azure-mgmt-deploymentmanager", + "azure.mgmt.machinelearningcompute" => "azure-mgmt-machinelearningcompute", + "azure.mgmt.mixedreality" => "azure-mgmt-mixedreality", + "azure.mgmt.peering" => "azure-mgmt-peering", + "azure.mgmt.storagesync" => "azure-mgmt-storagesync", + "azure.mgmt.customproviders" => "azure-mgmt-customproviders", + "azure.mgmt.hanaonazure" => "azure-mgmt-hanaonazure", + "azure.mgmt.datashare" => "azure-mgmt-datashare", + "azure.mgmt.powerbidedicated" => "azure-mgmt-powerbidedicated", + "azure.mgmt.timeseriesinsights" => "azure-mgmt-timeseriesinsights", + "azure.mgmt.healthbot" => "azure-mgmt-healthbot", + "azure.mgmt.attestation" => "azure-mgmt-attestation", + "azure.mgmt.advisor" => "azure-mgmt-advisor", + "azure.mgmt.operationsmanagement" => "azure-mgmt-operationsmanagement", + "azure.mgmt.videoanalyzer" => "azure-mgmt-videoanalyzer", + "azure.mgmt.app" => "azure-mgmt-app", + "azure.mgmt.changeanalysis" => "azure-mgmt-changeanalysis", + "azure.mgmt.regionmove" => "azure-mgmt-regionmove", + "azure.mgmt.edgegateway" => "azure-mgmt-edgegateway", + "azure.mgmt.nspkg" => "azure-mgmt-nspkg", + "azure.keyvault.secrets" => "azure-keyvault-secrets", + "azure.storage.blob" => "azure-storage-blob", + "azure.storage.filedatalake" => "azure-storage-file-datalake", + // Add new entry here ^ +}; + +/// import x.y.z +/// ^ replaces x with [?] +/// +/// Additional rules: +/// 1. in x "_" are replaced with "-" +/// 2. If full imports had a hit, this one will not be called +pub static SHORT_IMPORTS_MAP: PyMap = phf_map! { + // import => requirement + "psycopg2" => "psycopg2-binary", + "psycopg" => "psycopg[binary, pool]", + "yaml" => "pyyaml", + "git" => "GitPython", + "shopify" => "ShopifyAPI", + "seleniumwire" => "selenium-wire", + "openbb-terminal" => "openbb[all]", + "riskfolio" => "riskfolio-lib", + "smb" => "pysmb", + "PIL" => "Pillow", + "googleapiclient" => "google-api-python-client", + "googlecloudbigquery" => "google-cloud-bigquery", + "dateutil" => "python-dateutil", + "mailparser" => "mail-parser", + "mailparser-reply" => "mail-parser-reply", + "gitlab" => "python-gitlab", + "smbclient" => "smbprotocol", + "playhouse" => "peewee", + "dns" => "dnspython", + "msoffcrypto" => "msoffcrypto-tool", + "tabula" => "tabula-py", + "shapefile" => "pyshp", + "sklearn" => "scikit-learn", + "umap" => "umap-learn", + "cv2" => "opencv-python", + "atlassian" => "atlassian-python-api", + "mysql" => "mysql-connector-python", + "tenable" => "pytenable", + "ns1" => "ns1-python", + "pymsql" => "PyMySQL", + "haystack" => "haystack-ai", + "github" => "PyGithub", + "ldap" => "python-ldap", + "opensearchpy" => "opensearch-py", + "lokalise" => "python-lokalise-api", + "msgraph" => "msgraph-sdk", + "pythonjsonlogger" => "python-json-logger", + "socks" => "PySocks", + "taiga" => "python-taiga", + "docx" => "python-docx", + "vt" => "vt-py", + // Add new entry here ^ +}; diff --git a/backend/parsers/windmill-parser-py-imports/tests/fixtures/base.sql b/backend/parsers/windmill-parser-py-imports/tests/fixtures/base.sql index d75bf4396c..590ce1bd5f 100644 --- a/backend/parsers/windmill-parser-py-imports/tests/fixtures/base.sql +++ b/backend/parsers/windmill-parser-py-imports/tests/fixtures/base.sql @@ -2580,4 +2580,4 @@ import innerdifffolder '{}', '', '', -'f/foobar/bar', -28028598712388159, 'python3', ''); \ No newline at end of file +'f/foobar/bar', -28028598712388159, 'python3', ''); diff --git a/backend/parsers/windmill-parser-py-imports/tests/tests.rs b/backend/parsers/windmill-parser-py-imports/tests/tests.rs index a634f247dd..9fee9b21c9 100644 --- a/backend/parsers/windmill-parser-py-imports/tests/tests.rs +++ b/backend/parsers/windmill-parser-py-imports/tests/tests.rs @@ -19,7 +19,7 @@ def main(): "; let mut already_visited = vec![]; - let r = parse_python_imports( + let (r, ..) = parse_python_imports( code, "test-workspace", "f/foo/bar", @@ -29,7 +29,15 @@ def main(): ) .await?; // println!("{}", serde_json::to_string(&r)?); - assert_eq!(r, vec!["matplotlib", "wmill", "zanzibar"]); + assert_eq!( + r, + vec![ + "matplotlib # (mapped from matplotlib.pyplot)", + "wmill", + "zanzibar # (mapped from zanzibar.estonie)" + ] + ); + Ok(()) } @@ -52,7 +60,7 @@ def main(): "; let mut already_visited = vec![]; - let r = parse_python_imports( + let (r, ..) = parse_python_imports( code, "test-workspace", "f/foo/bar", @@ -83,7 +91,7 @@ def main(): "; let mut already_visited = vec![]; - let r = parse_python_imports( + let (r, ..) = parse_python_imports( code, "test-workspace", "f/foo/bar", diff --git a/backend/parsers/windmill-parser-py/src/lib.rs b/backend/parsers/windmill-parser-py/src/lib.rs index 738bc8ed7c..dbac8498ec 100644 --- a/backend/parsers/windmill-parser-py/src/lib.rs +++ b/backend/parsers/windmill-parser-py/src/lib.rs @@ -57,9 +57,12 @@ fn filter_non_main(code: &str, main_name: &str) -> String { return filtered_code; } +/// skip_params is a micro optimization for when we just want to find the main +/// function without parsing all the params. pub fn parse_python_signature( code: &str, override_main: Option, + skip_params: bool, ) -> anyhow::Result { let main_name = override_main.unwrap_or("main".to_string()); @@ -78,11 +81,13 @@ pub fn parse_python_signature( let ast = Suite::parse(&filtered_code, "main.py") .map_err(|e| anyhow::anyhow!("Error parsing code: {}", e.to_string()))?; - let param = ast.into_iter().find_map(|x| match x { + let params = ast.into_iter().find_map(|x| match x { Stmt::FunctionDef(StmtFunctionDef { name, args, .. }) if &name == &main_name => Some(*args), _ => None, }); - if let Some(params) = param { + + if !skip_params && params.is_some() { + let params = params.unwrap(); //println!("{:?}", params); let def_arg_start = params.args.len() - params.defaults().count(); Ok(MainArgSignature { @@ -93,11 +98,11 @@ pub fn parse_python_signature( .iter() .enumerate() .map(|(i, x)| { - let mut typ = x + let (mut typ, has_default) = x .as_arg() .annotation .as_ref() - .map_or(Typ::Unknown, |e| parse_expr(e)); + .map_or((Typ::Unknown, false), |e| parse_expr(e)); let default = if i >= def_arg_start { params @@ -135,7 +140,7 @@ pub fn parse_python_signature( otyp: None, name: x.as_arg().arg.to_string(), typ, - has_default: default.is_some(), + has_default: has_default || default.is_some(), default, oidx: None, } @@ -149,23 +154,33 @@ pub fn parse_python_signature( star_args: false, star_kwargs: false, args: vec![], - no_main_func: Some(true), + no_main_func: Some(params.is_none()), has_preprocessor: Some(has_preprocessor), }) } } -fn parse_expr(e: &Box) -> Typ { +fn parse_expr(e: &Box) -> (Typ, bool) { match e.as_ref() { - Expr::Name(ExprName { id, .. }) => parse_typ(id.as_ref()), + Expr::Name(ExprName { id, .. }) => (parse_typ(id.as_ref()), false), Expr::Attribute(x) => { if x.value .as_name_expr() .is_some_and(|x| x.id.as_str() == "wmill") { - parse_typ(x.attr.as_str()) + (parse_typ(x.attr.as_str()), false) } else { - Typ::Unknown + (Typ::Unknown, false) + } + } + Expr::BinOp(x) => { + if matches!( + x.right.as_ref(), + Expr::Constant(ExprConstant { value: Constant::None, .. }) + ) { + (parse_expr(&x.left).0, true) + } else { + (Typ::Unknown, false) } } Expr::Subscript(x) => match x.value.as_ref() { @@ -190,14 +205,15 @@ fn parse_expr(e: &Box) -> Typ { } _ => None, }; - Typ::Str(values) + (Typ::Str(values), false) } - "List" => Typ::List(Box::new(parse_expr(&x.slice))), - _ => Typ::Unknown, + "List" | "list" => (Typ::List(Box::new(parse_expr(&x.slice).0)), false), + "Optional" => (parse_expr(&x.slice).0, true), + _ => (Typ::Unknown, false), }, - _ => Typ::Unknown, + _ => (Typ::Unknown, false), }, - _ => Typ::Unknown, + _ => (Typ::Unknown, false), } } @@ -287,7 +303,7 @@ def main(test1: str, name: datetime.datetime = datetime.now(), byte: bytes = byt "; //println!("{}", serde_json::to_string()?); assert_eq!( - parse_python_signature(code, None)?, + parse_python_signature(code, None, false)?, MainArgSignature { star_args: false, star_kwargs: false, @@ -376,7 +392,7 @@ def main(test1: str, "; //println!("{}", serde_json::to_string()?); assert_eq!( - parse_python_signature(code, None)?, + parse_python_signature(code, None, false)?, MainArgSignature { star_args: false, star_kwargs: false, @@ -436,7 +452,7 @@ def main(test1: str, "; //println!("{}", serde_json::to_string()?); assert_eq!( - parse_python_signature(code, None)?, + parse_python_signature(code, None, false)?, MainArgSignature { star_args: false, star_kwargs: false, @@ -493,7 +509,7 @@ def main(test1: Literal["foo", "bar"], test2: List[Literal["foo", "bar"]]): retu "#; //println!("{}", serde_json::to_string()?); assert_eq!( - parse_python_signature(code, None)?, + parse_python_signature(code, None, false)?, MainArgSignature { star_args: false, star_kwargs: false, @@ -537,7 +553,7 @@ def main(test1: DynSelect_foo): return "#; //println!("{}", serde_json::to_string()?); assert_eq!( - parse_python_signature(code, None)?, + parse_python_signature(code, None, false)?, MainArgSignature { star_args: false, star_kwargs: false, @@ -568,7 +584,7 @@ def hello(): return "#; //println!("{}", serde_json::to_string()?); assert_eq!( - parse_python_signature(code, None)?, + parse_python_signature(code, None, false)?, MainArgSignature { star_args: false, star_kwargs: false, @@ -596,7 +612,7 @@ def main(): return "#; //println!("{}", serde_json::to_string()?); assert_eq!( - parse_python_signature(code, None)?, + parse_python_signature(code, None, false)?, MainArgSignature { star_args: false, star_kwargs: false, @@ -617,10 +633,10 @@ def main(a: list, e: List[int], b: list = [1,2,3,4], c = [1,2,3,4], d = ["a", "b "#; println!( "{}", - serde_json::to_string(&parse_python_signature(code, None)?)? + serde_json::to_string(&parse_python_signature(code, None, false)?)? ); assert_eq!( - parse_python_signature(code, None)?, + parse_python_signature(code, None, false)?, MainArgSignature { star_args: false, star_kwargs: false, @@ -673,4 +689,53 @@ def main(a: list, e: List[int], b: list = [1,2,3,4], c = [1,2,3,4], d = ["a", "b Ok(()) } + + #[test] + fn test_parse_python_sig_9() -> anyhow::Result<()> { + let code = r#" +from typing import Optional +def main(a: str, b: Optional[str], c: str | None): return +"#; + println!( + "{}", + serde_json::to_string(&parse_python_signature(code, None, false)?)? + ); + assert_eq!( + parse_python_signature(code, None, false)?, + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![ + Arg { + otyp: None, + name: "a".to_string(), + typ: Typ::Str(None), + default: None, + has_default: false, + oidx: None + }, + Arg { + otyp: None, + name: "b".to_string(), + typ: Typ::Str(None), + default: None, + has_default: true, + oidx: None + }, + Arg { + otyp: None, + name: "c".to_string(), + typ: Typ::Str(None), + default: None, + has_default: true, + oidx: None + }, + ], + no_main_func: Some(false), + has_preprocessor: Some(false) + } + ); + + Ok(()) + } } diff --git a/backend/parsers/windmill-parser-sql/src/lib.rs b/backend/parsers/windmill-parser-sql/src/lib.rs index f5ddb73151..5b9e1ddd6f 100644 --- a/backend/parsers/windmill-parser-sql/src/lib.rs +++ b/backend/parsers/windmill-parser-sql/src/lib.rs @@ -16,6 +16,9 @@ use std::{ }; pub use windmill_parser::{Arg, MainArgSignature, Typ}; +pub const SANITIZED_ENUM_STR: &str = "__sanitized_enum__"; +pub const SANITIZED_RAW_STRING_STR: &str = "__sanitized_raw_string__"; + pub fn parse_mysql_sig(code: &str) -> anyhow::Result { let parsed = parse_mysql_file(&code)?; if let Some(x) = parsed { @@ -147,7 +150,7 @@ lazy_static::lazy_static! { // -- $1 name (type) = default static ref RE_ARG_MYSQL: Regex = Regex::new(r#"(?m)^-- \? (\w+) \((\w+)\)(?: ?\= ?(.+))? *(?:\r|\n|$)"#).unwrap(); - pub static ref RE_ARG_MYSQL_NAMED: Regex = Regex::new(r#"(?m)^-- :([a-z_][a-z0-9_]*) \((\w+)\)(?: ?\= ?(.+))? *(?:\r|\n|$)"#).unwrap(); + pub static ref RE_ARG_MYSQL_NAMED: Regex = Regex::new(r#"(?m)^-- :([a-z_][a-z0-9_]*) \((\w+(?:\([\w, ]+\))?)\)(?: ?\= ?(.+))? *(?:\r|\n|$)"#).unwrap(); static ref RE_ARG_PGSQL: Regex = Regex::new(r#"(?m)^-- \$(\d+) (\w+)(?: ?\= ?(.+))? *(?:\r|\n|$)"#).unwrap(); @@ -159,6 +162,9 @@ lazy_static::lazy_static! { static ref RE_ARG_MSSQL: Regex = Regex::new(r#"(?m)^-- @(?:P|p)\d+ (\w+) \((\w+)\)(?: ?\= ?(.+))? *(?:\r|\n|$)"#).unwrap(); + // used for `unsafe` sql interpolation + // -- %%name%% (type) = default + static ref RE_ARG_SQL_INTERPOLATION: Regex = Regex::new(r#"(?m)^--\s*%%([a-z_][a-z0-9_]*)%%\s*([\s\w\/]+)?(?: ?\= ?(.+))? *(?:\r|\n|$)"#).unwrap(); } fn parsed_default(parsed_typ: &Typ, default: String) -> Option { @@ -225,9 +231,34 @@ fn parse_oracledb_file(code: &str) -> anyhow::Result>> { } } + args.append(&mut parse_sql_sanitized_interpolation(code)); Ok(Some(args)) } +fn parse_sql_sanitized_interpolation(code: &str) -> Vec { + let mut args: Vec = vec![]; + + for cap in RE_ARG_SQL_INTERPOLATION.captures_iter(code) { + let name = cap.get(1).map(|x| x.as_str().to_string()).unwrap(); + let typ = cap.get(2).map(|x| x.as_str()); + let default = cap.get(3).map(|x| x.as_str().to_string()); + let has_default = default.is_some(); + let (parsed_typ, otyp) = parse_unsafe_typ(typ); + + let parsed_default = default.and_then(|x| parsed_default(&parsed_typ, x)); + args.push(Arg { + name, + typ: parsed_typ, + default: parsed_default, + otyp: Some(otyp.to_string()), + has_default, + oidx: None, + }); + } + + args +} + fn parse_mysql_file(code: &str) -> anyhow::Result>> { let mut args: Vec = vec![]; @@ -279,6 +310,7 @@ fn parse_mysql_file(code: &str) -> anyhow::Result>> { } } + args.append(&mut parse_sql_sanitized_interpolation(code)); Ok(Some(args)) } @@ -431,6 +463,7 @@ fn parse_pg_file(code: &str) -> anyhow::Result>> { } } + args.append(&mut parse_sql_sanitized_interpolation(code)); Ok(Some(args)) } @@ -485,6 +518,7 @@ fn parse_bigquery_file(code: &str) -> anyhow::Result>> { }); } + args.append(&mut parse_sql_sanitized_interpolation(code)); Ok(Some(args)) } @@ -513,6 +547,7 @@ fn parse_snowflake_file(code: &str) -> anyhow::Result>> { }); } + args.append(&mut parse_sql_sanitized_interpolation(code)); Ok(Some(args)) } @@ -541,9 +576,25 @@ fn parse_mssql_file(code: &str) -> anyhow::Result>> { }); } + args.append(&mut parse_sql_sanitized_interpolation(code)); Ok(Some(args)) } +fn parse_unsafe_typ(typ: Option<&str>) -> (Typ, &'static str) { + match typ { + Some(s) => { + let variants = s + .split("/") + .map(|x| x.trim().to_string()) + .filter(|x| !x.is_empty()) + .collect(); + + (Typ::Str(Some(variants)), SANITIZED_ENUM_STR) + } + None => (Typ::Str(None), SANITIZED_RAW_STRING_STR), + } +} + pub fn parse_mysql_typ(typ: &str) -> Typ { match typ { "varchar" | "char" | "binary" | "varbinary" | "blob" | "text" | "enum" | "set" => { @@ -1048,6 +1099,55 @@ SELECT @P2; } ); + Ok(()) + } + #[test] + fn test_parse_oracledb_sig() -> anyhow::Result<()> { + let code = r#" +-- :name1 (int) = 3 +-- :name2 (text) +-- :name4 (text) +SELECT :name, :name2; +SELECT * FROM table_name WHERE thing = :name4; +"#; + + println!("{:#?}", parse_oracledb_sig(code)?); + assert_eq!( + parse_oracledb_sig(code)?, + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![ + Arg { + otyp: Some("int".to_string()), + name: "name1".to_string(), + typ: Typ::Int, + default: Some(json!(3)), + has_default: true, + oidx: None, + }, + Arg { + otyp: Some("text".to_string()), + name: "name2".to_string(), + typ: Typ::Str(None), + default: None, + has_default: false, + oidx: None, + }, + Arg { + otyp: Some("text".to_string()), + name: "name4".to_string(), + typ: Typ::Str(None), + default: None, + has_default: false, + oidx: None, + }, + ], + no_main_func: None, + has_preprocessor: None + } + ); + Ok(()) } } diff --git a/backend/parsers/windmill-parser-ts/src/lib.rs b/backend/parsers/windmill-parser-ts/src/lib.rs index f69169c0e2..d4d34cecd6 100644 --- a/backend/parsers/windmill-parser-ts/src/lib.rs +++ b/backend/parsers/windmill-parser-ts/src/lib.rs @@ -49,8 +49,12 @@ impl Visit for ImportsFinder { pub fn parse_expr_for_imports(code: &str) -> anyhow::Result> { let cm: Lrc = Default::default(); let fm = cm.new_source_file(FileName::Custom("main.d.ts".into()).into(), code.into()); + let mut tss = TsSyntax::default(); + tss.disallow_ambiguous_jsx_like; + tss.tsx = true; + tss.no_early_errors = true; let lexer = Lexer::new( - Syntax::Typescript(TsSyntax::default()), + Syntax::Typescript(tss), // EsVersion defaults to es5 Default::default(), StringInput::from(&*fm), @@ -131,9 +135,12 @@ pub fn parse_expr_for_ids(code: &str) -> anyhow::Result> { Ok(visitor.idents.into_iter().collect()) } +/// skip_params is a micro optimization for when we just want to find the main +/// function without parsing all the params. pub fn parse_deno_signature( code: &str, skip_dflt: bool, + skip_params: bool, main_override: Option, ) -> anyhow::Result { let cm: Lrc = Default::default(); @@ -179,27 +186,26 @@ pub fn parse_deno_signature( }); let mut c: u16 = 0; - if let Some(params) = params { - let r = MainArgSignature { - star_args: false, - star_kwargs: false, - args: params - .into_iter() - .map(|x| parse_param(x, &cm, skip_dflt, &mut c)) - .collect::>>()?, - no_main_func: Some(false), - has_preprocessor: Some(has_preprocessor), - }; - Ok(r) - } else { - Ok(MainArgSignature { - star_args: false, - star_kwargs: false, - args: vec![], - no_main_func: Some(true), - has_preprocessor: Some(has_preprocessor), - }) - } + let no_main_func = params.is_none(); + let r = MainArgSignature { + star_args: false, + star_kwargs: false, + args: if skip_params { + vec![] + } else { + params + .map(|x| { + x.into_iter() + .map(|x| parse_param(x, &cm, skip_dflt, &mut c)) + .collect::>>() + }) + .transpose()? + .unwrap_or_else(|| vec![]) + }, + no_main_func: Some(no_main_func), + has_preprocessor: Some(has_preprocessor), + }; + Ok(r) } fn parse_param( diff --git a/backend/parsers/windmill-parser-wasm/.envrc b/backend/parsers/windmill-parser-wasm/.envrc new file mode 100644 index 0000000000..f7604555f4 --- /dev/null +++ b/backend/parsers/windmill-parser-wasm/.envrc @@ -0,0 +1 @@ +use flake ../../#wasm diff --git a/backend/parsers/windmill-parser-wasm/Cargo.toml b/backend/parsers/windmill-parser-wasm/Cargo.toml index c78a9da69f..5b1c884143 100644 --- a/backend/parsers/windmill-parser-wasm/Cargo.toml +++ b/backend/parsers/windmill-parser-wasm/Cargo.toml @@ -27,6 +27,8 @@ rust-parser = [ "dep:windmill-parser-rust"] graphql-parser = [ "dep:windmill-parser-graphql"] ansible-parser = [ "dep:windmill-parser-yaml"] csharp-parser = [ "dep:windmill-parser-csharp"] +nu-parser = [ "dep:windmill-parser-nu"] +java-parser = [ "dep:windmill-parser-java"] [dependencies] anyhow.workspace = true @@ -41,6 +43,8 @@ windmill-parser-graphql = { workspace = true, optional = true } windmill-parser-rust = { workspace = true, optional = true } windmill-parser-yaml = { workspace = true, optional = true } windmill-parser-csharp = { workspace = true, optional = true } +windmill-parser-nu = { workspace = true, optional = true } +windmill-parser-java = { workspace = true, optional = true } wasm-bindgen.workspace = true serde_json.workspace = true getrandom = { workspace = true, features = ["js"] } diff --git a/backend/parsers/windmill-parser-wasm/README_DEV.md b/backend/parsers/windmill-parser-wasm/README_DEV.md index ff3f866f4e..1de11be4bf 100644 --- a/backend/parsers/windmill-parser-wasm/README_DEV.md +++ b/backend/parsers/windmill-parser-wasm/README_DEV.md @@ -1,3 +1,4 @@ + ### Windmill parser wasm How to build @@ -14,7 +15,20 @@ Install wasm-pack cargo install wasm-pack ``` -#### To use it on a dev environment +Or enter nix devshell + +``` +nix develop ../../#wasm +``` + +#### Dev locally + +``` +./dev.nu +``` + + +#### Or how to use it on a dev environment manually Go to frontend and run: @@ -23,3 +37,15 @@ npm install ../backend/parsers/windmill-parser-wasm/pkg ``` Make sure to not reset the package.json before commiting + +#### Testing with docker + +Go to the root +``` +sudo docker/dev.nu up --features "," --wasm-pkg +``` + +For example to test `nu`: +``` +sudo docker/dev.nu up --features "static_frontend,nu" --wasm-pkg nu +``` diff --git a/backend/parsers/windmill-parser-wasm/build-pkgs-cli.sh b/backend/parsers/windmill-parser-wasm/build-pkgs-cli.sh new file mode 100755 index 0000000000..33c6701e52 --- /dev/null +++ b/backend/parsers/windmill-parser-wasm/build-pkgs-cli.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +set -eou pipefail + +#-# bun and deno +OUT_DIR="../../../cli/wasm/ts" +mkdir -p $OUT_DIR +wasm-pack build --release --target deno --out-dir $OUT_DIR --features "ts-parser" \ + -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort +# sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-ts"/' $OUT_DIR/package.json +rm $OUT_DIR/.gitignore + +#-# sql languages, graphql and bash/powershell, since they all use regex +OUT_DIR="../../../cli/wasm/regex" +mkdir -p $OUT_DIR +wasm-pack build --release --target deno --out-dir $OUT_DIR \ + --features "sql-parser,graphql-parser,bash-parser" \ + -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort +# sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-regex"/' $OUT_DIR/package.json +rm $OUT_DIR/.gitignore + +#-# python +OUT_DIR="../../../cli/wasm/python" +mkdir -p $OUT_DIR +wasm-pack build --release --target deno --out-dir $OUT_DIR --features "py-parser" \ + -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort +# sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-py"/' $OUT_DIR/package.json +rm $OUT_DIR/.gitignore + +#-# go +OUT_DIR="../../../cli/wasm/go" +mkdir -p $OUT_DIR +wasm-pack build --release --target deno --out-dir $OUT_DIR --features "go-parser" \ + -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort +# sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-go"/' $OUT_DIR/package.json +rm $OUT_DIR/.gitignore + +#-# php +OUT_DIR="../../../cli/wasm/php" +mkdir -p $OUT_DIR +wasm-pack build --release --target deno --out-dir $OUT_DIR --features "php-parser" \ + -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort +# sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-php"/' $OUT_DIR/package.json +rm $OUT_DIR/.gitignore + +#-# rust +OUT_DIR="../../../cli/wasm/rust" +mkdir -p $OUT_DIR +wasm-pack build --release --target deno --out-dir $OUT_DIR --features "rust-parser" \ + -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort +# sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-rust"/' $OUT_DIR/package.json +rm $OUT_DIR/.gitignore + +#-# ansible +OUT_DIR="../../../cli/wasm/yaml" +mkdir -p $OUT_DIR +wasm-pack build --release --target deno --out-dir $OUT_DIR --features "ansible-parser" \ + -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort +# sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-yaml"/' $OUT_DIR/package.json +rm $OUT_DIR/.gitignore + +#-# C# (needs some more stuff to compile C tree sitter into wasm) +OUT_DIR="../../../cli/wasm/csharp" +mkdir -p $OUT_DIR +CFLAGS_wasm32_unknown_unknown="-I$(pwd)/wasm-sysroot -Wbad-function-cast -Wcast-function-type -fno-builtin" RUSTFLAGS="-Zwasm-c-abi=spec" wasm-pack build --release --target deno --out-dir $OUT_DIR --features "csharp-parser" +# sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-csharp"/' $OUT_DIR/package.json +rm $OUT_DIR/.gitignore + +#-# Nu +OUT_DIR="../../../cli/wasm/nu" +mkdir -p $OUT_DIR +wasm-pack build --release --target deno --out-dir $OUT_DIR --features "nu-parser" \ + -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort +rm $OUT_DIR/.gitignore + +#-# Java +OUT_DIR="../../../cli/wasm/java" +mkdir -p $OUT_DIR +CFLAGS_wasm32_unknown_unknown="-I$(pwd)/wasm-sysroot -Wbad-function-cast -Wcast-function-type -fno-builtin" RUSTFLAGS="-Zwasm-c-abi=spec" wasm-pack build --release --target deno --out-dir $OUT_DIR --features "java-parser" +rm $OUT_DIR/.gitignore diff --git a/backend/parsers/windmill-parser-wasm/build-pkgs-mac.sh b/backend/parsers/windmill-parser-wasm/build-pkgs-mac.sh index dcd5a78275..109a48e9b3 100755 --- a/backend/parsers/windmill-parser-wasm/build-pkgs-mac.sh +++ b/backend/parsers/windmill-parser-wasm/build-pkgs-mac.sh @@ -1,56 +1,68 @@ #!/bin/bash set -eou pipefail -# full pkg +#-# full pkg OUT_DIR="pkg" wasm-pack build --release --target web --out-dir $OUT_DIR --all-features \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort -# bun and deno +#-# bun and deno OUT_DIR="pkg-ts" wasm-pack build --release --target web --out-dir $OUT_DIR --features "ts-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-ts"/' $OUT_DIR/package.json -# sql languages, graphql and bash/powershell, since they all use regex +#-# sql languages, graphql and bash/powershell, since they all use regex OUT_DIR="pkg-regex" wasm-pack build --release --target web --out-dir $OUT_DIR \ --features "sql-parser,graphql-parser,bash-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-regex"/' $OUT_DIR/package.json -# python +#-# python OUT_DIR="pkg-py" wasm-pack build --release --target web --out-dir $OUT_DIR --features "py-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-py"/' $OUT_DIR/package.json -# go +#-# go OUT_DIR="pkg-go" wasm-pack build --release --target web --out-dir $OUT_DIR --features "go-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-go"/' $OUT_DIR/package.json -# php +#-# php OUT_DIR="pkg-php" wasm-pack build --release --target web --out-dir $OUT_DIR --features "php-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-php"/' $OUT_DIR/package.json -# rust +#-# rust OUT_DIR="pkg-rust" wasm-pack build --release --target web --out-dir $OUT_DIR --features "rust-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-rust"/' $OUT_DIR/package.json -# ansible +#-# ansible OUT_DIR="pkg-yaml" wasm-pack build --release --target web --out-dir $OUT_DIR --features "ansible-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-yaml"/' $OUT_DIR/package.json -# C# (needs some more stuff to compile C tree sitter into wasm) +#-# C# (needs some more stuff to compile C tree sitter into wasm) # TODO: hasn't been tested on mac, might need fixing OUT_DIR="pkg-csharp" CFLAGS_wasm32_unknown_unknown="-I$(pwd)/wasm-sysroot -Wbad-function-cast -Wcast-function-type -fno-builtin" RUSTFLAGS="-Zwasm-c-abi=spec" wasm-pack build --release --target web --out-dir $OUT_DIR --features "csharp-parser" sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-csharp"/' $OUT_DIR/package.json + +#-# nu +# TODO: hasn't been tested on mac, might need fixing +OUT_DIR="pkg-nu" +wasm-pack build --release --target web --out-dir $OUT_DIR --features "nu-parser" \ + -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort +sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-nu"/' $OUT_DIR/package.json + +#-# Java (needs some more stuff to compile C tree sitter into wasm) +OUT_DIR="pkg-java" +CFLAGS_wasm32_unknown_unknown="-I$(pwd)/wasm-sysroot -Wbad-function-cast -Wcast-function-type -fno-builtin" RUSTFLAGS="-Zwasm-c-abi=spec" wasm-pack build --release --target web --out-dir $OUT_DIR --features "java-parser" +sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-java"/' $OUT_DIR/package.json diff --git a/backend/parsers/windmill-parser-wasm/build-pkgs.sh b/backend/parsers/windmill-parser-wasm/build-pkgs.sh index ec7a1f5ee4..0d492e9dcb 100755 --- a/backend/parsers/windmill-parser-wasm/build-pkgs.sh +++ b/backend/parsers/windmill-parser-wasm/build-pkgs.sh @@ -1,55 +1,66 @@ -#!/bin/bash +#!/usr/bin/env bash set -eou pipefail -# full pkg +#-# full pkg OUT_DIR="pkg" wasm-pack build --release --target web --out-dir $OUT_DIR --all-features \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort -# bun and deno +#-# bun and deno OUT_DIR="pkg-ts" wasm-pack build --release --target web --out-dir $OUT_DIR --features "ts-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-ts"/' $OUT_DIR/package.json -# sql languages, graphql and bash/powershell, since they all use regex +#-# sql languages, graphql and bash/powershell, since they all use regex OUT_DIR="pkg-regex" wasm-pack build --release --target web --out-dir $OUT_DIR \ --features "sql-parser,graphql-parser,bash-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-regex"/' $OUT_DIR/package.json -# python +#-# python OUT_DIR="pkg-py" wasm-pack build --release --target web --out-dir $OUT_DIR --features "py-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-py"/' $OUT_DIR/package.json -# go +#-# go OUT_DIR="pkg-go" wasm-pack build --release --target web --out-dir $OUT_DIR --features "go-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-go"/' $OUT_DIR/package.json -# php +#-# php OUT_DIR="pkg-php" wasm-pack build --release --target web --out-dir $OUT_DIR --features "php-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-php"/' $OUT_DIR/package.json -# rust +#-# rust OUT_DIR="pkg-rust" wasm-pack build --release --target web --out-dir $OUT_DIR --features "rust-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-rust"/' $OUT_DIR/package.json -# ansible +#-# ansible OUT_DIR="pkg-yaml" wasm-pack build --release --target web --out-dir $OUT_DIR --features "ansible-parser" \ -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-yaml"/' $OUT_DIR/package.json -# C# (needs some more stuff to compile C tree sitter into wasm) +#-# C# (needs some more stuff to compile C tree sitter into wasm) OUT_DIR="pkg-csharp" CFLAGS_wasm32_unknown_unknown="-I$(pwd)/wasm-sysroot -Wbad-function-cast -Wcast-function-type -fno-builtin" RUSTFLAGS="-Zwasm-c-abi=spec" wasm-pack build --release --target web --out-dir $OUT_DIR --features "csharp-parser" sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-csharp"/' $OUT_DIR/package.json + +#-# Nu +OUT_DIR="pkg-nu" +wasm-pack build --release --target web --out-dir $OUT_DIR --features "nu-parser" \ + -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort +sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-nu"/' $OUT_DIR/package.json + +#-# Java (needs some more stuff to compile C tree sitter into wasm) +OUT_DIR="pkg-java" +CFLAGS_wasm32_unknown_unknown="-I$(pwd)/wasm-sysroot -Wbad-function-cast -Wcast-function-type -fno-builtin" RUSTFLAGS="-Zwasm-c-abi=spec" wasm-pack build --release --target web --out-dir $OUT_DIR --features "java-parser" +sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-java"/' $OUT_DIR/package.json diff --git a/backend/parsers/windmill-parser-wasm/build.sh b/backend/parsers/windmill-parser-wasm/build.sh deleted file mode 100755 index b872e2c912..0000000000 --- a/backend/parsers/windmill-parser-wasm/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -set -eou pipefail - -deno task wasmbuild --out ../../../cli/wasm/ -p windmill-parser-wasm --all-features diff --git a/backend/parsers/windmill-parser-wasm/deno.json b/backend/parsers/windmill-parser-wasm/deno.json index abdd5e39d1..1887a74f76 100644 --- a/backend/parsers/windmill-parser-wasm/deno.json +++ b/backend/parsers/windmill-parser-wasm/deno.json @@ -1,5 +1,5 @@ { "tasks": { - "wasmbuild": "deno run -A jsr:@deno/wasmbuild@0.17.2" + "wasmbuild": "deno run -A jsr:@deno/wasmbuild@0.19.0" } } diff --git a/backend/parsers/windmill-parser-wasm/dev.nu b/backend/parsers/windmill-parser-wasm/dev.nu new file mode 100755 index 0000000000..e80f3c302f --- /dev/null +++ b/backend/parsers/windmill-parser-wasm/dev.nu @@ -0,0 +1,25 @@ +#!/usr/bin/env nu + +# Build in debug mode specified lang parser to wasm +# and perform installation to frontend +def "main" [ + lang: string # Example: nu + --release(-r) +] { + let out_dir = $'pkg-($lang)' + if $release { + open build-pkgs.sh + | split row '#-' + | find $out_dir + | bash -c $"RUST_LOG=trace ($in.0)" + } else { + open build-pkgs.sh + | split row '#-' + | find $out_dir + | str replace "--release" "--no-opt" + | bash -c $"WASM_OPT=-Oz ($in.0)" + } + ( + cd ../../../frontend; npm install ../backend/parsers/windmill-parser-wasm/($out_dir) + ) +} diff --git a/backend/parsers/windmill-parser-wasm/flake.lock b/backend/parsers/windmill-parser-wasm/flake.lock deleted file mode 100644 index c8733c1921..0000000000 --- a/backend/parsers/windmill-parser-wasm/flake.lock +++ /dev/null @@ -1,95 +0,0 @@ -{ - "nodes": { - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1737885589, - "narHash": "sha256-Zf0hSrtzaM1DEz8//+Xs51k/wdSajticVrATqDrfQjg=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "852ff1d9e153d8875a83602e03fdef8a63f0ecf8", - "type": "github" - }, - "original": { - "id": "nixpkgs", - "ref": "nixos-unstable", - "type": "indirect" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1736320768, - "narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "4bc9c909d9ac828a039f288cf872d16d38185db8", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay" - } - }, - "rust-overlay": { - "inputs": { - "nixpkgs": "nixpkgs_2" - }, - "locked": { - "lastModified": 1738117527, - "narHash": "sha256-GFviGfaezjGLFUlxdv3zyC7rSZvTXqwcG/YsF6MDkOw=", - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "6a3dc6ce4132bd57359214d986db376f2333c14d", - "type": "github" - }, - "original": { - "owner": "oxalica", - "repo": "rust-overlay", - "type": "github" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/backend/parsers/windmill-parser-wasm/flake.nix b/backend/parsers/windmill-parser-wasm/flake.nix deleted file mode 100644 index ac97d8c80a..0000000000 --- a/backend/parsers/windmill-parser-wasm/flake.nix +++ /dev/null @@ -1,37 +0,0 @@ -{ - inputs = { - nixpkgs.url = "nixpkgs/nixos-unstable"; - flake-utils.url = "github:numtide/flake-utils"; - rust-overlay.url = "github:oxalica/rust-overlay"; - }; - - outputs = { - nixpkgs, - flake-utils, - rust-overlay, - ... - }: - flake-utils.lib.eachDefaultSystem (system: let - pkgs = import nixpkgs { - inherit system; - overlays = [(import rust-overlay)]; - }; - rust = pkgs.rust-bin.nightly.latest.default.override { - extensions = [ - "rust-src" - ]; - targets = ["wasm32-unknown-unknown"]; - }; - in { - devShell = pkgs.mkShell { - buildInputs = with pkgs; [ - rust - nodejs - wasm-pack - sccache - ]; - RUSTC_WRAPPER = "${pkgs.sccache}/bin/sccache"; - CARGO_PATH = "${rust}/bin/cargo"; - }; - }); -} diff --git a/backend/parsers/windmill-parser-wasm/publish-pkgs.sh b/backend/parsers/windmill-parser-wasm/publish-pkgs.sh index a4af8bb684..4180853b8b 100755 --- a/backend/parsers/windmill-parser-wasm/publish-pkgs.sh +++ b/backend/parsers/windmill-parser-wasm/publish-pkgs.sh @@ -27,3 +27,6 @@ popd pushd "pkg-csharp" && npm publish ${args} popd + +pushd "pkg-nu" && npm publish ${args} +popd diff --git a/backend/parsers/windmill-parser-wasm/src/lib.rs b/backend/parsers/windmill-parser-wasm/src/lib.rs index e3de739f96..2f4bc83c1f 100644 --- a/backend/parsers/windmill-parser-wasm/src/lib.rs +++ b/backend/parsers/windmill-parser-wasm/src/lib.rs @@ -17,10 +17,11 @@ fn wrap_sig(r: anyhow::Result) -> String { #[cfg(feature = "ts-parser")] #[wasm_bindgen] -pub fn parse_deno(code: &str, main_override: Option) -> String { +pub fn parse_deno(code: &str, main_override: Option, skip_params: Option) -> String { wrap_sig(windmill_parser_ts::parse_deno_signature( code, false, + false, main_override, )) } @@ -73,6 +74,7 @@ pub fn parse_python(code: &str, main_override: Option) -> String { wrap_sig(windmill_parser_py::parse_python_signature( code, main_override, + false, )) } @@ -147,3 +149,17 @@ pub fn parse_ansible(code: &str) -> String { pub fn parse_csharp(code: &str) -> String { wrap_sig(windmill_parser_csharp::parse_csharp_signature(code)) } + +#[cfg(feature = "nu-parser")] +#[wasm_bindgen] +pub fn parse_nu(code: &str) -> String { + wrap_sig(windmill_parser_nu::parse_nu_signature(code)) +} + +#[cfg(feature = "java-parser")] +#[wasm_bindgen] +pub fn parse_java(code: &str) -> String { + wrap_sig(windmill_parser_java::parse_java_signature(code)) +} + +// for related places search: ADD_NEW_LANG diff --git a/backend/parsers/windmill-parser-wasm/tests/wasm.rs b/backend/parsers/windmill-parser-wasm/tests/wasm.rs index 1c201ecd4e..e8dc1e986b 100644 --- a/backend/parsers/windmill-parser-wasm/tests/wasm.rs +++ b/backend/parsers/windmill-parser-wasm/tests/wasm.rs @@ -4,6 +4,7 @@ use windmill_parser::{Arg, MainArgSignature, ObjectProperty, Typ}; use windmill_parser_bash::parse_powershell_sig; use windmill_parser_ts::{parse_deno_signature, parse_expr_for_ids, parse_expr_for_imports}; +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_deno_sig() -> anyhow::Result<()> { let code = " @@ -18,7 +19,7 @@ export function main(test1?: string, test2: string = \"burkina\", } "; assert_eq!( - parse_deno_signature(code, false, None)?, + parse_deno_signature(code, false, false, None)?, MainArgSignature { star_args: false, star_kwargs: false, @@ -146,6 +147,8 @@ export function main(test1?: string, test2: string = \"burkina\", Ok(()) } + +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_deno_sig_implicit_types() -> anyhow::Result<()> { let code = " @@ -159,7 +162,7 @@ export function main(test2 = \"burkina\", } "; assert_eq!( - parse_deno_signature(code, false, None)?, + parse_deno_signature(code, false, false, None)?, MainArgSignature { star_args: false, star_kwargs: false, @@ -224,6 +227,7 @@ export function main(test2 = \"burkina\", Ok(()) } +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_deno_types() -> anyhow::Result<()> { let code = " @@ -236,7 +240,7 @@ export function main(foo: FooBar, {a, b}: FooBar, {c, d}: FooBar = {a: \"foo\", } "; assert_eq!( - parse_deno_signature(code, false, None)?, + parse_deno_signature(code, false, false, None)?, MainArgSignature { star_args: false, star_kwargs: false, @@ -274,6 +278,7 @@ export function main(foo: FooBar, {a, b}: FooBar, {c, d}: FooBar = {a: \"foo\", Ok(()) } +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_enum_list() -> anyhow::Result<()> { let code = " @@ -282,7 +287,7 @@ export function main(foo: (\"foo\" | \"bar\")[]) { } "; assert_eq!( - parse_deno_signature(code, false, None)?, + parse_deno_signature(code, false, false, None)?, MainArgSignature { star_args: false, star_kwargs: false, @@ -305,6 +310,7 @@ export function main(foo: (\"foo\" | \"bar\")[]) { Ok(()) } +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_extract_ident() -> anyhow::Result<()> { let code = " @@ -324,6 +330,7 @@ fn test_parse_extract_ident() -> anyhow::Result<()> { Ok(()) } +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_imports() -> anyhow::Result<()> { let code = " @@ -347,6 +354,7 @@ fn test_parse_imports() -> anyhow::Result<()> { Ok(()) } +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_imports_dts() -> anyhow::Result<()> { let code = " @@ -359,6 +367,7 @@ export type foo = number Ok(()) } +#[allow(dead_code)] #[wasm_bindgen_test] fn test_parse_powershell_sig() -> anyhow::Result<()> { let code = " diff --git a/backend/parsers/windmill-parser-yaml/src/lib.rs b/backend/parsers/windmill-parser-yaml/src/lib.rs index c2d417eb6c..06adac1ee3 100644 --- a/backend/parsers/windmill-parser-yaml/src/lib.rs +++ b/backend/parsers/windmill-parser-yaml/src/lib.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use anyhow::anyhow; use serde_json::json; use windmill_parser::{Arg, MainArgSignature, ObjectProperty, Typ}; @@ -208,15 +210,52 @@ pub struct AnsibleInventory { resource_type: Option, pub pinned_resource: Option, } + +#[derive(Debug, Clone)] +pub struct GitRepo { + pub url: String, + pub commit: Option, + pub branch: Option, + pub target_path: String, +} + #[derive(Debug, Clone)] pub struct AnsibleRequirements { pub python_reqs: Vec, - pub collections: Option, + pub roles_and_collections: Option, pub file_resources: Vec, pub inventories: Vec, pub vars: Vec<(String, String)>, pub resources: Vec<(String, String)>, pub options: AnsiblePlaybookOptions, + pub vault_password: Option, + pub vault_id: Vec, + pub git_repos: Vec, + pub git_ssh_identity: Vec, +} + +impl Default for AnsibleRequirements { + fn default() -> Self { + Self { + python_reqs: vec![], + roles_and_collections: None, + file_resources: vec![], + inventories: vec![], + vars: vec![], + resources: vec![], + options: AnsiblePlaybookOptions { + verbosity: None, + forks: None, + timeout: None, + flush_cache: None, + force_handlers: None, + }, + vault_password: None, + vault_id: vec![], + git_repos: vec![], + git_ssh_identity: vec![], + } + } } fn parse_inventories(inventory_yaml: &Yaml) -> anyhow::Result> { @@ -275,22 +314,7 @@ pub fn parse_ansible_reqs( return Ok((logs, None, inner_content.to_string())); } - let opts = AnsiblePlaybookOptions { - verbosity: None, - forks: None, - timeout: None, - flush_cache: None, - force_handlers: None, - }; - let mut ret = AnsibleRequirements { - python_reqs: vec![], - collections: None, - file_resources: vec![], - inventories: vec![], - vars: vec![], - resources: vec![], - options: opts, - }; + let mut ret = AnsibleRequirements::default(); if let Yaml::Hash(doc) = &docs[0] { for (key, value) in doc { @@ -303,7 +327,7 @@ pub fn parse_ansible_reqs( let mut out_str = String::new(); let mut emitter = YamlEmitter::new(&mut out_str); emitter.dump(galaxy_requirements)?; - ret.collections = Some(out_str); + ret.roles_and_collections = Some(out_str); } if let Some(Yaml::Array(py_reqs)) = deps.get(&Yaml::String("python".to_string())) @@ -345,11 +369,60 @@ pub fn parse_ansible_reqs( Yaml::String(key) if key == "inventory" => { ret.inventories = parse_inventories(value)?; } + Yaml::String(key) if key == "vault_password" => { + let Yaml::String(filename) = value else { + return Err(anyhow!( + "Vault Password File expects a String containing the file name" + )); + }; + ret.vault_password = Some(filename.to_string()); + } + Yaml::String(key) if key == "vault_id" => { + let Yaml::Array(filenames) = value else { + return Err(anyhow!("Vault ID field expects an array of strings in the format: `label@filename`")); + }; + + for f in filenames { + let Yaml::String(filename) = f else { + return Err(anyhow!("The elements of the vault_id field should be strings in the format: `label@filename`")); + }; + ret.vault_id.push(filename.to_string()); + } + } Yaml::String(key) if key == "options" => { if let Yaml::Array(opts) = &value { ret.options = parse_ansible_options(opts); } } + Yaml::String(key) if key == "git_repos" => { + let Yaml::Array(repos) = &value else { + return Err(anyhow!("git_repos field expects an array of repos")); + }; + + for r in repos { + ret.git_repos.push( + parse_git_repo(r) + .map_err(|e| anyhow!("Failed to parse git repo: {e}"))?, + ); + } + } + Yaml::String(key) if key == "git_ssh_identity" => { + let Yaml::Array(indentities) = &value else { + return Err(anyhow!( + "git_ssh_identity expects an array of windmill variables (or secrets) containing ssh IDs" + )); + }; + + for r in indentities { + let Yaml::String(file_name) = r else { + return Err(anyhow!( + "Git ssh identity file must be a string path to a Windmill variable/secret" + )); + }; + + ret.git_ssh_identity.push(file_name.clone()); + } + } Yaml::String(key) => logs.push_str(&format!("\nUnknown field `{}`. Ignoring", key)), _ => (), } @@ -364,6 +437,38 @@ pub fn parse_ansible_reqs( Ok((logs, Some(ret), out_str)) } +fn parse_git_repo(r: &Yaml) -> anyhow::Result { + let Yaml::Hash(repo) = r else { + return Err(anyhow!("Should be a Map")); + }; + + let url = repo + .get(&Yaml::String("url".to_string())) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()) + .ok_or(anyhow!("Expected `url` field"))?; + + let target_path = repo + .get(&Yaml::String("target".to_string())) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()) + .ok_or(anyhow!( + "Expected `target` field (target directory for cloning the repo)" + ))?; + + let branch = repo + .get(&Yaml::String("branch".to_string())) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + + let commit = repo + .get(&Yaml::String("commit".to_string())) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + + Ok(GitRepo { url, commit, branch, target_path }) +} + fn parse_ansible_options(opts: &Vec) -> AnsiblePlaybookOptions { let mut ret = AnsiblePlaybookOptions { verbosity: None, @@ -529,3 +634,78 @@ fn yaml_to_json(yaml: &Yaml) -> serde_json::Value { _ => serde_json::Value::Null, } } + +fn update_versions( + section: &str, + yaml: &mut Yaml, + versions: &HashMap, +) -> anyhow::Result { + let mut logs = String::new(); + + let Yaml::Hash(ref mut m) = yaml else { + return Err(anyhow!("{section} dependency should be a map")); + }; + + if let Some(Yaml::Array(elements)) = m.get_mut(&Yaml::String(section.to_string())) { + for el in elements { + let Yaml::Hash(ref mut h) = el else { + return Err(anyhow!("{section} dependency element should be a map")); + }; + + if let Some(name) = h + .get(&Yaml::String("name".to_string())) + .and_then(|n| n.as_str()) + { + if let Some(version) = versions.get(name) { + h.insert( + Yaml::String("version".to_string()), + Yaml::String(version.to_string()), + ); + } else { + logs.push_str(&format!("WARNING: {section} dependency `{name}` has no locked version, using the latest or system installed version.\n")); + } + } else { + return Err(anyhow!( + "{section} dependency element: missing or invalid `name` field" + )); + } + } + } + + Ok(logs) +} + +pub fn add_versions_to_requirements_yaml( + input: &str, + role_versions: &HashMap, + collection_versions: &HashMap, +) -> anyhow::Result<(String,String)> { + let mut docs = + YamlLoader::load_from_str(input).map_err(|e| anyhow!("YAML parse error: {}", e))?; + let doc = &mut docs[0]; + + let mut logs = String::new(); + + logs.push_str( + &update_versions("roles", doc, role_versions) + .map_err(|e| anyhow!("Error updating role versions: {e}"))?, + ); + logs.push_str( + &update_versions("collections", doc, collection_versions) + .map_err(|e| anyhow!("Error updating collection versions: {e}"))?, + ); + + if !logs.is_empty() { + logs.push_str("WARNING: You might want to try adding manual versions for these, otherwise there could be breaking changes on deployed scripts\n"); + } + + let mut out_str = String::new(); + { + let mut emitter = YamlEmitter::new(&mut out_str); + emitter + .dump(doc) + .map_err(|e| anyhow!("YAML emit error: {}", e))?; + } + + Ok((out_str, logs)) +} diff --git a/backend/parsers/windmill-parser/src/lib.rs b/backend/parsers/windmill-parser/src/lib.rs index 6f793d0cc7..617b906154 100644 --- a/backend/parsers/windmill-parser/src/lib.rs +++ b/backend/parsers/windmill-parser/src/lib.rs @@ -10,7 +10,7 @@ use convert_case::{Boundary, Case, Casing}; use serde::Serialize; use serde_json::Value; -#[derive(Serialize, Debug, PartialEq)] +#[derive(Serialize, Debug, PartialEq, Default)] pub struct MainArgSignature { pub star_args: bool, pub star_kwargs: bool, diff --git a/backend/plot2.py b/backend/plot2.py new file mode 100644 index 0000000000..5a6624bc14 --- /dev/null +++ b/backend/plot2.py @@ -0,0 +1,33 @@ +import json +import matplotlib.pyplot as plt + +# Path to the profiling JSON file +# file_path = "/tmp/windmill/profiling_main.json" +file_path = "/tmp/profiling.json" + +# Load the JSON data +with open(file_path, "r") as f: + data = json.load(f) + +# Extract timings for "pre pull->post pull" +pre_post_pull_timings = [ + timing / 1000000.0 for entry in data["timings"] + for step, timing in entry["timings"] + # if step == "pre pull->post pull" + if step == "->job pulled from DB" +] + +# Plotting the distribution +plt.figure(figsize=(10, 6)) +# plt.hist(pre_post_pull_timings, bins=10, edgecolor='black') +plt.scatter(range(len(pre_post_pull_timings)), pre_post_pull_timings, + alpha=1.0, # Transparency level + s=40) # Size of the dots`) +plt.title("Distribution of 'pre pull->post pull' timings") +# plt.xlabel("Time (ms)") +# plt.ylabel("Frequency") +plt.xlabel("Sample Index") +plt.ylabel("Time (ms)") +plt.grid(True) +plt.tight_layout() +plt.show() \ No newline at end of file diff --git a/backend/src/main.rs b/backend/src/main.rs index 3a1899c23b..e6dc72be80 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -8,20 +8,22 @@ use anyhow::Context; use monitor::{ - load_base_url, load_otel, reload_delete_logs_periodically_setting, reload_indexer_config, - reload_instance_python_version_setting, reload_nuget_config_setting, + load_base_url, load_otel, reload_critical_alerts_on_db_oversize, + reload_delete_logs_periodically_setting, reload_indexer_config, + reload_instance_python_version_setting, reload_maven_repos_setting, + reload_no_default_maven_setting, reload_nuget_config_setting, reload_timeout_wait_result_setting, send_current_log_file_to_object_store, - send_logs_to_object_store, + send_logs_to_object_store, WORKERS_NAMES, }; use rand::Rng; -use sqlx::{postgres::PgListener, Pool, Postgres}; +use sqlx::postgres::PgListener; use std::{ collections::HashMap, fs::{create_dir_all, DirBuilder}, net::{IpAddr, Ipv4Addr, SocketAddr}, - time::Duration, + time::{Duration, Instant}, }; -use tokio::{fs::File, io::AsyncReadExt}; +use tokio::{fs::File, io::AsyncReadExt, task::JoinHandle}; use uuid::Uuid; use windmill_api::HTTP_CLIENT; @@ -29,24 +31,30 @@ use windmill_api::HTTP_CLIENT; use windmill_common::ee::{maybe_renew_license_key_on_start, LICENSE_KEY_ID, LICENSE_KEY_VALID}; use windmill_common::{ + agent_workers::build_agent_http_client, + get_database_url, global_settings::{ - BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING, - CRITICAL_ERROR_CHANNELS_SETTING, CUSTOM_TAGS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING, - DEFAULT_TAGS_WORKSPACES_SETTING, EMAIL_DOMAIN_SETTING, ENV_SETTINGS, - EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING, - HUB_BASE_URL_SETTING, INDEXER_SETTING, INSTANCE_PYTHON_VERSION_SETTING, - JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, - LICENSE_KEY_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NPM_CONFIG_REGISTRY_SETTING, - NUGET_CONFIG_SETTING, OAUTH_SETTING, OTEL_SETTING, PIP_INDEX_URL_SETTING, - REQUEST_SIZE_LIMIT_SETTING, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, - RETENTION_PERIOD_SECS_SETTING, SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, SMTP_SETTING, - TEAMS_SETTING, TIMEOUT_WAIT_RESULT_SETTING, + BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING, + CRITICAL_ALERT_MUTE_UI_SETTING, CRITICAL_ERROR_CHANNELS_SETTING, CUSTOM_TAGS_SETTING, + DEFAULT_TAGS_PER_WORKSPACE_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING, EMAIL_DOMAIN_SETTING, + ENV_SETTINGS, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, + EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, INDEXER_SETTING, + INSTANCE_PYTHON_VERSION_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, + KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, MAVEN_REPOS_SETTING, + MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NO_DEFAULT_MAVEN_SETTING, + NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, OAUTH_SETTING, OTEL_SETTING, + PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, + REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING, + SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, SMTP_SETTING, TEAMS_SETTING, + TIMEOUT_WAIT_RESULT_SETTING, }, scripts::ScriptLang, stats_ee::schedule_stats, - utils::{hostname, rd_string, Mode, GIT_VERSION}, - worker::{reload_custom_tags_setting, HUB_CACHE_DIR, TMP_DIR, TMP_LOGS_DIR, WORKER_GROUP}, - DB, METRICS_ENABLED, + utils::{hostname, rd_string, Mode, GIT_VERSION, MODE_AND_ADDONS}, + worker::{ + reload_custom_tags_setting, Connection, HUB_CACHE_DIR, TMP_DIR, TMP_LOGS_DIR, WORKER_GROUP, + }, + KillpillSender, METRICS_ENABLED, }; #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] @@ -59,19 +67,15 @@ use tikv_jemallocator::Jemalloc; #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; -#[cfg(feature = "enterprise")] -use windmill_common::METRICS_ADDR; - #[cfg(feature = "parquet")] use windmill_common::global_settings::OBJECT_STORE_CACHE_CONFIG_SETTING; use windmill_worker::{ - get_hub_script_content_and_requirements, BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR, - BUN_DEPSTAR_CACHE_DIR, CSHARP_CACHE_DIR, DENO_CACHE_DIR, DENO_CACHE_DIR_DEPS, - DENO_CACHE_DIR_NPM, GO_BIN_CACHE_DIR, GO_CACHE_DIR, LOCK_CACHE_DIR, PIP_CACHE_DIR, - POWERSHELL_CACHE_DIR, PY310_CACHE_DIR, PY311_CACHE_DIR, PY312_CACHE_DIR, PY313_CACHE_DIR, - RUST_CACHE_DIR, TAR_PIP_CACHE_DIR, TAR_PY310_CACHE_DIR, TAR_PY311_CACHE_DIR, - TAR_PY312_CACHE_DIR, TAR_PY313_CACHE_DIR, UV_CACHE_DIR, + get_hub_script_content_and_requirements, BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR, CSHARP_CACHE_DIR, + DENO_CACHE_DIR, DENO_CACHE_DIR_DEPS, DENO_CACHE_DIR_NPM, GO_BIN_CACHE_DIR, GO_CACHE_DIR, + JAVA_CACHE_DIR, NU_CACHE_DIR, POWERSHELL_CACHE_DIR, PY310_CACHE_DIR, PY311_CACHE_DIR, + PY312_CACHE_DIR, PY313_CACHE_DIR, RUST_CACHE_DIR, TAR_JAVA_CACHE_DIR, TAR_PY310_CACHE_DIR, + TAR_PY311_CACHE_DIR, TAR_PY312_CACHE_DIR, TAR_PY313_CACHE_DIR, UV_CACHE_DIR, }; use crate::monitor::{ @@ -96,6 +100,31 @@ const DEFAULT_SERVER_BIND_ADDR: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0); mod ee; mod monitor; +pub fn setup_deno_runtime() -> anyhow::Result<()> { + // https://github.com/denoland/deno/blob/main/cli/main.rs#L477 + #[cfg(feature = "deno_core")] + let unrecognized_v8_flags = deno_core::v8_set_flags(vec![ + "--stack-size=1024".to_string(), + // TODO(bartlomieju): I think this can be removed as it's handled by `deno_core` + // and its settings. + // deno_ast removes TypeScript `assert` keywords, so this flag only affects JavaScript + // TODO(petamoriken): Need to check TypeScript `assert` keywords in deno_ast + "--no-harmony-import-assertions".to_string(), + ]) + .into_iter() + .skip(1) + .collect::>(); + + #[cfg(feature = "deno_core")] + if !unrecognized_v8_flags.is_empty() { + println!("Unrecognized V8 flags: {:?}", unrecognized_v8_flags); + } + + #[cfg(feature = "deno_core")] + deno_core::JsRuntime::init_platform(None, false); + Ok(()) +} + #[inline(always)] fn create_and_run_current_thread_inner(future: F) -> R where @@ -118,9 +147,15 @@ where rt.block_on(future) } +lazy_static::lazy_static! { + static ref PG_LISTENER_REFRESH_PERIOD_SECS: u64 = std::env::var("PG_LISTENER_REFRESH_PERIOD_SECS") + .ok() + .and_then(|x| x.parse::().ok()) + .unwrap_or(3600 * 12); +} + pub fn main() -> anyhow::Result<()> { - #[cfg(feature = "deno_core")] - deno_core::JsRuntime::init_platform(None, false); + setup_deno_runtime()?; create_and_run_current_thread_inner(windmill_main()) } @@ -221,65 +256,20 @@ async fn windmill_main() -> anyhow::Result<()> { std::env::set_var("RUST_LOG", "info") } + if let Err(_e) = rustls::crypto::ring::default_provider().install_default() { + tracing::error!("Failed to install rustls crypto provider"); + } + let hostname = hostname(); - let mut enable_standalone_indexer: bool = false; + let mode_and_addons = MODE_AND_ADDONS.clone(); + let mode = mode_and_addons.mode; - let mode = std::env::var("MODE") - .map(|x| x.to_lowercase()) - .map(|x| { - if &x == "server" { - println!("Binary is in 'server' mode"); - Mode::Server - } else if &x == "worker" { - tracing::info!("Binary is in 'worker' mode"); - #[cfg(windows)] - { - println!("It is highly recommended to use the agent mode instead on windows (MODE=agent) and to pass a BASE_INTERNAL_URL"); - } - Mode::Worker - } else if &x == "agent" { - println!("Binary is in 'agent' mode"); - if std::env::var("BASE_INTERNAL_URL").is_err() { - panic!("BASE_INTERNAL_URL is required in agent mode") - } - if std::env::var("JOB_TOKEN").is_err() { - println!("JOB_TOKEN is not passed, hence workers will still need to create permissions for each job and the DATABASE_URL needs to be of a role that can INSERT into the job_perms table") - } - - #[cfg(not(feature = "enterprise"))] - { - panic!("Agent mode is only available in the EE, ignoring..."); - } - #[cfg(feature = "enterprise")] - Mode::Agent - } else if &x == "indexer" { - tracing::info!("Binary is in 'indexer' mode"); - #[cfg(not(feature = "tantivy"))] - { - eprintln!("Cannot start the indexer because tantivy is not included in this binary/image. Make sure you are using the EE image if you want to access the full text search features."); - panic!("Indexer mode requires compiling with the tantivy feature flag."); - } - #[cfg(feature = "tantivy")] - Mode::Indexer - } else if &x == "standalone+search"{ - enable_standalone_indexer = true; - println!("Binary is in 'standalone' mode with search enabled"); - Mode::Standalone - } - else { - if &x != "standalone" { - eprintln!("mode not recognized, defaulting to standalone: {x}"); - } else { - println!("Binary is in 'standalone' mode"); - } - Mode::Standalone - } - }) - .unwrap_or_else(|_| { - tracing::info!("Mode not specified, defaulting to standalone"); - Mode::Standalone - }); + if mode == Mode::Standalone { + println!("Running in standalone mode"); + } else if mode == Mode::MCP { + println!("Running in MCP mode"); + } #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] println!("jemalloc enabled"); @@ -311,7 +301,7 @@ async fn windmill_main() -> anyhow::Result<()> { } #[allow(unused_mut)] - let mut num_workers = if mode == Mode::Server || mode == Mode::Indexer { + let mut num_workers = if mode == Mode::Server || mode == Mode::Indexer || mode == Mode::MCP { 0 } else { std::env::var("NUM_WORKERS") @@ -320,7 +310,7 @@ async fn windmill_main() -> anyhow::Result<()> { .unwrap_or(DEFAULT_NUM_WORKERS as i32) }; - if num_workers > 1 { + if num_workers > 1 && !std::env::var("WORKER_GROUP").is_ok_and(|x| x == "native") { println!( "We STRONGLY recommend using at most 1 worker per container, use at your own risks" ); @@ -333,8 +323,9 @@ async fn windmill_main() -> anyhow::Result<()> { && (mode == Mode::Server || mode == Mode::Standalone); let indexer_mode = mode == Mode::Indexer; + let mcp_mode = mode == Mode::MCP; - let server_bind_address: IpAddr = if server_mode || indexer_mode { + let server_bind_address: IpAddr = if server_mode || indexer_mode || mcp_mode { std::env::var("SERVER_BIND_ADDR") .ok() .and_then(|x| x.parse().ok()) @@ -343,14 +334,37 @@ async fn windmill_main() -> anyhow::Result<()> { IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)) }; - println!("Connecting to database..."); - let db = windmill_common::connect_db(server_mode, indexer_mode).await?; + let (conn, first_suffix) = if mode == Mode::Agent { + tracing::info!( + "Creating http client for cluster using base internal url {}", + std::env::var("BASE_INTERNAL_URL").unwrap_or_default() + ); + let suffix = windmill_common::utils::worker_suffix(&hostname, &rd_string(5)); + ( + Connection::Http(build_agent_http_client(&suffix)), + Some(suffix), + ) + } else { + println!("Connecting to database..."); - load_otel(&db).await; + let db = windmill_common::initial_connection().await?; - tracing::info!("Database connected"); + let num_version = sqlx::query_scalar!("SELECT version()").fetch_one(&db).await; - let environment = load_base_url(&db) + tracing::info!( + "PostgreSQL version: {} (windmill require PG >= 14)", + num_version + .ok() + .flatten() + .unwrap_or_else(|| "UNKNOWN".to_string()) + ); + load_otel(&db).await; + + tracing::info!("Database connected"); + (Connection::Sql(db), None) + }; + + let environment = load_base_url(&conn) .await .unwrap_or_else(|_| "local".to_string()) .trim_start_matches("https://") @@ -362,37 +376,40 @@ async fn windmill_main() -> anyhow::Result<()> { let _guard = windmill_common::tracing_init::initialize_tracing(&hostname, &mode, &environment); - let num_version = sqlx::query_scalar!("SELECT version()").fetch_one(&db).await; - - tracing::info!( - "PostgreSQL version: {} (windmill require PG >= 14)", - num_version - .ok() - .flatten() - .unwrap_or_else(|| "UNKNOWN".to_string()) - ); - let is_agent = mode == Mode::Agent; + let mut migration_handle: Option> = None; #[cfg(feature = "parquet")] let disable_s3_store = std::env::var("DISABLE_S3_STORE") .ok() .is_some_and(|x| x == "1" || x == "true"); - if !is_agent { - let skip_migration = std::env::var("SKIP_MIGRATION") - .map(|val| val == "true") - .unwrap_or(false); + if let Some(db) = conn.as_sql() { + if !is_agent && !indexer_mode && !mcp_mode { + let skip_migration = std::env::var("SKIP_MIGRATION") + .map(|val| val == "true") + .unwrap_or(false); - if !skip_migration { - // migration code to avoid break - windmill_api::migrate_db(&db).await?; - } else { - tracing::info!("SKIP_MIGRATION set, skipping db migration...") + if !skip_migration { + // migration code to avoid break + migration_handle = windmill_api::migrate_db(&db).await?; + } else { + tracing::info!("SKIP_MIGRATION set, skipping db migration...") + } } } - let (killpill_tx, mut killpill_rx) = tokio::sync::broadcast::channel::<()>(2); + let worker_mode = num_workers > 0; + + let conn = if mode == Mode::Agent { + conn + } else { + // This time we use a pool of connections + let db = windmill_common::connect_db(server_mode, indexer_mode, worker_mode).await?; + Connection::Sql(db) + }; + + let (killpill_tx, mut killpill_rx) = KillpillSender::new(2); let mut monitor_killpill_rx = killpill_tx.subscribe(); let (killpill_phase2_tx, _killpill_phase2_rx) = tokio::sync::broadcast::channel::<()>(2); let server_killpill_rx = killpill_phase2_tx.subscribe(); @@ -418,49 +435,43 @@ Windmill Community Edition {GIT_VERSION} display_config(&ENV_SETTINGS); - if let Err(e) = reload_base_url_setting(&db).await { - tracing::error!("Error loading base url: {:?}", e) - } - - if let Err(e) = reload_critical_error_channels_setting(&db).await { - tracing::error!("Could loading critical error emails setting: {:?}", e); - } - #[cfg(feature = "enterprise")] { // load the license key and check if it's valid // if not valid and not server mode just quit // if not expired and server mode then force renewal // if key still invalid and num_workers > 0, set to 0 - if let Err(err) = reload_license_key(&db).await { + if let Err(err) = reload_license_key(&conn).await { tracing::error!("Failed to reload license key: {err:#}"); } let valid_key = *LICENSE_KEY_VALID.read().await; if !valid_key && !server_mode { tracing::error!("Invalid license key, workers require a valid license key"); } - if server_mode { - // only force renewal if invalid but not empty (= expired) - let renewed_now = maybe_renew_license_key_on_start( - &HTTP_CLIENT, - &db, - !valid_key && !LICENSE_KEY_ID.read().await.is_empty(), - ) - .await; - if renewed_now { - if let Err(err) = reload_license_key(&db).await { - tracing::error!("Failed to reload license key: {err:#}"); + if server_mode || mcp_mode { + if let Some(db) = conn.as_sql() { + // only force renewal if invalid but not empty (= expired) + let renewed_now = maybe_renew_license_key_on_start( + &HTTP_CLIENT, + &db, + !valid_key && !LICENSE_KEY_ID.read().await.is_empty(), + ) + .await; + if renewed_now { + if let Err(err) = reload_license_key(&conn).await { + tracing::error!("Failed to reload license key: {err:#}"); + } } + } else { + panic!("Server mode requires a database connection"); } } } - let worker_mode = num_workers > 0; - - if server_mode || worker_mode || indexer_mode { + if server_mode || worker_mode || indexer_mode || mcp_mode { let port_var = std::env::var("PORT").ok().and_then(|x| x.parse().ok()); - let port = if server_mode || indexer_mode { + let port = if server_mode || indexer_mode || mcp_mode { port_var.unwrap_or(DEFAULT_PORT as u16) } else { port_var.unwrap_or(0) @@ -480,7 +491,7 @@ Windmill Community Edition {GIT_VERSION} }; initial_load( - &db, + &conn, killpill_tx.clone(), worker_mode, server_mode, @@ -490,7 +501,7 @@ Windmill Community Edition {GIT_VERSION} .await; monitor_db( - &db, + &conn, &base_internal_url, server_mode, worker_mode, @@ -500,9 +511,11 @@ Windmill Community Edition {GIT_VERSION} .await; #[cfg(feature = "prometheus")] - crate::monitor::monitor_pool(&db).await; + if let Some(db) = conn.as_sql() { + crate::monitor::monitor_pool(&db).await; + } - send_logs_to_object_store(&db, &hostname, &mode); + send_logs_to_object_store(&conn, &hostname, &mode); #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] if !worker_mode { @@ -519,17 +532,22 @@ Windmill Community Edition {GIT_VERSION} .expect("could not create initial server dir"); #[cfg(feature = "tantivy")] - let should_index_jobs = - mode == Mode::Indexer || (enable_standalone_indexer && mode == Mode::Standalone); + let should_index_jobs = mode == Mode::Indexer || mode_and_addons.indexer; - reload_indexer_config(&db).await; + #[cfg(feature = "tantivy")] + if should_index_jobs { + if let Some(db) = conn.as_sql() { + reload_indexer_config(&db).await; + } + } #[cfg(feature = "tantivy")] let (index_reader, index_writer) = if should_index_jobs { - let mut indexer_rx = killpill_rx.resubscribe(); + if let Some(db) = conn.as_sql() { + let mut indexer_rx = killpill_rx.resubscribe(); - let (mut reader, mut writer) = (None, None); - tokio::select! { + let (mut reader, mut writer) = (None, None); + tokio::select! { _ = indexer_rx.recv() => { tracing::info!("Received killpill, aborting index initialization"); }, @@ -539,8 +557,11 @@ Windmill Community Edition {GIT_VERSION} writer = Some(res.1); } + } + (reader, writer) + } else { + (None, None) } - (reader, writer) } else { (None, None) }; @@ -550,13 +571,15 @@ Windmill Community Edition {GIT_VERSION} let indexer_rx = killpill_rx.resubscribe(); let index_writer2 = index_writer.clone(); async { - if let Some(index_writer) = index_writer2 { - windmill_indexer::completed_runs_ee::run_indexer( - db.clone(), - index_writer, - indexer_rx, - ) - .await?; + if let Some(db) = conn.as_sql() { + if let Some(index_writer) = index_writer2 { + windmill_indexer::completed_runs_ee::run_indexer( + db.clone(), + index_writer, + indexer_rx, + ) + .await?; + } } Ok(()) } @@ -564,21 +587,25 @@ Windmill Community Edition {GIT_VERSION} #[cfg(all(feature = "tantivy", feature = "parquet"))] let (log_index_reader, log_index_writer) = if should_index_jobs { - let mut indexer_rx = killpill_rx.resubscribe(); + if let Some(db) = conn.as_sql() { + let mut indexer_rx = killpill_rx.resubscribe(); + + let (mut reader, mut writer) = (None, None); + tokio::select! { + _ = indexer_rx.recv() => { + tracing::info!("Received killpill, aborting index initialization"); + }, + res = windmill_indexer::service_logs_ee::init_index(&db, killpill_tx.clone()) => { + let res = res?; + reader = Some(res.0); + writer = Some(res.1); + } - let (mut reader, mut writer) = (None, None); - tokio::select! { - _ = indexer_rx.recv() => { - tracing::info!("Received killpill, aborting index initialization"); - }, - res = windmill_indexer::service_logs_ee::init_index(&db, killpill_tx.clone()) => { - let res = res?; - reader = Some(res.0); - writer = Some(res.1); } - + (reader, writer) + } else { + (None, None) } - (reader, writer) } else { (None, None) }; @@ -588,13 +615,15 @@ Windmill Community Edition {GIT_VERSION} let log_indexer_rx = killpill_rx.resubscribe(); let log_index_writer2 = log_index_writer.clone(); async { - if let Some(log_index_writer) = log_index_writer2 { - windmill_indexer::service_logs_ee::run_indexer( - db.clone(), - log_index_writer, - log_indexer_rx, - ) - .await?; + if let Some(db) = conn.as_sql() { + if let Some(log_index_writer) = log_index_writer2 { + windmill_indexer::service_logs_ee::run_indexer( + db.clone(), + log_index_writer, + log_indexer_rx, + ) + .await?; + } } Ok(()) } @@ -614,18 +643,20 @@ Windmill Community Edition {GIT_VERSION} let server_f = async { if !is_agent { - windmill_api::run_server( - db.clone(), - index_reader, - log_index_reader, - addr, - server_killpill_rx, - base_internal_tx, - server_mode, - #[cfg(feature = "smtp")] - base_internal_url.clone(), - ) - .await?; + if let Some(db) = conn.as_sql() { + windmill_api::run_server( + db.clone(), + index_reader, + log_index_reader, + addr, + server_killpill_rx, + base_internal_tx, + server_mode, + mcp_mode, + base_internal_url.clone(), + ) + .await?; + } } else { base_internal_tx .send(base_internal_url.clone()) @@ -642,18 +673,38 @@ Windmill Community Edition {GIT_VERSION} if !killpill_rx.try_recv().is_ok() { let base_internal_url = base_internal_rx.await?; if worker_mode { + let mut workers = vec![]; + for i in 0..num_workers { + let suffix: String = if i == 0 && first_suffix.as_ref().is_some() { + first_suffix.as_ref().unwrap().clone() + } else { + windmill_common::utils::worker_suffix(&hostname, &rd_string(5)) + }; + let worker_conn = WorkerConn { + conn: if i == 0 || mode != Mode::Agent { + conn.clone() + } else { + Connection::Http(build_agent_http_client(&suffix)) + }, + worker_name: windmill_common::utils::worker_name_with_suffix( + mode == Mode::Agent, + WORKER_GROUP.as_str(), + &suffix, + ), + }; + workers.push(worker_conn); + } + run_workers( - db.clone(), rx, killpill_tx.clone(), - num_workers, base_internal_url.clone(), - is_agent, hostname.clone(), + &workers, ) .await?; tracing::info!("All workers exited."); - killpill_tx.send(())?; + killpill_tx.send(); } else { rx.recv().await?; } @@ -671,269 +722,379 @@ Windmill Community Edition {GIT_VERSION} }; let monitor_f = async { - let db = db.clone(); let tx = killpill_tx.clone(); - - let base_internal_url = base_internal_url.to_string(); - let h = tokio::spawn(async move { - let mut listener = retry_listen_pg(&db).await; - - loop { - tokio::select! { - biased; - _ = monitor_killpill_rx.recv() => { - tracing::info!("received killpill for monitor job"); - break; - }, - _ = tokio::time::sleep(Duration::from_secs(30)) => { - monitor_db( - &db, - &base_internal_url, - server_mode, - worker_mode, - false, - tx.clone(), - ) - .await; - }, - notification = listener.recv() => { - match notification { - Ok(n) => { - tracing::info!("Received new pg notification: {n:?}"); - match n.channel() { - "notify_config_change" => { - match n.payload() { - "server" if server_mode => { - tracing::error!("Server config change detected but server config is obsolete: {}", n.payload()); + let conn = conn.clone(); + match conn { + Connection::Sql(ref db) => { + let base_internal_url = base_internal_url.to_string(); + let db_url: String = get_database_url().await?; + let db = db.clone(); + let h = tokio::spawn(async move { + let mut listener = retry_listen_pg(&db_url).await; + let mut last_listener_refresh = Instant::now(); + loop { + let db = db.clone(); + tokio::select! { + biased; + Some(_) = async { if let Some(jh) = migration_handle.take() { + tracing::info!("migration job finished"); + Some(jh.await) + } else { + None + }} => { + continue; + }, + _ = monitor_killpill_rx.recv() => { + tracing::info!("received killpill for monitor job"); + break; + }, + notification = listener.try_recv() => { + match notification { + Ok(n) => { + if n.is_none() { + tracing::error!("Could not receive notification, attempting to reconnect to pg listener"); + continue; + } + let n = n.unwrap(); + tracing::info!("Received new pg notification: {n:?}"); + match n.channel() { + "notify_config_change" => { + match n.payload() { + "server" if server_mode => { + tracing::error!("Server config change detected but server config is obsolete: {}", n.payload()); + }, + a@ _ if worker_mode && a == format!("worker__{}", *WORKER_GROUP) => { + tracing::info!("Worker config change detected: {}", n.payload()); + reload_worker_config(&db, tx.clone(), true).await; + }, + _ => { + tracing::debug!("config changed but did not target this server/worker"); + } + } }, - a@ _ if worker_mode && a == format!("worker__{}", *WORKER_GROUP) => { - tracing::info!("Worker config change detected: {}", n.payload()); - reload_worker_config(&db, tx.clone(), true).await; + "notify_webhook_change" => { + let workspace_id = n.payload(); + tracing::info!("Webhook change detected, invalidating webhook cache: {}", workspace_id); + windmill_api::webhook_util::WEBHOOK_CACHE.remove(workspace_id); + }, + "notify_workspace_envs_change" => { + let workspace_id = n.payload(); + tracing::info!("Workspace envs change detected, invalidating workspace envs cache: {}", workspace_id); + windmill_common::variables::CUSTOM_ENVS_CACHE.remove(workspace_id); + }, + "notify_workspace_premium_change" => { + let workspace_id = n.payload(); + tracing::info!("Workspace premium change detected, invalidating workspace premium cache: {}", workspace_id); + windmill_common::workspaces::IS_PREMIUM_CACHE.remove(workspace_id); + }, + "notify_global_setting_change" => { + tracing::info!("Global setting change detected: {}", n.payload()); + match n.payload() { + BASE_URL_SETTING => { + if let Err(e) = reload_base_url_setting(&conn).await { + tracing::error!(error = %e, "Could not reload base url setting"); + } + }, + OAUTH_SETTING => { + if let Err(e) = reload_base_url_setting(&conn).await { + tracing::error!(error = %e, "Could not reload oauth setting"); + } + }, + CUSTOM_TAGS_SETTING => { + if let Err(e) = reload_custom_tags_setting(&db).await { + tracing::error!(error = %e, "Could not reload custom tags setting"); + } + }, + LICENSE_KEY_SETTING => { + if let Err(e) = reload_license_key(&db.into()).await { + tracing::error!("Failed to reload license key: {e:#}"); + } + }, + DEFAULT_TAGS_PER_WORKSPACE_SETTING => { + if let Err(e) = load_tag_per_workspace_enabled(&db).await { + tracing::error!("Error loading default tag per workspace: {e:#}"); + } + }, + DEFAULT_TAGS_WORKSPACES_SETTING => { + if let Err(e) = load_tag_per_workspace_workspaces(&db).await { + tracing::error!("Error loading default tag per workspace workspaces: {e:#}"); + } + } + SMTP_SETTING => { + reload_smtp_config(&db).await; + }, + TEAMS_SETTING => { + tracing::info!("Teams setting changed."); + }, + INDEXER_SETTING => { + reload_indexer_config(&db).await; + }, + TIMEOUT_WAIT_RESULT_SETTING => { + reload_timeout_wait_result_setting(&conn).await + }, + RETENTION_PERIOD_SECS_SETTING => { + reload_retention_period_setting(&conn).await + }, + MONITOR_LOGS_ON_OBJECT_STORE_SETTING => { + reload_delete_logs_periodically_setting(&conn).await + }, + JOB_DEFAULT_TIMEOUT_SECS_SETTING => { + reload_job_default_timeout_setting(&conn).await + }, + #[cfg(feature = "parquet")] + OBJECT_STORE_CACHE_CONFIG_SETTING => { + if !disable_s3_store { + reload_s3_cache_setting(&db).await + } + }, + SCIM_TOKEN_SETTING => { + reload_scim_token_setting(&conn).await + }, + EXTRA_PIP_INDEX_URL_SETTING => { + reload_extra_pip_index_url_setting(&conn).await + }, + PIP_INDEX_URL_SETTING => { + reload_pip_index_url_setting(&conn).await + }, + INSTANCE_PYTHON_VERSION_SETTING => { + reload_instance_python_version_setting(&conn).await + }, + NPM_CONFIG_REGISTRY_SETTING => { + reload_npm_config_registry_setting(&conn).await + }, + BUNFIG_INSTALL_SCOPES_SETTING => { + reload_bunfig_install_scopes_setting(&conn).await + }, + NUGET_CONFIG_SETTING => { + reload_nuget_config_setting(&conn).await + }, + MAVEN_REPOS_SETTING => { + reload_maven_repos_setting(&conn).await + }, + NO_DEFAULT_MAVEN_SETTING => { + reload_no_default_maven_setting(&conn).await + }, + KEEP_JOB_DIR_SETTING => { + load_keep_job_dir(&conn).await; + }, + REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING => { + load_require_preexisting_user(&db).await; + }, + EXPOSE_METRICS_SETTING => { + tracing::info!("Metrics setting changed, restarting"); + send_delayed_killpill(&tx, 40, "metrics setting change").await; + }, + EMAIL_DOMAIN_SETTING => { + tracing::info!("Email domain setting changed"); + if server_mode { + send_delayed_killpill(&tx, 4, "email domain setting change").await; + } + }, + EXPOSE_DEBUG_METRICS_SETTING => { + if let Err(e) = load_metrics_debug_enabled(&conn).await { + tracing::error!(error = %e, "Could not reload debug metrics setting"); + } + }, + OTEL_SETTING => { + tracing::info!("OTEL setting changed, restarting"); + send_delayed_killpill(&tx, 4, "OTEL setting change").await; + }, + REQUEST_SIZE_LIMIT_SETTING => { + if server_mode { + tracing::info!("Request limit size change detected, killing server expecting to be restarted"); + send_delayed_killpill(&tx, 4, "request size limit change").await; + } + }, + SAML_METADATA_SETTING => { + tracing::info!("SAML metadata change detected, killing server expecting to be restarted"); + send_delayed_killpill(&tx, 0, "SAML metadata change").await; + }, + HUB_BASE_URL_SETTING => { + if let Err(e) = reload_hub_base_url_setting(&conn, server_mode).await { + tracing::error!(error = %e, "Could not reload hub base url setting"); + } + }, + CRITICAL_ERROR_CHANNELS_SETTING => { + if let Err(e) = reload_critical_error_channels_setting(&db).await { + tracing::error!(error = %e, "Could not reload critical error emails setting"); + } + }, + CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING => { + if let Err(e) = reload_critical_alerts_on_db_oversize(&db).await { + tracing::error!(error = %e, "Could not reload critical alerts on db oversize setting"); + } + + }, + JWT_SECRET_SETTING => { + if let Err(e) = reload_jwt_secret_setting(&db).await { + tracing::error!(error = %e, "Could not reload jwt secret setting"); + } + }, + CRITICAL_ALERT_MUTE_UI_SETTING => { + tracing::info!("Critical alert UI setting changed"); + if let Err(e) = reload_critical_alert_mute_ui_setting(&conn).await { + tracing::error!(error = %e, "Could not reload critical alert UI setting"); + } + }, + + a @_ => { + tracing::info!("Unrecognized Global Setting Change Payload: {:?}", a); + } + } }, _ => { - tracing::debug!("config changed but did not target this server/worker"); + tracing::warn!("Unknown notification received"); + continue; } } }, - "notify_global_setting_change" => { - tracing::info!("Global setting change detected: {}", n.payload()); - match n.payload() { - BASE_URL_SETTING => { - if let Err(e) = reload_base_url_setting(&db).await { - tracing::error!(error = %e, "Could not reload base url setting"); - } + Err(e) => { + tracing::error!(error = %e, "Could not receive notification, attempting to reconnect listener"); + tokio::select! { + biased; + _ = monitor_killpill_rx.recv() => { + tracing::info!("received killpill for monitor job"); + break; }, - OAUTH_SETTING => { - if let Err(e) = reload_base_url_setting(&db).await { - tracing::error!(error = %e, "Could not reload oauth setting"); - } - }, - CUSTOM_TAGS_SETTING => { - if let Err(e) = reload_custom_tags_setting(&db).await { - tracing::error!(error = %e, "Could not reload custom tags setting"); - } - }, - LICENSE_KEY_SETTING => { - if let Err(e) = reload_license_key(&db).await { - tracing::error!("Failed to reload license key: {e:#}"); - } - }, - DEFAULT_TAGS_PER_WORKSPACE_SETTING => { - if let Err(e) = load_tag_per_workspace_enabled(&db).await { - tracing::error!("Error loading default tag per workspace: {e:#}"); - } - }, - DEFAULT_TAGS_WORKSPACES_SETTING => { - if let Err(e) = load_tag_per_workspace_workspaces(&db).await { - tracing::error!("Error loading default tag per workspace workspaces: {e:#}"); - } - } - SMTP_SETTING => { - reload_smtp_config(&db).await; - }, - TEAMS_SETTING => { - tracing::info!("Teams setting changed."); - }, - INDEXER_SETTING => { - reload_indexer_config(&db).await; - }, - TIMEOUT_WAIT_RESULT_SETTING => { - reload_timeout_wait_result_setting(&db).await - }, - RETENTION_PERIOD_SECS_SETTING => { - reload_retention_period_setting(&db).await - }, - MONITOR_LOGS_ON_OBJECT_STORE_SETTING => { - reload_delete_logs_periodically_setting(&db).await - }, - JOB_DEFAULT_TIMEOUT_SECS_SETTING => { - reload_job_default_timeout_setting(&db).await - }, - #[cfg(feature = "parquet")] - OBJECT_STORE_CACHE_CONFIG_SETTING => { - if !disable_s3_store { - reload_s3_cache_setting(&db).await - } - }, - SCIM_TOKEN_SETTING => { - reload_scim_token_setting(&db).await - }, - EXTRA_PIP_INDEX_URL_SETTING => { - reload_extra_pip_index_url_setting(&db).await - }, - PIP_INDEX_URL_SETTING => { - reload_pip_index_url_setting(&db).await - }, - INSTANCE_PYTHON_VERSION_SETTING => { - reload_instance_python_version_setting(&db).await - }, - NPM_CONFIG_REGISTRY_SETTING => { - reload_npm_config_registry_setting(&db).await - }, - BUNFIG_INSTALL_SCOPES_SETTING => { - reload_bunfig_install_scopes_setting(&db).await - }, - NUGET_CONFIG_SETTING => { - reload_nuget_config_setting(&db).await - }, - KEEP_JOB_DIR_SETTING => { - load_keep_job_dir(&db).await; - }, - REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING => { - load_require_preexisting_user(&db).await; - }, - EXPOSE_METRICS_SETTING => { - tracing::info!("Metrics setting changed, restarting"); - send_delayed_killpill(&tx, 40, "metrics setting change").await; - }, - EMAIL_DOMAIN_SETTING => { - tracing::info!("Email domain setting changed"); - if server_mode { - send_delayed_killpill(&tx, 4, "email domain setting change").await; - } - }, - EXPOSE_DEBUG_METRICS_SETTING => { - if let Err(e) = load_metrics_debug_enabled(&db).await { - tracing::error!(error = %e, "Could not reload debug metrics setting"); - } - }, - OTEL_SETTING => { - tracing::info!("OTEL setting changed, restarting"); - send_delayed_killpill(&tx, 4, "OTEL setting change").await; - }, - REQUEST_SIZE_LIMIT_SETTING => { - if server_mode { - tracing::info!("Request limit size change detected, killing server expecting to be restarted"); - send_delayed_killpill(&tx, 4, "request size limit change").await; - } - }, - SAML_METADATA_SETTING => { - tracing::info!("SAML metadata change detected, killing server expecting to be restarted"); - send_delayed_killpill(&tx, 0, "SAML metadata change").await; - }, - HUB_BASE_URL_SETTING => { - if let Err(e) = reload_hub_base_url_setting(&db, server_mode).await { - tracing::error!(error = %e, "Could not reload hub base url setting"); - } - }, - CRITICAL_ERROR_CHANNELS_SETTING => { - if let Err(e) = reload_critical_error_channels_setting(&db).await { - tracing::error!(error = %e, "Could not reload critical error emails setting"); - } - }, - JWT_SECRET_SETTING => { - if let Err(e) = reload_jwt_secret_setting(&db).await { - tracing::error!(error = %e, "Could not reload jwt secret setting"); - } - }, - CRITICAL_ALERT_MUTE_UI_SETTING => { - tracing::info!("Critical alert UI setting changed"); - if let Err(e) = reload_critical_alert_mute_ui_setting(&db).await { - tracing::error!(error = %e, "Could not reload critical alert UI setting"); - } - }, - a @_ => { - tracing::info!("Unrecognized Global Setting Change Payload: {:?}", a); + new_listener = retry_listen_pg(&db_url) => { + listener = new_listener; + continue; } } - }, - _ => { - tracing::warn!("Unknown notification received"); - continue; } + }; + }, + _ = tokio::time::sleep(Duration::from_secs(30)) => { + if last_listener_refresh.elapsed() > Duration::from_secs(*PG_LISTENER_REFRESH_PERIOD_SECS) { + tracing::info!("Refreshing pg listeners, settings and license key after {}s", Duration::from_secs(*PG_LISTENER_REFRESH_PERIOD_SECS).as_secs()); + if let Err(e) = listener.unlisten_all().await { + tracing::error!(error = %e, "Could not unlisten to database"); + } + listener = retry_listen_pg(&db_url).await; + initial_load( + &conn, + tx.clone(), + worker_mode, + server_mode, + #[cfg(feature = "parquet")] + disable_s3_store, + ) + .await; + #[cfg(feature = "enterprise")] + if let Err(err) = reload_license_key(&conn).await { + tracing::error!("Failed to reload license key: {err:#}"); + } + last_listener_refresh = Instant::now(); + } + + if server_mode { + tracing::info!("monitor task started"); + } + monitor_db( + &conn, + &base_internal_url, + server_mode, + worker_mode, + false, + tx.clone(), + ) + .await; + if server_mode { + tracing::info!("monitor task finished"); } }, - Err(e) => { - tracing::error!(error = %e, "Could not receive notification, attempting to reconnect listener"); - tokio::select! { - biased; - _ = monitor_killpill_rx.recv() => { - tracing::info!("received killpill for monitor job"); - break; - }, - new_listener = retry_listen_pg(&db) => { - listener = new_listener; - continue; - } - } - } - }; + } } + }); + + if let Err(e) = h.await { + tracing::error!("Error waiting for monitor handle: {e:#}") } } - }); + Connection::Http(_) => loop { + tokio::select! { + _ = monitor_killpill_rx.recv() => { + tracing::info!("Received killpill, exiting"); + break; + }, + _ = tokio::time::sleep(Duration::from_secs(12 * 60 * 60)) => { + tracing::info!("Reloading config after 12 hours"); + initial_load(&conn, tx.clone(), worker_mode, server_mode, #[cfg(feature = "parquet")] disable_s3_store).await; + #[cfg(feature = "enterprise")] + ee::verify_license_key().await; + } + } + }, + }; - if let Err(e) = h.await { - tracing::error!("Error waiting for monitor handle: {e:#}") - } tracing::info!("Monitor exited"); + killpill_tx.send(); Ok(()) as anyhow::Result<()> }; let metrics_f = async { - if METRICS_ENABLED.load(std::sync::atomic::Ordering::Relaxed) { - #[cfg(not(feature = "enterprise"))] - tracing::error!("Metrics are only available in the EE, ignoring..."); + let enabled = METRICS_ENABLED.load(std::sync::atomic::Ordering::Relaxed); - #[cfg(feature = "enterprise")] - windmill_common::serve_metrics(*METRICS_ADDR, _killpill_phase2_rx, num_workers > 0) - .await; + #[cfg(not(all(feature = "enterprise", feature = "prometheus")))] + if enabled { + tracing::error!("Metrics are only available in the EE, ignoring..."); } + + #[cfg(all(feature = "enterprise", feature = "prometheus"))] + if let Err(e) = windmill_common::serve_metrics( + *windmill_common::METRICS_ADDR, + _killpill_phase2_rx, + num_workers > 0, + enabled, + ) + .await + { + tracing::error!("Error serving metrics: {e:#}"); + } + Ok(()) as anyhow::Result<()> }; if server_mode { - schedule_stats(&db, &HTTP_CLIENT).await; + if let Some(db) = conn.as_sql() { + schedule_stats(&db, &HTTP_CLIENT).await; + } } - futures::try_join!( - shutdown_signal, - workers_f, - monitor_f, - server_f, - metrics_f, - indexer_f, - log_indexer_f - )?; + if mcp_mode { + futures::try_join!(shutdown_signal, workers_f, server_f)?; + } else { + futures::try_join!( + shutdown_signal, + workers_f, + monitor_f, + server_f, + metrics_f, + indexer_f, + log_indexer_f + )?; + } } else { tracing::info!("Nothing to do, exiting."); } - send_current_log_file_to_object_store(&db, &hostname, &mode).await; + send_current_log_file_to_object_store(&conn, &hostname, &mode).await; - tracing::info!("Exiting connection pool"); - tokio::select! { - _ = db.close() => { - tracing::info!("Database connection pool closed"); - }, - _ = tokio::time::sleep(Duration::from_secs(15)) => { - tracing::warn!("Could not close database connection pool in time (15s). Exiting anyway."); + if let Some(db) = conn.as_sql() { + tracing::info!("Exiting connection pool"); + tokio::select! { + _ = db.close() => { + tracing::info!("Database connection pool closed"); + }, + _ = tokio::time::sleep(Duration::from_secs(15)) => { + tracing::warn!("Could not close database connection pool in time (15s). Exiting anyway."); + } } } Ok(()) } -async fn listen_pg(db: &DB) -> Option { - let mut listener = match PgListener::connect_with(&db).await { +async fn listen_pg(url: &str) -> Option { + let mut listener = match PgListener::connect(url).await { Ok(l) => l, Err(e) => { tracing::error!(error = %e, "Could not connect to database"); @@ -941,10 +1102,17 @@ async fn listen_pg(db: &DB) -> Option { } }; - if let Err(e) = listener - .listen_all(vec!["notify_config_change", "notify_global_setting_change"]) - .await - { + #[allow(unused_mut)] + let mut channels = vec![ + "notify_config_change", + "notify_global_setting_change", + "notify_webhook_change", + "notify_workspace_envs_change", + ]; + #[cfg(feature = "cloud")] + channels.push("notify_workspace_premium_change"); + + if let Err(e) = listener.listen_all(channels).await { tracing::error!(error = %e, "Could not listen to database"); return None; } @@ -952,13 +1120,13 @@ async fn listen_pg(db: &DB) -> Option { return Some(listener); } -async fn retry_listen_pg(db: &DB) -> PgListener { - let mut listener = listen_pg(db).await; +async fn retry_listen_pg(url: &str) -> PgListener { + let mut listener = listen_pg(url).await; loop { if listener.is_none() { tracing::info!("Retrying listening to pg listen in 5 seconds"); tokio::time::sleep(Duration::from_secs(5)).await; - listener = listen_pg(db).await; + listener = listen_pg(url).await; } else { tracing::info!("Successfully connected to pg listen"); return listener.unwrap(); @@ -983,16 +1151,20 @@ fn display_config(envs: &[&str]) { ) } +pub struct WorkerConn { + conn: Connection, + worker_name: String, +} + pub async fn run_workers( - db: Pool, mut rx: tokio::sync::broadcast::Receiver<()>, - tx: tokio::sync::broadcast::Sender<()>, - num_workers: i32, + tx: KillpillSender, base_internal_url: String, - agent_mode: bool, hostname: String, + workers: &[WorkerConn], ) -> anyhow::Result<()> { let mut killpill_rxs = vec![]; + let num_workers = workers.len(); for _ in 0..num_workers { killpill_rxs.push(rx.resubscribe()); } @@ -1001,14 +1173,6 @@ pub async fn run_workers( tracing::info!("Received killpill, exiting"); return Ok(()); } - let instance_name = hostname - .clone() - .replace(" ", "") - .split("-") - .last() - .unwrap() - .to_ascii_lowercase() - .to_string(); // #[cfg(tokio_unstable)] // let monitor = tokio_metrics::TaskMonitor::new(); @@ -1023,10 +1187,8 @@ pub async fn run_workers( let mut handles = Vec::with_capacity(num_workers as usize); for x in [ - LOCK_CACHE_DIR, TMP_LOGS_DIR, UV_CACHE_DIR, - TAR_PIP_CACHE_DIR, DENO_CACHE_DIR, DENO_CACHE_DIR_DEPS, DENO_CACHE_DIR_NPM, @@ -1039,15 +1201,16 @@ pub async fn run_workers( TAR_PY311_CACHE_DIR, TAR_PY312_CACHE_DIR, TAR_PY313_CACHE_DIR, - PIP_CACHE_DIR, - BUN_DEPSTAR_CACHE_DIR, BUN_BUNDLE_CACHE_DIR, GO_CACHE_DIR, GO_BIN_CACHE_DIR, RUST_CACHE_DIR, CSHARP_CACHE_DIR, + NU_CACHE_DIR, HUB_CACHE_DIR, POWERSHELL_CACHE_DIR, + JAVA_CACHE_DIR, + TAR_JAVA_CACHE_DIR, // for related places search: ADD_NEW_LANG ] { DirBuilder::new() .recursive(true) @@ -1059,10 +1222,12 @@ pub async fn run_workers( "Starting {num_workers} workers and SLEEP_QUEUE={}ms", *windmill_worker::SLEEP_QUEUE ); + for i in 1..(num_workers + 1) { - let db1 = db.clone(); - let instance_name = instance_name.clone(); - let worker_name = format!("wk-{}-{}-{}", *WORKER_GROUP, &instance_name, rd_string(5)); + let wk_conf = &workers[i as usize - 1]; + let conn1 = wk_conf.conn.clone(); + let worker_name = wk_conf.worker_name.clone(); + WORKERS_NAMES.write().await.push(worker_name.clone()); let ip = ip.clone(); let rx = killpill_rxs.pop().unwrap(); let tx = tx.clone(); @@ -1075,7 +1240,7 @@ pub async fn run_workers( } let f = windmill_worker::run_worker( - &db1, + &conn1, &hostname, worker_name, i as u64, @@ -1084,7 +1249,6 @@ pub async fn run_workers( rx, tx, &base_internal_url, - agent_mode, ); // #[cfg(tokio_unstable)] @@ -1103,17 +1267,14 @@ pub async fn run_workers( Ok(()) } -async fn send_delayed_killpill( - tx: &tokio::sync::broadcast::Sender<()>, - max_delay_secs: u64, - context: &str, -) { +async fn send_delayed_killpill(tx: &KillpillSender, mut max_delay_secs: u64, context: &str) { + if max_delay_secs == 0 { + max_delay_secs = 1; + } // Random delay to avoid all servers/workers shutting down simultaneously let rd_delay = rand::rng().random_range(0..max_delay_secs); tracing::info!("Scheduling {context} shutdown in {rd_delay}s"); tokio::time::sleep(Duration::from_secs(rd_delay)).await; - if let Err(e) = tx.send(()) { - tracing::error!(error = %e, "Could not send killpill for {context}"); - } + tx.send(); } diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index cb57d94622..233007c67f 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -6,14 +6,14 @@ use std::{ str::FromStr, sync::{ atomic::{AtomicU16, Ordering}, - Arc, + Arc, Mutex, }, time::Duration, }; use chrono::{NaiveDateTime, Utc}; use futures::{stream::FuturesUnordered, StreamExt}; -use serde::{de::DeserializeOwned, Deserializer}; +use serde::{de::DeserializeOwned, Deserialize}; use sqlx::{Pool, Postgres}; use tokio::{ join, @@ -30,47 +30,54 @@ use windmill_api::{ #[cfg(feature = "enterprise")] use windmill_common::ee::{jobs_waiting_alerts, worker_groups_alerts}; +#[cfg(feature = "enterprise")] +use windmill_common::ee::low_disk_alerts; #[cfg(feature = "oauth2")] use windmill_common::global_settings::OAUTH_SETTING; use windmill_common::{ - auth::JWT_SECRET, + agent_workers::DECODED_AGENT_TOKEN, + auth::create_token_for_owner, ee::CriticalErrorChannel, error, flow_status::{FlowStatus, FlowStatusModule}, global_settings::{ - BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING, - CRITICAL_ERROR_CHANNELS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING, - DEFAULT_TAGS_WORKSPACES_SETTING, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, - EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, INSTANCE_PYTHON_VERSION_SETTING, - JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, - LICENSE_KEY_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NPM_CONFIG_REGISTRY_SETTING, - NUGET_CONFIG_SETTING, OTEL_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, + BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING, + CRITICAL_ALERT_MUTE_UI_SETTING, CRITICAL_ERROR_CHANNELS_SETTING, + DEFAULT_TAGS_PER_WORKSPACE_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING, + EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING, + HUB_BASE_URL_SETTING, INSTANCE_PYTHON_VERSION_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, + JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, + MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, + OTEL_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING, SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, TIMEOUT_WAIT_RESULT_SETTING, }, indexer::load_indexer_config, jobs::QueuedJob, + jwt::JWT_SECRET, oauth2::REQUIRE_PREEXISTING_USER_FOR_OAUTH, server::load_smtp_config, tracing_init::JSON_FMT, users::truncate_token, + utils::empty_string_as_none, utils::{now_from_db, rd_string, report_critical_error, Mode}, worker::{ - load_worker_config, make_pull_query, make_suspended_pull_query, reload_custom_tags_setting, - update_min_version, DEFAULT_TAGS_PER_WORKSPACE, DEFAULT_TAGS_WORKSPACES, INDEXER_CONFIG, - SMTP_CONFIG, TMP_DIR, WORKER_CONFIG, WORKER_GROUP, + load_worker_config, reload_custom_tags_setting, store_pull_query, + store_suspended_pull_query, update_min_version, Connection, DEFAULT_TAGS_PER_WORKSPACE, + DEFAULT_TAGS_WORKSPACES, INDEXER_CONFIG, SCRIPT_TOKEN_EXPIRY, SMTP_CONFIG, TMP_DIR, + WORKER_CONFIG, WORKER_GROUP, }, - BASE_URL, CRITICAL_ALERT_MUTE_UI_ENABLED, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, - HUB_BASE_URL, JOB_RETENTION_SECS, METRICS_DEBUG_ENABLED, METRICS_ENABLED, - MONITOR_LOGS_ON_OBJECT_STORE, OTEL_LOGS_ENABLED, OTEL_METRICS_ENABLED, OTEL_TRACING_ENABLED, - SERVICE_LOG_RETENTION_SECS, + KillpillSender, BASE_URL, CRITICAL_ALERTS_ON_DB_OVERSIZE, CRITICAL_ALERT_MUTE_UI_ENABLED, + CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, JOB_RETENTION_SECS, + METRICS_DEBUG_ENABLED, METRICS_ENABLED, MONITOR_LOGS_ON_OBJECT_STORE, OTEL_LOGS_ENABLED, + OTEL_METRICS_ENABLED, OTEL_TRACING_ENABLED, SERVICE_LOG_RETENTION_SECS, }; -use windmill_queue::cancel_job; +use windmill_queue::{cancel_job, MiniPulledJob, SameWorkerPayload}; use windmill_worker::{ - create_token_for_owner, handle_job_error, AuthedClient, SameWorkerPayload, SameWorkerSender, - SendResult, BUNFIG_INSTALL_SCOPES, INSTANCE_PYTHON_VERSION, JOB_DEFAULT_TIMEOUT, KEEP_JOB_DIR, - NPM_CONFIG_REGISTRY, NUGET_CONFIG, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL, SCRIPT_TOKEN_EXPIRY, + handle_job_error, AuthedClient, JobCompletedSender, SameWorkerSender, BUNFIG_INSTALL_SCOPES, + INSTANCE_PYTHON_VERSION, JOB_DEFAULT_TIMEOUT, KEEP_JOB_DIR, MAVEN_REPOS, NO_DEFAULT_MAVEN, + NPM_CONFIG_REGISTRY, NUGET_CONFIG, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL, }; #[cfg(feature = "parquet")] @@ -125,87 +132,133 @@ lazy_static::lazy_static! { .and_then(|x| x.parse::().ok()) .unwrap_or(true); + pub static ref DISABLE_ZOMBIE_JOBS_MONITORING: bool = std::env::var("DISABLE_ZOMBIE_JOBS_MONITORING") + .ok() + .and_then(|x| x.parse::().ok()) + .unwrap_or(false); + pub static ref WORKERS_NAMES: Arc>> = Arc::new(RwLock::new(Vec::new())); static ref QUEUE_COUNT_TAGS: Arc>> = Arc::new(RwLock::new(Vec::new())); + static ref DISABLE_CONCURRENCY_LIMIT: bool = std::env::var("DISABLE_CONCURRENCY_LIMIT").is_ok_and(|s| s == "true"); } pub async fn initial_load( - db: &Pool, - tx: tokio::sync::broadcast::Sender<()>, + conn: &Connection, + tx: KillpillSender, worker_mode: bool, server_mode: bool, #[cfg(feature = "parquet")] disable_s3_store: bool, ) { - if let Err(e) = load_metrics_enabled(db).await { + if let Err(e) = reload_base_url_setting(&conn).await { + tracing::error!("Error loading base url: {:?}", e) + } + + if let Some(db) = conn.as_sql() { + if let Err(e) = reload_critical_error_channels_setting(&db).await { + tracing::error!("Could loading critical error emails setting: {:?}", e); + } + } + + if let Err(e) = load_metrics_enabled(conn).await { tracing::error!("Error loading expose metrics: {e:#}"); } - if let Err(e) = load_metrics_debug_enabled(db).await { + if let Err(e) = load_metrics_debug_enabled(conn).await { tracing::error!("Error loading expose debug metrics: {e:#}"); } - if let Err(e) = reload_critical_alert_mute_ui_setting(db).await { + if let Err(e) = reload_critical_alert_mute_ui_setting(conn).await { tracing::error!("Error loading critical alert mute ui setting: {e:#}"); } - if let Err(e) = load_tag_per_workspace_enabled(db).await { - tracing::error!("Error loading default tag per workpsace: {e:#}"); - } + if let Some(db) = conn.as_sql() { + if let Err(e) = load_tag_per_workspace_enabled(db).await { + tracing::error!("Error loading default tag per workpsace: {e:#}"); + } - if let Err(e) = load_tag_per_workspace_workspaces(db).await { - tracing::error!("Error loading default tag per workpsace workspaces: {e:#}"); + if let Err(e) = load_tag_per_workspace_workspaces(db).await { + tracing::error!("Error loading default tag per workpsace workspaces: {e:#}"); + } } if server_mode { - load_require_preexisting_user(db).await; + if let Some(db) = conn.as_sql() { + load_require_preexisting_user(db).await; + if let Err(e) = reload_critical_alerts_on_db_oversize(db).await { + tracing::error!( + "Error reloading critical alerts on db oversize setting: {:?}", + e + ) + } + } } if worker_mode { - load_keep_job_dir(db).await; - reload_worker_config(&db, tx, false).await; + load_keep_job_dir(conn).await; + match conn { + Connection::Sql(db) => { + reload_worker_config(&db, tx, false).await; + } + Connection::Http(_) => { + // TODO: reload worker config from http + WORKER_CONFIG.write().await.worker_tags = DECODED_AGENT_TOKEN + .as_ref() + .map(|x| x.tags.clone()) + .unwrap_or_default(); + } + } } - if let Err(e) = reload_custom_tags_setting(db).await { - tracing::error!("Error reloading custom tags: {:?}", e) - } - - if let Err(e) = reload_hub_base_url_setting(db, server_mode).await { + if let Err(e) = reload_hub_base_url_setting(conn, server_mode).await { tracing::error!("Error reloading hub base url: {:?}", e) } - if let Err(e) = reload_jwt_secret_setting(&db).await { - tracing::error!("Could not reload jwt secret setting: {:?}", e); + if let Some(db) = conn.as_sql() { + if let Err(e) = reload_jwt_secret_setting(db).await { + tracing::error!("Could not reload jwt secret setting: {:?}", e); + } + + if let Err(e) = reload_custom_tags_setting(db).await { + tracing::error!("Error reloading custom tags: {:?}", e) + } } #[cfg(feature = "parquet")] if !disable_s3_store { - reload_s3_cache_setting(&db).await; + if let Some(db) = conn.as_sql() { + reload_s3_cache_setting(db).await; + } } - reload_smtp_config(&db).await; + if let Some(db) = conn.as_sql() { + reload_smtp_config(db).await; + } if server_mode { - reload_retention_period_setting(&db).await; - reload_request_size(&db).await; - reload_saml_metadata_setting(&db).await; - reload_scim_token_setting(&db).await; + reload_retention_period_setting(&conn).await; + reload_request_size(&conn).await; + reload_saml_metadata_setting(&conn).await; + reload_scim_token_setting(&conn).await; } if worker_mode { - reload_job_default_timeout_setting(&db).await; - reload_extra_pip_index_url_setting(&db).await; - reload_pip_index_url_setting(&db).await; - reload_npm_config_registry_setting(&db).await; - reload_bunfig_install_scopes_setting(&db).await; - reload_instance_python_version_setting(&db).await; - reload_nuget_config_setting(&db).await; + reload_job_default_timeout_setting(&conn).await; + reload_extra_pip_index_url_setting(&conn).await; + reload_pip_index_url_setting(&conn).await; + reload_npm_config_registry_setting(&conn).await; + reload_bunfig_install_scopes_setting(&conn).await; + reload_instance_python_version_setting(&conn).await; + reload_nuget_config_setting(&conn).await; + reload_maven_repos_setting(&conn).await; + reload_no_default_maven_setting(&conn).await; } } -pub async fn load_metrics_enabled(db: &DB) -> error::Result<()> { - let metrics_enabled = load_value_from_global_settings(db, EXPOSE_METRICS_SETTING).await; +pub async fn load_metrics_enabled(conn: &Connection) -> error::Result<()> { + let metrics_enabled = + load_value_from_global_settings_with_conn(conn, EXPOSE_METRICS_SETTING, true).await; match metrics_enabled { Ok(Some(serde_json::Value::Bool(t))) => METRICS_ENABLED.store(t, Ordering::Relaxed), _ => (), @@ -213,14 +266,6 @@ pub async fn load_metrics_enabled(db: &DB) -> error::Result<()> { Ok(()) } -fn empty_string_as_none<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let option = as serde::Deserialize>::deserialize(deserializer)?; - Ok(option.filter(|s| !s.is_empty())) -} - #[derive(serde::Deserialize)] struct OtelSetting { metrics_enabled: Option, @@ -242,19 +287,40 @@ pub async fn load_otel(db: &DB) { if let Some(v) = v { let deser = serde_json::from_value::(v); if let Ok(o) = deser { - let metrics_enabled = o.metrics_enabled.unwrap_or(false); - let logs_enabled = o.logs_enabled.unwrap_or(false); - let tracing_enabled = o.tracing_enabled.unwrap_or(false); + let metrics_enabled = o.metrics_enabled.unwrap_or_else(|| { + std::env::var("OTEL_METRICS_ENABLED") + .map(|x| x.parse::().unwrap_or(false)) + .unwrap_or(false) + }); + let logs_enabled = o.logs_enabled.unwrap_or_else(|| { + std::env::var("OTEL_LOGS_ENABLED") + .map(|x| x.parse::().unwrap_or(false)) + .unwrap_or(false) + }); + let tracing_enabled = o.tracing_enabled.unwrap_or_else(|| { + std::env::var("OTEL_TRACING_ENABLED") + .map(|x| x.parse::().unwrap_or(false)) + .unwrap_or(false) + }); OTEL_METRICS_ENABLED.store(metrics_enabled, Ordering::Relaxed); OTEL_LOGS_ENABLED.store(logs_enabled, Ordering::Relaxed); OTEL_TRACING_ENABLED.store(tracing_enabled, Ordering::Relaxed); - if let Some(endpoint) = o.otel_exporter_otlp_endpoint.as_ref() { - std::env::set_var("OTEL_EXPORTER_OTLP_ENDPOINT", endpoint); - } - if let Some(headers) = o.otel_exporter_otlp_headers.as_ref() { - std::env::set_var("OTEL_EXPORTER_OTLP_HEADERS", headers); - } + + let endpoint = if let Some(endpoint) = o.otel_exporter_otlp_endpoint { + std::env::set_var("OTEL_EXPORTER_OTLP_ENDPOINT", endpoint.clone()); + Some(endpoint.clone()) + } else { + std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").ok() + }; + + let headers = if let Some(headers) = o.otel_exporter_otlp_headers { + std::env::set_var("OTEL_EXPORTER_OTLP_HEADERS", headers.clone()); + Some(headers.clone()) + } else { + std::env::var("OTEL_EXPORTER_OTLP_HEADERS").ok() + }; + if let Some(protocol) = o.otel_exporter_otlp_protocol { std::env::set_var("OTEL_EXPORTER_OTLP_PROTOCOL", protocol); } @@ -262,7 +328,7 @@ pub async fn load_otel(db: &DB) { std::env::set_var("OTEL_EXPORTER_OTLP_COMPRESSION", compression); } println!("OTEL settings loaded: tracing ({tracing_enabled}), logs ({logs_enabled}), metrics ({metrics_enabled}), endpoint ({:?}), headers defined: ({})", - o.otel_exporter_otlp_endpoint, o.otel_exporter_otlp_headers.is_some()); + endpoint, headers.is_some()); } else { tracing::error!("Error deserializing otel settings"); } @@ -307,26 +373,18 @@ pub async fn load_tag_per_workspace_workspaces(db: &DB) -> error::Result<()> { Ok(()) } -pub async fn reload_critical_alert_mute_ui_setting(db: &DB) -> error::Result<()> { +pub async fn reload_critical_alert_mute_ui_setting(conn: &Connection) -> error::Result<()> { if let Ok(Some(serde_json::Value::Bool(t))) = - load_value_from_global_settings(db, CRITICAL_ALERT_MUTE_UI_SETTING).await + load_value_from_global_settings_with_conn(conn, CRITICAL_ALERT_MUTE_UI_SETTING, true).await { CRITICAL_ALERT_MUTE_UI_ENABLED.store(t, Ordering::Relaxed); - - if t { - if let Err(e) = sqlx::query!("UPDATE alerts SET acknowledged = true") - .execute(db) - .await - { - tracing::error!("Error updating alerts: {}", e.to_string()); - } - } } Ok(()) } -pub async fn load_metrics_debug_enabled(db: &DB) -> error::Result<()> { - let metrics_enabled = load_value_from_global_settings(db, EXPOSE_DEBUG_METRICS_SETTING).await; +pub async fn load_metrics_debug_enabled(conn: &Connection) -> error::Result<()> { + let metrics_enabled = + load_value_from_global_settings_with_conn(conn, EXPOSE_DEBUG_METRICS_SETTING, true).await; match metrics_enabled { Ok(Some(serde_json::Value::Bool(t))) => { METRICS_DEBUG_ENABLED.store(t, Ordering::Relaxed); @@ -477,8 +535,8 @@ fn get_worker_group(mode: &Mode) -> Option { } } -pub fn send_logs_to_object_store(db: &DB, hostname: &str, mode: &Mode) { - let db = db.clone(); +pub fn send_logs_to_object_store(conn: &Connection, hostname: &str, mode: &Mode) { + let conn = conn.clone(); let hostname = hostname.to_string(); let mode = mode.clone(); let worker_group = get_worker_group(&mode); @@ -493,7 +551,7 @@ pub fn send_logs_to_object_store(db: &DB, hostname: &str, mode: &Mode) { &hostname, &mode, &worker_group, - &db, + &conn, snd_highest_file, false, ) @@ -502,11 +560,11 @@ pub fn send_logs_to_object_store(db: &DB, hostname: &str, mode: &Mode) { }); } -pub async fn send_current_log_file_to_object_store(db: &DB, hostname: &str, mode: &Mode) { +pub async fn send_current_log_file_to_object_store(conn: &Connection, hostname: &str, mode: &Mode) { tracing::info!("Sending current log file to object store"); let (highest_file, _) = find_two_highest_files(hostname).await; let worker_group = get_worker_group(&mode); - send_log_file_to_object_store(hostname, mode, &worker_group, db, highest_file, true).await; + send_log_file_to_object_store(hostname, mode, &worker_group, conn, highest_file, true).await; } fn get_now_and_str() -> (NaiveDateTime, String) { @@ -518,11 +576,15 @@ fn get_now_and_str() -> (NaiveDateTime, String) { ) } +lazy_static::lazy_static! { + static ref LAST_LOG_FILE_SENT: Arc>> = Arc::new(Mutex::new(None)); +} + async fn send_log_file_to_object_store( hostname: &str, mode: &Mode, worker_group: &Option, - db: &Pool, + conn: &Connection, snd_highest_file: Option, use_now: bool, ) { @@ -545,23 +607,14 @@ async fn send_log_file_to_object_store( .unwrap_or_else(get_now_and_str) }; - let exists = sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM log_file WHERE hostname = $1 AND log_ts = $2)", - hostname, - ts - ) - .fetch_one(db) - .await; + let exists = LAST_LOG_FILE_SENT.lock().map(|last_log_file_sent| { + last_log_file_sent + .map(|last_log_file_sent| last_log_file_sent >= ts) + .unwrap_or(false) + }); - match exists { - Ok(Some(true)) => { - return; - } - Err(e) => { - tracing::error!("Error checking if log file exists: {:?}", e); - return; - } - _ => (), + if exists.unwrap_or(false) { + return; } #[cfg(feature = "parquet")] @@ -593,11 +646,25 @@ async fn send_log_file_to_object_store( let (ok_lines, err_lines) = read_log_counters(ts_str); - if let Err(e) = sqlx::query!("INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)", - hostname, mode.to_string(), worker_group.clone(), ts, highest_file, ok_lines as i64, err_lines as i64, *JSON_FMT) - .execute(db) - .await { - tracing::error!("Error inserting log file: {:?}", e); + if let Some(db) = conn.as_sql() { + if let Err(e) = sqlx::query!("INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) + VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8) + ON CONFLICT (hostname, log_ts) DO UPDATE SET ok_lines = log_file.ok_lines + $6, err_lines = log_file.err_lines + $7", + hostname, mode.to_string(), worker_group.clone(), ts, highest_file, ok_lines as i64, err_lines as i64, *JSON_FMT) + .execute(db) + .await { + tracing::error!("Error inserting log file: {:?}", e); + } else { + if let Err(e) = LAST_LOG_FILE_SENT.lock().map(|mut last_log_file_sent| { + last_log_file_sent.replace(ts); + }) { + tracing::error!("Error updating last log file sent: {:?}", e); + } + tracing::info!("Log file sent: {}", highest_file); + } + } else { + // tracing::warn!("Not sending log file to object store in agent mode"); + () } } } @@ -620,8 +687,8 @@ fn read_log_counters(ts_str: String) -> (usize, usize) { (ok_lines, err_lines) } -pub async fn load_keep_job_dir(db: &DB) { - let value = load_value_from_global_settings(db, KEEP_JOB_DIR_SETTING).await; +pub async fn load_keep_job_dir(conn: &Connection) { + let value = load_value_from_global_settings_with_conn(conn, KEEP_JOB_DIR_SETTING, true).await; match value { Ok(Some(serde_json::Value::Bool(t))) => KEEP_JOB_DIR.store(t, Ordering::Relaxed), Err(e) => { @@ -731,6 +798,22 @@ pub async fn delete_expired_items(db: &DB) -> () { Err(e) => tracing::error!("Error deleting log file: {:?}", e), } + #[cfg(not(feature = "enterprise"))] + let audit_retention_secs = 1 * 60 * 60 * 24 * 14; + + #[cfg(feature = "enterprise")] + let audit_retention_secs = 1 * 60 * 60 * 24 * 365; + + if let Err(e) = sqlx::query_scalar!( + "DELETE FROM audit WHERE timestamp <= now() - ($1::bigint::text || ' s')::interval", + audit_retention_secs, + ) + .fetch_all(db) + .await + { + tracing::error!("Error deleting audit log on CE: {:?}", e); + } + let job_retention_secs = *JOB_RETENTION_SECS.read().await; if job_retention_secs > 0 { match db.begin().await { @@ -872,23 +955,23 @@ async fn delete_log_files_from_disk_and_store( let _: Vec<_> = delete_futures.collect().await; } -pub async fn reload_scim_token_setting(db: &DB) { - reload_option_setting_with_tracing(db, SCIM_TOKEN_SETTING, "SCIM_TOKEN", SCIM_TOKEN.clone()) +pub async fn reload_scim_token_setting(conn: &Connection) { + reload_option_setting_with_tracing(conn, SCIM_TOKEN_SETTING, "SCIM_TOKEN", SCIM_TOKEN.clone()) .await; } -pub async fn reload_timeout_wait_result_setting(db: &DB) { +pub async fn reload_timeout_wait_result_setting(conn: &Connection) { reload_option_setting_with_tracing( - db, + conn, TIMEOUT_WAIT_RESULT_SETTING, "TIMEOUT_WAIT_RESULT", TIMEOUT_WAIT_RESULT.clone(), ) .await; } -pub async fn reload_saml_metadata_setting(db: &DB) { +pub async fn reload_saml_metadata_setting(conn: &Connection) { reload_option_setting_with_tracing( - db, + conn, SAML_METADATA_SETTING, "SAML_METADATA", SAML_METADATA.clone(), @@ -896,9 +979,9 @@ pub async fn reload_saml_metadata_setting(db: &DB) { .await; } -pub async fn reload_extra_pip_index_url_setting(db: &DB) { +pub async fn reload_extra_pip_index_url_setting(conn: &Connection) { reload_option_setting_with_tracing( - db, + conn, EXTRA_PIP_INDEX_URL_SETTING, "PIP_EXTRA_INDEX_URL", PIP_EXTRA_INDEX_URL.clone(), @@ -906,9 +989,9 @@ pub async fn reload_extra_pip_index_url_setting(db: &DB) { .await; } -pub async fn reload_pip_index_url_setting(db: &DB) { +pub async fn reload_pip_index_url_setting(conn: &Connection) { reload_option_setting_with_tracing( - db, + conn, PIP_INDEX_URL_SETTING, "PIP_INDEX_URL", PIP_INDEX_URL.clone(), @@ -916,9 +999,9 @@ pub async fn reload_pip_index_url_setting(db: &DB) { .await; } -pub async fn reload_instance_python_version_setting(db: &DB) { +pub async fn reload_instance_python_version_setting(conn: &Connection) { reload_option_setting_with_tracing( - db, + conn, INSTANCE_PYTHON_VERSION_SETTING, "INSTANCE_PYTHON_VERSION", INSTANCE_PYTHON_VERSION.clone(), @@ -926,9 +1009,9 @@ pub async fn reload_instance_python_version_setting(db: &DB) { .await; } -pub async fn reload_npm_config_registry_setting(db: &DB) { +pub async fn reload_npm_config_registry_setting(conn: &Connection) { reload_option_setting_with_tracing( - db, + conn, NPM_CONFIG_REGISTRY_SETTING, "NPM_CONFIG_REGISTRY", NPM_CONFIG_REGISTRY.clone(), @@ -936,9 +1019,9 @@ pub async fn reload_npm_config_registry_setting(db: &DB) { .await; } -pub async fn reload_bunfig_install_scopes_setting(db: &DB) { +pub async fn reload_bunfig_install_scopes_setting(conn: &Connection) { reload_option_setting_with_tracing( - db, + conn, BUNFIG_INSTALL_SCOPES_SETTING, "BUNFIG_INSTALL_SCOPES", BUNFIG_INSTALL_SCOPES.clone(), @@ -946,19 +1029,43 @@ pub async fn reload_bunfig_install_scopes_setting(db: &DB) { .await; } -pub async fn reload_nuget_config_setting(db: &DB) { +pub async fn reload_nuget_config_setting(conn: &Connection) { reload_option_setting_with_tracing( - db, + conn, NUGET_CONFIG_SETTING, "NUGET_CONFIG", NUGET_CONFIG.clone(), ) .await; } +pub async fn reload_maven_repos_setting(conn: &Connection) { + reload_option_setting_with_tracing( + conn, + windmill_common::global_settings::MAVEN_REPOS_SETTING, + "MAVEN_REPOS", + MAVEN_REPOS.clone(), + ) + .await; +} +pub async fn reload_no_default_maven_setting(conn: &Connection) { + let value = load_value_from_global_settings_with_conn( + conn, + windmill_common::global_settings::NO_DEFAULT_MAVEN_SETTING, + true, + ) + .await; + match value { + Ok(Some(serde_json::Value::Bool(t))) => NO_DEFAULT_MAVEN.store(t, Ordering::Relaxed), + Err(e) => { + tracing::error!("Error loading no default maven repository: {e:#}"); + } + _ => (), + }; +} -pub async fn reload_retention_period_setting(db: &DB) { +pub async fn reload_retention_period_setting(conn: &Connection) { if let Err(e) = reload_setting( - db, + conn, RETENTION_PERIOD_SECS_SETTING, "JOB_RETENTION_SECS", 60 * 60 * 24 * 30, @@ -970,9 +1077,9 @@ pub async fn reload_retention_period_setting(db: &DB) { tracing::error!("Error reloading retention period: {:?}", e) } } -pub async fn reload_delete_logs_periodically_setting(db: &DB) { +pub async fn reload_delete_logs_periodically_setting(conn: &Connection) { if let Err(e) = reload_setting( - db, + conn, MONITOR_LOGS_ON_OBJECT_STORE_SETTING, "MONITOR_LOGS_ON_OBJECT_STORE", false, @@ -1040,9 +1147,9 @@ pub async fn reload_s3_cache_setting(db: &DB) { } } -pub async fn reload_job_default_timeout_setting(db: &DB) { +pub async fn reload_job_default_timeout_setting(conn: &Connection) { reload_option_setting_with_tracing( - db, + conn, JOB_DEFAULT_TIMEOUT_SECS_SETTING, "JOB_DEFAULT_TIMEOUT_SECS", JOB_DEFAULT_TIMEOUT.clone(), @@ -1050,9 +1157,9 @@ pub async fn reload_job_default_timeout_setting(db: &DB) { .await; } -pub async fn reload_request_size(db: &DB) { +pub async fn reload_request_size(conn: &Connection) { if let Err(e) = reload_setting( - db, + conn, REQUEST_SIZE_LIMIT_SETTING, "REQUEST_SIZE_LIMIT", DEFAULT_BODY_LIMIT, @@ -1065,8 +1172,8 @@ pub async fn reload_request_size(db: &DB) { } } -pub async fn reload_license_key(db: &DB) -> anyhow::Result<()> { - let q = load_value_from_global_settings(db, LICENSE_KEY_SETTING) +pub async fn reload_license_key(conn: &Connection) -> anyhow::Result<()> { + let q = load_value_from_global_settings_with_conn(conn, LICENSE_KEY_SETTING, true) .await .map_err(|err| anyhow::anyhow!("Error reloading license key: {}", err.to_string()))?; @@ -1091,12 +1198,12 @@ pub async fn reload_license_key(db: &DB) -> anyhow::Result<()> { } pub async fn reload_option_setting_with_tracing( - db: &DB, + conn: &Connection, setting_name: &str, std_env_var: &str, lock: Arc>>, ) { - if let Err(e) = reload_option_setting(db, setting_name, std_env_var, lock.clone()).await { + if let Err(e) = reload_option_setting(conn, setting_name, std_env_var, lock.clone()).await { tracing::error!("Error reloading setting {}: {:?}", setting_name, e) } } @@ -1115,8 +1222,31 @@ pub async fn load_value_from_global_settings( Ok(r) } +pub async fn load_value_from_global_settings_with_conn( + conn: &Connection, + setting_name: &str, + load_from_http: bool, +) -> anyhow::Result> { + match conn { + Connection::Sql(db) => Ok(load_value_from_global_settings(db, setting_name).await?), + Connection::Http(client) => { + if load_from_http { + client + .get::>(&format!( + "/api/agent_workers/get_global_setting/{}", + setting_name + )) + .await + .map_err(|e| anyhow::anyhow!("Error loading setting {}: {}", setting_name, e)) + } else { + Ok(None) + } + } + } +} + pub async fn reload_option_setting( - db: &DB, + conn: &Connection, setting_name: &str, std_env_var: &str, lock: Arc>>, @@ -1131,7 +1261,7 @@ pub async fn reload_option_setting( return Ok(()); } - let q = load_value_from_global_settings(db, setting_name).await?; + let q = load_value_from_global_settings_with_conn(conn, setting_name, true).await?; let mut value = std::env::var(std_env_var) .ok() @@ -1158,14 +1288,14 @@ pub async fn reload_option_setting( } pub async fn reload_setting( - db: &DB, + conn: &Connection, setting_name: &str, std_env_var: &str, default: T, lock: Arc>, transformer: fn(T) -> T, ) -> error::Result<()> { - let q = load_value_from_global_settings(db, setting_name).await?; + let q = load_value_from_global_settings_with_conn(conn, setting_name, true).await?; let mut value = std::env::var(std_env_var) .ok() @@ -1223,27 +1353,32 @@ pub async fn monitor_pool(db: &DB) { } pub async fn monitor_db( - db: &Pool, + conn: &Connection, base_internal_url: &str, server_mode: bool, _worker_mode: bool, initial_load: bool, - _killpill_tx: tokio::sync::broadcast::Sender<()>, + _killpill_tx: KillpillSender, ) { + tracing::info!("Starting periodic monitor task"); let zombie_jobs_f = async { - if server_mode && !initial_load { - handle_zombie_jobs(db, base_internal_url, "server").await; - match handle_zombie_flows(db).await { - Err(err) => { - tracing::error!("Error handling zombie flows: {:?}", err); + if server_mode && !initial_load && !*DISABLE_ZOMBIE_JOBS_MONITORING { + if let Some(db) = conn.as_sql() { + handle_zombie_jobs(db, base_internal_url, "server").await; + match handle_zombie_flows(db).await { + Err(err) => { + tracing::error!("Error handling zombie flows: {:?}", err); + } + _ => {} } - _ => {} } } }; let expired_items_f = async { if server_mode && !initial_load { - delete_expired_items(&db).await; + if let Some(db) = conn.as_sql() { + delete_expired_items(&db).await; + } } }; @@ -1256,35 +1391,60 @@ pub async fn monitor_db( let expose_queue_metrics_f = async { if !initial_load && server_mode { - expose_queue_metrics(&db).await; + if let Some(db) = conn.as_sql() { + expose_queue_metrics(&db).await; + } } }; let worker_groups_alerts_f = async { #[cfg(feature = "enterprise")] if server_mode && !initial_load { - worker_groups_alerts(&db).await; + if let Some(db) = conn.as_sql() { + worker_groups_alerts(&db).await; + } } }; let jobs_waiting_alerts_f = async { #[cfg(feature = "enterprise")] if server_mode { - jobs_waiting_alerts(&db).await; + if let Some(db) = conn.as_sql() { + jobs_waiting_alerts(&db).await; + } + } + }; + + let low_disk_alerts_f = async { + #[cfg(feature = "enterprise")] + if let Some(db) = conn.as_sql() { + low_disk_alerts( + &db, + server_mode, + _worker_mode, + WORKERS_NAMES.read().await.clone(), + ) + .await; + } + #[cfg(not(feature = "enterprise"))] + { + () } }; let apply_autoscaling_f = async { #[cfg(feature = "enterprise")] if server_mode && !initial_load { - if let Err(e) = windmill_autoscaling::apply_all_autoscaling(db).await { - tracing::error!("Error applying autoscaling: {:?}", e); + if let Some(db) = conn.as_sql() { + if let Err(e) = windmill_autoscaling::apply_all_autoscaling(db).await { + tracing::error!("Error applying autoscaling: {:?}", e); + } } } }; let update_min_worker_version_f = async { - update_min_version(db).await; + update_min_version(conn).await; }; join!( @@ -1294,9 +1454,11 @@ pub async fn monitor_db( verify_license_key_f, worker_groups_alerts_f, jobs_waiting_alerts_f, + low_disk_alerts_f, apply_autoscaling_f, update_min_worker_version_f, ); + tracing::info!("Periodic monitor task completed"); } pub async fn expose_queue_metrics(db: &Pool) { @@ -1402,12 +1564,8 @@ pub async fn reload_indexer_config(db: &Pool) { } } -pub async fn reload_worker_config( - db: &DB, - tx: tokio::sync::broadcast::Sender<()>, - kill_if_change: bool, -) { - let config = load_worker_config(&db, tx.clone()).await; +pub async fn reload_worker_config(db: &DB, tx: KillpillSender, kill_if_change: bool) { + let config = load_worker_config(db, tx.clone()).await; if let Err(e) = config { tracing::error!("Error reloading worker config: {:?}", e) } else { @@ -1419,17 +1577,17 @@ pub async fn reload_worker_config( || (*wc).dedicated_worker != config.dedicated_worker { tracing::info!("Dedicated worker config changed, sending killpill. Expecting to be restarted by supervisor."); - let _ = tx.send(()); + let _ = tx.send(); } if (*wc).init_bash != config.init_bash { tracing::info!("Init bash config changed, sending killpill. Expecting to be restarted by supervisor."); - let _ = tx.send(()); + let _ = tx.send(); } if (*wc).cache_clear != config.cache_clear { tracing::info!("Cache clear changed, sending killpill. Expecting to be restarted by supervisor."); - let _ = tx.send(()); + let _ = tx.send(); tracing::info!("Waiting 5 seconds to allow others workers to start potential jobs that depend on a potential shared cache volume"); tokio::time::sleep(Duration::from_secs(5)).await; if let Err(e) = windmill_worker::common::clean_cache().await { @@ -1441,15 +1599,16 @@ pub async fn reload_worker_config( let mut wc = WORKER_CONFIG.write().await; tracing::info!("Reloading worker config..."); - make_suspended_pull_query(&config).await; - make_pull_query(&config).await; + store_suspended_pull_query(&config).await; + store_pull_query(&config).await; *wc = config } } } -pub async fn load_base_url(db: &DB) -> error::Result { - let q_base_url = load_value_from_global_settings(db, BASE_URL_SETTING).await?; +pub async fn load_base_url(conn: &Connection) -> error::Result { + let q_base_url = + load_value_from_global_settings_with_conn(conn, BASE_URL_SETTING, false).await?; let std_base_url = std::env::var("BASE_URL") .ok() @@ -1479,34 +1638,38 @@ pub async fn load_base_url(db: &DB) -> error::Result { Ok(base_url) } -pub async fn reload_base_url_setting(db: &DB) -> error::Result<()> { +pub async fn reload_base_url_setting(conn: &Connection) -> error::Result<()> { #[cfg(feature = "oauth2")] - let q_oauth = load_value_from_global_settings(db, OAUTH_SETTING).await?; + let oauths = if let Some(db) = conn.as_sql() { + let q_oauth = load_value_from_global_settings(db, OAUTH_SETTING).await?; - #[cfg(feature = "oauth2")] - let oauths = if let Some(q) = q_oauth { - if let Ok(v) = serde_json::from_value::< - Option>, - >(q.clone()) - { - v + if let Some(q) = q_oauth { + if let Ok(v) = serde_json::from_value::< + Option>, + >(q.clone()) + { + v + } else { + tracing::error!("Could not parse oauth setting as a json, found: {:#?}", &q); + None + } } else { - tracing::error!("Could not parse oauth setting as a json, found: {:#?}", &q); None } } else { None }; - - let base_url = load_base_url(db).await?; + let base_url = load_base_url(conn).await?; let is_secure = base_url.starts_with("https://"); #[cfg(feature = "oauth2")] { - let mut l = windmill_api::OAUTH_CLIENTS.write().await; - *l = windmill_api::oauth2_ee::build_oauth_clients(&base_url, oauths, db).await - .map_err(|e| tracing::error!("Error building oauth clients (is the oauth.json mounted and in correct format? Use '{}' as minimal oauth.json): {}", "{}", e)) - .unwrap(); + if let Some(db) = conn.as_sql() { + let mut l = windmill_api::OAUTH_CLIENTS.write().await; + *l = windmill_api::oauth2_ee::build_oauth_clients(&base_url, oauths, db).await + .map_err(|e| tracing::error!("Error building oauth clients (is the oauth.json mounted and in correct format? Use '{}' as minimal oauth.json): {}", "{}", e)) + .unwrap(); + } } { @@ -1517,18 +1680,44 @@ pub async fn reload_base_url_setting(db: &DB) -> error::Result<()> { Ok(()) } +const RESTART_LIMIT: i32 = 3; + async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker_name: &str) { + let mut zombie_jobs_uuid_restart_limit_reached = vec![]; + if *RESTART_ZOMBIE_JOBS { let restarted = sqlx::query!( - "WITH zombie_jobs AS ( - UPDATE v2_job_queue q SET running = false, started_at = null - FROM v2_job j, v2_job_runtime r - WHERE j.id = q.id AND j.id = r.id - AND ping < now() - ($1 || ' seconds')::interval + "WITH to_update AS ( + SELECT q.id, q.workspace_id, r.ping, COALESCE(zjc.counter, 0) as counter + FROM v2_job_queue q + JOIN v2_job j ON j.id = q.id + JOIN v2_job_runtime r ON r.id = j.id + LEFT JOIN zombie_job_counter zjc ON zjc.job_id = q.id + WHERE ping < now() - ($1 || ' seconds')::interval AND running = true AND kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow') AND same_worker = false - RETURNING q.id, q.workspace_id, ping + AND (zjc.counter IS NULL OR zjc.counter <= $2) + FOR UPDATE of q SKIP LOCKED + ), + zombie_jobs AS ( + UPDATE v2_job_queue q + SET running = false, started_at = null + FROM to_update tu + WHERE q.id = tu.id AND (tu.counter IS NULL OR tu.counter < $2) + RETURNING q.id, q.workspace_id, ping, tu.counter + ), + update_ping AS ( + UPDATE v2_job_runtime r + SET ping = null + FROM zombie_jobs zj + WHERE r.id = zj.id + ), + increment_counter AS ( + INSERT INTO zombie_job_counter (job_id, counter) + SELECT id, 1 FROM to_update WHERE counter < $2 + ON CONFLICT (job_id) DO UPDATE + SET counter = zombie_job_counter.counter + 1 ), update_concurrency AS ( UPDATE concurrency_counter cc @@ -1537,8 +1726,9 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker INNER JOIN concurrency_key ck ON ck.job_id = zj.id WHERE cc.concurrency_id = ck.key ) - SELECT id, workspace_id, ping FROM zombie_jobs", + SELECT id AS \"id!\", workspace_id AS \"workspace_id!\", ping, counter + 1 AS counter FROM to_update", *ZOMBIE_JOB_TIMEOUT, + RESTART_LIMIT ) .fetch_all(db) .await @@ -1558,53 +1748,187 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker "no last ping".to_string() }; let url = format!("{}/run/{}?workspace={}", base_url, r.id, r.workspace_id,); - let error_message = format!( - "Zombie job {} on {} ({}) detected, restarting it, {}", - r.id, r.workspace_id, url, last_ping - ); + let restart = r.counter.is_none_or(|x| x < RESTART_LIMIT); + let (critical_error_message, restart_message) = if restart { + ( + format!( + "Zombie job {} on {} ({}) detected, restarting it ({}/{} attempts), last ping: {}", + r.id, + r.workspace_id, + url, + r.counter.unwrap_or(0) + 1, + RESTART_LIMIT, + last_ping + ), + format!( + "Restarted job after not receiving job's ping for too long the {} ({}/{} attempts)\n\n", + last_ping, + r.counter.unwrap_or(0) + 1, + RESTART_LIMIT + ) + ) + } else { + ( + format!( + "Zombie job {} on {} ({}) detected, but restart limit ({}) reached, job will be processed as an error, last ping: {}", + r.id, r.workspace_id, url, RESTART_LIMIT, last_ping + ), + format!( + "job's ping was received last at {}, job will be processed as an error since all {} restart attempts failed", + last_ping, RESTART_LIMIT + ) + ) + }; - let _ = sqlx::query!(" + let _ = sqlx::query!( + " INSERT INTO job_logs (job_id, logs) - VALUES ($1, 'Restarted job after not receiving job''s ping for too long the ' || now() || '\n\n') + VALUES ($1, $2) ON CONFLICT (job_id) DO UPDATE SET logs = job_logs.logs || '\n' || EXCLUDED.logs WHERE job_logs.job_id = $1", - r.id + r.id, + restart_message ) .execute(db) .await; - tracing::error!(error_message); - report_critical_error(error_message, db.clone(), Some(&r.workspace_id), None).await; + tracing::error!(critical_error_message); + report_critical_error( + critical_error_message, + db.clone(), + Some(&r.workspace_id), + None, + ) + .await; + + if !restart { + zombie_jobs_uuid_restart_limit_reached.push(r.id); + } } } - let mut timeout_query = - "SELECT * FROM v2_as_queue WHERE last_ping < now() - ($1 || ' seconds')::interval - AND running = true AND job_kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow')" - .to_string(); - if *RESTART_ZOMBIE_JOBS { - timeout_query.push_str(" AND same_worker = true"); - }; - let timeouts = sqlx::query_as::<_, QueuedJob>(&timeout_query) - .bind(ZOMBIE_JOB_TIMEOUT.as_str()) + let same_worker_timeout_jobs = { + let long_same_worker_jobs = sqlx::query!( + "SELECT worker, array_agg(v2_job_queue.id) as ids FROM v2_job_queue LEFT JOIN v2_job ON v2_job_queue.id = v2_job.id LEFT JOIN v2_job_runtime ON v2_job_queue.id = v2_job_runtime.id WHERE v2_job_queue.created_at < now() - ('60 seconds')::interval + AND running = true AND ping IS NULL AND same_worker = true AND worker IS NOT NULL GROUP BY worker", + ) .fetch_all(db) .await .ok() .unwrap_or_else(|| vec![]); + let worker_ids = long_same_worker_jobs + .iter() + .map(|x| x.worker.clone().unwrap_or_default()) + .collect::>(); + + let long_dead_workers: std::collections::HashSet = sqlx::query_scalar!( + "WITH worker_ids AS (SELECT unnest($1::text[]) as worker) + SELECT worker_ids.worker FROM worker_ids + LEFT JOIN worker_ping ON worker_ids.worker = worker_ping.worker + WHERE worker_ping.worker IS NULL OR worker_ping.ping_at < now() - ('60 seconds')::interval", + &worker_ids[..] + ) + .fetch_all(db) + .await + .ok() + .unwrap_or_else(|| vec![]) + .into_iter() + .filter_map(|x| x) + .collect(); + + let mut timeouts: Vec = vec![]; + for worker in long_same_worker_jobs { + if worker.worker.is_some() && long_dead_workers.contains(&worker.worker.unwrap()) { + if let Some(ids) = worker.ids { + timeouts.extend(ids); + } + } + } + if !timeouts.is_empty() { + tracing::error!( + "Failing same worker zombie jobs: {:?}", + timeouts + .iter() + .map(|x| x.hyphenated().to_string()) + .collect::>() + .join(",") + ); + } + + let jobs = sqlx::query_as::<_, QueuedJob>("SELECT * FROM v2_as_queue WHERE id = ANY($1)") + .bind(&timeouts[..]) + .fetch_all(db) + .await + .map_err(|e| tracing::error!("Error fetching same worker jobs: {:?}", e)) + .unwrap_or_default(); + + jobs + }; + + let non_restartable_jobs = if *RESTART_ZOMBIE_JOBS { + vec![] + } else { + sqlx::query_as::<_, QueuedJob>("SELECT * FROM v2_as_queue WHERE last_ping < now() - ($1 || ' seconds')::interval + AND running = true AND job_kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow') AND same_worker = false") + .bind(ZOMBIE_JOB_TIMEOUT.as_str()) + .fetch_all(db) + .await + .ok() + .unwrap_or_else(|| vec![]) + }; + + enum ErrorMessage { + RestartLimit, + SameWorker, + RestartDisabled, + } + + impl ErrorMessage { + fn to_string(&self) -> String { + match self { + ErrorMessage::RestartLimit => format!("RestartLimit ({})", RESTART_LIMIT), + ErrorMessage::SameWorker => "SameWorker".to_string(), + ErrorMessage::RestartDisabled => "RestartDisabled".to_string(), + } + } + } + + let zombie_jobs_restart_limit_reached = + sqlx::query_as::<_, QueuedJob>("SELECT * FROM v2_as_queue WHERE id = ANY($1)") + .bind(&zombie_jobs_uuid_restart_limit_reached[..]) + .fetch_all(db) + .await + .ok() + .unwrap_or_else(|| vec![]); + + let timeouts = non_restartable_jobs + .into_iter() + .map(|x| (x, ErrorMessage::RestartDisabled)) + .chain( + same_worker_timeout_jobs + .into_iter() + .map(|x| (x, ErrorMessage::SameWorker)), + ) + .chain( + zombie_jobs_restart_limit_reached + .into_iter() + .map(|x| (x, ErrorMessage::RestartLimit)), + ) + .collect::>(); + #[cfg(feature = "prometheus")] if METRICS_ENABLED.load(std::sync::atomic::Ordering::Relaxed) { QUEUE_ZOMBIE_DELETE_COUNT.inc_by(timeouts.len() as _); } - for job in timeouts { - tracing::info!("timedout zombie job {} {}", job.id, job.workspace_id,); - + for (job, error_kind) in timeouts { // since the job is unrecoverable, the same worker queue should never be sent anything let (same_worker_tx_never_used, _same_worker_rx_never_used) = mpsc::channel::(1); let same_worker_tx_never_used = SameWorkerSender(same_worker_tx_never_used, Arc::new(AtomicU16::new(0))); - let (send_result_never_used, _send_result_rx_never_used) = mpsc::channel::(1); + let (send_result_never_used, _send_result_rx_never_used) = + JobCompletedSender::new_never_used(); let label = if job.permissioned_as != format!("u/{}", job.created_by) && job.permissioned_as != job.created_by @@ -1621,6 +1945,7 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker *SCRIPT_TOKEN_EXPIRY, &job.email, &job.id, + None, ) .await .expect("could not create job token"); @@ -1633,19 +1958,21 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker }; let last_ping = job.last_ping.clone(); + let error_message = format!( + "Job timed out after no ping from job since {} (ZOMBIE_JOB_TIMEOUT: {}, reason: {:?})", + last_ping + .map(|x| x.to_string()) + .unwrap_or_else(|| "no ping".to_string()), + *ZOMBIE_JOB_TIMEOUT, + error_kind.to_string() + ); let _ = handle_job_error( db, &client, - &job, + &MiniPulledJob::from(&job), 0, None, - error::Error::ExecutionErr(format!( - "Job timed out after no ping from job since {} (ZOMBIE_JOB_TIMEOUT: {})", - last_ping - .map(|x| x.to_string()) - .unwrap_or_else(|| "no ping".to_string()), - *ZOMBIE_JOB_TIMEOUT - )), + error::Error::ExecutionErr(error_message), true, same_worker_tx_never_used, "", @@ -1702,13 +2029,17 @@ async fn handle_zombie_flows(db: &DB) -> error::Result<()> { .await?; if let Some(key) = concurrency_key { - sqlx::query!( - "UPDATE concurrency_counter SET job_uuids = job_uuids - $2 WHERE concurrency_id = $1", - key, - flow.id.hyphenated().to_string() - ) - .execute(&mut *tx) - .await?; + if *DISABLE_CONCURRENCY_LIMIT { + tracing::warn!("Concurrency limit is disabled, skipping"); + } else { + sqlx::query!( + "UPDATE concurrency_counter SET job_uuids = job_uuids - $2 WHERE concurrency_id = $1", + key, + flow.id.hyphenated().to_string() + ) + .execute(&mut *tx) + .await?; + } } sqlx::query!( @@ -1724,16 +2055,27 @@ async fn handle_zombie_flows(db: &DB) -> error::Result<()> { let id = flow.id.clone(); let last_ping = flow.last_ping.clone(); let now = now_from_db(db).await?; + let base_url = BASE_URL.read().await; + let workspace_id = flow.workspace_id.clone(); let reason = format!( "{} was hanging in between 2 steps. Last ping: {last_ping:?} (now: {now})", if flow.is_flow_step.unwrap_or(false) && flow.parent_job.is_some() { - format!("Flow was cancelled because subflow {id}") + format!("Flow was cancelled because subflow {id} ({base_url}/run/{id}?workspace={workspace_id})") } else { - format!("Flow {id} was cancelled because it") + format!("Flow {id} ({base_url}/run/{id}?workspace={workspace_id}) was cancelled because it") } ); report_critical_error(reason.clone(), db.clone(), Some(&flow.workspace_id), None).await; - cancel_zombie_flow_job(db, flow.id, &flow.workspace_id, reason).await?; + cancel_zombie_flow_job(db, flow.id, &flow.workspace_id, + format!(r#"{reason} +This would happen if a worker was interrupted, killed or crashed while doing a state transition at the end of a job which is always an unexpected behavior that should never happen. +Please check your worker logs for more details and feel free to report it to the Windmill team on our Discord or support@windmill.dev (response for non EE customers will be best effort) with as much context as possible, ideally: +- Windmill version +- Worker logs right after the job referenced has finished running +- Is the error consistent when running the same flow +- A minimal flow and its flow.yaml that reproduces the error and that is importable in a fresh workspace +- Your infra setup (helm, docker-compose, configuration of the workers and their number, memory of the database, etc.) +"#)).await?; } } @@ -1741,7 +2083,7 @@ async fn handle_zombie_flows(db: &DB) -> error::Result<()> { r#" DELETE FROM parallel_monitor_lock - WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval + WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval RETURNING parent_flow_id, job_id, last_ping, (SELECT workspace_id FROM v2_job_queue q WHERE q.id = parent_flow_id AND q.running = true AND q.canceled_by IS NULL ) AS workspace_id @@ -1796,8 +2138,12 @@ async fn cancel_zombie_flow_job( Ok(()) } -pub async fn reload_hub_base_url_setting(db: &DB, server_mode: bool) -> error::Result<()> { - let hub_base_url = load_value_from_global_settings(db, HUB_BASE_URL_SETTING).await?; +pub async fn reload_hub_base_url_setting( + conn: &Connection, + server_mode: bool, +) -> error::Result<()> { + let hub_base_url = + load_value_from_global_settings_with_conn(conn, HUB_BASE_URL_SETTING, true).await?; let base_url = if let Some(q) = hub_base_url { if let Ok(v) = serde_json::from_value::(q.clone()) { @@ -1820,16 +2166,18 @@ pub async fn reload_hub_base_url_setting(db: &DB, server_mode: bool) -> error::R let mut l = HUB_BASE_URL.write().await; if server_mode { #[cfg(feature = "embedding")] - if *l != base_url { - let disable_embedding = std::env::var("DISABLE_EMBEDDING") - .ok() - .map(|x| x.parse::().unwrap_or(false)) - .unwrap_or(false); - if !disable_embedding { - let db_clone = db.clone(); - tokio::spawn(async move { - update_embeddings_db(&db_clone).await; - }); + if let Some(db) = conn.as_sql() { + if *l != base_url { + let disable_embedding = std::env::var("DISABLE_EMBEDDING") + .ok() + .map(|x| x.parse::().unwrap_or(false)) + .unwrap_or(false); + if !disable_embedding { + let db_clone = db.clone(); + tokio::spawn(async move { + update_embeddings_db(&db_clone).await; + }); + } } } } @@ -1838,16 +2186,16 @@ pub async fn reload_hub_base_url_setting(db: &DB, server_mode: bool) -> error::R Ok(()) } -pub async fn reload_critical_error_channels_setting(db: &DB) -> error::Result<()> { +pub async fn reload_critical_error_channels_setting(conn: &DB) -> error::Result<()> { let critical_error_channels = - load_value_from_global_settings(db, CRITICAL_ERROR_CHANNELS_SETTING).await?; + load_value_from_global_settings(conn, CRITICAL_ERROR_CHANNELS_SETTING).await?; let critical_error_channels = if let Some(q) = critical_error_channels { if let Ok(v) = serde_json::from_value::>(q.clone()) { v } else { tracing::error!( - "Could not parse critical_error_emails setting as an array of channels, found: {:#?}", + "Could not parse critical_error_channels setting as an array of channels, found: {:#?}", &q ); vec![] @@ -1862,6 +2210,37 @@ pub async fn reload_critical_error_channels_setting(db: &DB) -> error::Result<() Ok(()) } +pub async fn reload_critical_alerts_on_db_oversize(conn: &DB) -> error::Result<()> { + #[derive(Deserialize)] + struct DBOversize { + enabled: bool, + value: f32, + } + let db_oversize_value = + load_value_from_global_settings(conn, CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING).await?; + + let db_oversize = if let Some(q) = db_oversize_value { + match serde_json::from_value::(q.clone()) { + Ok(DBOversize { enabled: true, value }) => Some(value), + Ok(_) => None, + Err(q) => { + tracing::error!( + "Could not parse critical_alerts_on_db_oversize setting, found: {:#?}", + &q + ); + None + } + } + } else { + None + }; + + let mut l = CRITICAL_ALERTS_ON_DB_OVERSIZE.write().await; + *l = db_oversize; + + Ok(()) +} + async fn generate_and_save_jwt_secret(db: &DB) -> error::Result { let secret = rd_string(32); sqlx::query!( diff --git a/backend/substitute_ee_code.sh b/backend/substitute_ee_code.sh index c15d19a259..28b890990b 100755 --- a/backend/substitute_ee_code.sh +++ b/backend/substitute_ee_code.sh @@ -68,7 +68,7 @@ fi if [ "$REVERT" == "YES" ]; then for ee_file in $(find ${EE_CODE_DIR} -name "*ee.rs"); do - ce_file="${ee_file/${EE_CODE_DIR}/.}" + ce_file="${ee_file/${EE_CODE_DIR}/}" ce_file="${root_dirpath}/backend/${ce_file}" if [ "$REVERT_PREVIOUS" == "YES" ]; then git checkout HEAD@{3} ${ce_file} || true @@ -80,7 +80,7 @@ if [ "$REVERT" == "YES" ]; then else # This replaces all files in current repo with alternative EE files in windmill-ee-private for ee_file in $(find "${EE_CODE_DIR}" -name "*ee.rs"); do - ce_file="${ee_file/${EE_CODE_DIR}/.}" + ce_file="${ee_file/${EE_CODE_DIR}/}" ce_file="${root_dirpath}/backend/${ce_file}" if [[ -f "${ce_file}" ]]; then rm "${ce_file}" diff --git a/backend/tests/fixtures/base.sql b/backend/tests/fixtures/base.sql index 0e38b5c7bd..5e96b75b99 100644 --- a/backend/tests/fixtures/base.sql +++ b/backend/tests/fixtures/base.sql @@ -53,183 +53,74 @@ EXECUTE FUNCTION "notify_queue" (); WHEN (NEW.flow_status IS DISTINCT FROM OLD.flow_status) EXECUTE FUNCTION "notify_queue" (); --- TODO(uael): remove before phase 4 -CREATE OR REPLACE FUNCTION zzz_v2_job_queue_integrity_check() RETURNS TRIGGER AS $$ -DECLARE job v2_job; -DECLARE job_runtime v2_job_runtime; -DECLARE job_status v2_job_status; -BEGIN - IF (OLD.canceled_by IS NOT NULL) IS DISTINCT FROM OLD.__canceled THEN - RAISE EXCEPTION 'canceled mismatch'; - END IF; - -- v2_job: - SELECT * INTO job FROM v2_job WHERE id = OLD.id; - IF job.tag IS DISTINCT FROM OLD.tag THEN - RAISE EXCEPTION 'tag mismatch'; - END IF; - IF job.workspace_id IS DISTINCT FROM OLD.workspace_id THEN - RAISE EXCEPTION 'workspace_id mismatch'; - END IF; - IF job.created_at IS DISTINCT FROM OLD.created_at THEN - RAISE EXCEPTION 'created_at mismatch'; - END IF; - IF job.created_by IS DISTINCT FROM OLD.__created_by THEN - RAISE EXCEPTION 'created_by mismatch'; - END IF; - IF job.permissioned_as IS DISTINCT FROM OLD.__permissioned_as THEN - RAISE EXCEPTION 'permissioned_as mismatch'; - END IF; - IF job.permissioned_as_email IS DISTINCT FROM OLD.__email THEN - RAISE EXCEPTION 'permissioned_as_email mismatch'; - END IF; - IF job.kind IS DISTINCT FROM OLD.__job_kind THEN - RAISE EXCEPTION 'kind mismatch'; - END IF; - IF job.runnable_id IS DISTINCT FROM OLD.__script_hash THEN - RAISE EXCEPTION 'runnable_id mismatch'; - END IF; - IF job.runnable_path IS DISTINCT FROM OLD.__script_path THEN - RAISE EXCEPTION 'runnable_path mismatch'; - END IF; - IF job.parent_job IS DISTINCT FROM OLD.__parent_job THEN - RAISE EXCEPTION 'parent_job mismatch'; - END IF; - IF job.script_lang IS DISTINCT FROM OLD.__language THEN - RAISE EXCEPTION 'script_lang mismatch'; - END IF; - IF job.script_entrypoint_override IS DISTINCT FROM NULLIF(OLD.__args->>'_ENTRYPOINT_OVERRIDE', '__WM_PREPROCESSOR') - AND OLD.__args->>'reason' IS DISTINCT FROM 'PREPROCESSOR_ARGS_ARE_DISCARDED' - THEN - RAISE EXCEPTION 'script_entrypoint_override mismatch'; - END IF; - IF job.flow_step_id IS DISTINCT FROM OLD.__flow_step_id THEN - RAISE EXCEPTION 'flow_step_id mismatch'; - END IF; - IF (job.flow_step_id IS NOT NULL) IS DISTINCT FROM OLD.__is_flow_step THEN - RAISE EXCEPTION 'is_flow_step mismatch'; - END IF; - IF job.flow_innermost_root_job IS DISTINCT FROM OLD.__root_job THEN - RAISE EXCEPTION 'flow_innermost_root_job mismatch'; - END IF; - IF job.trigger IS DISTINCT FROM OLD.__schedule_path THEN - RAISE EXCEPTION 'trigger mismatch'; - END IF; - IF job.same_worker IS DISTINCT FROM OLD.__same_worker THEN - RAISE EXCEPTION 'same_worker mismatch'; - END IF; - IF job.visible_to_owner IS DISTINCT FROM OLD.__visible_to_owner THEN - RAISE EXCEPTION 'visible_to_owner mismatch'; - END IF; - IF job.concurrent_limit IS DISTINCT FROM OLD.__concurrent_limit THEN - RAISE EXCEPTION 'concurrent_limit mismatch'; - END IF; - IF job.concurrency_time_window_s IS DISTINCT FROM OLD.__concurrency_time_window_s THEN - RAISE EXCEPTION 'concurrency_time_window_s mismatch'; - END IF; - IF job.cache_ttl IS DISTINCT FROM OLD.__cache_ttl THEN - RAISE EXCEPTION 'cache_ttl mismatch'; - END IF; - IF job.timeout IS DISTINCT FROM OLD.__timeout THEN - RAISE EXCEPTION 'timeout mismatch'; - END IF; - IF job.priority IS DISTINCT FROM OLD.priority THEN - RAISE EXCEPTION 'priority mismatch'; - END IF; - IF job.args::TEXT IS DISTINCT FROM OLD.__args::TEXT AND OLD.__args->>'_ENTRYPOINT_OVERRIDE' IS DISTINCT FROM '__WM_PREPROCESSOR' THEN - RAISE EXCEPTION 'args mismatch'; - END IF; - IF job.pre_run_error IS DISTINCT FROM OLD.__pre_run_error THEN - RAISE EXCEPTION 'pre_run_error mismatch'; - END IF; - -- v2_job_runtime: - SELECT * INTO job_runtime FROM v2_job_runtime WHERE id = OLD.id; - IF job_runtime.ping IS DISTINCT FROM OLD.__last_ping THEN - RAISE EXCEPTION 'ping mismatch'; - END IF; - IF job_runtime.memory_peak IS DISTINCT FROM OLD.__mem_peak THEN - RAISE EXCEPTION 'memory_peak mismatch'; - END IF; - -- v2_job_status: - IF EXISTS(SELECT 1 FROM v2_job_status WHERE id = OLD.id) THEN - SELECT * INTO job_status FROM v2_job_status WHERE id = OLD.id; - IF COALESCE(job_status.flow_status, job_status.workflow_as_code_status)::TEXT IS DISTINCT FROM OLD.__flow_status::TEXT - THEN - RAISE EXCEPTION 'flow_status mismatch'; - END IF; - IF job_status.flow_leaf_jobs::TEXT IS DISTINCT FROM OLD.__leaf_jobs::TEXT THEN - RAISE EXCEPTION 'leaf_jobs mismatch'; - END IF; - END IF; - RETURN OLD; -END $$ LANGUAGE PLPGSQL; +-- 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; -CREATE OR REPLACE TRIGGER zzz_v2_job_queue_integrity_check_before_delete - BEFORE DELETE ON v2_job_queue - FOR EACH ROW -EXECUTE FUNCTION zzz_v2_job_queue_integrity_check(); +DROP VIEW IF EXISTS completed_job, completed_job_view, job, queue, queue_view CASCADE; --- TODO(uael): remove before phase 4 -CREATE OR REPLACE FUNCTION zzz_v2_job_completed_integrity_check() RETURNS TRIGGER AS $$ -DECLARE job v2_job; -BEGIN - IF (NEW.canceled_by IS NOT NULL) IS DISTINCT FROM NEW.__canceled THEN - RAISE EXCEPTION 'canceled mismatch'; - END IF; - SELECT * INTO job FROM v2_job WHERE id = NEW.id; - IF job.tag IS DISTINCT FROM NEW.__tag THEN - RAISE EXCEPTION 'tag mismatch % %', job.tag, NEW.__tag; - END IF; - IF job.workspace_id IS DISTINCT FROM NEW.workspace_id THEN - RAISE EXCEPTION 'workspace_id mismatch'; - END IF; - IF job.created_at IS DISTINCT FROM NEW.__created_at THEN - RAISE EXCEPTION 'created_at mismatch'; - END IF; - IF job.created_by IS DISTINCT FROM NEW.__created_by THEN - RAISE EXCEPTION 'created_by mismatch'; - END IF; - IF job.permissioned_as IS DISTINCT FROM NEW.__permissioned_as THEN - RAISE EXCEPTION 'permissioned_as mismatch'; - END IF; - IF job.permissioned_as_email IS DISTINCT FROM NEW.__email THEN - RAISE EXCEPTION 'permissioned_as_email mismatch'; - END IF; - IF job.kind IS DISTINCT FROM NEW.__job_kind THEN - RAISE EXCEPTION 'kind mismatch'; - END IF; - IF job.runnable_id IS DISTINCT FROM NEW.__script_hash THEN - RAISE EXCEPTION 'runnable_id mismatch'; - END IF; - IF job.runnable_path IS DISTINCT FROM NEW.__script_path THEN - RAISE EXCEPTION 'runnable_path mismatch'; - END IF; - IF job.parent_job IS DISTINCT FROM NEW.__parent_job THEN - RAISE EXCEPTION 'parent_job mismatch'; - END IF; - IF job.script_lang IS DISTINCT FROM NEW.__language THEN - RAISE EXCEPTION 'script_lang mismatch'; - END IF; - IF job.script_entrypoint_override IS DISTINCT FROM NULLIF(NEW.__args->>'_ENTRYPOINT_OVERRIDE', '__WM_PREPROCESSOR') - AND NEW.__args->>'reason' IS DISTINCT FROM 'PREPROCESSOR_ARGS_ARE_DISCARDED' - THEN - RAISE EXCEPTION 'script_entrypoint_override mismatch'; - END IF; - IF (job.flow_step_id IS NOT NULL) IS DISTINCT FROM NEW.__is_flow_step THEN - RAISE EXCEPTION 'is_flow_step mismatch'; - END IF; - IF job.trigger IS DISTINCT FROM NEW.__schedule_path THEN - RAISE EXCEPTION 'trigger mismatch'; - END IF; - IF job.visible_to_owner IS DISTINCT FROM NEW.__visible_to_owner THEN - RAISE EXCEPTION 'visible_to_owner mismatch'; - END IF; - IF job.args::TEXT IS DISTINCT FROM NEW.__args::TEXT AND NEW.__args->>'_ENTRYPOINT_OVERRIDE' IS DISTINCT FROM '__WM_PREPROCESSOR' THEN - RAISE EXCEPTION 'args mismatch'; - END IF; - RETURN NEW; -END $$ LANGUAGE PLPGSQL; +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; -CREATE OR REPLACE TRIGGER zzz_v2_job_completed_integrity_check_after_insert - AFTER INSERT ON v2_job_completed - FOR EACH ROW -EXECUTE FUNCTION zzz_v2_job_completed_integrity_check(); +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; diff --git a/backend/tests/fixtures/lockfile_python.sql b/backend/tests/fixtures/lockfile_python.sql new file mode 100644 index 0000000000..27f7b103f0 --- /dev/null +++ b/backend/tests/fixtures/lockfile_python.sql @@ -0,0 +1,51 @@ +INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES ( +'test-workspace', +'test-user', +' +# requirements: +# microdot==2.2.0 + +import pandas +import requests +import tiny # pin: tiny==0.1.2 + +def main(): + pass +', +'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}', +'', +'', +'f/system/requirements', 12346, 'python3', ''); + +INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES ( +'test-workspace', +'test-user', +' +# extra_requirements: +# bottle==0.13.2 + +import tiny + +def main(): + pass +', +'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}', +'', +'', +'f/system/extra_requirements', 12347, 'python3', ''); + + +INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES ( +'test-workspace', +'test-user', +' +import tiny # pin: bottle==0.13.2 +import simplejson # pin: simplejson==3.19.3 + +def main(): + return [test1(), test2(), test3(), test4()] +', +'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}', +'', +'', +'f/system/pins', 12348, 'python3', ''); diff --git a/backend/tests/fixtures/result_format.sql b/backend/tests/fixtures/result_format.sql index 2cf8fa7299..ff99891526 100644 --- a/backend/tests/fixtures/result_format.sql +++ b/backend/tests/fixtures/result_format.sql @@ -4,8 +4,8 @@ INSERT INTO public.v2_job ( '1eecb96a-c8b0-4a3d-b1b6-087878c55e41', 'test-workspace', 'test-user', '2023-01-01 00:00:00', 'script', 'postgresql' ); -INSERT INTO public.completed_job ( - id, workspace_id, created_by, created_at, duration_ms, success, flow_status, result, job_kind, language +INSERT INTO public.v2_job_completed ( + id, workspace_id, duration_ms, status, result_columns, result ) VALUES ( - '1eecb96a-c8b0-4a3d-b1b6-087878c55e41', 'test-workspace', 'test-user', '2023-01-01 00:00:00', 1000, true, '{"_metadata": {"column_order": ["b", "a"]}}', '[{"a": "second", "b": "first"}]', 'script', 'postgresql' + '1eecb96a-c8b0-4a3d-b1b6-087878c55e41', 'test-workspace', 1000, 'success'::job_status, '{b,a}', '[{"a": "second", "b": "first"}]' ) \ No newline at end of file diff --git a/backend/tests/worker.rs b/backend/tests/worker.rs index 12298795ad..4f33707eff 100644 --- a/backend/tests/worker.rs +++ b/backend/tests/worker.rs @@ -1,7 +1,7 @@ use serde::de::DeserializeOwned; use std::future::Future; use std::{str::FromStr, sync::Arc}; -use windmill_api_client::types::{NewScript, ScriptLang as NewScriptLanguage}; +use windmill_common::KillpillSender; #[cfg(feature = "enterprise")] use chrono::Timelike; @@ -16,18 +16,22 @@ use tokio::sync::RwLock; #[cfg(feature = "enterprise")] use tokio::time::{timeout, Duration}; +#[cfg(feature = "python")] use windmill_api_client::types::{CreateFlowBody, RawScript}; - #[cfg(feature = "enterprise")] use windmill_api_client::types::{EditSchedule, NewSchedule, ScriptArgs}; +use windmill_api_client::types::{NewScript, ScriptLang as NewScriptLanguage}; use serde::Serialize; -use windmill_common::auth::JWT_SECRET; +#[cfg(feature = "deno_core")] +use windmill_common::flows::InputTransform; use windmill_common::worker::WORKER_CONFIG; + use windmill_common::{ flow_status::{FlowStatus, FlowStatusModule, RestartedFrom}, - flows::{FlowModule, FlowModuleValue, FlowValue, InputTransform}, + flows::{FlowModule, FlowModuleValue, FlowValue}, jobs::{JobKind, JobPayload, RawCode}, + jwt::JWT_SECRET, scripts::{ScriptHash, ScriptLang}, worker::{ MIN_VERSION_IS_AT_LEAST_1_427, MIN_VERSION_IS_AT_LEAST_1_432, MIN_VERSION_IS_AT_LEAST_1_440, @@ -137,11 +141,11 @@ impl ApiServer { rx, port_tx, false, - #[cfg(feature = "smtp")] + false, format!("http://localhost:{}", addr.port()), )); - _port_rx.await.unwrap(); + _port_rx.await.expect("failed to receive port"); // clear the cache between tests windmill_common::cache::clear(); @@ -167,6 +171,7 @@ impl ApiServer { // Ok(()) // } +#[cfg(feature = "python")] fn get_module(cjob: &CompletedJob, id: &str) -> Option { cjob.flow_status.clone().and_then(|fs| { find_module_in_vec( @@ -176,6 +181,7 @@ fn get_module(cjob: &CompletedJob, id: &str) -> Option { }) } +#[cfg(feature = "python")] fn find_module_in_vec(modules: Vec, id: &str) -> Option { modules.into_iter().find(|s| s.id() == id) } @@ -284,6 +290,7 @@ mod suspend_resume { .unwrap() } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test(db: Pool) { initialize_tracing().await; @@ -315,7 +322,7 @@ mod suspend_resume { let second = completed.next().await.unwrap(); // print_job(second, &db).await; - let token = windmill_worker::create_token_for_owner(&db, "test-workspace", "u/test-user", "", 100, "", &Uuid::nil()).await.unwrap(); + let token = windmill_common::auth::create_token_for_owner(&db, "test-workspace", "u/test-user", "", 100, "", &Uuid::nil(), None).await.unwrap(); let secret = reqwest::get(format!( "http://localhost:{port}/api/w/test-workspace/jobs/job_signature/{second}/0?token={token}&approver=ruben" )) @@ -366,6 +373,7 @@ mod suspend_resume { ); } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn cancel_from_job(db: Pool) { initialize_tracing().await; @@ -391,6 +399,7 @@ mod suspend_resume { ); } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn cancel_after_suspend(db: Pool) { initialize_tracing().await; @@ -418,7 +427,7 @@ mod suspend_resume { /* ... and send a request resume it. */ let second = completed.next().await.unwrap(); - let token = windmill_worker::create_token_for_owner(&db, "test-workspace", "u/test-user", "", 100, "", &Uuid::nil()).await.unwrap(); + let token = windmill_common::auth::create_token_for_owner(&db, "test-workspace", "u/test-user", "", 100, "", &Uuid::nil(), None).await.unwrap(); let secret = reqwest::get(format!( "http://localhost:{port}/api/w/test-workspace/jobs/job_signature/{second}/0?token={token}" )) @@ -564,6 +573,7 @@ def main(last, port): .unwrap() } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_pass(db: Pool) { initialize_tracing().await; @@ -609,6 +619,7 @@ def main(last, port): assert_eq!(json!([3, 5, 7, 9]), result); } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_fail_step_zero(db: Pool) { initialize_tracing().await; @@ -652,6 +663,7 @@ def main(last, port): ); } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_fail_step_one(db: Pool) { initialize_tracing().await; @@ -693,6 +705,7 @@ def main(last, port): .contains("index out of range")); } + #[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_with_failure_module(db: Pool) { initialize_tracing().await; @@ -769,6 +782,7 @@ def main(error, port): } } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_iteration(db: Pool) { initialize_tracing().await; @@ -827,6 +841,7 @@ async fn test_iteration(db: Pool) { .contains("2")); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_iteration_parallel(db: Pool) { initialize_tracing().await; @@ -999,7 +1014,7 @@ async fn in_test_worker( }; /* ensure the worker quits before we return */ - quit.send(()).expect("send"); + quit.send(); let _: () = worker .await @@ -1011,21 +1026,13 @@ async fn in_test_worker( fn spawn_test_worker( db: &Pool, port: u16, -) -> ( - tokio::sync::broadcast::Sender<()>, - tokio::task::JoinHandle<()>, -) { - for x in [ - windmill_worker::LOCK_CACHE_DIR, - windmill_worker::GO_BIN_CACHE_DIR, - ] { - std::fs::DirBuilder::new() - .recursive(true) - .create(x) - .expect("could not create initial worker dir"); - } +) -> (KillpillSender, tokio::task::JoinHandle<()>) { + std::fs::DirBuilder::new() + .recursive(true) + .create(windmill_worker::GO_BIN_CACHE_DIR) + .expect("could not create initial worker dir"); - let (tx, rx) = tokio::sync::broadcast::channel(1); + let (tx, rx) = KillpillSender::new(1); let db = db.to_owned(); let worker_instance: &str = "test worker instance"; let worker_name: String = next_worker_name(); @@ -1041,11 +1048,11 @@ fn spawn_test_worker( priority: 0, tags: (*wc).worker_tags.clone(), }]; - windmill_common::worker::make_suspended_pull_query(&wc).await; - windmill_common::worker::make_pull_query(&wc).await; + windmill_common::worker::store_suspended_pull_query(&wc).await; + windmill_common::worker::store_pull_query(&wc).await; } windmill_worker::run_worker( - &db, + &db.into(), worker_instance, worker_name, 1, @@ -1054,7 +1061,6 @@ fn spawn_test_worker( rx, tx2, &base_internal_url, - false, ) .await }; @@ -1114,6 +1120,7 @@ trait StreamFind: futures::Stream + Unpin + Sized { impl StreamFind for T {} +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_deno_flow(db: Pool) { initialize_tracing().await; @@ -1232,6 +1239,7 @@ async fn test_deno_flow(db: Pool) { } } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_identity(db: Pool) { initialize_tracing().await; @@ -1269,6 +1277,7 @@ async fn test_identity(db: Pool) { assert_eq!(result, serde_json::json!(42)); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_deno_flow_same_worker(db: Pool) { initialize_tracing().await; @@ -1544,6 +1553,7 @@ async fn test_flow_result_by_id(db: Pool) { assert_eq!(result, serde_json::json!([[42]])); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_stop_after_if(db: Pool) { initialize_tracing().await; @@ -1597,6 +1607,7 @@ async fn test_stop_after_if(db: Pool) { assert_eq!(json!(-123), result); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_stop_after_if_nested(db: Pool) { initialize_tracing().await; @@ -1655,6 +1666,7 @@ async fn test_stop_after_if_nested(db: Pool) { assert_eq!(json!([-123]), result); } +#[cfg(all(feature = "deno_core", feature = "python"))] #[sqlx::test(fixtures("base"))] async fn test_python_flow(db: Pool) { initialize_tracing().await; @@ -1712,6 +1724,7 @@ async fn test_python_flow(db: Pool) { } } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_python_flow_2(db: Pool) { initialize_tracing().await; @@ -1786,6 +1799,7 @@ func main(derp string) (string, error) { assert_eq!(result, serde_json::json!("hello world")); } +#[cfg(feature = "rust")] #[sqlx::test(fixtures("base"))] async fn test_rust_job(db: Pool) { initialize_tracing().await; @@ -1893,6 +1907,155 @@ echo "hello $msg" assert_eq!(job.json_result(), Some(json!("hello world"))); } +#[cfg(feature = "nu")] +#[sqlx::test(fixtures("base"))] +async fn test_nu_job(db: Pool) { + initialize_tracing().await; + let server = ApiServer::start(db.clone()).await; + let port = server.addr.port(); + + let content = r#" +def main [ msg: string ] { + "hello " + $msg +} +"# + .to_owned(); + + let job = RunJob::from(JobPayload::Code(RawCode { + hash: None, + content, + path: None, + lock: None, + language: ScriptLang::Nu, + custom_concurrency_key: None, + concurrent_limit: None, + concurrency_time_window_s: None, + cache_ttl: None, + dedicated_worker: None, + })) + .arg("msg", json!("world")) + .run_until_complete(&db, port) + .await; + assert_eq!(job.json_result(), Some(json!("hello world"))); +} + +#[cfg(feature = "nu")] +#[sqlx::test(fixtures("base"))] +async fn test_nu_job_full(db: Pool) { + initialize_tracing().await; + let server = ApiServer::start(db.clone()).await; + let port = server.addr.port(); + + let content = r#" +def main [ + # Required + ## Primitive + a + b: any + c: bool + d: float + e: datetime + f: string + j: nothing + ## Nesting + g: record + h: list + i: table + # Optional + m? + n = "foo" + o: any = "foo" + p?: any + # TODO: ...x + ] { + 0 +} + "# + .to_owned(); + + let result = RunJob::from(JobPayload::Code(RawCode { + hash: None, + content, + path: None, + lock: None, + language: ScriptLang::Nu, + custom_concurrency_key: None, + concurrent_limit: None, + concurrency_time_window_s: None, + cache_ttl: None, + dedicated_worker: None, + })) + .arg("a", json!("3")) + .arg("b", json!("null")) + .arg("c", json!(true)) + .arg("d", json!(3.0)) + .arg("e", json!("2024-09-24T10:00:00.000Z")) + .arg("f", json!("str")) + .arg("j", json!(null)) + .arg("g", json!({"a": 32})) + .arg("h", json!(["foo"])) + .arg( + "i", + json!([ + {"a": 1, "b": "foo", "c": true}, + {"a": 2, "b": "baz", "c": false} + ]), + ) + .arg("n", json!("baz")) + .run_until_complete(&db, port) + .await + .json_result() + .unwrap(); + + assert_eq!(result, serde_json::json!(0)); +} + +#[cfg(feature = "java")] +#[sqlx::test(fixtures("base"))] +async fn test_java_job(db: Pool) { + initialize_tracing().await; + let server = ApiServer::start(db.clone()).await; + let port = server.addr.port(); + + let content = r#" +public class Main { + public static Object main( + // Primitive + int a, + float b, + // Objects + Integer age, + Float d + ){ + return "hello world"; + } +} + +"# + .to_owned(); + + let job = RunJob::from(JobPayload::Code(RawCode { + hash: None, + content, + path: None, + lock: None, + language: ScriptLang::Java, + custom_concurrency_key: None, + concurrent_limit: None, + concurrency_time_window_s: None, + cache_ttl: None, + dedicated_worker: None, + })) + .arg("a", json!(3)) + .arg("b", json!(3.0)) + .arg("age", json!(30)) + .arg("d", json!(3.0)) + .run_until_complete(&db, port) + .await; + assert_eq!(job.json_result(), Some(json!("hello world"))); +} + +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_python_job(db: Pool) { initialize_tracing().await; @@ -1926,6 +2089,7 @@ def main(): assert_eq!(result, serde_json::json!("hello world")); } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_python_job_heavy_dep(db: Pool) { initialize_tracing().await; @@ -1962,6 +2126,7 @@ def main(): assert_eq!(result, serde_json::json!(3)); } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_python_job_with_imports(db: Pool) { initialize_tracing().await; @@ -2065,6 +2230,7 @@ export async function main(a: Date) { assert_eq!(result, serde_json::json!("object")); } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_python_job_datetime_and_bytes(db: Pool) { initialize_tracing().await; @@ -2100,6 +2266,7 @@ def main(a: datetime, b: bytes): assert_eq!(result, serde_json::json!([true, true])); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_empty_loop_1(db: Pool) { initialize_tracing().await; @@ -2156,6 +2323,7 @@ async fn test_empty_loop_1(db: Pool) { assert_eq!(result, serde_json::json!(0)); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_invalid_first_step(db: Pool) { initialize_tracing().await; @@ -2236,6 +2404,7 @@ async fn test_empty_loop_2(db: Pool) { assert_eq!(result, serde_json::json!([])); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_step_after_loop(db: Pool) { initialize_tracing().await; @@ -2359,6 +2528,7 @@ async fn test_branchone_simple(db: Pool) { assert_eq!(result, serde_json::json!([1, 2])); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_branchone_with_cond(db: Pool) { initialize_tracing().await; @@ -2395,6 +2565,7 @@ async fn test_branchone_with_cond(db: Pool) { assert_eq!(result, serde_json::json!([1, 3])); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_branchall_sequential(db: Pool) { initialize_tracing().await; @@ -2433,6 +2604,7 @@ async fn test_branchall_sequential(db: Pool) { assert_eq!(result, serde_json::json!([[1, 2], [1, 3]])); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_branchall_simple(db: Pool) { initialize_tracing().await; @@ -2560,6 +2732,7 @@ async fn test_branchall_skip_failure(db: Pool) { ); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_branchone_nested(db: Pool) { initialize_tracing().await; @@ -2679,6 +2852,7 @@ async fn test_branchall_nested(db: Pool) { ); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_failure_module(db: Pool) { initialize_tracing().await; @@ -2791,6 +2965,7 @@ async fn test_failure_module(db: Pool) { assert_eq!(json!({ "l": [0, 1, 2] }), result); } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_flow_lock_all(db: Pool) { use futures::StreamExt; @@ -2905,7 +3080,7 @@ async fn test_flow_lock_all(db: Pool) { .await .unwrap() .into_inner() - .subtype_0 + .open_flow .value .modules; modules.into_iter() @@ -2929,6 +3104,7 @@ async fn test_flow_lock_all(db: Pool) { }); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_complex_flow_restart(db: Pool) { @@ -3613,6 +3789,7 @@ export async function main() { run_preview_relative_imports(&db, content, ScriptLang::Bun).await; } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "relative_bun"))] async fn test_nested_imports_bun(db: Pool) { let content = r#" @@ -3661,6 +3838,7 @@ export async function main() { run_preview_relative_imports(&db, content, ScriptLang::Deno).await; } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base", "relative_python"))] async fn test_relative_imports_python(db: Pool) { let content = r#" @@ -3678,6 +3856,7 @@ def main(): run_preview_relative_imports(&db, content, ScriptLang::Python3).await; } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base", "relative_python"))] async fn test_nested_imports_python(db: Pool) { let content = r#" @@ -3693,6 +3872,212 @@ def main(): run_preview_relative_imports(&db, content, ScriptLang::Python3).await; } +#[cfg(feature = "python")] +async fn assert_lockfile( + db: &Pool, + script_content: String, + language: ScriptLang, + expected_lines: Vec<&str>, +) { + initialize_tracing().await; + let server = ApiServer::start(db.clone()).await; + let port = server.addr.port(); + let client = windmill_api_client::create_client( + &format!("http://localhost:{port}"), + "SECRET_TOKEN".to_string(), + ); + + client + .create_script( + "test-workspace", + &NewScript { + language: NewScriptLanguage::from_str(language.as_str()).unwrap(), + content: script_content, + path: "f/system/test_import".to_string(), + concurrent_limit: None, + concurrency_time_window_s: None, + cache_ttl: None, + dedicated_worker: None, + description: "".to_string(), + draft_only: None, + envs: vec![], + is_template: None, + kind: None, + parent_hash: None, + lock: None, + summary: "".to_string(), + tag: None, + schema: std::collections::HashMap::new(), + ws_error_handler_muted: Some(false), + priority: None, + delete_after_use: None, + timeout: None, + restart_unless_cancelled: None, + deployment_message: None, + concurrency_key: None, + visible_to_runner_only: None, + no_main_func: None, + codebase: None, + has_preprocessor: None, + on_behalf_of_email: None, + }, + ) + .await + .unwrap(); + + let mut completed = listen_for_completed_jobs(&db).await; + let db2 = db.clone(); + in_test_worker( + &db, + async move { + completed.next().await; // deployed script + + let script = sqlx::query!( + "SELECT hash FROM script WHERE path = $1", + "f/system/test_import".to_string() + ) + .fetch_one(&db2) + .await + .unwrap(); + + let job = RunJob::from(JobPayload::Dependencies { + path: "f/system/test_import".to_string(), + hash: ScriptHash(script.hash), + dedicated_worker: None, + language, + }) + .push(&db2) + .await; + + completed.next().await; // completed job + + let result = completed_job(job, &db2).await.json_result().unwrap(); + + assert_eq!( + result, + json!({ + "lock": expected_lines.join("\n"), + "status": "Successful lock file generation" + }) + ); + }, + port, + ) + .await; +} + +#[cfg(feature = "python")] +#[sqlx::test(fixtures("base", "lockfile_python"))] +async fn test_requirements_python(db: Pool) { + let content = r#" +# py311 +# requirements: +# tiny==0.1.3 + +import bar +import baz # pin: foo +import baz # repin: fee +import bug # repin: free + +def main(): + pass +"# + .to_string(); + + assert_lockfile( + &db, + content, + ScriptLang::Python3, + vec!["# py311", "tiny==0.1.3"], + ) + .await; +} + +#[cfg(feature = "python")] +#[sqlx::test(fixtures("base", "lockfile_python"))] +async fn test_extra_requirements_python(db: Pool) { + { + let content = r#" +# py311 +# extra_requirements: +# tiny + +import f.system.extra_requirements +import tiny # pin: tiny==0.1.0 +import tiny # pin: tiny==0.1.1 +import tiny # repin: tiny==0.1.2 + +def main(): + pass + "# + .to_string(); + + assert_lockfile( + &db, + content, + ScriptLang::Python3, + vec!["# py311", "bottle==0.13.2", "tiny==0.1.2"], + ) + .await; + } +} + +#[cfg(feature = "python")] +#[sqlx::test(fixtures("base", "lockfile_python"))] +async fn test_extra_requirements_python2(db: Pool) { + let content = r#" +# py311 +# extra_requirements: +# tiny==0.1.3 + +import simplejson # pin: simplejson==3.20.1 +def main(): + pass +"# + .to_string(); + + assert_lockfile( + &db, + content, + ScriptLang::Python3, + vec!["# py311", "simplejson==3.20.1", "tiny==0.1.3"], + ) + .await; +} + +#[cfg(feature = "python")] +#[sqlx::test(fixtures("base", "lockfile_python"))] +async fn test_pins_python(db: Pool) { + let content = r#" +# py311 +# extra_requirements: +# tiny==0.1.3 +# bottle==0.13.2 + +import f.system.requirements +import f.system.pins +import tiny # repin: tiny==0.1.3 +import simplejson + +def main(): + pass +"# + .to_string(); + + assert_lockfile( + &db, + content, + ScriptLang::Python3, + vec![ + "# py311", + "bottle==0.13.2", + "microdot==2.2.0", + "simplejson==3.19.3", + "tiny==0.1.3", + ], + ) + .await; +} #[sqlx::test(fixtures("base", "result_format"))] async fn test_result_format(db: Pool) { let ordered_result_job_id = "1eecb96a-c8b0-4a3d-b1b6-087878c55e41"; @@ -3703,7 +4088,7 @@ async fn test_result_format(db: Pool) { let port = server.addr.port(); - let token = windmill_worker::create_token_for_owner( + let token = windmill_common::auth::create_token_for_owner( &db, "test-workspace", "u/test-user", @@ -3711,6 +4096,7 @@ async fn test_result_format(db: Pool) { 100, "", &Uuid::nil(), + None, ) .await .unwrap(); @@ -3743,7 +4129,7 @@ async fn test_result_format(db: Pool) { assert_eq!(job_result.get(), correct_result); let response = windmill_api::jobs::run_wait_result( - &db, + &db.into(), Uuid::parse_str(ordered_result_job_id).unwrap(), "test-workspace".to_string(), None, @@ -3875,6 +4261,7 @@ async fn test_workflow_as_code(db: Pool) { .await; assert_eq!(job.json_result().unwrap(), json!(["OK", 3])); + let workflow_as_code_status = sqlx::query_scalar!( "SELECT workflow_as_code_status FROM v2_job_completed WHERE id = $1", job.id @@ -3883,10 +4270,33 @@ async fn test_workflow_as_code(db: Pool) { .await .unwrap() .unwrap(); - assert_eq!( - workflow_as_code_status.get("name"), - Some(&json!("send_result")) - ); + + #[derive(Deserialize)] + #[allow(dead_code)] + struct WorkflowJobStatus { + name: String, + started_at: String, + scheduled_for: String, + duration_ms: i64, + } + + let workflow_as_code_status: std::collections::HashMap = + serde_json::from_value(workflow_as_code_status).unwrap(); + + let uuids = sqlx::query_scalar!("SELECT id FROM v2_job WHERE parent_job = $1", job.id) + .fetch_all(db) + .await + .unwrap(); + + assert_eq!(uuids.len(), 4); + for uuid in uuids { + let status = workflow_as_code_status.get(&uuid.to_string()); + assert!(status.is_some()); + assert!( + status.unwrap().name == "send_result" + || status.unwrap().name == "heavy_compute" + ); + } }, port, ) @@ -3919,6 +4329,7 @@ mod job_payload { ]; } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "hello"))] async fn test_script_hash_payload(db: Pool) { initialize_tracing().await; @@ -3981,11 +4392,6 @@ mod job_payload { let args = job.args.as_ref().unwrap(); assert_eq!(args.get("foo"), Some(&json!("bar"))); assert_eq!(args.get("bar"), Some(&json!("baz"))); - // TODO: remove this check on v2 phase 4 - assert_eq!( - job.flow_status.as_ref().unwrap().get("_metadata"), - Some(&json!({"preprocessed_args": true})) - ); assert_eq!(job.json_result().unwrap(), json!("Hello bar baz")); let job = sqlx::query!("SELECT preprocessed FROM v2_job WHERE id = $1", job.id) .fetch_one(db) @@ -4084,6 +4490,7 @@ mod job_payload { test_for_versions(VERSION_FLAGS.iter().cloned(), test).await; } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "hello"))] async fn test_flow_node_payload(db: Pool) { initialize_tracing().await; @@ -4268,6 +4675,7 @@ mod job_payload { test_for_versions(VERSION_FLAGS.iter().cloned(), test).await; } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "hello"))] async fn test_flow_payload(db: Pool) { initialize_tracing().await; @@ -4310,6 +4718,7 @@ mod job_payload { test_for_versions(VERSION_FLAGS.iter().cloned(), test).await; } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "hello"))] async fn test_flow_payload_with_preprocessor(db: Pool) { initialize_tracing().await; @@ -4377,6 +4786,7 @@ mod job_payload { test_for_versions(VERSION_FLAGS.iter().cloned(), test).await; } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "hello"))] async fn test_restarted_flow_payload(db: Pool) { initialize_tracing().await; @@ -4429,6 +4839,7 @@ mod job_payload { test_for_versions(VERSION_FLAGS.iter().cloned(), test).await; } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "hello"))] async fn test_raw_flow_payload(db: Pool) { initialize_tracing().await; @@ -4474,4 +4885,125 @@ mod job_payload { }; test_for_versions(VERSION_FLAGS.iter().cloned(), test).await; } + + #[cfg(feature = "deno_core")] + #[sqlx::test(fixtures("base", "hello"))] + async fn test_raw_flow_payload_with_restarted_from(db: Pool) { + initialize_tracing().await; + let server = ApiServer::start(db.clone()).await; + let port = server.addr.port(); + + let db = &db; + let test = |restarted_from, arg, result| async move { + let job = RunJob::from(JobPayload::RawFlow { + value: serde_json::from_value(json!({ + "modules": [{ + "id": "a", + "value": { + "type": "rawscript", + "content": r#"export function main(world: string) { + return `Hello ${world}!`; + }"#, + "language": "deno", + "input_transforms": { + "world": { "type": "javascript", "expr": "flow_input.world" } + } + } + }, { + "id": "b", + "value": { + "type": "rawscript", + "content": r#"export function main(world: string, a: string) { + return `${a} ${world}!`; + }"#, + "language": "deno", + "input_transforms": { + "world": { "type": "javascript", "expr": "flow_input.world" }, + "a": { "type": "javascript", "expr": "results.a" } + } + } + }, { + "id": "c", + "value": { + "type": "forloopflow", + "iterator": { "type": "javascript", "expr": "['a', 'b', 'c']" }, + "modules": [{ + "value": { + "input_transforms": { + "world": { "type": "javascript", "expr": "flow_input.world" }, + "b": { "type": "javascript", "expr": "results.b" }, + "x": { "type": "javascript", "expr": "flow_input.iter.value" } + }, + "type": "rawscript", + "language": "deno", + "content": r#"export function main(world: string, b: string, x: string) { + return `${x}: ${b} ${world}!`; + }"#, + }, + }], + } + }], + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { "world": { "type": "string" } }, + "type": "object", + "order": [ "world" ] + } + })) + .unwrap(), + path: None, + restarted_from, + }) + .arg("world", arg) + .run_until_complete(db, port) + .await; + + assert_eq!(job.json_result().unwrap(), result); + job.id + }; + let flow_job_id = test( + None, + json!("foo"), + json!([ + "a: Hello foo! foo! foo!", + "b: Hello foo! foo! foo!", + "c: Hello foo! foo! foo!" + ]), + ) + .await; + let flow_job_id = test( + Some(RestartedFrom { flow_job_id, step_id: "a".into(), branch_or_iteration_n: None }), + json!("foo"), + json!([ + "a: Hello foo! foo! foo!", + "b: Hello foo! foo! foo!", + "c: Hello foo! foo! foo!" + ]), + ) + .await; + let flow_job_id = test( + Some(RestartedFrom { flow_job_id, step_id: "b".into(), branch_or_iteration_n: None }), + json!("bar"), + json!([ + "a: Hello foo! bar! bar!", + "b: Hello foo! bar! bar!", + "c: Hello foo! bar! bar!" + ]), + ) + .await; + let _ = test( + Some(RestartedFrom { + flow_job_id, + step_id: "c".into(), + branch_or_iteration_n: Some(1), + }), + json!("yolo"), + json!([ + "a: Hello foo! bar! bar!", + "b: Hello foo! bar! yolo!", + "c: Hello foo! bar! yolo!" + ]), + ) + .await; + } } diff --git a/backend/update_sqlx.sh b/backend/update_sqlx.sh index 9ee8ab350d..11b5196ad8 100755 --- a/backend/update_sqlx.sh +++ b/backend/update_sqlx.sh @@ -1,3 +1,22 @@ ./substitute_ee_code.sh --dir ../windmill-ee-private + +# Check if running on macOS +if [[ "$(uname)" == "Darwin" ]]; then + echo "Running on macOS - substituting samael..." + # Comment out the version-based samael dependency + sed -i '' 's/^samael = { version="0.0.14", features = \["xmlsec"\] }/#samael = { version="0.0.14", features = ["xmlsec"] }/' Cargo.toml + # Uncomment the git-based samael dependency + sed -i '' 's/^# \(samael = { git="https:\/\/github.com\/njaremko\/samael", rev="464d015e3ae393e4b5dd00b4d6baa1b617de0dd6", features = \["xmlsec"\] }\)/\1/' Cargo.toml +fi + cargo sqlx prepare --workspace -- --all-targets --all-features ./substitute_ee_code.sh -r --dir ../windmill-ee-private + +# Undo the samael changes on macOS +if [[ "$(uname)" == "Darwin" ]]; then + echo "Reverting samael changes..." + # Uncomment the version-based samael dependency + sed -i '' 's/^#samael = { version="0.0.14", features = \["xmlsec"\] }/samael = { version="0.0.14", features = ["xmlsec"] }/' Cargo.toml + # Comment out the git-based samael dependency + sed -i '' 's/^\(samael = { git="https:\/\/github.com\/njaremko\/samael", rev="464d015e3ae393e4b5dd00b4d6baa1b617de0dd6", features = \["xmlsec"\] }\)/# \1/' Cargo.toml +fi diff --git a/backend/windmill-api-client/Cargo.toml b/backend/windmill-api-client/Cargo.toml index ab764e6670..a2d9a64baf 100644 --- a/backend/windmill-api-client/Cargo.toml +++ b/backend/windmill-api-client/Cargo.toml @@ -3,7 +3,6 @@ name = "windmill-api-client" version.workspace = true authors.workspace = true edition.workspace = true -build = "build.rs" [lib] name = "windmill_api_client" @@ -21,9 +20,3 @@ rand.workspace = true base64.workspace = true openapiv3 = "=1.0.2" -[build-dependencies] -prettyplease = "0.1.25" -progenitor = { git = "https://github.com/oxidecomputer/progenitor", rev = "3d96016ae8d422e90513b2d34fb5b63eeab30b01" } -serde_json = "1.0" -syn = "1.0" -openapiv3 = "=1.0.2" \ No newline at end of file diff --git a/backend/windmill-api-client/build.rs b/backend/windmill-api-client/build.rs deleted file mode 100644 index 202952e547..0000000000 --- a/backend/windmill-api-client/build.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::{ - env, - fs::{self, File}, - path::Path, - process::Command, -}; - -fn main() { - let src = "../windmill-api/openapi.yaml"; - println!("cargo:rerun-if-changed={}", src); - Command::new("sh").args(&["bundle.sh"]).status().unwrap(); - let file = File::open("./bundled.json").unwrap(); - let spec = serde_json::from_reader(file).unwrap(); - let mut generator = progenitor::Generator::default(); - - let tokens = generator.generate_tokens(&spec).unwrap(); - let ast = syn::parse2(tokens).unwrap(); - let content = prettyplease::unparse(&ast); - - let mut out_file = Path::new(&env::var("OUT_DIR").unwrap()).to_path_buf(); - out_file.push("codegen.rs"); - - fs::write(out_file, content).unwrap(); -} diff --git a/backend/windmill-api-client/build.sh b/backend/windmill-api-client/build.sh new file mode 100755 index 0000000000..a3afecda3d --- /dev/null +++ b/backend/windmill-api-client/build.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cd build_cargo +cargo run --bin windmill_api_client_build diff --git a/backend/windmill-api-client/build_cargo/Cargo.lock b/backend/windmill-api-client/build_cargo/Cargo.lock new file mode 100644 index 0000000000..ddf1947942 --- /dev/null +++ b/backend/windmill-api-client/build_cargo/Cargo.lock @@ -0,0 +1,2059 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "built" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99c4cdc7b2c2364182331055623bdf45254fcb679fea565c40c3c11c101889a" +dependencies = [ + "cargo-lock", + "git2", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cargo-lock" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11c675378efb449ed3ce8de78d75d0d80542fc98487c26aba28eb3b82feac72" +dependencies = [ + "semver", + "serde", + "toml 0.7.8", + "url", +] + +[[package]] +name = "cc" +version = "1.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "clap" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "git2" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b989d6a7ca95a362cf2cfc5ad688b3a467be1f87e480b8dad07fee8c79b0044" +dependencies = [ + "bitflags 1.3.2", + "libc", + "libgit2-sys", + "log", + "url", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.8.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "libgit2-sys" +version = "0.15.2+1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a80df2e11fb4a61f4ba2ab42dbe7f74468da143f1a75c74e11dee7c813f694fa" +dependencies = [ + "cc", + "libc", + "libz-sys", + "pkg-config", +] + +[[package]] +name = "libz-sys" +version = "1.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" + +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + +[[package]] +name = "log" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" + +[[package]] +name = "openapiv3" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1a9f106eb0a780abd17ba9fca8e0843e3461630bcbe2af0ad4d5d3ba4e9aa4" +dependencies = [ + "indexmap 1.9.3", + "serde", + "serde_json", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "progenitor" +version = "0.3.0" +source = "git+https://github.com/oxidecomputer/progenitor?rev=3d96016ae8d422e90513b2d34fb5b63eeab30b01#3d96016ae8d422e90513b2d34fb5b63eeab30b01" +dependencies = [ + "anyhow", + "built", + "clap", + "openapiv3", + "progenitor-client", + "progenitor-impl", + "progenitor-macro", + "project-root", + "rustfmt-wrapper", + "serde", + "serde_json", + "serde_yaml", +] + +[[package]] +name = "progenitor-client" +version = "0.3.0" +source = "git+https://github.com/oxidecomputer/progenitor?rev=3d96016ae8d422e90513b2d34fb5b63eeab30b01#3d96016ae8d422e90513b2d34fb5b63eeab30b01" +dependencies = [ + "bytes", + "futures-core", + "percent-encoding", + "reqwest", + "serde", + "serde_json", + "serde_urlencoded", +] + +[[package]] +name = "progenitor-impl" +version = "0.3.0" +source = "git+https://github.com/oxidecomputer/progenitor?rev=3d96016ae8d422e90513b2d34fb5b63eeab30b01#3d96016ae8d422e90513b2d34fb5b63eeab30b01" +dependencies = [ + "getopts", + "heck 0.4.1", + "http", + "indexmap 1.9.3", + "openapiv3", + "proc-macro2", + "quote", + "regex", + "schemars", + "serde", + "serde_json", + "syn 2.0.100", + "thiserror", + "typify", + "unicode-ident", +] + +[[package]] +name = "progenitor-macro" +version = "0.3.0" +source = "git+https://github.com/oxidecomputer/progenitor?rev=3d96016ae8d422e90513b2d34fb5b63eeab30b01#3d96016ae8d422e90513b2d34fb5b63eeab30b01" +dependencies = [ + "openapiv3", + "proc-macro2", + "progenitor-impl", + "quote", + "schemars", + "serde", + "serde_json", + "serde_tokenstream", + "serde_yaml", + "syn 2.0.100", +] + +[[package]] +name = "project-root" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bccbff07d5ed689c4087d20d7307a52ab6141edeedf487c3876a55b86cf63df" + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "regress" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a9ecfa0cb04d0b04dddb99b8ccf4f66bc8dfd23df694b398570bd8ae3a50fb" +dependencies = [ + "hashbrown 0.13.2", + "memchr", +] + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustfmt-wrapper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1adc9dfed5cc999077978cc7163b9282c5751c8d39827c4ea8c8c220ca5a440" +dependencies = [ + "serde", + "tempfile", + "thiserror", + "toml 0.8.20", + "toolchain_find", +] + +[[package]] +name = "rustix" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "chrono", + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", + "uuid", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.100", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_tokenstream" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64060d864397305347a78851c51588fd283767e7e7589829e8121d65512340f1" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 2.0.100", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.8.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.44.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-util" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.24", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.8.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap 2.8.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.7.4", +] + +[[package]] +name = "toolchain_find" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc8c9a7f0a2966e1acdaf0461023d0b01471eeead645370cf4c3f5cff153f2a" +dependencies = [ + "home", + "once_cell", + "regex", + "semver", + "walkdir", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typify" +version = "0.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6658d09e71bfe59e7987dc95ee7f71809fdb5793ab0cdc1503cc0073990484d" +dependencies = [ + "typify-impl", + "typify-macro", +] + +[[package]] +name = "typify-impl" +version = "0.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34d3bb47587b13edf526d6ed02bf360ecefe083ab47a4ef29fc43112828b2bef" +dependencies = [ + "heck 0.4.1", + "log", + "proc-macro2", + "quote", + "regress", + "schemars", + "serde_json", + "syn 2.0.100", + "thiserror", + "unicode-ident", +] + +[[package]] +name = "typify-macro" +version = "0.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3f7e627c18be12d53bc1f261830b9c2763437b6a86ac57293b9085af2d32ffe" +dependencies = [ + "proc-macro2", + "quote", + "schemars", + "serde", + "serde_json", + "serde_tokenstream", + "syn 2.0.100", + "typify-impl", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.100", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windmill-api-client-build" +version = "0.1.0" +dependencies = [ + "openapiv3", + "prettyplease", + "progenitor", + "serde_json", + "syn 1.0.109", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] diff --git a/backend/windmill-api-client/build_cargo/Cargo.toml b/backend/windmill-api-client/build_cargo/Cargo.toml new file mode 100644 index 0000000000..1e6a9ce192 --- /dev/null +++ b/backend/windmill-api-client/build_cargo/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "windmill-api-client-build" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "windmill_api_client_build" +path = "./main.rs" + + +[dependencies] +prettyplease = "0.1.25" +progenitor = { git = "https://github.com/oxidecomputer/progenitor", rev = "3d96016ae8d422e90513b2d34fb5b63eeab30b01" } +serde_json = "1.0" +syn = "1.0" +openapiv3 = "=1.0.2" + +[workspace] diff --git a/backend/windmill-api-client/build_cargo/bundle.sh b/backend/windmill-api-client/build_cargo/bundle.sh new file mode 100755 index 0000000000..665fae59fb --- /dev/null +++ b/backend/windmill-api-client/build_cargo/bundle.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +npx swagger-cli bundle ../../windmill-api/openapi.yaml > bundled.json \ No newline at end of file diff --git a/backend/windmill-api-client/build_cargo/main.rs b/backend/windmill-api-client/build_cargo/main.rs new file mode 100644 index 0000000000..0a6f1b447e --- /dev/null +++ b/backend/windmill-api-client/build_cargo/main.rs @@ -0,0 +1,32 @@ +use std::{ + fs::{self, File}, + path::Path, + process::Command, +}; + +fn main() { + Command::new("sh").args(&["./bundle.sh"]).status().unwrap(); + let file = File::open("./bundled.json").unwrap(); + let mut spec: openapiv3::OpenAPI = serde_json::from_reader(file).unwrap(); + spec.paths.paths.retain(|key, _| { + [ + "/w/{workspace}/flows/create", + "/w/{workspace}/flows/get/{path}", + "/w/{workspace}/scripts/create", + "/workspaces/list", + "/w/{workspace}/schedules/create", + "/w/{workspace}/schedules/update/{path}", + ] + .contains(&key.as_str()) + }); + + let mut generator = progenitor::Generator::default(); + let tokens = generator.generate_tokens(&spec).unwrap(); + let ast = syn::parse2(tokens).unwrap(); + let content = prettyplease::unparse(&ast); + + let mut out_file = Path::new("../src").to_path_buf(); + out_file.push("codegen.rs"); + + fs::write(out_file, content).unwrap(); +} diff --git a/backend/windmill-api-client/bundle.sh b/backend/windmill-api-client/bundle.sh deleted file mode 100755 index 0fe4b172ca..0000000000 --- a/backend/windmill-api-client/bundle.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -npx swagger-cli bundle ../windmill-api/openapi.yaml > bundled.json \ No newline at end of file diff --git a/backend/windmill-api-client/codegen.rs b/backend/windmill-api-client/codegen.rs new file mode 100644 index 0000000000..202145159e --- /dev/null +++ b/backend/windmill-api-client/codegen.rs @@ -0,0 +1,24391 @@ +pub use progenitor_client::{ByteStream, Error, ResponseValue}; +#[allow(unused_imports)] +use progenitor_client::{encode_path, RequestBuilderExt}; +#[allow(unused_imports)] +use reqwest::header::{HeaderMap, HeaderValue}; +pub mod types { + use serde::{Deserialize, Serialize}; + #[allow(unused_imports)] + use std::convert::TryFrom; + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AcceptInviteBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub username: Option, + pub workspace_id: String, + } + impl From<&AcceptInviteBody> for AcceptInviteBody { + fn from(value: &AcceptInviteBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AddGranularAclsBody { + pub owner: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub write: Option, + } + impl From<&AddGranularAclsBody> for AddGranularAclsBody { + fn from(value: &AddGranularAclsBody) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum AddGranularAclsKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "group_")] + Group, + #[serde(rename = "resource")] + Resource, + #[serde(rename = "schedule")] + Schedule, + #[serde(rename = "variable")] + Variable, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "folder")] + Folder, + #[serde(rename = "app")] + App, + #[serde(rename = "raw_app")] + RawApp, + #[serde(rename = "http_trigger")] + HttpTrigger, + #[serde(rename = "websocket_trigger")] + WebsocketTrigger, + #[serde(rename = "kafka_trigger")] + KafkaTrigger, + #[serde(rename = "nats_trigger")] + NatsTrigger, + #[serde(rename = "postgres_trigger")] + PostgresTrigger, + #[serde(rename = "mqtt_trigger")] + MqttTrigger, + #[serde(rename = "sqs_trigger")] + SqsTrigger, + } + impl From<&AddGranularAclsKind> for AddGranularAclsKind { + fn from(value: &AddGranularAclsKind) -> Self { + value.clone() + } + } + impl ToString for AddGranularAclsKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Group => "group_".to_string(), + Self::Resource => "resource".to_string(), + Self::Schedule => "schedule".to_string(), + Self::Variable => "variable".to_string(), + Self::Flow => "flow".to_string(), + Self::Folder => "folder".to_string(), + Self::App => "app".to_string(), + Self::RawApp => "raw_app".to_string(), + Self::HttpTrigger => "http_trigger".to_string(), + Self::WebsocketTrigger => "websocket_trigger".to_string(), + Self::KafkaTrigger => "kafka_trigger".to_string(), + Self::NatsTrigger => "nats_trigger".to_string(), + Self::PostgresTrigger => "postgres_trigger".to_string(), + Self::MqttTrigger => "mqtt_trigger".to_string(), + Self::SqsTrigger => "sqs_trigger".to_string(), + } + } + } + impl std::str::FromStr for AddGranularAclsKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "group_" => Ok(Self::Group), + "resource" => Ok(Self::Resource), + "schedule" => Ok(Self::Schedule), + "variable" => Ok(Self::Variable), + "flow" => Ok(Self::Flow), + "folder" => Ok(Self::Folder), + "app" => Ok(Self::App), + "raw_app" => Ok(Self::RawApp), + "http_trigger" => Ok(Self::HttpTrigger), + "websocket_trigger" => Ok(Self::WebsocketTrigger), + "kafka_trigger" => Ok(Self::KafkaTrigger), + "nats_trigger" => Ok(Self::NatsTrigger), + "postgres_trigger" => Ok(Self::PostgresTrigger), + "mqtt_trigger" => Ok(Self::MqttTrigger), + "sqs_trigger" => Ok(Self::SqsTrigger), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for AddGranularAclsKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for AddGranularAclsKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for AddGranularAclsKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AddOwnerToFolderBody { + pub owner: String, + } + impl From<&AddOwnerToFolderBody> for AddOwnerToFolderBody { + fn from(value: &AddOwnerToFolderBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AddUserBody { + pub email: String, + pub is_admin: bool, + pub operator: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub username: Option, + } + impl From<&AddUserBody> for AddUserBody { + fn from(value: &AddUserBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AddUserToGroupBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub username: Option, + } + impl From<&AddUserToGroupBody> for AddUserToGroupBody { + fn from(value: &AddUserToGroupBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AddUserToInstanceGroupBody { + pub email: String, + } + impl From<&AddUserToInstanceGroupBody> for AddUserToInstanceGroupBody { + fn from(value: &AddUserToInstanceGroupBody) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum AiProvider { + #[serde(rename = "openai")] + Openai, + #[serde(rename = "anthropic")] + Anthropic, + #[serde(rename = "mistral")] + Mistral, + #[serde(rename = "deepseek")] + Deepseek, + #[serde(rename = "googleai")] + Googleai, + #[serde(rename = "groq")] + Groq, + #[serde(rename = "openrouter")] + Openrouter, + #[serde(rename = "customai")] + Customai, + } + impl From<&AiProvider> for AiProvider { + fn from(value: &AiProvider) -> Self { + value.clone() + } + } + impl ToString for AiProvider { + fn to_string(&self) -> String { + match *self { + Self::Openai => "openai".to_string(), + Self::Anthropic => "anthropic".to_string(), + Self::Mistral => "mistral".to_string(), + Self::Deepseek => "deepseek".to_string(), + Self::Googleai => "googleai".to_string(), + Self::Groq => "groq".to_string(), + Self::Openrouter => "openrouter".to_string(), + Self::Customai => "customai".to_string(), + } + } + } + impl std::str::FromStr for AiProvider { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "openai" => Ok(Self::Openai), + "anthropic" => Ok(Self::Anthropic), + "mistral" => Ok(Self::Mistral), + "deepseek" => Ok(Self::Deepseek), + "googleai" => Ok(Self::Googleai), + "groq" => Ok(Self::Groq), + "openrouter" => Ok(Self::Openrouter), + "customai" => Ok(Self::Customai), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for AiProvider { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for AiProvider { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for AiProvider { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AiResource { + pub path: String, + pub provider: AiProvider, + } + impl From<&AiResource> for AiResource { + fn from(value: &AiResource) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AppHistory { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_msg: Option, + pub version: i64, + } + impl From<&AppHistory> for AppHistory { + fn from(value: &AppHistory) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AppWithLastVersion { + pub created_at: chrono::DateTime, + pub created_by: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub custom_path: Option, + pub execution_mode: AppWithLastVersionExecutionMode, + pub extra_perms: std::collections::HashMap, + pub id: i64, + pub path: String, + pub policy: Policy, + pub summary: String, + pub value: std::collections::HashMap, + pub versions: Vec, + pub workspace_id: String, + } + impl From<&AppWithLastVersion> for AppWithLastVersion { + fn from(value: &AppWithLastVersion) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum AppWithLastVersionExecutionMode { + #[serde(rename = "viewer")] + Viewer, + #[serde(rename = "publisher")] + Publisher, + #[serde(rename = "anonymous")] + Anonymous, + } + impl From<&AppWithLastVersionExecutionMode> for AppWithLastVersionExecutionMode { + fn from(value: &AppWithLastVersionExecutionMode) -> Self { + value.clone() + } + } + impl ToString for AppWithLastVersionExecutionMode { + fn to_string(&self) -> String { + match *self { + Self::Viewer => "viewer".to_string(), + Self::Publisher => "publisher".to_string(), + Self::Anonymous => "anonymous".to_string(), + } + } + } + impl std::str::FromStr for AppWithLastVersionExecutionMode { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "viewer" => Ok(Self::Viewer), + "publisher" => Ok(Self::Publisher), + "anonymous" => Ok(Self::Anonymous), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for AppWithLastVersionExecutionMode { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for AppWithLastVersionExecutionMode { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for AppWithLastVersionExecutionMode { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AppWithLastVersionWDraft { + #[serde(flatten)] + pub app_with_last_version: AppWithLastVersion, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft_only: Option, + } + impl From<&AppWithLastVersionWDraft> for AppWithLastVersionWDraft { + fn from(value: &AppWithLastVersionWDraft) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ArchiveFlowByPathBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub archived: Option, + } + impl From<&ArchiveFlowByPathBody> for ArchiveFlowByPathBody { + fn from(value: &ArchiveFlowByPathBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AuditLog { + pub action_kind: AuditLogActionKind, + pub id: i64, + pub operation: AuditLogOperation, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub parameters: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub resource: Option, + pub timestamp: chrono::DateTime, + pub username: String, + } + impl From<&AuditLog> for AuditLog { + fn from(value: &AuditLog) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum AuditLogActionKind { + Created, + Updated, + Delete, + Execute, + } + impl From<&AuditLogActionKind> for AuditLogActionKind { + fn from(value: &AuditLogActionKind) -> Self { + value.clone() + } + } + impl ToString for AuditLogActionKind { + fn to_string(&self) -> String { + match *self { + Self::Created => "Created".to_string(), + Self::Updated => "Updated".to_string(), + Self::Delete => "Delete".to_string(), + Self::Execute => "Execute".to_string(), + } + } + } + impl std::str::FromStr for AuditLogActionKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "Created" => Ok(Self::Created), + "Updated" => Ok(Self::Updated), + "Delete" => Ok(Self::Delete), + "Execute" => Ok(Self::Execute), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for AuditLogActionKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for AuditLogActionKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for AuditLogActionKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum AuditLogOperation { + #[serde(rename = "jobs.run")] + JobsRun, + #[serde(rename = "jobs.run.script")] + JobsRunScript, + #[serde(rename = "jobs.run.preview")] + JobsRunPreview, + #[serde(rename = "jobs.run.flow")] + JobsRunFlow, + #[serde(rename = "jobs.run.flow_preview")] + JobsRunFlowPreview, + #[serde(rename = "jobs.run.script_hub")] + JobsRunScriptHub, + #[serde(rename = "jobs.run.dependencies")] + JobsRunDependencies, + #[serde(rename = "jobs.run.identity")] + JobsRunIdentity, + #[serde(rename = "jobs.run.noop")] + JobsRunNoop, + #[serde(rename = "jobs.flow_dependencies")] + JobsFlowDependencies, + #[serde(rename = "jobs")] + Jobs, + #[serde(rename = "jobs.cancel")] + JobsCancel, + #[serde(rename = "jobs.force_cancel")] + JobsForceCancel, + #[serde(rename = "jobs.disapproval")] + JobsDisapproval, + #[serde(rename = "jobs.delete")] + JobsDelete, + #[serde(rename = "account.delete")] + AccountDelete, + #[serde(rename = "ai.request")] + AiRequest, + #[serde(rename = "resources.create")] + ResourcesCreate, + #[serde(rename = "resources.update")] + ResourcesUpdate, + #[serde(rename = "resources.delete")] + ResourcesDelete, + #[serde(rename = "resource_types.create")] + ResourceTypesCreate, + #[serde(rename = "resource_types.update")] + ResourceTypesUpdate, + #[serde(rename = "resource_types.delete")] + ResourceTypesDelete, + #[serde(rename = "schedule.create")] + ScheduleCreate, + #[serde(rename = "schedule.setenabled")] + ScheduleSetenabled, + #[serde(rename = "schedule.edit")] + ScheduleEdit, + #[serde(rename = "schedule.delete")] + ScheduleDelete, + #[serde(rename = "scripts.create")] + ScriptsCreate, + #[serde(rename = "scripts.update")] + ScriptsUpdate, + #[serde(rename = "scripts.archive")] + ScriptsArchive, + #[serde(rename = "scripts.delete")] + ScriptsDelete, + #[serde(rename = "users.create")] + UsersCreate, + #[serde(rename = "users.delete")] + UsersDelete, + #[serde(rename = "users.update")] + UsersUpdate, + #[serde(rename = "users.login")] + UsersLogin, + #[serde(rename = "users.login_failure")] + UsersLoginFailure, + #[serde(rename = "users.logout")] + UsersLogout, + #[serde(rename = "users.accept_invite")] + UsersAcceptInvite, + #[serde(rename = "users.decline_invite")] + UsersDeclineInvite, + #[serde(rename = "users.token.create")] + UsersTokenCreate, + #[serde(rename = "users.token.delete")] + UsersTokenDelete, + #[serde(rename = "users.add_to_workspace")] + UsersAddToWorkspace, + #[serde(rename = "users.add_global")] + UsersAddGlobal, + #[serde(rename = "users.setpassword")] + UsersSetpassword, + #[serde(rename = "users.impersonate")] + UsersImpersonate, + #[serde(rename = "users.leave_workspace")] + UsersLeaveWorkspace, + #[serde(rename = "oauth.login")] + OauthLogin, + #[serde(rename = "oauth.login_failure")] + OauthLoginFailure, + #[serde(rename = "oauth.signup")] + OauthSignup, + #[serde(rename = "variables.create")] + VariablesCreate, + #[serde(rename = "variables.delete")] + VariablesDelete, + #[serde(rename = "variables.update")] + VariablesUpdate, + #[serde(rename = "flows.create")] + FlowsCreate, + #[serde(rename = "flows.update")] + FlowsUpdate, + #[serde(rename = "flows.delete")] + FlowsDelete, + #[serde(rename = "flows.archive")] + FlowsArchive, + #[serde(rename = "apps.create")] + AppsCreate, + #[serde(rename = "apps.update")] + AppsUpdate, + #[serde(rename = "apps.delete")] + AppsDelete, + #[serde(rename = "folder.create")] + FolderCreate, + #[serde(rename = "folder.update")] + FolderUpdate, + #[serde(rename = "folder.delete")] + FolderDelete, + #[serde(rename = "folder.add_owner")] + FolderAddOwner, + #[serde(rename = "folder.remove_owner")] + FolderRemoveOwner, + #[serde(rename = "group.create")] + GroupCreate, + #[serde(rename = "group.delete")] + GroupDelete, + #[serde(rename = "group.edit")] + GroupEdit, + #[serde(rename = "group.adduser")] + GroupAdduser, + #[serde(rename = "group.removeuser")] + GroupRemoveuser, + #[serde(rename = "igroup.create")] + IgroupCreate, + #[serde(rename = "igroup.delete")] + IgroupDelete, + #[serde(rename = "igroup.adduser")] + IgroupAdduser, + #[serde(rename = "igroup.removeuser")] + IgroupRemoveuser, + #[serde(rename = "variables.decrypt_secret")] + VariablesDecryptSecret, + #[serde(rename = "workspaces.edit_command_script")] + WorkspacesEditCommandScript, + #[serde(rename = "workspaces.edit_deploy_to")] + WorkspacesEditDeployTo, + #[serde(rename = "workspaces.edit_auto_invite_domain")] + WorkspacesEditAutoInviteDomain, + #[serde(rename = "workspaces.edit_webhook")] + WorkspacesEditWebhook, + #[serde(rename = "workspaces.edit_copilot_config")] + WorkspacesEditCopilotConfig, + #[serde(rename = "workspaces.edit_error_handler")] + WorkspacesEditErrorHandler, + #[serde(rename = "workspaces.create")] + WorkspacesCreate, + #[serde(rename = "workspaces.update")] + WorkspacesUpdate, + #[serde(rename = "workspaces.archive")] + WorkspacesArchive, + #[serde(rename = "workspaces.unarchive")] + WorkspacesUnarchive, + #[serde(rename = "workspaces.delete")] + WorkspacesDelete, + } + impl From<&AuditLogOperation> for AuditLogOperation { + fn from(value: &AuditLogOperation) -> Self { + value.clone() + } + } + impl ToString for AuditLogOperation { + fn to_string(&self) -> String { + match *self { + Self::JobsRun => "jobs.run".to_string(), + Self::JobsRunScript => "jobs.run.script".to_string(), + Self::JobsRunPreview => "jobs.run.preview".to_string(), + Self::JobsRunFlow => "jobs.run.flow".to_string(), + Self::JobsRunFlowPreview => "jobs.run.flow_preview".to_string(), + Self::JobsRunScriptHub => "jobs.run.script_hub".to_string(), + Self::JobsRunDependencies => "jobs.run.dependencies".to_string(), + Self::JobsRunIdentity => "jobs.run.identity".to_string(), + Self::JobsRunNoop => "jobs.run.noop".to_string(), + Self::JobsFlowDependencies => "jobs.flow_dependencies".to_string(), + Self::Jobs => "jobs".to_string(), + Self::JobsCancel => "jobs.cancel".to_string(), + Self::JobsForceCancel => "jobs.force_cancel".to_string(), + Self::JobsDisapproval => "jobs.disapproval".to_string(), + Self::JobsDelete => "jobs.delete".to_string(), + Self::AccountDelete => "account.delete".to_string(), + Self::AiRequest => "ai.request".to_string(), + Self::ResourcesCreate => "resources.create".to_string(), + Self::ResourcesUpdate => "resources.update".to_string(), + Self::ResourcesDelete => "resources.delete".to_string(), + Self::ResourceTypesCreate => "resource_types.create".to_string(), + Self::ResourceTypesUpdate => "resource_types.update".to_string(), + Self::ResourceTypesDelete => "resource_types.delete".to_string(), + Self::ScheduleCreate => "schedule.create".to_string(), + Self::ScheduleSetenabled => "schedule.setenabled".to_string(), + Self::ScheduleEdit => "schedule.edit".to_string(), + Self::ScheduleDelete => "schedule.delete".to_string(), + Self::ScriptsCreate => "scripts.create".to_string(), + Self::ScriptsUpdate => "scripts.update".to_string(), + Self::ScriptsArchive => "scripts.archive".to_string(), + Self::ScriptsDelete => "scripts.delete".to_string(), + Self::UsersCreate => "users.create".to_string(), + Self::UsersDelete => "users.delete".to_string(), + Self::UsersUpdate => "users.update".to_string(), + Self::UsersLogin => "users.login".to_string(), + Self::UsersLoginFailure => "users.login_failure".to_string(), + Self::UsersLogout => "users.logout".to_string(), + Self::UsersAcceptInvite => "users.accept_invite".to_string(), + Self::UsersDeclineInvite => "users.decline_invite".to_string(), + Self::UsersTokenCreate => "users.token.create".to_string(), + Self::UsersTokenDelete => "users.token.delete".to_string(), + Self::UsersAddToWorkspace => "users.add_to_workspace".to_string(), + Self::UsersAddGlobal => "users.add_global".to_string(), + Self::UsersSetpassword => "users.setpassword".to_string(), + Self::UsersImpersonate => "users.impersonate".to_string(), + Self::UsersLeaveWorkspace => "users.leave_workspace".to_string(), + Self::OauthLogin => "oauth.login".to_string(), + Self::OauthLoginFailure => "oauth.login_failure".to_string(), + Self::OauthSignup => "oauth.signup".to_string(), + Self::VariablesCreate => "variables.create".to_string(), + Self::VariablesDelete => "variables.delete".to_string(), + Self::VariablesUpdate => "variables.update".to_string(), + Self::FlowsCreate => "flows.create".to_string(), + Self::FlowsUpdate => "flows.update".to_string(), + Self::FlowsDelete => "flows.delete".to_string(), + Self::FlowsArchive => "flows.archive".to_string(), + Self::AppsCreate => "apps.create".to_string(), + Self::AppsUpdate => "apps.update".to_string(), + Self::AppsDelete => "apps.delete".to_string(), + Self::FolderCreate => "folder.create".to_string(), + Self::FolderUpdate => "folder.update".to_string(), + Self::FolderDelete => "folder.delete".to_string(), + Self::FolderAddOwner => "folder.add_owner".to_string(), + Self::FolderRemoveOwner => "folder.remove_owner".to_string(), + Self::GroupCreate => "group.create".to_string(), + Self::GroupDelete => "group.delete".to_string(), + Self::GroupEdit => "group.edit".to_string(), + Self::GroupAdduser => "group.adduser".to_string(), + Self::GroupRemoveuser => "group.removeuser".to_string(), + Self::IgroupCreate => "igroup.create".to_string(), + Self::IgroupDelete => "igroup.delete".to_string(), + Self::IgroupAdduser => "igroup.adduser".to_string(), + Self::IgroupRemoveuser => "igroup.removeuser".to_string(), + Self::VariablesDecryptSecret => "variables.decrypt_secret".to_string(), + Self::WorkspacesEditCommandScript => { + "workspaces.edit_command_script".to_string() + } + Self::WorkspacesEditDeployTo => "workspaces.edit_deploy_to".to_string(), + Self::WorkspacesEditAutoInviteDomain => { + "workspaces.edit_auto_invite_domain".to_string() + } + Self::WorkspacesEditWebhook => "workspaces.edit_webhook".to_string(), + Self::WorkspacesEditCopilotConfig => { + "workspaces.edit_copilot_config".to_string() + } + Self::WorkspacesEditErrorHandler => { + "workspaces.edit_error_handler".to_string() + } + Self::WorkspacesCreate => "workspaces.create".to_string(), + Self::WorkspacesUpdate => "workspaces.update".to_string(), + Self::WorkspacesArchive => "workspaces.archive".to_string(), + Self::WorkspacesUnarchive => "workspaces.unarchive".to_string(), + Self::WorkspacesDelete => "workspaces.delete".to_string(), + } + } + } + impl std::str::FromStr for AuditLogOperation { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "jobs.run" => Ok(Self::JobsRun), + "jobs.run.script" => Ok(Self::JobsRunScript), + "jobs.run.preview" => Ok(Self::JobsRunPreview), + "jobs.run.flow" => Ok(Self::JobsRunFlow), + "jobs.run.flow_preview" => Ok(Self::JobsRunFlowPreview), + "jobs.run.script_hub" => Ok(Self::JobsRunScriptHub), + "jobs.run.dependencies" => Ok(Self::JobsRunDependencies), + "jobs.run.identity" => Ok(Self::JobsRunIdentity), + "jobs.run.noop" => Ok(Self::JobsRunNoop), + "jobs.flow_dependencies" => Ok(Self::JobsFlowDependencies), + "jobs" => Ok(Self::Jobs), + "jobs.cancel" => Ok(Self::JobsCancel), + "jobs.force_cancel" => Ok(Self::JobsForceCancel), + "jobs.disapproval" => Ok(Self::JobsDisapproval), + "jobs.delete" => Ok(Self::JobsDelete), + "account.delete" => Ok(Self::AccountDelete), + "ai.request" => Ok(Self::AiRequest), + "resources.create" => Ok(Self::ResourcesCreate), + "resources.update" => Ok(Self::ResourcesUpdate), + "resources.delete" => Ok(Self::ResourcesDelete), + "resource_types.create" => Ok(Self::ResourceTypesCreate), + "resource_types.update" => Ok(Self::ResourceTypesUpdate), + "resource_types.delete" => Ok(Self::ResourceTypesDelete), + "schedule.create" => Ok(Self::ScheduleCreate), + "schedule.setenabled" => Ok(Self::ScheduleSetenabled), + "schedule.edit" => Ok(Self::ScheduleEdit), + "schedule.delete" => Ok(Self::ScheduleDelete), + "scripts.create" => Ok(Self::ScriptsCreate), + "scripts.update" => Ok(Self::ScriptsUpdate), + "scripts.archive" => Ok(Self::ScriptsArchive), + "scripts.delete" => Ok(Self::ScriptsDelete), + "users.create" => Ok(Self::UsersCreate), + "users.delete" => Ok(Self::UsersDelete), + "users.update" => Ok(Self::UsersUpdate), + "users.login" => Ok(Self::UsersLogin), + "users.login_failure" => Ok(Self::UsersLoginFailure), + "users.logout" => Ok(Self::UsersLogout), + "users.accept_invite" => Ok(Self::UsersAcceptInvite), + "users.decline_invite" => Ok(Self::UsersDeclineInvite), + "users.token.create" => Ok(Self::UsersTokenCreate), + "users.token.delete" => Ok(Self::UsersTokenDelete), + "users.add_to_workspace" => Ok(Self::UsersAddToWorkspace), + "users.add_global" => Ok(Self::UsersAddGlobal), + "users.setpassword" => Ok(Self::UsersSetpassword), + "users.impersonate" => Ok(Self::UsersImpersonate), + "users.leave_workspace" => Ok(Self::UsersLeaveWorkspace), + "oauth.login" => Ok(Self::OauthLogin), + "oauth.login_failure" => Ok(Self::OauthLoginFailure), + "oauth.signup" => Ok(Self::OauthSignup), + "variables.create" => Ok(Self::VariablesCreate), + "variables.delete" => Ok(Self::VariablesDelete), + "variables.update" => Ok(Self::VariablesUpdate), + "flows.create" => Ok(Self::FlowsCreate), + "flows.update" => Ok(Self::FlowsUpdate), + "flows.delete" => Ok(Self::FlowsDelete), + "flows.archive" => Ok(Self::FlowsArchive), + "apps.create" => Ok(Self::AppsCreate), + "apps.update" => Ok(Self::AppsUpdate), + "apps.delete" => Ok(Self::AppsDelete), + "folder.create" => Ok(Self::FolderCreate), + "folder.update" => Ok(Self::FolderUpdate), + "folder.delete" => Ok(Self::FolderDelete), + "folder.add_owner" => Ok(Self::FolderAddOwner), + "folder.remove_owner" => Ok(Self::FolderRemoveOwner), + "group.create" => Ok(Self::GroupCreate), + "group.delete" => Ok(Self::GroupDelete), + "group.edit" => Ok(Self::GroupEdit), + "group.adduser" => Ok(Self::GroupAdduser), + "group.removeuser" => Ok(Self::GroupRemoveuser), + "igroup.create" => Ok(Self::IgroupCreate), + "igroup.delete" => Ok(Self::IgroupDelete), + "igroup.adduser" => Ok(Self::IgroupAdduser), + "igroup.removeuser" => Ok(Self::IgroupRemoveuser), + "variables.decrypt_secret" => Ok(Self::VariablesDecryptSecret), + "workspaces.edit_command_script" => Ok(Self::WorkspacesEditCommandScript), + "workspaces.edit_deploy_to" => Ok(Self::WorkspacesEditDeployTo), + "workspaces.edit_auto_invite_domain" => { + Ok(Self::WorkspacesEditAutoInviteDomain) + } + "workspaces.edit_webhook" => Ok(Self::WorkspacesEditWebhook), + "workspaces.edit_copilot_config" => Ok(Self::WorkspacesEditCopilotConfig), + "workspaces.edit_error_handler" => Ok(Self::WorkspacesEditErrorHandler), + "workspaces.create" => Ok(Self::WorkspacesCreate), + "workspaces.update" => Ok(Self::WorkspacesUpdate), + "workspaces.archive" => Ok(Self::WorkspacesArchive), + "workspaces.unarchive" => Ok(Self::WorkspacesUnarchive), + "workspaces.delete" => Ok(Self::WorkspacesDelete), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for AuditLogOperation { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for AuditLogOperation { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for AuditLogOperation { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AutoscalingEvent { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub applied_at: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub desired_workers: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub event_type: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub reason: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub worker_group: Option, + } + impl From<&AutoscalingEvent> for AutoscalingEvent { + fn from(value: &AutoscalingEvent) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct BranchAll { + pub branches: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parallel: Option, + #[serde(rename = "type")] + pub type_: BranchAllType, + } + impl From<&BranchAll> for BranchAll { + fn from(value: &BranchAll) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct BranchAllBranchesItem { + pub modules: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub skip_failure: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&BranchAllBranchesItem> for BranchAllBranchesItem { + fn from(value: &BranchAllBranchesItem) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum BranchAllType { + #[serde(rename = "branchall")] + Branchall, + } + impl From<&BranchAllType> for BranchAllType { + fn from(value: &BranchAllType) -> Self { + value.clone() + } + } + impl ToString for BranchAllType { + fn to_string(&self) -> String { + match *self { + Self::Branchall => "branchall".to_string(), + } + } + } + impl std::str::FromStr for BranchAllType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "branchall" => Ok(Self::Branchall), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for BranchAllType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for BranchAllType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for BranchAllType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct BranchOne { + pub branches: Vec, + pub default: Vec, + #[serde(rename = "type")] + pub type_: BranchOneType, + } + impl From<&BranchOne> for BranchOne { + fn from(value: &BranchOne) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct BranchOneBranchesItem { + pub expr: String, + pub modules: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&BranchOneBranchesItem> for BranchOneBranchesItem { + fn from(value: &BranchOneBranchesItem) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum BranchOneType { + #[serde(rename = "branchone")] + Branchone, + } + impl From<&BranchOneType> for BranchOneType { + fn from(value: &BranchOneType) -> Self { + value.clone() + } + } + impl ToString for BranchOneType { + fn to_string(&self) -> String { + match *self { + Self::Branchone => "branchone".to_string(), + } + } + } + impl std::str::FromStr for BranchOneType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "branchone" => Ok(Self::Branchone), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for BranchOneType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for BranchOneType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for BranchOneType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CancelPersistentQueuedJobsBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub reason: Option, + } + impl From<&CancelPersistentQueuedJobsBody> for CancelPersistentQueuedJobsBody { + fn from(value: &CancelPersistentQueuedJobsBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CancelQueuedJobBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub reason: Option, + } + impl From<&CancelQueuedJobBody> for CancelQueuedJobBody { + fn from(value: &CancelQueuedJobBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Capture { + pub created_at: chrono::DateTime, + pub id: i64, + pub payload: serde_json::Value, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub trigger_extra: Option, + pub trigger_kind: CaptureTriggerKind, + } + impl From<&Capture> for Capture { + fn from(value: &Capture) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CaptureConfig { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub trigger_config: Option, + pub trigger_kind: CaptureTriggerKind, + } + impl From<&CaptureConfig> for CaptureConfig { + fn from(value: &CaptureConfig) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum CaptureTriggerKind { + #[serde(rename = "webhook")] + Webhook, + #[serde(rename = "http")] + Http, + #[serde(rename = "websocket")] + Websocket, + #[serde(rename = "kafka")] + Kafka, + #[serde(rename = "email")] + Email, + #[serde(rename = "nats")] + Nats, + #[serde(rename = "postgres")] + Postgres, + #[serde(rename = "sqs")] + Sqs, + #[serde(rename = "mqtt")] + Mqtt, + } + impl From<&CaptureTriggerKind> for CaptureTriggerKind { + fn from(value: &CaptureTriggerKind) -> Self { + value.clone() + } + } + impl ToString for CaptureTriggerKind { + fn to_string(&self) -> String { + match *self { + Self::Webhook => "webhook".to_string(), + Self::Http => "http".to_string(), + Self::Websocket => "websocket".to_string(), + Self::Kafka => "kafka".to_string(), + Self::Email => "email".to_string(), + Self::Nats => "nats".to_string(), + Self::Postgres => "postgres".to_string(), + Self::Sqs => "sqs".to_string(), + Self::Mqtt => "mqtt".to_string(), + } + } + } + impl std::str::FromStr for CaptureTriggerKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "webhook" => Ok(Self::Webhook), + "http" => Ok(Self::Http), + "websocket" => Ok(Self::Websocket), + "kafka" => Ok(Self::Kafka), + "email" => Ok(Self::Email), + "nats" => Ok(Self::Nats), + "postgres" => Ok(Self::Postgres), + "sqs" => Ok(Self::Sqs), + "mqtt" => Ok(Self::Mqtt), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for CaptureTriggerKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for CaptureTriggerKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for CaptureTriggerKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ChangeWorkspaceColorBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub color: Option, + } + impl From<&ChangeWorkspaceColorBody> for ChangeWorkspaceColorBody { + fn from(value: &ChangeWorkspaceColorBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ChangeWorkspaceIdBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub new_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub new_name: Option, + } + impl From<&ChangeWorkspaceIdBody> for ChangeWorkspaceIdBody { + fn from(value: &ChangeWorkspaceIdBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ChangeWorkspaceNameBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub new_name: Option, + } + impl From<&ChangeWorkspaceNameBody> for ChangeWorkspaceNameBody { + fn from(value: &ChangeWorkspaceNameBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ChannelInfo { + ///The unique identifier of the channel + pub channel_id: String, + ///The display name of the channel + pub channel_name: String, + ///The service URL for the channel + pub service_url: String, + ///The Microsoft Teams tenant identifier + pub tenant_id: String, + } + impl From<&ChannelInfo> for ChannelInfo { + fn from(value: &ChannelInfo) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum ClearIndexIdxName { + JobIndex, + ServiceLogIndex, + } + impl From<&ClearIndexIdxName> for ClearIndexIdxName { + fn from(value: &ClearIndexIdxName) -> Self { + value.clone() + } + } + impl ToString for ClearIndexIdxName { + fn to_string(&self) -> String { + match *self { + Self::JobIndex => "JobIndex".to_string(), + Self::ServiceLogIndex => "ServiceLogIndex".to_string(), + } + } + } + impl std::str::FromStr for ClearIndexIdxName { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "JobIndex" => Ok(Self::JobIndex), + "ServiceLogIndex" => Ok(Self::ServiceLogIndex), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for ClearIndexIdxName { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for ClearIndexIdxName { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for ClearIndexIdxName { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CompletedJob { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub aggregate_wait_time_ms: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub args: Option, + pub canceled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub canceled_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub canceled_reason: Option, + pub created_at: chrono::DateTime, + pub created_by: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deleted: Option, + pub duration_ms: i64, + pub email: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub flow_status: Option, + pub id: uuid::Uuid, + pub is_flow_step: bool, + pub is_skipped: bool, + pub job_kind: CompletedJobJobKind, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub labels: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub language: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub logs: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mem_peak: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parent_job: Option, + /**The user (u/userfoo) or group (g/groupfoo) whom +the execution of this script will be permissioned_as and by extension its DT_TOKEN. +*/ + pub permissioned_as: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub preprocessed: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_code: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_flow: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub result: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schedule_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub script_hash: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub script_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub self_wait_time_ms: Option, + pub started_at: chrono::DateTime, + pub success: bool, + pub tag: String, + pub visible_to_owner: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&CompletedJob> for CompletedJob { + fn from(value: &CompletedJob) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum CompletedJobJobKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "preview")] + Preview, + #[serde(rename = "dependencies")] + Dependencies, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "flowdependencies")] + Flowdependencies, + #[serde(rename = "appdependencies")] + Appdependencies, + #[serde(rename = "flowpreview")] + Flowpreview, + #[serde(rename = "script_hub")] + ScriptHub, + #[serde(rename = "identity")] + Identity, + #[serde(rename = "deploymentcallback")] + Deploymentcallback, + #[serde(rename = "singlescriptflow")] + Singlescriptflow, + #[serde(rename = "flowscript")] + Flowscript, + #[serde(rename = "flownode")] + Flownode, + #[serde(rename = "appscript")] + Appscript, + } + impl From<&CompletedJobJobKind> for CompletedJobJobKind { + fn from(value: &CompletedJobJobKind) -> Self { + value.clone() + } + } + impl ToString for CompletedJobJobKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Preview => "preview".to_string(), + Self::Dependencies => "dependencies".to_string(), + Self::Flow => "flow".to_string(), + Self::Flowdependencies => "flowdependencies".to_string(), + Self::Appdependencies => "appdependencies".to_string(), + Self::Flowpreview => "flowpreview".to_string(), + Self::ScriptHub => "script_hub".to_string(), + Self::Identity => "identity".to_string(), + Self::Deploymentcallback => "deploymentcallback".to_string(), + Self::Singlescriptflow => "singlescriptflow".to_string(), + Self::Flowscript => "flowscript".to_string(), + Self::Flownode => "flownode".to_string(), + Self::Appscript => "appscript".to_string(), + } + } + } + impl std::str::FromStr for CompletedJobJobKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "preview" => Ok(Self::Preview), + "dependencies" => Ok(Self::Dependencies), + "flow" => Ok(Self::Flow), + "flowdependencies" => Ok(Self::Flowdependencies), + "appdependencies" => Ok(Self::Appdependencies), + "flowpreview" => Ok(Self::Flowpreview), + "script_hub" => Ok(Self::ScriptHub), + "identity" => Ok(Self::Identity), + "deploymentcallback" => Ok(Self::Deploymentcallback), + "singlescriptflow" => Ok(Self::Singlescriptflow), + "flowscript" => Ok(Self::Flowscript), + "flownode" => Ok(Self::Flownode), + "appscript" => Ok(Self::Appscript), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for CompletedJobJobKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for CompletedJobJobKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for CompletedJobJobKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ConcurrencyGroup { + pub concurrency_key: String, + pub total_running: f64, + } + impl From<&ConcurrencyGroup> for ConcurrencyGroup { + fn from(value: &ConcurrencyGroup) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Config { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub config: std::collections::HashMap, + pub name: String, + } + impl From<&Config> for Config { + fn from(value: &Config) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ConnectCallbackBody { + pub code: String, + pub state: String, + } + impl From<&ConnectCallbackBody> for ConnectCallbackBody { + fn from(value: &ConnectCallbackBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ConnectSlackCallbackBody { + pub code: String, + pub state: String, + } + impl From<&ConnectSlackCallbackBody> for ConnectSlackCallbackBody { + fn from(value: &ConnectSlackCallbackBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ConnectSlackCallbackInstanceBody { + pub code: String, + pub state: String, + } + impl From<&ConnectSlackCallbackInstanceBody> for ConnectSlackCallbackInstanceBody { + fn from(value: &ConnectSlackCallbackInstanceBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ConnectTeamsBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub team_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub team_name: Option, + } + impl From<&ConnectTeamsBody> for ConnectTeamsBody { + fn from(value: &ConnectTeamsBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ContextualVariable { + pub description: String, + pub is_custom: bool, + pub name: String, + pub value: String, + } + impl From<&ContextualVariable> for ContextualVariable { + fn from(value: &ContextualVariable) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CountJobsByTagResponseItem { + pub count: i64, + pub tag: String, + } + impl From<&CountJobsByTagResponseItem> for CountJobsByTagResponseItem { + fn from(value: &CountJobsByTagResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CountSearchLogsIndexResponse { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub count_per_host: std::collections::HashMap, + ///a list of the terms that couldn't be parsed (and thus ignored) + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub query_parse_errors: Vec, + } + impl From<&CountSearchLogsIndexResponse> for CountSearchLogsIndexResponse { + fn from(value: &CountSearchLogsIndexResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateAccountBody { + pub client: String, + pub expires_in: i64, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub refresh_token: Option, + } + impl From<&CreateAccountBody> for CreateAccountBody { + fn from(value: &CreateAccountBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateAppBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub custom_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_message: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft_only: Option, + pub path: String, + pub policy: Policy, + pub summary: String, + pub value: serde_json::Value, + } + impl From<&CreateAppBody> for CreateAppBody { + fn from(value: &CreateAppBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateDraftBody { + pub path: String, + pub typ: CreateDraftBodyTyp, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + } + impl From<&CreateDraftBody> for CreateDraftBody { + fn from(value: &CreateDraftBody) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum CreateDraftBodyTyp { + #[serde(rename = "flow")] + Flow, + #[serde(rename = "script")] + Script, + #[serde(rename = "app")] + App, + } + impl From<&CreateDraftBodyTyp> for CreateDraftBodyTyp { + fn from(value: &CreateDraftBodyTyp) -> Self { + value.clone() + } + } + impl ToString for CreateDraftBodyTyp { + fn to_string(&self) -> String { + match *self { + Self::Flow => "flow".to_string(), + Self::Script => "script".to_string(), + Self::App => "app".to_string(), + } + } + } + impl std::str::FromStr for CreateDraftBodyTyp { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "flow" => Ok(Self::Flow), + "script" => Ok(Self::Script), + "app" => Ok(Self::App), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for CreateDraftBodyTyp { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for CreateDraftBodyTyp { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for CreateDraftBodyTyp { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateFlowBody { + #[serde(flatten)] + pub open_flow_w_path: OpenFlowWPath, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_message: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft_only: Option, + } + impl From<&CreateFlowBody> for CreateFlowBody { + fn from(value: &CreateFlowBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateFolderBody { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub extra_perms: std::collections::HashMap, + pub name: String, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub owners: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&CreateFolderBody> for CreateFolderBody { + fn from(value: &CreateFolderBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateGroupBody { + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&CreateGroupBody> for CreateGroupBody { + fn from(value: &CreateGroupBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateInput { + pub args: std::collections::HashMap, + pub name: String, + } + impl From<&CreateInput> for CreateInput { + fn from(value: &CreateInput) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateInstanceGroupBody { + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&CreateInstanceGroupBody> for CreateInstanceGroupBody { + fn from(value: &CreateInstanceGroupBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateRawAppBody { + pub path: String, + pub summary: String, + pub value: String, + } + impl From<&CreateRawAppBody> for CreateRawAppBody { + fn from(value: &CreateRawAppBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateResource { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + pub path: String, + pub resource_type: String, + pub value: serde_json::Value, + } + impl From<&CreateResource> for CreateResource { + fn from(value: &CreateResource) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateUserGloballyBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub company: Option, + pub email: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + pub password: String, + pub super_admin: bool, + } + impl From<&CreateUserGloballyBody> for CreateUserGloballyBody { + fn from(value: &CreateUserGloballyBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateVariable { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub account: Option, + pub description: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expires_at: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_oauth: Option, + pub is_secret: bool, + pub path: String, + pub value: String, + } + impl From<&CreateVariable> for CreateVariable { + fn from(value: &CreateVariable) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateWorkspace { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub color: Option, + pub id: String, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub username: Option, + } + impl From<&CreateWorkspace> for CreateWorkspace { + fn from(value: &CreateWorkspace) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CriticalAlert { + ///Acknowledgment status of the alert, can be true, false, or null if not set + #[serde(default, skip_serializing_if = "Option::is_none")] + pub acknowledged: Option, + ///Type of alert (e.g., critical_error) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub alert_type: Option, + ///Time when the alert was created + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_at: Option>, + ///Unique identifier for the alert + #[serde(default, skip_serializing_if = "Option::is_none")] + pub id: Option, + ///The message content of the alert + #[serde(default, skip_serializing_if = "Option::is_none")] + pub message: Option, + ///Workspace id if the alert is in the scope of a workspace + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&CriticalAlert> for CriticalAlert { + fn from(value: &CriticalAlert) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DeclineInviteBody { + pub workspace_id: String, + } + impl From<&DeclineInviteBody> for DeclineInviteBody { + fn from(value: &DeclineInviteBody) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum DeleteDraftKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "app")] + App, + } + impl From<&DeleteDraftKind> for DeleteDraftKind { + fn from(value: &DeleteDraftKind) -> Self { + value.clone() + } + } + impl ToString for DeleteDraftKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Flow => "flow".to_string(), + Self::App => "app".to_string(), + } + } + } + impl std::str::FromStr for DeleteDraftKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "flow" => Ok(Self::Flow), + "app" => Ok(Self::App), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for DeleteDraftKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for DeleteDraftKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for DeleteDraftKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DeleteInviteBody { + pub email: String, + pub is_admin: bool, + pub operator: bool, + } + impl From<&DeleteInviteBody> for DeleteInviteBody { + fn from(value: &DeleteInviteBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DuckdbConnectionSettingsBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub s3_resource: Option, + } + impl From<&DuckdbConnectionSettingsBody> for DuckdbConnectionSettingsBody { + fn from(value: &DuckdbConnectionSettingsBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DuckdbConnectionSettingsResponse { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub connection_settings_str: Option, + } + impl From<&DuckdbConnectionSettingsResponse> for DuckdbConnectionSettingsResponse { + fn from(value: &DuckdbConnectionSettingsResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DuckdbConnectionSettingsV2Body { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub s3_resource_path: Option, + } + impl From<&DuckdbConnectionSettingsV2Body> for DuckdbConnectionSettingsV2Body { + fn from(value: &DuckdbConnectionSettingsV2Body) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DuckdbConnectionSettingsV2Response { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub azure_container_path: Option, + pub connection_settings_str: String, + } + impl From<&DuckdbConnectionSettingsV2Response> + for DuckdbConnectionSettingsV2Response { + fn from(value: &DuckdbConnectionSettingsV2Response) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditAutoInviteBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub auto_add: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub invite_all: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub operator: Option, + } + impl From<&EditAutoInviteBody> for EditAutoInviteBody { + fn from(value: &EditAutoInviteBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditCopilotConfigBody { + pub ai_models: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ai_resource: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub code_completion_model: Option, + } + impl From<&EditCopilotConfigBody> for EditCopilotConfigBody { + fn from(value: &EditCopilotConfigBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditDeployToBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deploy_to: Option, + } + impl From<&EditDeployToBody> for EditDeployToBody { + fn from(value: &EditDeployToBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditErrorHandlerBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error_handler: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error_handler_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error_handler_muted_on_cancel: Option, + } + impl From<&EditErrorHandlerBody> for EditErrorHandlerBody { + fn from(value: &EditErrorHandlerBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditHttpTrigger { + pub http_method: EditHttpTriggerHttpMethod, + pub is_async: bool, + pub is_flow: bool, + pub is_static_website: bool, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_string: Option, + pub requires_auth: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub route_path: Option, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub static_asset_config: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspaced_route: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub wrap_body: Option, + } + impl From<&EditHttpTrigger> for EditHttpTrigger { + fn from(value: &EditHttpTrigger) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum EditHttpTriggerHttpMethod { + #[serde(rename = "get")] + Get, + #[serde(rename = "post")] + Post, + #[serde(rename = "put")] + Put, + #[serde(rename = "delete")] + Delete, + #[serde(rename = "patch")] + Patch, + } + impl From<&EditHttpTriggerHttpMethod> for EditHttpTriggerHttpMethod { + fn from(value: &EditHttpTriggerHttpMethod) -> Self { + value.clone() + } + } + impl ToString for EditHttpTriggerHttpMethod { + fn to_string(&self) -> String { + match *self { + Self::Get => "get".to_string(), + Self::Post => "post".to_string(), + Self::Put => "put".to_string(), + Self::Delete => "delete".to_string(), + Self::Patch => "patch".to_string(), + } + } + } + impl std::str::FromStr for EditHttpTriggerHttpMethod { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "get" => Ok(Self::Get), + "post" => Ok(Self::Post), + "put" => Ok(Self::Put), + "delete" => Ok(Self::Delete), + "patch" => Ok(Self::Patch), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for EditHttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for EditHttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for EditHttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditHttpTriggerStaticAssetConfig { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub filename: Option, + pub s3: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub storage: Option, + } + impl From<&EditHttpTriggerStaticAssetConfig> for EditHttpTriggerStaticAssetConfig { + fn from(value: &EditHttpTriggerStaticAssetConfig) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditKafkaTrigger { + pub group_id: String, + pub is_flow: bool, + pub kafka_resource_path: String, + pub path: String, + pub script_path: String, + pub topics: Vec, + } + impl From<&EditKafkaTrigger> for EditKafkaTrigger { + fn from(value: &EditKafkaTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditLargeFileStorageConfigBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub large_file_storage: Option, + } + impl From<&EditLargeFileStorageConfigBody> for EditLargeFileStorageConfigBody { + fn from(value: &EditLargeFileStorageConfigBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditMqttTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub client_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub client_version: Option, + pub enabled: bool, + pub is_flow: bool, + pub mqtt_resource_path: String, + pub path: String, + pub script_path: String, + pub subscribe_topics: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub v3_config: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub v5_config: Option, + } + impl From<&EditMqttTrigger> for EditMqttTrigger { + fn from(value: &EditMqttTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditNatsTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub consumer_name: Option, + pub is_flow: bool, + pub nats_resource_path: String, + pub path: String, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stream_name: Option, + pub subjects: Vec, + pub use_jetstream: bool, + } + impl From<&EditNatsTrigger> for EditNatsTrigger { + fn from(value: &EditNatsTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditPostgresTrigger { + pub enabled: bool, + pub is_flow: bool, + pub path: String, + pub postgres_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub publication: Option, + pub publication_name: String, + pub replication_slot_name: String, + pub script_path: String, + } + impl From<&EditPostgresTrigger> for EditPostgresTrigger { + fn from(value: &EditPostgresTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditResource { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + } + impl From<&EditResource> for EditResource { + fn from(value: &EditResource) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditResourceType { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schema: Option, + } + impl From<&EditResourceType> for EditResourceType { + fn from(value: &EditResourceType) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditSchedule { + pub args: ScriptArgs, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cron_version: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub no_flow_overlap: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_exact: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_times: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery_times: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_success: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_success_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub paused_until: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub retry: Option, + pub schedule: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + pub timezone: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&EditSchedule> for EditSchedule { + fn from(value: &EditSchedule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditSlackCommandBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub slack_command_script: Option, + } + impl From<&EditSlackCommandBody> for EditSlackCommandBody { + fn from(value: &EditSlackCommandBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditSqsTrigger { + pub aws_resource_path: String, + pub enabled: bool, + pub is_flow: bool, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub message_attributes: Vec, + pub path: String, + pub queue_url: String, + pub script_path: String, + } + impl From<&EditSqsTrigger> for EditSqsTrigger { + fn from(value: &EditSqsTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditTeamsCommandBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub slack_command_script: Option, + } + impl From<&EditTeamsCommandBody> for EditTeamsCommandBody { + fn from(value: &EditTeamsCommandBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditVariable { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_secret: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + } + impl From<&EditVariable> for EditVariable { + fn from(value: &EditVariable) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditWebhookBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub webhook: Option, + } + impl From<&EditWebhookBody> for EditWebhookBody { + fn from(value: &EditWebhookBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditWebsocketTrigger { + pub can_return_message: bool, + pub filters: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub initial_messages: Vec, + pub is_flow: bool, + pub path: String, + pub script_path: String, + pub url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url_runnable_args: Option, + } + impl From<&EditWebsocketTrigger> for EditWebsocketTrigger { + fn from(value: &EditWebsocketTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditWebsocketTriggerFiltersItem { + pub key: String, + pub value: serde_json::Value, + } + impl From<&EditWebsocketTriggerFiltersItem> for EditWebsocketTriggerFiltersItem { + fn from(value: &EditWebsocketTriggerFiltersItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditWorkspaceDefaultAppBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub default_app_path: Option, + } + impl From<&EditWorkspaceDefaultAppBody> for EditWorkspaceDefaultAppBody { + fn from(value: &EditWorkspaceDefaultAppBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditWorkspaceDeployUiSettingsBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deploy_ui_settings: Option, + } + impl From<&EditWorkspaceDeployUiSettingsBody> for EditWorkspaceDeployUiSettingsBody { + fn from(value: &EditWorkspaceDeployUiSettingsBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditWorkspaceGitSyncConfigBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub git_sync_settings: Option, + } + impl From<&EditWorkspaceGitSyncConfigBody> for EditWorkspaceGitSyncConfigBody { + fn from(value: &EditWorkspaceGitSyncConfigBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditWorkspaceUser { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub disabled: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_admin: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub operator: Option, + } + impl From<&EditWorkspaceUser> for EditWorkspaceUser { + fn from(value: &EditWorkspaceUser) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ExecuteComponentBody { + pub args: serde_json::Value, + pub component: String, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub force_viewer_allow_user_resources: Vec, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub force_viewer_one_of_fields: std::collections::HashMap< + String, + serde_json::Value, + >, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub force_viewer_static_fields: std::collections::HashMap< + String, + serde_json::Value, + >, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_code: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub version: Option, + } + impl From<&ExecuteComponentBody> for ExecuteComponentBody { + fn from(value: &ExecuteComponentBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ExecuteComponentBodyRawCode { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cache_ttl: Option, + pub content: String, + pub language: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + } + impl From<&ExecuteComponentBodyRawCode> for ExecuteComponentBodyRawCode { + fn from(value: &ExecuteComponentBodyRawCode) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ExistsRouteBody { + pub http_method: ExistsRouteBodyHttpMethod, + pub route_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub trigger_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspaced_route: Option, + } + impl From<&ExistsRouteBody> for ExistsRouteBody { + fn from(value: &ExistsRouteBody) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum ExistsRouteBodyHttpMethod { + #[serde(rename = "get")] + Get, + #[serde(rename = "post")] + Post, + #[serde(rename = "put")] + Put, + #[serde(rename = "delete")] + Delete, + #[serde(rename = "patch")] + Patch, + } + impl From<&ExistsRouteBodyHttpMethod> for ExistsRouteBodyHttpMethod { + fn from(value: &ExistsRouteBodyHttpMethod) -> Self { + value.clone() + } + } + impl ToString for ExistsRouteBodyHttpMethod { + fn to_string(&self) -> String { + match *self { + Self::Get => "get".to_string(), + Self::Post => "post".to_string(), + Self::Put => "put".to_string(), + Self::Delete => "delete".to_string(), + Self::Patch => "patch".to_string(), + } + } + } + impl std::str::FromStr for ExistsRouteBodyHttpMethod { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "get" => Ok(Self::Get), + "post" => Ok(Self::Post), + "put" => Ok(Self::Put), + "delete" => Ok(Self::Delete), + "patch" => Ok(Self::Patch), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for ExistsRouteBodyHttpMethod { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for ExistsRouteBodyHttpMethod { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for ExistsRouteBodyHttpMethod { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ExistsUsernameBody { + pub id: String, + pub username: String, + } + impl From<&ExistsUsernameBody> for ExistsUsernameBody { + fn from(value: &ExistsUsernameBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ExistsWorkspaceBody { + pub id: String, + } + impl From<&ExistsWorkspaceBody> for ExistsWorkspaceBody { + fn from(value: &ExistsWorkspaceBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ExportedInstanceGroup { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub emails: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub external_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub id: Option, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub scim_display_name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&ExportedInstanceGroup> for ExportedInstanceGroup { + fn from(value: &ExportedInstanceGroup) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ExportedUser { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub company: Option, + pub email: String, + pub first_time_user: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub password_hash: Option, + pub super_admin: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub username: Option, + pub verified: bool, + } + impl From<&ExportedUser> for ExportedUser { + fn from(value: &ExportedUser) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ExtendedJobs { + pub jobs: Vec, + pub obscured_jobs: Vec, + ///Obscured jobs omitted for security because of too specific filtering + #[serde(default, skip_serializing_if = "Option::is_none")] + pub omitted_obscured_jobs: Option, + } + impl From<&ExtendedJobs> for ExtendedJobs { + fn from(value: &ExtendedJobs) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ExtraPerms(pub std::collections::HashMap); + impl std::ops::Deref for ExtraPerms { + type Target = std::collections::HashMap; + fn deref(&self) -> &std::collections::HashMap { + &self.0 + } + } + impl From for std::collections::HashMap { + fn from(value: ExtraPerms) -> Self { + value.0 + } + } + impl From<&ExtraPerms> for ExtraPerms { + fn from(value: &ExtraPerms) -> Self { + value.clone() + } + } + impl From> for ExtraPerms { + fn from(value: std::collections::HashMap) -> Self { + Self(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FileUploadResponse { + pub file_key: String, + } + impl From<&FileUploadResponse> for FileUploadResponse { + fn from(value: &FileUploadResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Flow { + #[serde(flatten)] + pub open_flow: OpenFlow, + #[serde(flatten)] + pub flow_metadata: FlowMetadata, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock_error_logs: Option, + } + impl From<&Flow> for Flow { + fn from(value: &Flow) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowMetadata { + pub archived: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dedicated_worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft_only: Option, + pub edited_at: chrono::DateTime, + pub edited_by: String, + pub extra_perms: ExtraPerms, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_behalf_of_email: Option, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub starred: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub visible_to_runner_only: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&FlowMetadata> for FlowMetadata { + fn from(value: &FlowMetadata) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModule { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cache_ttl: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub continue_on_error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub delete_after_use: Option, + pub id: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mock: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub retry: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub skip_if: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sleep: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stop_after_all_iters_if: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stop_after_if: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub suspend: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeout: Option, + pub value: FlowModuleValue, + } + impl From<&FlowModule> for FlowModule { + fn from(value: &FlowModule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModuleMock { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub return_value: Option, + } + impl From<&FlowModuleMock> for FlowModuleMock { + fn from(value: &FlowModuleMock) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModuleSkipIf { + pub expr: String, + } + impl From<&FlowModuleSkipIf> for FlowModuleSkipIf { + fn from(value: &FlowModuleSkipIf) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModuleStopAfterAllItersIf { + pub expr: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub skip_if_stopped: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error_message: Option + } + impl From<&FlowModuleStopAfterAllItersIf> for FlowModuleStopAfterAllItersIf { + fn from(value: &FlowModuleStopAfterAllItersIf) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModuleStopAfterIf { + pub expr: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub skip_if_stopped: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error_message: Option + } + impl From<&FlowModuleStopAfterIf> for FlowModuleStopAfterIf { + fn from(value: &FlowModuleStopAfterIf) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModuleSuspend { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub continue_on_disapprove_timeout: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hide_cancel: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub required_events: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub resume_form: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub self_approval_disabled: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub user_auth_required: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub user_groups_required: Option, + } + impl From<&FlowModuleSuspend> for FlowModuleSuspend { + fn from(value: &FlowModuleSuspend) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModuleSuspendResumeForm { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub schema: std::collections::HashMap, + } + impl From<&FlowModuleSuspendResumeForm> for FlowModuleSuspendResumeForm { + fn from(value: &FlowModuleSuspendResumeForm) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum FlowModuleValue { + RawScript(RawScript), + PathScript(PathScript), + PathFlow(PathFlow), + ForloopFlow(ForloopFlow), + WhileloopFlow(WhileloopFlow), + BranchOne(BranchOne), + BranchAll(BranchAll), + Identity(Identity), + } + impl From<&FlowModuleValue> for FlowModuleValue { + fn from(value: &FlowModuleValue) -> Self { + value.clone() + } + } + impl From for FlowModuleValue { + fn from(value: RawScript) -> Self { + Self::RawScript(value) + } + } + impl From for FlowModuleValue { + fn from(value: PathScript) -> Self { + Self::PathScript(value) + } + } + impl From for FlowModuleValue { + fn from(value: PathFlow) -> Self { + Self::PathFlow(value) + } + } + impl From for FlowModuleValue { + fn from(value: ForloopFlow) -> Self { + Self::ForloopFlow(value) + } + } + impl From for FlowModuleValue { + fn from(value: WhileloopFlow) -> Self { + Self::WhileloopFlow(value) + } + } + impl From for FlowModuleValue { + fn from(value: BranchOne) -> Self { + Self::BranchOne(value) + } + } + impl From for FlowModuleValue { + fn from(value: BranchAll) -> Self { + Self::BranchAll(value) + } + } + impl From for FlowModuleValue { + fn from(value: Identity) -> Self { + Self::Identity(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowPreview { + pub args: ScriptArgs, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub restarted_from: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + pub value: FlowValue, + } + impl From<&FlowPreview> for FlowPreview { + fn from(value: &FlowPreview) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatus { + pub failure_module: FlowStatusFailureModule, + pub modules: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub preprocessor_module: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub retry: Option, + pub step: i64, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub user_states: std::collections::HashMap, + } + impl From<&FlowStatus> for FlowStatus { + fn from(value: &FlowStatus) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusFailureModule { + #[serde(flatten)] + pub flow_status_module: FlowStatusModule, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parent_module: Option, + } + impl From<&FlowStatusFailureModule> for FlowStatusFailureModule { + fn from(value: &FlowStatusFailureModule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusModule { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub approvers: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub branch_chosen: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub branchall: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub count: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub failed_retries: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub flow_jobs: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub flow_jobs_success: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub iterator: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub job: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub progress: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub skipped: Option, + #[serde(rename = "type")] + pub type_: FlowStatusModuleType, + } + impl From<&FlowStatusModule> for FlowStatusModule { + fn from(value: &FlowStatusModule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusModuleApproversItem { + pub approver: String, + pub resume_id: i64, + } + impl From<&FlowStatusModuleApproversItem> for FlowStatusModuleApproversItem { + fn from(value: &FlowStatusModuleApproversItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusModuleBranchChosen { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub branch: Option, + #[serde(rename = "type")] + pub type_: FlowStatusModuleBranchChosenType, + } + impl From<&FlowStatusModuleBranchChosen> for FlowStatusModuleBranchChosen { + fn from(value: &FlowStatusModuleBranchChosen) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum FlowStatusModuleBranchChosenType { + #[serde(rename = "branch")] + Branch, + #[serde(rename = "default")] + Default, + } + impl From<&FlowStatusModuleBranchChosenType> for FlowStatusModuleBranchChosenType { + fn from(value: &FlowStatusModuleBranchChosenType) -> Self { + value.clone() + } + } + impl ToString for FlowStatusModuleBranchChosenType { + fn to_string(&self) -> String { + match *self { + Self::Branch => "branch".to_string(), + Self::Default => "default".to_string(), + } + } + } + impl std::str::FromStr for FlowStatusModuleBranchChosenType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "branch" => Ok(Self::Branch), + "default" => Ok(Self::Default), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for FlowStatusModuleBranchChosenType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for FlowStatusModuleBranchChosenType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for FlowStatusModuleBranchChosenType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusModuleBranchall { + pub branch: i64, + pub len: i64, + } + impl From<&FlowStatusModuleBranchall> for FlowStatusModuleBranchall { + fn from(value: &FlowStatusModuleBranchall) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusModuleIterator { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub index: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub itered: Vec, + } + impl From<&FlowStatusModuleIterator> for FlowStatusModuleIterator { + fn from(value: &FlowStatusModuleIterator) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum FlowStatusModuleType { + WaitingForPriorSteps, + WaitingForEvents, + WaitingForExecutor, + InProgress, + Success, + Failure, + } + impl From<&FlowStatusModuleType> for FlowStatusModuleType { + fn from(value: &FlowStatusModuleType) -> Self { + value.clone() + } + } + impl ToString for FlowStatusModuleType { + fn to_string(&self) -> String { + match *self { + Self::WaitingForPriorSteps => "WaitingForPriorSteps".to_string(), + Self::WaitingForEvents => "WaitingForEvents".to_string(), + Self::WaitingForExecutor => "WaitingForExecutor".to_string(), + Self::InProgress => "InProgress".to_string(), + Self::Success => "Success".to_string(), + Self::Failure => "Failure".to_string(), + } + } + } + impl std::str::FromStr for FlowStatusModuleType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "WaitingForPriorSteps" => Ok(Self::WaitingForPriorSteps), + "WaitingForEvents" => Ok(Self::WaitingForEvents), + "WaitingForExecutor" => Ok(Self::WaitingForExecutor), + "InProgress" => Ok(Self::InProgress), + "Success" => Ok(Self::Success), + "Failure" => Ok(Self::Failure), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for FlowStatusModuleType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for FlowStatusModuleType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for FlowStatusModuleType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusRetry { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub fail_count: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub failed_jobs: Vec, + } + impl From<&FlowStatusRetry> for FlowStatusRetry { + fn from(value: &FlowStatusRetry) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowValue { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cache_ttl: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_key: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_time_window_s: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrent_limit: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub early_return: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub failure_module: Option, + pub modules: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub preprocessor_module: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub same_worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub skip_expr: Option, + } + impl From<&FlowValue> for FlowValue { + fn from(value: &FlowValue) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowVersion { + pub created_at: chrono::DateTime, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_msg: Option, + pub id: i64, + } + impl From<&FlowVersion> for FlowVersion { + fn from(value: &FlowVersion) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Folder { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub edited_at: Option>, + pub extra_perms: std::collections::HashMap, + pub name: String, + pub owners: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&Folder> for Folder { + fn from(value: &Folder) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ForceCancelQueuedJobBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub reason: Option, + } + impl From<&ForceCancelQueuedJobBody> for ForceCancelQueuedJobBody { + fn from(value: &ForceCancelQueuedJobBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ForloopFlow { + pub iterator: InputTransform, + pub modules: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parallel: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parallelism: Option, + pub skip_failures: bool, + #[serde(rename = "type")] + pub type_: ForloopFlowType, + } + impl From<&ForloopFlow> for ForloopFlow { + fn from(value: &ForloopFlow) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum ForloopFlowType { + #[serde(rename = "forloopflow")] + Forloopflow, + } + impl From<&ForloopFlowType> for ForloopFlowType { + fn from(value: &ForloopFlowType) -> Self { + value.clone() + } + } + impl ToString for ForloopFlowType { + fn to_string(&self) -> String { + match *self { + Self::Forloopflow => "forloopflow".to_string(), + } + } + } + impl std::str::FromStr for ForloopFlowType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "forloopflow" => Ok(Self::Forloopflow), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for ForloopFlowType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for ForloopFlowType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for ForloopFlowType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum GetCaptureConfigsRunnableKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "flow")] + Flow, + } + impl From<&GetCaptureConfigsRunnableKind> for GetCaptureConfigsRunnableKind { + fn from(value: &GetCaptureConfigsRunnableKind) -> Self { + value.clone() + } + } + impl ToString for GetCaptureConfigsRunnableKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Flow => "flow".to_string(), + } + } + } + impl std::str::FromStr for GetCaptureConfigsRunnableKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "flow" => Ok(Self::Flow), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for GetCaptureConfigsRunnableKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for GetCaptureConfigsRunnableKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for GetCaptureConfigsRunnableKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetCompletedCountResponse { + pub database_length: i64, + } + impl From<&GetCompletedCountResponse> for GetCompletedCountResponse { + fn from(value: &GetCompletedCountResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetCompletedJobResultMaybeResponse { + pub completed: bool, + pub result: serde_json::Value, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub started: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub success: Option, + } + impl From<&GetCompletedJobResultMaybeResponse> + for GetCompletedJobResultMaybeResponse { + fn from(value: &GetCompletedJobResultMaybeResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetCriticalAlertsResponse { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub alerts: Vec, + ///Total number of pages based on the page size. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub total_pages: Option, + ///Total number of rows matching the query. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub total_rows: Option, + } + impl From<&GetCriticalAlertsResponse> for GetCriticalAlertsResponse { + fn from(value: &GetCriticalAlertsResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetDeployToResponse { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deploy_to: Option, + } + impl From<&GetDeployToResponse> for GetDeployToResponse { + fn from(value: &GetDeployToResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetFlowByPathWithDraftResponse { + #[serde(flatten)] + pub flow: Flow, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft: Option, + } + impl From<&GetFlowByPathWithDraftResponse> for GetFlowByPathWithDraftResponse { + fn from(value: &GetFlowByPathWithDraftResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetFlowDeploymentStatusResponse { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock_error_logs: Option, + } + impl From<&GetFlowDeploymentStatusResponse> for GetFlowDeploymentStatusResponse { + fn from(value: &GetFlowDeploymentStatusResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetFolderUsageResponse { + pub apps: f64, + pub flows: f64, + pub resources: f64, + pub schedules: f64, + pub scripts: f64, + pub variables: f64, + } + impl From<&GetFolderUsageResponse> for GetFolderUsageResponse { + fn from(value: &GetFolderUsageResponse) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum GetGranularAclsKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "group_")] + Group, + #[serde(rename = "resource")] + Resource, + #[serde(rename = "schedule")] + Schedule, + #[serde(rename = "variable")] + Variable, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "folder")] + Folder, + #[serde(rename = "app")] + App, + #[serde(rename = "raw_app")] + RawApp, + #[serde(rename = "http_trigger")] + HttpTrigger, + #[serde(rename = "websocket_trigger")] + WebsocketTrigger, + #[serde(rename = "kafka_trigger")] + KafkaTrigger, + #[serde(rename = "nats_trigger")] + NatsTrigger, + #[serde(rename = "postgres_trigger")] + PostgresTrigger, + #[serde(rename = "mqtt_trigger")] + MqttTrigger, + #[serde(rename = "sqs_trigger")] + SqsTrigger, + } + impl From<&GetGranularAclsKind> for GetGranularAclsKind { + fn from(value: &GetGranularAclsKind) -> Self { + value.clone() + } + } + impl ToString for GetGranularAclsKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Group => "group_".to_string(), + Self::Resource => "resource".to_string(), + Self::Schedule => "schedule".to_string(), + Self::Variable => "variable".to_string(), + Self::Flow => "flow".to_string(), + Self::Folder => "folder".to_string(), + Self::App => "app".to_string(), + Self::RawApp => "raw_app".to_string(), + Self::HttpTrigger => "http_trigger".to_string(), + Self::WebsocketTrigger => "websocket_trigger".to_string(), + Self::KafkaTrigger => "kafka_trigger".to_string(), + Self::NatsTrigger => "nats_trigger".to_string(), + Self::PostgresTrigger => "postgres_trigger".to_string(), + Self::MqttTrigger => "mqtt_trigger".to_string(), + Self::SqsTrigger => "sqs_trigger".to_string(), + } + } + } + impl std::str::FromStr for GetGranularAclsKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "group_" => Ok(Self::Group), + "resource" => Ok(Self::Resource), + "schedule" => Ok(Self::Schedule), + "variable" => Ok(Self::Variable), + "flow" => Ok(Self::Flow), + "folder" => Ok(Self::Folder), + "app" => Ok(Self::App), + "raw_app" => Ok(Self::RawApp), + "http_trigger" => Ok(Self::HttpTrigger), + "websocket_trigger" => Ok(Self::WebsocketTrigger), + "kafka_trigger" => Ok(Self::KafkaTrigger), + "nats_trigger" => Ok(Self::NatsTrigger), + "postgres_trigger" => Ok(Self::PostgresTrigger), + "mqtt_trigger" => Ok(Self::MqttTrigger), + "sqs_trigger" => Ok(Self::SqsTrigger), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for GetGranularAclsKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for GetGranularAclsKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for GetGranularAclsKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetHubAppByIdResponse { + pub app: GetHubAppByIdResponseApp, + } + impl From<&GetHubAppByIdResponse> for GetHubAppByIdResponse { + fn from(value: &GetHubAppByIdResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetHubAppByIdResponseApp { + pub summary: String, + pub value: serde_json::Value, + } + impl From<&GetHubAppByIdResponseApp> for GetHubAppByIdResponseApp { + fn from(value: &GetHubAppByIdResponseApp) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetHubFlowByIdResponse { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub flow: Option, + } + impl From<&GetHubFlowByIdResponse> for GetHubFlowByIdResponse { + fn from(value: &GetHubFlowByIdResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetHubScriptByPathResponse { + pub content: String, + pub language: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lockfile: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schema: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&GetHubScriptByPathResponse> for GetHubScriptByPathResponse { + fn from(value: &GetHubScriptByPathResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetJobMetricsBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub from_timestamp: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeseries_max_datapoints: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub to_timestamp: Option>, + } + impl From<&GetJobMetricsBody> for GetJobMetricsBody { + fn from(value: &GetJobMetricsBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetJobMetricsResponse { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub metrics_metadata: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub scalar_metrics: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub timeseries_metrics: Vec, + } + impl From<&GetJobMetricsResponse> for GetJobMetricsResponse { + fn from(value: &GetJobMetricsResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetJobUpdatesResponse { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub completed: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub flow_status: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub log_offset: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mem_peak: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub new_logs: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub progress: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub running: Option, + } + impl From<&GetJobUpdatesResponse> for GetJobUpdatesResponse { + fn from(value: &GetJobUpdatesResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetLatestKeyRenewalAttemptResponse { + pub attempted_at: chrono::DateTime, + pub result: String, + } + impl From<&GetLatestKeyRenewalAttemptResponse> + for GetLatestKeyRenewalAttemptResponse { + fn from(value: &GetLatestKeyRenewalAttemptResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetOAuthConnectResponse { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub extra_params: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub scopes: Vec, + } + impl From<&GetOAuthConnectResponse> for GetOAuthConnectResponse { + fn from(value: &GetOAuthConnectResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetPremiumInfoResponse { + pub automatic_billing: bool, + pub owner: String, + pub premium: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub seats: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub usage: Option, + } + impl From<&GetPremiumInfoResponse> for GetPremiumInfoResponse { + fn from(value: &GetPremiumInfoResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetQueueCountResponse { + pub database_length: i64, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub suspended: Option, + } + impl From<&GetQueueCountResponse> for GetQueueCountResponse { + fn from(value: &GetQueueCountResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetQueueMetricsResponseItem { + pub id: String, + pub values: Vec, + } + impl From<&GetQueueMetricsResponseItem> for GetQueueMetricsResponseItem { + fn from(value: &GetQueueMetricsResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetQueueMetricsResponseItemValuesItem { + pub created_at: String, + pub value: f64, + } + impl From<&GetQueueMetricsResponseItemValuesItem> + for GetQueueMetricsResponseItemValuesItem { + fn from(value: &GetQueueMetricsResponseItemValuesItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetResumeUrlsResponse { + #[serde(rename = "approvalPage")] + pub approval_page: String, + pub cancel: String, + pub resume: String, + } + impl From<&GetResumeUrlsResponse> for GetResumeUrlsResponse { + fn from(value: &GetResumeUrlsResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetRunnableResponse { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + pub endpoint_async: String, + pub endpoint_openai_sync: String, + pub endpoint_sync: String, + pub kind: String, + pub summary: String, + pub workspace: String, + } + impl From<&GetRunnableResponse> for GetRunnableResponse { + fn from(value: &GetRunnableResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetScriptDeploymentStatusResponse { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock_error_logs: Option, + } + impl From<&GetScriptDeploymentStatusResponse> for GetScriptDeploymentStatusResponse { + fn from(value: &GetScriptDeploymentStatusResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetSettingsResponse { + pub ai_models: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ai_resource: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub auto_add: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub auto_invite_domain: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub auto_invite_operator: Option, + pub automatic_billing: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub code_completion_model: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub color: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub customer_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub default_app: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub default_scripts: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deploy_to: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deploy_ui: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error_handler: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error_handler_extra_args: Option, + pub error_handler_muted_on_cancel: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub git_sync: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub large_file_storage: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mute_critical_alerts: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub operator_settings: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub plan: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub slack_command_script: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub slack_name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub slack_team_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub teams_command_script: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub teams_team_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub teams_team_name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub webhook: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&GetSettingsResponse> for GetSettingsResponse { + fn from(value: &GetSettingsResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetSuspendedJobFlowResponse { + pub approvers: Vec, + pub job: Job, + } + impl From<&GetSuspendedJobFlowResponse> for GetSuspendedJobFlowResponse { + fn from(value: &GetSuspendedJobFlowResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetSuspendedJobFlowResponseApproversItem { + pub approver: String, + pub resume_id: i64, + } + impl From<&GetSuspendedJobFlowResponseApproversItem> + for GetSuspendedJobFlowResponseApproversItem { + fn from(value: &GetSuspendedJobFlowResponseApproversItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetThresholdAlertResponse { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_alert_sent: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub threshold_alert_amount: Option, + } + impl From<&GetThresholdAlertResponse> for GetThresholdAlertResponse { + fn from(value: &GetThresholdAlertResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetTopHubScriptsResponse { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub asks: Vec, + } + impl From<&GetTopHubScriptsResponse> for GetTopHubScriptsResponse { + fn from(value: &GetTopHubScriptsResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetTopHubScriptsResponseAsksItem { + pub app: String, + pub ask_id: f64, + pub id: f64, + pub kind: HubScriptKind, + pub summary: String, + pub version_id: f64, + pub views: f64, + pub votes: f64, + } + impl From<&GetTopHubScriptsResponseAsksItem> for GetTopHubScriptsResponseAsksItem { + fn from(value: &GetTopHubScriptsResponseAsksItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetTutorialProgressResponse { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub progress: Option, + } + impl From<&GetTutorialProgressResponse> for GetTutorialProgressResponse { + fn from(value: &GetTutorialProgressResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetUsedTriggersResponse { + pub http_routes_used: bool, + pub kafka_used: bool, + pub mqtt_used: bool, + pub nats_used: bool, + pub postgres_used: bool, + pub sqs_used: bool, + pub websocket_used: bool, + } + impl From<&GetUsedTriggersResponse> for GetUsedTriggersResponse { + fn from(value: &GetUsedTriggersResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetWorkspaceDefaultAppResponse { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub default_app_path: Option, + } + impl From<&GetWorkspaceDefaultAppResponse> for GetWorkspaceDefaultAppResponse { + fn from(value: &GetWorkspaceDefaultAppResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetWorkspaceEncryptionKeyResponse { + pub key: String, + } + impl From<&GetWorkspaceEncryptionKeyResponse> for GetWorkspaceEncryptionKeyResponse { + fn from(value: &GetWorkspaceEncryptionKeyResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GitRepositorySettings { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub exclude_types_override: Vec, + pub git_repo_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub group_by_folder: Option, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub use_individual_branch: Option, + } + impl From<&GitRepositorySettings> for GitRepositorySettings { + fn from(value: &GitRepositorySettings) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum GitRepositorySettingsExcludeTypesOverrideItem { + #[serde(rename = "script")] + Script, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "app")] + App, + #[serde(rename = "folder")] + Folder, + #[serde(rename = "resource")] + Resource, + #[serde(rename = "variable")] + Variable, + #[serde(rename = "secret")] + Secret, + #[serde(rename = "resourcetype")] + Resourcetype, + #[serde(rename = "schedule")] + Schedule, + #[serde(rename = "user")] + User, + #[serde(rename = "group")] + Group, + } + impl From<&GitRepositorySettingsExcludeTypesOverrideItem> + for GitRepositorySettingsExcludeTypesOverrideItem { + fn from(value: &GitRepositorySettingsExcludeTypesOverrideItem) -> Self { + value.clone() + } + } + impl ToString for GitRepositorySettingsExcludeTypesOverrideItem { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Flow => "flow".to_string(), + Self::App => "app".to_string(), + Self::Folder => "folder".to_string(), + Self::Resource => "resource".to_string(), + Self::Variable => "variable".to_string(), + Self::Secret => "secret".to_string(), + Self::Resourcetype => "resourcetype".to_string(), + Self::Schedule => "schedule".to_string(), + Self::User => "user".to_string(), + Self::Group => "group".to_string(), + } + } + } + impl std::str::FromStr for GitRepositorySettingsExcludeTypesOverrideItem { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "flow" => Ok(Self::Flow), + "app" => Ok(Self::App), + "folder" => Ok(Self::Folder), + "resource" => Ok(Self::Resource), + "variable" => Ok(Self::Variable), + "secret" => Ok(Self::Secret), + "resourcetype" => Ok(Self::Resourcetype), + "schedule" => Ok(Self::Schedule), + "user" => Ok(Self::User), + "group" => Ok(Self::Group), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for GitRepositorySettingsExcludeTypesOverrideItem { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> + for GitRepositorySettingsExcludeTypesOverrideItem { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom + for GitRepositorySettingsExcludeTypesOverrideItem { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GlobalSetting { + pub name: String, + pub value: std::collections::HashMap, + } + impl From<&GlobalSetting> for GlobalSetting { + fn from(value: &GlobalSetting) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GlobalUserInfo { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub company: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub devops: Option, + pub email: String, + pub login_type: GlobalUserInfoLoginType, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub operator_only: Option, + pub super_admin: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub username: Option, + pub verified: bool, + } + impl From<&GlobalUserInfo> for GlobalUserInfo { + fn from(value: &GlobalUserInfo) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum GlobalUserInfoLoginType { + #[serde(rename = "password")] + Password, + #[serde(rename = "github")] + Github, + } + impl From<&GlobalUserInfoLoginType> for GlobalUserInfoLoginType { + fn from(value: &GlobalUserInfoLoginType) -> Self { + value.clone() + } + } + impl ToString for GlobalUserInfoLoginType { + fn to_string(&self) -> String { + match *self { + Self::Password => "password".to_string(), + Self::Github => "github".to_string(), + } + } + } + impl std::str::FromStr for GlobalUserInfoLoginType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "password" => Ok(Self::Password), + "github" => Ok(Self::Github), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for GlobalUserInfoLoginType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for GlobalUserInfoLoginType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for GlobalUserInfoLoginType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GlobalUserRenameBody { + pub new_username: String, + } + impl From<&GlobalUserRenameBody> for GlobalUserRenameBody { + fn from(value: &GlobalUserRenameBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GlobalUserUpdateBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_devops: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_super_admin: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + } + impl From<&GlobalUserUpdateBody> for GlobalUserUpdateBody { + fn from(value: &GlobalUserUpdateBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GlobalUsernameInfoResponse { + pub username: String, + pub workspace_usernames: Vec, + } + impl From<&GlobalUsernameInfoResponse> for GlobalUsernameInfoResponse { + fn from(value: &GlobalUsernameInfoResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GlobalUsernameInfoResponseWorkspaceUsernamesItem { + pub username: String, + pub workspace_id: String, + } + impl From<&GlobalUsernameInfoResponseWorkspaceUsernamesItem> + for GlobalUsernameInfoResponseWorkspaceUsernamesItem { + fn from(value: &GlobalUsernameInfoResponseWorkspaceUsernamesItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Group { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub extra_perms: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub members: Vec, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&Group> for Group { + fn from(value: &Group) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct HttpTrigger { + pub http_method: HttpTriggerHttpMethod, + pub is_async: bool, + pub is_static_website: bool, + pub raw_string: bool, + pub requires_auth: bool, + pub route_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub static_asset_config: Option, + pub workspaced_route: bool, + pub wrap_body: bool, + } + impl From<&HttpTrigger> for HttpTrigger { + fn from(value: &HttpTrigger) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum HttpTriggerHttpMethod { + #[serde(rename = "get")] + Get, + #[serde(rename = "post")] + Post, + #[serde(rename = "put")] + Put, + #[serde(rename = "delete")] + Delete, + #[serde(rename = "patch")] + Patch, + } + impl From<&HttpTriggerHttpMethod> for HttpTriggerHttpMethod { + fn from(value: &HttpTriggerHttpMethod) -> Self { + value.clone() + } + } + impl ToString for HttpTriggerHttpMethod { + fn to_string(&self) -> String { + match *self { + Self::Get => "get".to_string(), + Self::Post => "post".to_string(), + Self::Put => "put".to_string(), + Self::Delete => "delete".to_string(), + Self::Patch => "patch".to_string(), + } + } + } + impl std::str::FromStr for HttpTriggerHttpMethod { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "get" => Ok(Self::Get), + "post" => Ok(Self::Post), + "put" => Ok(Self::Put), + "delete" => Ok(Self::Delete), + "patch" => Ok(Self::Patch), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for HttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for HttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for HttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct HttpTriggerStaticAssetConfig { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub filename: Option, + pub s3: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub storage: Option, + } + impl From<&HttpTriggerStaticAssetConfig> for HttpTriggerStaticAssetConfig { + fn from(value: &HttpTriggerStaticAssetConfig) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct HubScriptKind(pub serde_json::Value); + impl std::ops::Deref for HubScriptKind { + type Target = serde_json::Value; + fn deref(&self) -> &serde_json::Value { + &self.0 + } + } + impl From for serde_json::Value { + fn from(value: HubScriptKind) -> Self { + value.0 + } + } + impl From<&HubScriptKind> for HubScriptKind { + fn from(value: &HubScriptKind) -> Self { + value.clone() + } + } + impl From for HubScriptKind { + fn from(value: serde_json::Value) -> Self { + Self(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Identity { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub flow: Option, + #[serde(rename = "type")] + pub type_: IdentityType, + } + impl From<&Identity> for Identity { + fn from(value: &Identity) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum IdentityType { + #[serde(rename = "identity")] + Identity, + } + impl From<&IdentityType> for IdentityType { + fn from(value: &IdentityType) -> Self { + value.clone() + } + } + impl ToString for IdentityType { + fn to_string(&self) -> String { + match *self { + Self::Identity => "identity".to_string(), + } + } + } + impl std::str::FromStr for IdentityType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "identity" => Ok(Self::Identity), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for IdentityType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for IdentityType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for IdentityType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Input { + pub created_at: chrono::DateTime, + pub created_by: String, + pub id: String, + pub is_public: bool, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub success: Option, + } + impl From<&Input> for Input { + fn from(value: &Input) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum InputTransform { + StaticTransform(StaticTransform), + JavascriptTransform(JavascriptTransform), + } + impl From<&InputTransform> for InputTransform { + fn from(value: &InputTransform) -> Self { + value.clone() + } + } + impl From for InputTransform { + fn from(value: StaticTransform) -> Self { + Self::StaticTransform(value) + } + } + impl From for InputTransform { + fn from(value: JavascriptTransform) -> Self { + Self::JavascriptTransform(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct InstanceGroup { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub emails: Vec, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&InstanceGroup> for InstanceGroup { + fn from(value: &InstanceGroup) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct InviteUserBody { + pub email: String, + pub is_admin: bool, + pub operator: bool, + } + impl From<&InviteUserBody> for InviteUserBody { + fn from(value: &InviteUserBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct JavascriptTransform { + pub expr: String, + #[serde(rename = "type")] + pub type_: JavascriptTransformType, + } + impl From<&JavascriptTransform> for JavascriptTransform { + fn from(value: &JavascriptTransform) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum JavascriptTransformType { + #[serde(rename = "javascript")] + Javascript, + } + impl From<&JavascriptTransformType> for JavascriptTransformType { + fn from(value: &JavascriptTransformType) -> Self { + value.clone() + } + } + impl ToString for JavascriptTransformType { + fn to_string(&self) -> String { + match *self { + Self::Javascript => "javascript".to_string(), + } + } + } + impl std::str::FromStr for JavascriptTransformType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "javascript" => Ok(Self::Javascript), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for JavascriptTransformType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for JavascriptTransformType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for JavascriptTransformType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum Job { + Variant0(JobVariant0), + Variant1(JobVariant1), + } + impl From<&Job> for Job { + fn from(value: &Job) -> Self { + value.clone() + } + } + impl From for Job { + fn from(value: JobVariant0) -> Self { + Self::Variant0(value) + } + } + impl From for Job { + fn from(value: JobVariant1) -> Self { + Self::Variant1(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct JobSearchHit { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dancer: Option, + } + impl From<&JobSearchHit> for JobSearchHit { + fn from(value: &JobSearchHit) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct JobVariant0 { + #[serde(flatten)] + pub completed_job: CompletedJob, + #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")] + pub type_: Option, + } + impl From<&JobVariant0> for JobVariant0 { + fn from(value: &JobVariant0) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum JobVariant0Type { + CompletedJob, + } + impl From<&JobVariant0Type> for JobVariant0Type { + fn from(value: &JobVariant0Type) -> Self { + value.clone() + } + } + impl ToString for JobVariant0Type { + fn to_string(&self) -> String { + match *self { + Self::CompletedJob => "CompletedJob".to_string(), + } + } + } + impl std::str::FromStr for JobVariant0Type { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "CompletedJob" => Ok(Self::CompletedJob), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for JobVariant0Type { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for JobVariant0Type { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for JobVariant0Type { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct JobVariant1 { + #[serde(flatten)] + pub queued_job: QueuedJob, + #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")] + pub type_: Option, + } + impl From<&JobVariant1> for JobVariant1 { + fn from(value: &JobVariant1) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum JobVariant1Type { + QueuedJob, + } + impl From<&JobVariant1Type> for JobVariant1Type { + fn from(value: &JobVariant1Type) -> Self { + value.clone() + } + } + impl ToString for JobVariant1Type { + fn to_string(&self) -> String { + match *self { + Self::QueuedJob => "QueuedJob".to_string(), + } + } + } + impl std::str::FromStr for JobVariant1Type { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "QueuedJob" => Ok(Self::QueuedJob), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for JobVariant1Type { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for JobVariant1Type { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for JobVariant1Type { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct KafkaTrigger { + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + pub group_id: String, + pub kafka_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + pub topics: Vec, + } + impl From<&KafkaTrigger> for KafkaTrigger { + fn from(value: &KafkaTrigger) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum Language { + Typescript, + } + impl From<&Language> for Language { + fn from(value: &Language) -> Self { + value.clone() + } + } + impl ToString for Language { + fn to_string(&self) -> String { + match *self { + Self::Typescript => "Typescript".to_string(), + } + } + } + impl std::str::FromStr for Language { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "Typescript" => Ok(Self::Typescript), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for Language { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for Language { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for Language { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct LargeFileStorage { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub azure_blob_resource_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub public_resource: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub s3_resource_path: Option, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub secondary_storage: std::collections::HashMap< + String, + LargeFileStorageSecondaryStorageValue, + >, + #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")] + pub type_: Option, + } + impl From<&LargeFileStorage> for LargeFileStorage { + fn from(value: &LargeFileStorage) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct LargeFileStorageSecondaryStorageValue { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub azure_blob_resource_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub public_resource: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub s3_resource_path: Option, + #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")] + pub type_: Option, + } + impl From<&LargeFileStorageSecondaryStorageValue> + for LargeFileStorageSecondaryStorageValue { + fn from(value: &LargeFileStorageSecondaryStorageValue) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum LargeFileStorageSecondaryStorageValueType { + S3Storage, + AzureBlobStorage, + AzureWorkloadIdentity, + S3AwsOidc, + } + impl From<&LargeFileStorageSecondaryStorageValueType> + for LargeFileStorageSecondaryStorageValueType { + fn from(value: &LargeFileStorageSecondaryStorageValueType) -> Self { + value.clone() + } + } + impl ToString for LargeFileStorageSecondaryStorageValueType { + fn to_string(&self) -> String { + match *self { + Self::S3Storage => "S3Storage".to_string(), + Self::AzureBlobStorage => "AzureBlobStorage".to_string(), + Self::AzureWorkloadIdentity => "AzureWorkloadIdentity".to_string(), + Self::S3AwsOidc => "S3AwsOidc".to_string(), + } + } + } + impl std::str::FromStr for LargeFileStorageSecondaryStorageValueType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "S3Storage" => Ok(Self::S3Storage), + "AzureBlobStorage" => Ok(Self::AzureBlobStorage), + "AzureWorkloadIdentity" => Ok(Self::AzureWorkloadIdentity), + "S3AwsOidc" => Ok(Self::S3AwsOidc), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for LargeFileStorageSecondaryStorageValueType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for LargeFileStorageSecondaryStorageValueType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for LargeFileStorageSecondaryStorageValueType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum LargeFileStorageType { + S3Storage, + AzureBlobStorage, + AzureWorkloadIdentity, + S3AwsOidc, + } + impl From<&LargeFileStorageType> for LargeFileStorageType { + fn from(value: &LargeFileStorageType) -> Self { + value.clone() + } + } + impl ToString for LargeFileStorageType { + fn to_string(&self) -> String { + match *self { + Self::S3Storage => "S3Storage".to_string(), + Self::AzureBlobStorage => "AzureBlobStorage".to_string(), + Self::AzureWorkloadIdentity => "AzureWorkloadIdentity".to_string(), + Self::S3AwsOidc => "S3AwsOidc".to_string(), + } + } + } + impl std::str::FromStr for LargeFileStorageType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "S3Storage" => Ok(Self::S3Storage), + "AzureBlobStorage" => Ok(Self::AzureBlobStorage), + "AzureWorkloadIdentity" => Ok(Self::AzureWorkloadIdentity), + "S3AwsOidc" => Ok(Self::S3AwsOidc), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for LargeFileStorageType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for LargeFileStorageType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for LargeFileStorageType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum ListAuditLogsActionKind { + Create, + Update, + Delete, + Execute, + } + impl From<&ListAuditLogsActionKind> for ListAuditLogsActionKind { + fn from(value: &ListAuditLogsActionKind) -> Self { + value.clone() + } + } + impl ToString for ListAuditLogsActionKind { + fn to_string(&self) -> String { + match *self { + Self::Create => "Create".to_string(), + Self::Update => "Update".to_string(), + Self::Delete => "Delete".to_string(), + Self::Execute => "Execute".to_string(), + } + } + } + impl std::str::FromStr for ListAuditLogsActionKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "Create" => Ok(Self::Create), + "Update" => Ok(Self::Update), + "Delete" => Ok(Self::Delete), + "Execute" => Ok(Self::Execute), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for ListAuditLogsActionKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for ListAuditLogsActionKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for ListAuditLogsActionKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListAvailableTeamsChannelsResponseItem { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub channel_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub channel_name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub service_url: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tenant_id: Option, + } + impl From<&ListAvailableTeamsChannelsResponseItem> + for ListAvailableTeamsChannelsResponseItem { + fn from(value: &ListAvailableTeamsChannelsResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListAvailableTeamsIdsResponseItem { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub team_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub team_name: Option, + } + impl From<&ListAvailableTeamsIdsResponseItem> for ListAvailableTeamsIdsResponseItem { + fn from(value: &ListAvailableTeamsIdsResponseItem) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum ListCapturesRunnableKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "flow")] + Flow, + } + impl From<&ListCapturesRunnableKind> for ListCapturesRunnableKind { + fn from(value: &ListCapturesRunnableKind) -> Self { + value.clone() + } + } + impl ToString for ListCapturesRunnableKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Flow => "flow".to_string(), + } + } + } + impl std::str::FromStr for ListCapturesRunnableKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "flow" => Ok(Self::Flow), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for ListCapturesRunnableKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for ListCapturesRunnableKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for ListCapturesRunnableKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum ListFlowPathsFromWorkspaceRunnableRunnableKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "flow")] + Flow, + } + impl From<&ListFlowPathsFromWorkspaceRunnableRunnableKind> + for ListFlowPathsFromWorkspaceRunnableRunnableKind { + fn from(value: &ListFlowPathsFromWorkspaceRunnableRunnableKind) -> Self { + value.clone() + } + } + impl ToString for ListFlowPathsFromWorkspaceRunnableRunnableKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Flow => "flow".to_string(), + } + } + } + impl std::str::FromStr for ListFlowPathsFromWorkspaceRunnableRunnableKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "flow" => Ok(Self::Flow), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for ListFlowPathsFromWorkspaceRunnableRunnableKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> + for ListFlowPathsFromWorkspaceRunnableRunnableKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom + for ListFlowPathsFromWorkspaceRunnableRunnableKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListFlowsResponseItem { + #[serde(flatten)] + pub flow: Flow, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft_only: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub has_draft: Option, + } + impl From<&ListFlowsResponseItem> for ListFlowsResponseItem { + fn from(value: &ListFlowsResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListHubAppsResponse { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub apps: Vec, + } + impl From<&ListHubAppsResponse> for ListHubAppsResponse { + fn from(value: &ListHubAppsResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListHubAppsResponseAppsItem { + pub app_id: f64, + pub approved: bool, + pub apps: Vec, + pub id: f64, + pub summary: String, + pub votes: f64, + } + impl From<&ListHubAppsResponseAppsItem> for ListHubAppsResponseAppsItem { + fn from(value: &ListHubAppsResponseAppsItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListHubFlowsResponse { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub flows: Vec, + } + impl From<&ListHubFlowsResponse> for ListHubFlowsResponse { + fn from(value: &ListHubFlowsResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListHubFlowsResponseFlowsItem { + pub approved: bool, + pub apps: Vec, + pub flow_id: f64, + pub id: f64, + pub summary: String, + pub votes: f64, + } + impl From<&ListHubFlowsResponseFlowsItem> for ListHubFlowsResponseFlowsItem { + fn from(value: &ListHubFlowsResponseFlowsItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListHubIntegrationsResponseItem { + pub name: String, + } + impl From<&ListHubIntegrationsResponseItem> for ListHubIntegrationsResponseItem { + fn from(value: &ListHubIntegrationsResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListLogFilesResponseItem { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub err_lines: Option, + pub file_path: String, + pub hostname: String, + pub json_fmt: bool, + pub log_ts: chrono::DateTime, + pub mode: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ok_lines: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub worker_group: Option, + } + impl From<&ListLogFilesResponseItem> for ListLogFilesResponseItem { + fn from(value: &ListLogFilesResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListOAuthLoginsResponse { + pub oauth: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub saml: Option, + } + impl From<&ListOAuthLoginsResponse> for ListOAuthLoginsResponse { + fn from(value: &ListOAuthLoginsResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListOAuthLoginsResponseOauthItem { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub display_name: Option, + #[serde(rename = "type")] + pub type_: String, + } + impl From<&ListOAuthLoginsResponseOauthItem> for ListOAuthLoginsResponseOauthItem { + fn from(value: &ListOAuthLoginsResponseOauthItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListResourceNamesResponseItem { + pub name: String, + pub path: String, + } + impl From<&ListResourceNamesResponseItem> for ListResourceNamesResponseItem { + fn from(value: &ListResourceNamesResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListSearchAppResponseItem { + pub path: String, + pub value: serde_json::Value, + } + impl From<&ListSearchAppResponseItem> for ListSearchAppResponseItem { + fn from(value: &ListSearchAppResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListSearchFlowResponseItem { + pub path: String, + pub value: serde_json::Value, + } + impl From<&ListSearchFlowResponseItem> for ListSearchFlowResponseItem { + fn from(value: &ListSearchFlowResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListSearchResourceResponseItem { + pub path: String, + pub value: serde_json::Value, + } + impl From<&ListSearchResourceResponseItem> for ListSearchResourceResponseItem { + fn from(value: &ListSearchResourceResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListSearchScriptResponseItem { + pub content: String, + pub path: String, + } + impl From<&ListSearchScriptResponseItem> for ListSearchScriptResponseItem { + fn from(value: &ListSearchScriptResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListStoredFilesResponse { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub next_marker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub restricted_access: Option, + pub windmill_large_files: Vec, + } + impl From<&ListStoredFilesResponse> for ListStoredFilesResponse { + fn from(value: &ListStoredFilesResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListWorkerGroupsResponseItem { + pub config: serde_json::Value, + pub name: String, + } + impl From<&ListWorkerGroupsResponseItem> for ListWorkerGroupsResponseItem { + fn from(value: &ListWorkerGroupsResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListableApp { + pub edited_at: chrono::DateTime, + pub execution_mode: ListableAppExecutionMode, + pub extra_perms: std::collections::HashMap, + pub id: i64, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub starred: Option, + pub summary: String, + pub version: i64, + pub workspace_id: String, + } + impl From<&ListableApp> for ListableApp { + fn from(value: &ListableApp) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum ListableAppExecutionMode { + #[serde(rename = "viewer")] + Viewer, + #[serde(rename = "publisher")] + Publisher, + #[serde(rename = "anonymous")] + Anonymous, + } + impl From<&ListableAppExecutionMode> for ListableAppExecutionMode { + fn from(value: &ListableAppExecutionMode) -> Self { + value.clone() + } + } + impl ToString for ListableAppExecutionMode { + fn to_string(&self) -> String { + match *self { + Self::Viewer => "viewer".to_string(), + Self::Publisher => "publisher".to_string(), + Self::Anonymous => "anonymous".to_string(), + } + } + } + impl std::str::FromStr for ListableAppExecutionMode { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "viewer" => Ok(Self::Viewer), + "publisher" => Ok(Self::Publisher), + "anonymous" => Ok(Self::Anonymous), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for ListableAppExecutionMode { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for ListableAppExecutionMode { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for ListableAppExecutionMode { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListableRawApp { + pub edited_at: chrono::DateTime, + pub extra_perms: std::collections::HashMap, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub starred: Option, + pub summary: String, + pub version: f64, + pub workspace_id: String, + } + impl From<&ListableRawApp> for ListableRawApp { + fn from(value: &ListableRawApp) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListableResource { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub account: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub edited_at: Option>, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub extra_perms: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_expired: Option, + pub is_linked: bool, + pub is_oauth: bool, + pub is_refreshed: bool, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub refresh_error: Option, + pub resource_type: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&ListableResource> for ListableResource { + fn from(value: &ListableResource) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListableVariable { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub account: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expires_at: Option>, + pub extra_perms: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_expired: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_linked: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_oauth: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_refreshed: Option, + pub is_secret: bool, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub refresh_error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + pub workspace_id: String, + } + impl From<&ListableVariable> for ListableVariable { + fn from(value: &ListableVariable) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct LoadTableRowCountResponse { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub count: Option, + } + impl From<&LoadTableRowCountResponse> for LoadTableRowCountResponse { + fn from(value: &LoadTableRowCountResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct LogSearchHit { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dancer: Option, + } + impl From<&LogSearchHit> for LogSearchHit { + fn from(value: &LogSearchHit) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Login { + pub email: String, + pub password: String, + } + impl From<&Login> for Login { + fn from(value: &Login) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct LoginWithOauthBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub code: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub state: Option, + } + impl From<&LoginWithOauthBody> for LoginWithOauthBody { + fn from(value: &LoginWithOauthBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MainArgSignature { + pub args: Vec, + pub error: String, + pub has_preprocessor: Option, + pub no_main_func: Option, + pub star_args: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub star_kwargs: Option, + #[serde(rename = "type")] + pub type_: MainArgSignatureType, + } + impl From<&MainArgSignature> for MainArgSignature { + fn from(value: &MainArgSignature) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MainArgSignatureArgsItem { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub default: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub has_default: Option, + pub name: String, + pub typ: MainArgSignatureArgsItemTyp, + } + impl From<&MainArgSignatureArgsItem> for MainArgSignatureArgsItem { + fn from(value: &MainArgSignatureArgsItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub enum MainArgSignatureArgsItemTyp { + #[serde(rename = "float")] + Float, + #[serde(rename = "int")] + Int, + #[serde(rename = "bool")] + Bool, + #[serde(rename = "email")] + Email, + #[serde(rename = "unknown")] + Unknown, + #[serde(rename = "bytes")] + Bytes, + #[serde(rename = "dict")] + Dict, + #[serde(rename = "datetime")] + Datetime, + #[serde(rename = "sql")] + Sql, + #[serde(rename = "resource")] + Resource(Option), + #[serde(rename = "str")] + Str(Option>), + #[serde(rename = "object")] + Object(Vec), + #[serde(rename = "list")] + List(MainArgSignatureArgsItemTypList), + } + impl From<&MainArgSignatureArgsItemTyp> for MainArgSignatureArgsItemTyp { + fn from(value: &MainArgSignatureArgsItemTyp) -> Self { + value.clone() + } + } + impl From> for MainArgSignatureArgsItemTyp { + fn from(value: Option) -> Self { + Self::Resource(value) + } + } + impl From>> for MainArgSignatureArgsItemTyp { + fn from(value: Option>) -> Self { + Self::Str(value) + } + } + impl From> + for MainArgSignatureArgsItemTyp { + fn from(value: Vec) -> Self { + Self::Object(value) + } + } + impl From for MainArgSignatureArgsItemTyp { + fn from(value: MainArgSignatureArgsItemTypList) -> Self { + Self::List(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub enum MainArgSignatureArgsItemTypList { + #[serde(rename = "float")] + Float, + #[serde(rename = "int")] + Int, + #[serde(rename = "bool")] + Bool, + #[serde(rename = "email")] + Email, + #[serde(rename = "unknown")] + Unknown, + #[serde(rename = "bytes")] + Bytes, + #[serde(rename = "dict")] + Dict, + #[serde(rename = "datetime")] + Datetime, + #[serde(rename = "sql")] + Sql, + #[serde(rename = "str")] + Str(serde_json::Value), + } + impl From<&MainArgSignatureArgsItemTypList> for MainArgSignatureArgsItemTypList { + fn from(value: &MainArgSignatureArgsItemTypList) -> Self { + value.clone() + } + } + impl From for MainArgSignatureArgsItemTypList { + fn from(value: serde_json::Value) -> Self { + Self::Str(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MainArgSignatureArgsItemTypObjectItem { + pub key: String, + pub typ: MainArgSignatureArgsItemTypObjectItemTyp, + } + impl From<&MainArgSignatureArgsItemTypObjectItem> + for MainArgSignatureArgsItemTypObjectItem { + fn from(value: &MainArgSignatureArgsItemTypObjectItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub enum MainArgSignatureArgsItemTypObjectItemTyp { + #[serde(rename = "float")] + Float, + #[serde(rename = "int")] + Int, + #[serde(rename = "bool")] + Bool, + #[serde(rename = "email")] + Email, + #[serde(rename = "unknown")] + Unknown, + #[serde(rename = "bytes")] + Bytes, + #[serde(rename = "dict")] + Dict, + #[serde(rename = "datetime")] + Datetime, + #[serde(rename = "sql")] + Sql, + #[serde(rename = "str")] + Str(serde_json::Value), + } + impl From<&MainArgSignatureArgsItemTypObjectItemTyp> + for MainArgSignatureArgsItemTypObjectItemTyp { + fn from(value: &MainArgSignatureArgsItemTypObjectItemTyp) -> Self { + value.clone() + } + } + impl From for MainArgSignatureArgsItemTypObjectItemTyp { + fn from(value: serde_json::Value) -> Self { + Self::Str(value) + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum MainArgSignatureType { + Valid, + Invalid, + } + impl From<&MainArgSignatureType> for MainArgSignatureType { + fn from(value: &MainArgSignatureType) -> Self { + value.clone() + } + } + impl ToString for MainArgSignatureType { + fn to_string(&self) -> String { + match *self { + Self::Valid => "Valid".to_string(), + Self::Invalid => "Invalid".to_string(), + } + } + } + impl std::str::FromStr for MainArgSignatureType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "Valid" => Ok(Self::Valid), + "Invalid" => Ok(Self::Invalid), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for MainArgSignatureType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for MainArgSignatureType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for MainArgSignatureType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MetricDataPoint { + pub timestamp: chrono::DateTime, + pub value: f64, + } + impl From<&MetricDataPoint> for MetricDataPoint { + fn from(value: &MetricDataPoint) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MetricMetadata { + pub id: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + } + impl From<&MetricMetadata> for MetricMetadata { + fn from(value: &MetricMetadata) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MoveCapturesAndConfigsBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub new_path: Option, + } + impl From<&MoveCapturesAndConfigsBody> for MoveCapturesAndConfigsBody { + fn from(value: &MoveCapturesAndConfigsBody) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum MoveCapturesAndConfigsRunnableKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "flow")] + Flow, + } + impl From<&MoveCapturesAndConfigsRunnableKind> + for MoveCapturesAndConfigsRunnableKind { + fn from(value: &MoveCapturesAndConfigsRunnableKind) -> Self { + value.clone() + } + } + impl ToString for MoveCapturesAndConfigsRunnableKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Flow => "flow".to_string(), + } + } + } + impl std::str::FromStr for MoveCapturesAndConfigsRunnableKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "flow" => Ok(Self::Flow), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for MoveCapturesAndConfigsRunnableKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for MoveCapturesAndConfigsRunnableKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for MoveCapturesAndConfigsRunnableKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum MqttClientVersion { + #[serde(rename = "v3")] + V3, + #[serde(rename = "v5")] + V5, + } + impl From<&MqttClientVersion> for MqttClientVersion { + fn from(value: &MqttClientVersion) -> Self { + value.clone() + } + } + impl ToString for MqttClientVersion { + fn to_string(&self) -> String { + match *self { + Self::V3 => "v3".to_string(), + Self::V5 => "v5".to_string(), + } + } + } + impl std::str::FromStr for MqttClientVersion { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "v3" => Ok(Self::V3), + "v5" => Ok(Self::V5), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for MqttClientVersion { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for MqttClientVersion { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for MqttClientVersion { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum MqttQoS { + #[serde(rename = "qos0")] + Qos0, + #[serde(rename = "qos1")] + Qos1, + #[serde(rename = "qos2")] + Qos2, + } + impl From<&MqttQoS> for MqttQoS { + fn from(value: &MqttQoS) -> Self { + value.clone() + } + } + impl ToString for MqttQoS { + fn to_string(&self) -> String { + match *self { + Self::Qos0 => "qos0".to_string(), + Self::Qos1 => "qos1".to_string(), + Self::Qos2 => "qos2".to_string(), + } + } + } + impl std::str::FromStr for MqttQoS { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "qos0" => Ok(Self::Qos0), + "qos1" => Ok(Self::Qos1), + "qos2" => Ok(Self::Qos2), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for MqttQoS { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for MqttQoS { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for MqttQoS { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MqttSubscribeTopic { + pub qos: MqttQoS, + pub topic: String, + } + impl From<&MqttSubscribeTopic> for MqttSubscribeTopic { + fn from(value: &MqttSubscribeTopic) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MqttTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub client_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub client_version: Option, + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + pub mqtt_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + pub subscribe_topics: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub v3_config: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub v5_config: Option, + } + impl From<&MqttTrigger> for MqttTrigger { + fn from(value: &MqttTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MqttV3Config { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub clean_session: Option, + } + impl From<&MqttV3Config> for MqttV3Config { + fn from(value: &MqttV3Config) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MqttV5Config { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub clean_start: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub session_expiry_interval: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub topic_alias: Option, + } + impl From<&MqttV5Config> for MqttV5Config { + fn from(value: &MqttV5Config) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NatsTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub consumer_name: Option, + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + pub nats_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stream_name: Option, + pub subjects: Vec, + pub use_jetstream: bool, + } + impl From<&NatsTrigger> for NatsTrigger { + fn from(value: &NatsTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewHttpTrigger { + pub http_method: NewHttpTriggerHttpMethod, + pub is_async: bool, + pub is_flow: bool, + pub is_static_website: bool, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_string: Option, + pub requires_auth: bool, + pub route_path: String, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub static_asset_config: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspaced_route: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub wrap_body: Option, + } + impl From<&NewHttpTrigger> for NewHttpTrigger { + fn from(value: &NewHttpTrigger) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum NewHttpTriggerHttpMethod { + #[serde(rename = "get")] + Get, + #[serde(rename = "post")] + Post, + #[serde(rename = "put")] + Put, + #[serde(rename = "delete")] + Delete, + #[serde(rename = "patch")] + Patch, + } + impl From<&NewHttpTriggerHttpMethod> for NewHttpTriggerHttpMethod { + fn from(value: &NewHttpTriggerHttpMethod) -> Self { + value.clone() + } + } + impl ToString for NewHttpTriggerHttpMethod { + fn to_string(&self) -> String { + match *self { + Self::Get => "get".to_string(), + Self::Post => "post".to_string(), + Self::Put => "put".to_string(), + Self::Delete => "delete".to_string(), + Self::Patch => "patch".to_string(), + } + } + } + impl std::str::FromStr for NewHttpTriggerHttpMethod { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "get" => Ok(Self::Get), + "post" => Ok(Self::Post), + "put" => Ok(Self::Put), + "delete" => Ok(Self::Delete), + "patch" => Ok(Self::Patch), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for NewHttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for NewHttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for NewHttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewHttpTriggerStaticAssetConfig { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub filename: Option, + pub s3: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub storage: Option, + } + impl From<&NewHttpTriggerStaticAssetConfig> for NewHttpTriggerStaticAssetConfig { + fn from(value: &NewHttpTriggerStaticAssetConfig) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewKafkaTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + pub group_id: String, + pub is_flow: bool, + pub kafka_resource_path: String, + pub path: String, + pub script_path: String, + pub topics: Vec, + } + impl From<&NewKafkaTrigger> for NewKafkaTrigger { + fn from(value: &NewKafkaTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewMqttTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub client_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub client_version: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + pub is_flow: bool, + pub mqtt_resource_path: String, + pub path: String, + pub script_path: String, + pub subscribe_topics: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub v3_config: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub v5_config: Option, + } + impl From<&NewMqttTrigger> for NewMqttTrigger { + fn from(value: &NewMqttTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewNatsTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub consumer_name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + pub is_flow: bool, + pub nats_resource_path: String, + pub path: String, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stream_name: Option, + pub subjects: Vec, + pub use_jetstream: bool, + } + impl From<&NewNatsTrigger> for NewNatsTrigger { + fn from(value: &NewNatsTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewPostgresTrigger { + pub enabled: bool, + pub is_flow: bool, + pub path: String, + pub postgres_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub publication: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub publication_name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub replication_slot_name: Option, + pub script_path: String, + } + impl From<&NewPostgresTrigger> for NewPostgresTrigger { + fn from(value: &NewPostgresTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewSchedule { + pub args: ScriptArgs, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cron_version: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + pub is_flow: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub no_flow_overlap: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_exact: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_times: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery_times: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_success: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_success_extra_args: Option, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub paused_until: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub retry: Option, + pub schedule: String, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + pub timezone: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&NewSchedule> for NewSchedule { + fn from(value: &NewSchedule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewScript { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cache_ttl: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub codebase: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_key: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_time_window_s: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrent_limit: Option, + pub content: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dedicated_worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub delete_after_use: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_message: Option, + pub description: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft_only: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub envs: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub has_preprocessor: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_template: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + pub language: ScriptLang, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub no_main_func: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_behalf_of_email: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parent_hash: Option, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub restart_unless_cancelled: Option, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub schema: std::collections::HashMap, + pub summary: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub visible_to_runner_only: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&NewScript> for NewScript { + fn from(value: &NewScript) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum NewScriptKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "failure")] + Failure, + #[serde(rename = "trigger")] + Trigger, + #[serde(rename = "command")] + Command, + #[serde(rename = "approval")] + Approval, + #[serde(rename = "preprocessor")] + Preprocessor, + } + impl From<&NewScriptKind> for NewScriptKind { + fn from(value: &NewScriptKind) -> Self { + value.clone() + } + } + impl ToString for NewScriptKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Failure => "failure".to_string(), + Self::Trigger => "trigger".to_string(), + Self::Command => "command".to_string(), + Self::Approval => "approval".to_string(), + Self::Preprocessor => "preprocessor".to_string(), + } + } + } + impl std::str::FromStr for NewScriptKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "failure" => Ok(Self::Failure), + "trigger" => Ok(Self::Trigger), + "command" => Ok(Self::Command), + "approval" => Ok(Self::Approval), + "preprocessor" => Ok(Self::Preprocessor), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for NewScriptKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for NewScriptKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for NewScriptKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewScriptWithDraft { + #[serde(flatten)] + pub new_script: NewScript, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft: Option, + pub hash: String, + } + impl From<&NewScriptWithDraft> for NewScriptWithDraft { + fn from(value: &NewScriptWithDraft) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewSqsTrigger { + pub aws_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + pub is_flow: bool, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub message_attributes: Vec, + pub path: String, + pub queue_url: String, + pub script_path: String, + } + impl From<&NewSqsTrigger> for NewSqsTrigger { + fn from(value: &NewSqsTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewToken { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expiration: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub scopes: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&NewToken> for NewToken { + fn from(value: &NewToken) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewTokenImpersonate { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expiration: Option>, + pub impersonate_email: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&NewTokenImpersonate> for NewTokenImpersonate { + fn from(value: &NewTokenImpersonate) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewWebsocketTrigger { + pub can_return_message: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + pub filters: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub initial_messages: Vec, + pub is_flow: bool, + pub path: String, + pub script_path: String, + pub url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url_runnable_args: Option, + } + impl From<&NewWebsocketTrigger> for NewWebsocketTrigger { + fn from(value: &NewWebsocketTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewWebsocketTriggerFiltersItem { + pub key: String, + pub value: serde_json::Value, + } + impl From<&NewWebsocketTriggerFiltersItem> for NewWebsocketTriggerFiltersItem { + fn from(value: &NewWebsocketTriggerFiltersItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ObscuredJob { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub duration_ms: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub started_at: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub typ: Option, + } + impl From<&ObscuredJob> for ObscuredJob { + fn from(value: &ObscuredJob) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct OpenFlow { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub schema: std::collections::HashMap, + pub summary: String, + pub value: FlowValue, + } + impl From<&OpenFlow> for OpenFlow { + fn from(value: &OpenFlow) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct OpenFlowWPath { + #[serde(flatten)] + pub open_flow: OpenFlow, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dedicated_worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_behalf_of_email: Option, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub visible_to_runner_only: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&OpenFlowWPath> for OpenFlowWPath { + fn from(value: &OpenFlowWPath) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct OperatorSettings(pub Option); + impl std::ops::Deref for OperatorSettings { + type Target = Option; + fn deref(&self) -> &Option { + &self.0 + } + } + impl From for Option { + fn from(value: OperatorSettings) -> Self { + value.0 + } + } + impl From<&OperatorSettings> for OperatorSettings { + fn from(value: &OperatorSettings) -> Self { + value.clone() + } + } + impl From> for OperatorSettings { + fn from(value: Option) -> Self { + Self(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct OperatorSettingsInner { + ///Whether operators can view audit logs + pub audit_logs: bool, + ///Whether operators can view folders page + pub folders: bool, + ///Whether operators can view groups page + pub groups: bool, + ///Whether operators can view resources + pub resources: bool, + ///Whether operators can view runs + pub runs: bool, + ///Whether operators can view schedules + pub schedules: bool, + ///Whether operators can view triggers + pub triggers: bool, + ///Whether operators can view variables + pub variables: bool, + ///Whether operators can view workers page + pub workers: bool, + } + impl From<&OperatorSettingsInner> for OperatorSettingsInner { + fn from(value: &OperatorSettingsInner) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PathFlow { + pub input_transforms: std::collections::HashMap, + pub path: String, + #[serde(rename = "type")] + pub type_: PathFlowType, + } + impl From<&PathFlow> for PathFlow { + fn from(value: &PathFlow) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum PathFlowType { + #[serde(rename = "flow")] + Flow, + } + impl From<&PathFlowType> for PathFlowType { + fn from(value: &PathFlowType) -> Self { + value.clone() + } + } + impl ToString for PathFlowType { + fn to_string(&self) -> String { + match *self { + Self::Flow => "flow".to_string(), + } + } + } + impl std::str::FromStr for PathFlowType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "flow" => Ok(Self::Flow), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for PathFlowType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for PathFlowType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for PathFlowType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PathScript { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hash: Option, + pub input_transforms: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_trigger: Option, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag_override: Option, + #[serde(rename = "type")] + pub type_: PathScriptType, + } + impl From<&PathScript> for PathScript { + fn from(value: &PathScript) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum PathScriptType { + #[serde(rename = "script")] + Script, + } + impl From<&PathScriptType> for PathScriptType { + fn from(value: &PathScriptType) -> Self { + value.clone() + } + } + impl ToString for PathScriptType { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + } + } + } + impl std::str::FromStr for PathScriptType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for PathScriptType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for PathScriptType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for PathScriptType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum PingCaptureConfigRunnableKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "flow")] + Flow, + } + impl From<&PingCaptureConfigRunnableKind> for PingCaptureConfigRunnableKind { + fn from(value: &PingCaptureConfigRunnableKind) -> Self { + value.clone() + } + } + impl ToString for PingCaptureConfigRunnableKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Flow => "flow".to_string(), + } + } + } + impl std::str::FromStr for PingCaptureConfigRunnableKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "flow" => Ok(Self::Flow), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for PingCaptureConfigRunnableKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for PingCaptureConfigRunnableKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for PingCaptureConfigRunnableKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PolarsClientKwargs { + pub region_name: String, + } + impl From<&PolarsClientKwargs> for PolarsClientKwargs { + fn from(value: &PolarsClientKwargs) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PolarsConnectionSettingsBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub s3_resource: Option, + } + impl From<&PolarsConnectionSettingsBody> for PolarsConnectionSettingsBody { + fn from(value: &PolarsConnectionSettingsBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PolarsConnectionSettingsResponse { + pub cache_regions: bool, + pub client_kwargs: PolarsClientKwargs, + pub endpoint_url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub key: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub secret: Option, + pub use_ssl: bool, + } + impl From<&PolarsConnectionSettingsResponse> for PolarsConnectionSettingsResponse { + fn from(value: &PolarsConnectionSettingsResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PolarsConnectionSettingsV2Body { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub s3_resource_path: Option, + } + impl From<&PolarsConnectionSettingsV2Body> for PolarsConnectionSettingsV2Body { + fn from(value: &PolarsConnectionSettingsV2Body) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PolarsConnectionSettingsV2Response { + pub s3fs_args: PolarsConnectionSettingsV2ResponseS3fsArgs, + pub storage_options: PolarsConnectionSettingsV2ResponseStorageOptions, + } + impl From<&PolarsConnectionSettingsV2Response> + for PolarsConnectionSettingsV2Response { + fn from(value: &PolarsConnectionSettingsV2Response) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PolarsConnectionSettingsV2ResponseS3fsArgs { + pub cache_regions: bool, + pub client_kwargs: PolarsClientKwargs, + pub endpoint_url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub key: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub secret: Option, + pub use_ssl: bool, + } + impl From<&PolarsConnectionSettingsV2ResponseS3fsArgs> + for PolarsConnectionSettingsV2ResponseS3fsArgs { + fn from(value: &PolarsConnectionSettingsV2ResponseS3fsArgs) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PolarsConnectionSettingsV2ResponseStorageOptions { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub aws_access_key_id: Option, + pub aws_allow_http: String, + pub aws_endpoint_url: String, + pub aws_region: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub aws_secret_access_key: Option, + } + impl From<&PolarsConnectionSettingsV2ResponseStorageOptions> + for PolarsConnectionSettingsV2ResponseStorageOptions { + fn from(value: &PolarsConnectionSettingsV2ResponseStorageOptions) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Policy { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub allowed_s3_keys: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub execution_mode: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_behalf_of: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_behalf_of_email: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub s3_inputs: Vec>, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub triggerables: std::collections::HashMap< + String, + std::collections::HashMap, + >, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub triggerables_v2: std::collections::HashMap< + String, + std::collections::HashMap, + >, + } + impl From<&Policy> for Policy { + fn from(value: &Policy) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PolicyAllowedS3KeysItem { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub resource: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub s3_path: Option, + } + impl From<&PolicyAllowedS3KeysItem> for PolicyAllowedS3KeysItem { + fn from(value: &PolicyAllowedS3KeysItem) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum PolicyExecutionMode { + #[serde(rename = "viewer")] + Viewer, + #[serde(rename = "publisher")] + Publisher, + #[serde(rename = "anonymous")] + Anonymous, + } + impl From<&PolicyExecutionMode> for PolicyExecutionMode { + fn from(value: &PolicyExecutionMode) -> Self { + value.clone() + } + } + impl ToString for PolicyExecutionMode { + fn to_string(&self) -> String { + match *self { + Self::Viewer => "viewer".to_string(), + Self::Publisher => "publisher".to_string(), + Self::Anonymous => "anonymous".to_string(), + } + } + } + impl std::str::FromStr for PolicyExecutionMode { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "viewer" => Ok(Self::Viewer), + "publisher" => Ok(Self::Publisher), + "anonymous" => Ok(Self::Anonymous), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for PolicyExecutionMode { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for PolicyExecutionMode { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for PolicyExecutionMode { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PostgresTrigger { + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + pub postgres_resource_path: String, + pub publication_name: String, + pub replication_slot_name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + } + impl From<&PostgresTrigger> for PostgresTrigger { + fn from(value: &PostgresTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Preview { + pub args: ScriptArgs, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub content: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dedicated_worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub language: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + } + impl From<&Preview> for Preview { + fn from(value: &Preview) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum PreviewKind { + #[serde(rename = "code")] + Code, + #[serde(rename = "identity")] + Identity, + #[serde(rename = "http")] + Http, + } + impl From<&PreviewKind> for PreviewKind { + fn from(value: &PreviewKind) -> Self { + value.clone() + } + } + impl ToString for PreviewKind { + fn to_string(&self) -> String { + match *self { + Self::Code => "code".to_string(), + Self::Identity => "identity".to_string(), + Self::Http => "http".to_string(), + } + } + } + impl std::str::FromStr for PreviewKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "code" => Ok(Self::Code), + "identity" => Ok(Self::Identity), + "http" => Ok(Self::Http), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for PreviewKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for PreviewKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for PreviewKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PreviewScheduleBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cron_version: Option, + pub schedule: String, + pub timezone: String, + } + impl From<&PreviewScheduleBody> for PreviewScheduleBody { + fn from(value: &PreviewScheduleBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PublicationData { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub table_to_track: Vec, + pub transaction_to_track: Vec, + } + impl From<&PublicationData> for PublicationData { + fn from(value: &PublicationData) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct QueryHubScriptsResponseItem { + pub app: String, + pub ask_id: f64, + pub id: f64, + pub kind: HubScriptKind, + pub score: f64, + pub summary: String, + pub version_id: f64, + } + impl From<&QueryHubScriptsResponseItem> for QueryHubScriptsResponseItem { + fn from(value: &QueryHubScriptsResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct QueryResourceTypesResponseItem { + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schema: Option, + pub score: f64, + } + impl From<&QueryResourceTypesResponseItem> for QueryResourceTypesResponseItem { + fn from(value: &QueryResourceTypesResponseItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct QueuedJob { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub aggregate_wait_time_ms: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub args: Option, + pub canceled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub canceled_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub canceled_reason: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_at: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_by: Option, + pub email: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub flow_status: Option, + pub id: uuid::Uuid, + pub is_flow_step: bool, + pub job_kind: QueuedJobJobKind, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub language: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_ping: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub logs: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mem_peak: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parent_job: Option, + /**The user (u/userfoo) or group (g/groupfoo) whom +the execution of this script will be permissioned_as and by extension its DT_TOKEN. +*/ + pub permissioned_as: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub preprocessed: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_code: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_flow: Option, + pub running: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schedule_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub scheduled_for: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub script_hash: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub script_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub self_wait_time_ms: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub started_at: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub suspend: Option, + pub tag: String, + pub visible_to_owner: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&QueuedJob> for QueuedJob { + fn from(value: &QueuedJob) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum QueuedJobJobKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "preview")] + Preview, + #[serde(rename = "dependencies")] + Dependencies, + #[serde(rename = "flowdependencies")] + Flowdependencies, + #[serde(rename = "appdependencies")] + Appdependencies, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "flowpreview")] + Flowpreview, + #[serde(rename = "script_hub")] + ScriptHub, + #[serde(rename = "identity")] + Identity, + #[serde(rename = "deploymentcallback")] + Deploymentcallback, + #[serde(rename = "singlescriptflow")] + Singlescriptflow, + #[serde(rename = "flowscript")] + Flowscript, + #[serde(rename = "flownode")] + Flownode, + #[serde(rename = "appscript")] + Appscript, + } + impl From<&QueuedJobJobKind> for QueuedJobJobKind { + fn from(value: &QueuedJobJobKind) -> Self { + value.clone() + } + } + impl ToString for QueuedJobJobKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Preview => "preview".to_string(), + Self::Dependencies => "dependencies".to_string(), + Self::Flowdependencies => "flowdependencies".to_string(), + Self::Appdependencies => "appdependencies".to_string(), + Self::Flow => "flow".to_string(), + Self::Flowpreview => "flowpreview".to_string(), + Self::ScriptHub => "script_hub".to_string(), + Self::Identity => "identity".to_string(), + Self::Deploymentcallback => "deploymentcallback".to_string(), + Self::Singlescriptflow => "singlescriptflow".to_string(), + Self::Flowscript => "flowscript".to_string(), + Self::Flownode => "flownode".to_string(), + Self::Appscript => "appscript".to_string(), + } + } + } + impl std::str::FromStr for QueuedJobJobKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "preview" => Ok(Self::Preview), + "dependencies" => Ok(Self::Dependencies), + "flowdependencies" => Ok(Self::Flowdependencies), + "appdependencies" => Ok(Self::Appdependencies), + "flow" => Ok(Self::Flow), + "flowpreview" => Ok(Self::Flowpreview), + "script_hub" => Ok(Self::ScriptHub), + "identity" => Ok(Self::Identity), + "deploymentcallback" => Ok(Self::Deploymentcallback), + "singlescriptflow" => Ok(Self::Singlescriptflow), + "flowscript" => Ok(Self::Flowscript), + "flownode" => Ok(Self::Flownode), + "appscript" => Ok(Self::Appscript), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for QueuedJobJobKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for QueuedJobJobKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for QueuedJobJobKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RawScript { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_time_window_s: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrent_limit: Option, + pub content: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub custom_concurrency_key: Option, + pub input_transforms: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_trigger: Option, + pub language: RawScriptLanguage, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + #[serde(rename = "type")] + pub type_: RawScriptType, + } + impl From<&RawScript> for RawScript { + fn from(value: &RawScript) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RawScriptForDependencies { + pub language: ScriptLang, + pub path: String, + pub raw_code: String, + } + impl From<&RawScriptForDependencies> for RawScriptForDependencies { + fn from(value: &RawScriptForDependencies) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum RawScriptLanguage { + #[serde(rename = "deno")] + Deno, + #[serde(rename = "bun")] + Bun, + #[serde(rename = "python3")] + Python3, + #[serde(rename = "go")] + Go, + #[serde(rename = "bash")] + Bash, + #[serde(rename = "powershell")] + Powershell, + #[serde(rename = "postgresql")] + Postgresql, + #[serde(rename = "mysql")] + Mysql, + #[serde(rename = "bigquery")] + Bigquery, + #[serde(rename = "snowflake")] + Snowflake, + #[serde(rename = "mssql")] + Mssql, + #[serde(rename = "oracledb")] + Oracledb, + #[serde(rename = "graphql")] + Graphql, + #[serde(rename = "nativets")] + Nativets, + #[serde(rename = "php")] + Php, + } + impl From<&RawScriptLanguage> for RawScriptLanguage { + fn from(value: &RawScriptLanguage) -> Self { + value.clone() + } + } + impl ToString for RawScriptLanguage { + fn to_string(&self) -> String { + match *self { + Self::Deno => "deno".to_string(), + Self::Bun => "bun".to_string(), + Self::Python3 => "python3".to_string(), + Self::Go => "go".to_string(), + Self::Bash => "bash".to_string(), + Self::Powershell => "powershell".to_string(), + Self::Postgresql => "postgresql".to_string(), + Self::Mysql => "mysql".to_string(), + Self::Bigquery => "bigquery".to_string(), + Self::Snowflake => "snowflake".to_string(), + Self::Mssql => "mssql".to_string(), + Self::Oracledb => "oracledb".to_string(), + Self::Graphql => "graphql".to_string(), + Self::Nativets => "nativets".to_string(), + Self::Php => "php".to_string(), + } + } + } + impl std::str::FromStr for RawScriptLanguage { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "deno" => Ok(Self::Deno), + "bun" => Ok(Self::Bun), + "python3" => Ok(Self::Python3), + "go" => Ok(Self::Go), + "bash" => Ok(Self::Bash), + "powershell" => Ok(Self::Powershell), + "postgresql" => Ok(Self::Postgresql), + "mysql" => Ok(Self::Mysql), + "bigquery" => Ok(Self::Bigquery), + "snowflake" => Ok(Self::Snowflake), + "mssql" => Ok(Self::Mssql), + "oracledb" => Ok(Self::Oracledb), + "graphql" => Ok(Self::Graphql), + "nativets" => Ok(Self::Nativets), + "php" => Ok(Self::Php), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for RawScriptLanguage { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for RawScriptLanguage { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for RawScriptLanguage { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum RawScriptType { + #[serde(rename = "rawscript")] + Rawscript, + } + impl From<&RawScriptType> for RawScriptType { + fn from(value: &RawScriptType) -> Self { + value.clone() + } + } + impl ToString for RawScriptType { + fn to_string(&self) -> String { + match *self { + Self::Rawscript => "rawscript".to_string(), + } + } + } + impl std::str::FromStr for RawScriptType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "rawscript" => Ok(Self::Rawscript), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for RawScriptType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for RawScriptType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for RawScriptType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RefreshTokenBody { + pub path: String, + } + impl From<&RefreshTokenBody> for RefreshTokenBody { + fn from(value: &RefreshTokenBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Relations { + pub schema_name: String, + pub table_to_track: TableToTrack, + } + impl From<&Relations> for Relations { + fn from(value: &Relations) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RemoveGranularAclsBody { + pub owner: String, + } + impl From<&RemoveGranularAclsBody> for RemoveGranularAclsBody { + fn from(value: &RemoveGranularAclsBody) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum RemoveGranularAclsKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "group_")] + Group, + #[serde(rename = "resource")] + Resource, + #[serde(rename = "schedule")] + Schedule, + #[serde(rename = "variable")] + Variable, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "folder")] + Folder, + #[serde(rename = "app")] + App, + #[serde(rename = "raw_app")] + RawApp, + #[serde(rename = "http_trigger")] + HttpTrigger, + #[serde(rename = "websocket_trigger")] + WebsocketTrigger, + #[serde(rename = "kafka_trigger")] + KafkaTrigger, + #[serde(rename = "nats_trigger")] + NatsTrigger, + #[serde(rename = "postgres_trigger")] + PostgresTrigger, + #[serde(rename = "mqtt_trigger")] + MqttTrigger, + #[serde(rename = "sqs_trigger")] + SqsTrigger, + } + impl From<&RemoveGranularAclsKind> for RemoveGranularAclsKind { + fn from(value: &RemoveGranularAclsKind) -> Self { + value.clone() + } + } + impl ToString for RemoveGranularAclsKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Group => "group_".to_string(), + Self::Resource => "resource".to_string(), + Self::Schedule => "schedule".to_string(), + Self::Variable => "variable".to_string(), + Self::Flow => "flow".to_string(), + Self::Folder => "folder".to_string(), + Self::App => "app".to_string(), + Self::RawApp => "raw_app".to_string(), + Self::HttpTrigger => "http_trigger".to_string(), + Self::WebsocketTrigger => "websocket_trigger".to_string(), + Self::KafkaTrigger => "kafka_trigger".to_string(), + Self::NatsTrigger => "nats_trigger".to_string(), + Self::PostgresTrigger => "postgres_trigger".to_string(), + Self::MqttTrigger => "mqtt_trigger".to_string(), + Self::SqsTrigger => "sqs_trigger".to_string(), + } + } + } + impl std::str::FromStr for RemoveGranularAclsKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "group_" => Ok(Self::Group), + "resource" => Ok(Self::Resource), + "schedule" => Ok(Self::Schedule), + "variable" => Ok(Self::Variable), + "flow" => Ok(Self::Flow), + "folder" => Ok(Self::Folder), + "app" => Ok(Self::App), + "raw_app" => Ok(Self::RawApp), + "http_trigger" => Ok(Self::HttpTrigger), + "websocket_trigger" => Ok(Self::WebsocketTrigger), + "kafka_trigger" => Ok(Self::KafkaTrigger), + "nats_trigger" => Ok(Self::NatsTrigger), + "postgres_trigger" => Ok(Self::PostgresTrigger), + "mqtt_trigger" => Ok(Self::MqttTrigger), + "sqs_trigger" => Ok(Self::SqsTrigger), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for RemoveGranularAclsKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for RemoveGranularAclsKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for RemoveGranularAclsKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RemoveOwnerToFolderBody { + pub owner: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub write: Option, + } + impl From<&RemoveOwnerToFolderBody> for RemoveOwnerToFolderBody { + fn from(value: &RemoveOwnerToFolderBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RemoveUserFromInstanceGroupBody { + pub email: String, + } + impl From<&RemoveUserFromInstanceGroupBody> for RemoveUserFromInstanceGroupBody { + fn from(value: &RemoveUserFromInstanceGroupBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RemoveUserToGroupBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub username: Option, + } + impl From<&RemoveUserToGroupBody> for RemoveUserToGroupBody { + fn from(value: &RemoveUserToGroupBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Resource { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub edited_at: Option>, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub extra_perms: std::collections::HashMap, + pub is_oauth: bool, + pub path: String, + pub resource_type: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&Resource> for Resource { + fn from(value: &Resource) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ResourceType { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub edited_at: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub format_extension: Option, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schema: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&ResourceType> for ResourceType { + fn from(value: &ResourceType) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RestartedFrom { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub branch_or_iteration_n: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub flow_job_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub step_id: Option, + } + impl From<&RestartedFrom> for RestartedFrom { + fn from(value: &RestartedFrom) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Retry { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub constant: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub exponential: Option, + } + impl From<&Retry> for Retry { + fn from(value: &Retry) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RetryConstant { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub attempts: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub seconds: Option, + } + impl From<&RetryConstant> for RetryConstant { + fn from(value: &RetryConstant) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RetryExponential { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub attempts: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub multiplier: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub random_factor: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub seconds: Option, + } + impl From<&RetryExponential> for RetryExponential { + fn from(value: &RetryExponential) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RunRawScriptDependenciesBody { + pub entrypoint: String, + pub raw_scripts: Vec, + } + impl From<&RunRawScriptDependenciesBody> for RunRawScriptDependenciesBody { + fn from(value: &RunRawScriptDependenciesBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RunRawScriptDependenciesResponse { + pub lock: String, + } + impl From<&RunRawScriptDependenciesResponse> for RunRawScriptDependenciesResponse { + fn from(value: &RunRawScriptDependenciesResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RunSlackMessageTestJobBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub channel: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hub_script_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub test_msg: Option, + } + impl From<&RunSlackMessageTestJobBody> for RunSlackMessageTestJobBody { + fn from(value: &RunSlackMessageTestJobBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RunTeamsMessageTestJobBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub channel: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hub_script_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub test_msg: Option, + } + impl From<&RunTeamsMessageTestJobBody> for RunTeamsMessageTestJobBody { + fn from(value: &RunTeamsMessageTestJobBody) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum RunnableType { + ScriptHash, + ScriptPath, + FlowPath, + } + impl From<&RunnableType> for RunnableType { + fn from(value: &RunnableType) -> Self { + value.clone() + } + } + impl ToString for RunnableType { + fn to_string(&self) -> String { + match *self { + Self::ScriptHash => "ScriptHash".to_string(), + Self::ScriptPath => "ScriptPath".to_string(), + Self::FlowPath => "FlowPath".to_string(), + } + } + } + impl std::str::FromStr for RunnableType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "ScriptHash" => Ok(Self::ScriptHash), + "ScriptPath" => Ok(Self::ScriptPath), + "FlowPath" => Ok(Self::FlowPath), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for RunnableType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for RunnableType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for RunnableType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct S3Resource { + #[serde(rename = "accessKey", default, skip_serializing_if = "Option::is_none")] + pub access_key: Option, + pub bucket: String, + #[serde(rename = "endPoint")] + pub end_point: String, + #[serde(rename = "pathStyle")] + pub path_style: bool, + pub region: String, + #[serde(rename = "secretKey", default, skip_serializing_if = "Option::is_none")] + pub secret_key: Option, + #[serde(rename = "useSSL")] + pub use_ssl: bool, + } + impl From<&S3Resource> for S3Resource { + fn from(value: &S3Resource) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct S3ResourceInfoBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub s3_resource_path: Option, + } + impl From<&S3ResourceInfoBody> for S3ResourceInfoBody { + fn from(value: &S3ResourceInfoBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ScalarMetric { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub metric_id: Option, + pub value: f64, + } + impl From<&ScalarMetric> for ScalarMetric { + fn from(value: &ScalarMetric) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Schedule { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cron_version: Option, + pub edited_at: chrono::DateTime, + pub edited_by: String, + pub email: String, + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + pub extra_perms: std::collections::HashMap, + pub is_flow: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub no_flow_overlap: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_exact: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_times: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery_times: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_success: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_success_extra_args: Option, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub paused_until: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub retry: Option, + pub schedule: String, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + pub timezone: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&Schedule> for Schedule { + fn from(value: &Schedule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ScheduleWJobs { + #[serde(flatten)] + pub schedule: Schedule, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub jobs: Vec, + } + impl From<&ScheduleWJobs> for ScheduleWJobs { + fn from(value: &ScheduleWJobs) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ScheduleWJobsJobsItem { + pub duration_ms: f64, + pub id: String, + pub success: bool, + } + impl From<&ScheduleWJobsJobsItem> for ScheduleWJobsJobsItem { + fn from(value: &ScheduleWJobsJobsItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Script { + pub archived: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cache_ttl: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub codebase: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_key: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_time_window_s: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrent_limit: Option, + pub content: String, + pub created_at: chrono::DateTime, + pub created_by: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dedicated_worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub delete_after_use: Option, + pub deleted: bool, + pub description: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft_only: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub envs: Vec, + pub extra_perms: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub has_draft: Option, + pub has_preprocessor: bool, + pub hash: String, + pub is_template: bool, + pub kind: ScriptKind, + pub language: ScriptLang, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock_error_logs: Option, + pub no_main_func: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_behalf_of_email: Option, + /**The first element is the direct parent of the script, the second is the parent of the first, etc +*/ + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub parent_hashes: Vec, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub restart_unless_cancelled: Option, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub schema: std::collections::HashMap, + pub starred: bool, + pub summary: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub visible_to_runner_only: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&Script> for Script { + fn from(value: &Script) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ScriptArgs(pub std::collections::HashMap); + impl std::ops::Deref for ScriptArgs { + type Target = std::collections::HashMap; + fn deref(&self) -> &std::collections::HashMap { + &self.0 + } + } + impl From for std::collections::HashMap { + fn from(value: ScriptArgs) -> Self { + value.0 + } + } + impl From<&ScriptArgs> for ScriptArgs { + fn from(value: &ScriptArgs) -> Self { + value.clone() + } + } + impl From> for ScriptArgs { + fn from(value: std::collections::HashMap) -> Self { + Self(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ScriptHistory { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_msg: Option, + pub script_hash: String, + } + impl From<&ScriptHistory> for ScriptHistory { + fn from(value: &ScriptHistory) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum ScriptKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "failure")] + Failure, + #[serde(rename = "trigger")] + Trigger, + #[serde(rename = "command")] + Command, + #[serde(rename = "approval")] + Approval, + #[serde(rename = "preprocessor")] + Preprocessor, + } + impl From<&ScriptKind> for ScriptKind { + fn from(value: &ScriptKind) -> Self { + value.clone() + } + } + impl ToString for ScriptKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Failure => "failure".to_string(), + Self::Trigger => "trigger".to_string(), + Self::Command => "command".to_string(), + Self::Approval => "approval".to_string(), + Self::Preprocessor => "preprocessor".to_string(), + } + } + } + impl std::str::FromStr for ScriptKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "failure" => Ok(Self::Failure), + "trigger" => Ok(Self::Trigger), + "command" => Ok(Self::Command), + "approval" => Ok(Self::Approval), + "preprocessor" => Ok(Self::Preprocessor), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for ScriptKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for ScriptKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for ScriptKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum ScriptLang { + #[serde(rename = "python3")] + Python3, + #[serde(rename = "deno")] + Deno, + #[serde(rename = "go")] + Go, + #[serde(rename = "bash")] + Bash, + #[serde(rename = "powershell")] + Powershell, + #[serde(rename = "postgresql")] + Postgresql, + #[serde(rename = "mysql")] + Mysql, + #[serde(rename = "bigquery")] + Bigquery, + #[serde(rename = "snowflake")] + Snowflake, + #[serde(rename = "mssql")] + Mssql, + #[serde(rename = "oracledb")] + Oracledb, + #[serde(rename = "graphql")] + Graphql, + #[serde(rename = "nativets")] + Nativets, + #[serde(rename = "bun")] + Bun, + #[serde(rename = "php")] + Php, + #[serde(rename = "rust")] + Rust, + #[serde(rename = "ansible")] + Ansible, + #[serde(rename = "csharp")] + Csharp, + #[serde(rename = "nu")] + Nu, + } + impl From<&ScriptLang> for ScriptLang { + fn from(value: &ScriptLang) -> Self { + value.clone() + } + } + impl ToString for ScriptLang { + fn to_string(&self) -> String { + match *self { + Self::Python3 => "python3".to_string(), + Self::Deno => "deno".to_string(), + Self::Go => "go".to_string(), + Self::Bash => "bash".to_string(), + Self::Powershell => "powershell".to_string(), + Self::Postgresql => "postgresql".to_string(), + Self::Mysql => "mysql".to_string(), + Self::Bigquery => "bigquery".to_string(), + Self::Snowflake => "snowflake".to_string(), + Self::Mssql => "mssql".to_string(), + Self::Oracledb => "oracledb".to_string(), + Self::Graphql => "graphql".to_string(), + Self::Nativets => "nativets".to_string(), + Self::Bun => "bun".to_string(), + Self::Php => "php".to_string(), + Self::Rust => "rust".to_string(), + Self::Ansible => "ansible".to_string(), + Self::Csharp => "csharp".to_string(), + Self::Nu => "nu".to_string(), + } + } + } + impl std::str::FromStr for ScriptLang { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "python3" => Ok(Self::Python3), + "deno" => Ok(Self::Deno), + "go" => Ok(Self::Go), + "bash" => Ok(Self::Bash), + "powershell" => Ok(Self::Powershell), + "postgresql" => Ok(Self::Postgresql), + "mysql" => Ok(Self::Mysql), + "bigquery" => Ok(Self::Bigquery), + "snowflake" => Ok(Self::Snowflake), + "mssql" => Ok(Self::Mssql), + "oracledb" => Ok(Self::Oracledb), + "graphql" => Ok(Self::Graphql), + "nativets" => Ok(Self::Nativets), + "bun" => Ok(Self::Bun), + "php" => Ok(Self::Php), + "rust" => Ok(Self::Rust), + "ansible" => Ok(Self::Ansible), + "csharp" => Ok(Self::Csharp), + "nu" => Ok(Self::Nu), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for ScriptLang { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for ScriptLang { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for ScriptLang { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SearchJobsIndexResponse { + ///the jobs that matched the query + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub hits: Vec, + ///a list of the terms that couldn't be parsed (and thus ignored) + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub query_parse_errors: Vec, + } + impl From<&SearchJobsIndexResponse> for SearchJobsIndexResponse { + fn from(value: &SearchJobsIndexResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SearchJobsIndexResponseQueryParseErrorsItem { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dancer: Option, + } + impl From<&SearchJobsIndexResponseQueryParseErrorsItem> + for SearchJobsIndexResponseQueryParseErrorsItem { + fn from(value: &SearchJobsIndexResponseQueryParseErrorsItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SearchLogsIndexResponse { + ///log files that matched the query + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub hits: Vec, + ///a list of the terms that couldn't be parsed (and thus ignored) + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub query_parse_errors: Vec, + } + impl From<&SearchLogsIndexResponse> for SearchLogsIndexResponse { + fn from(value: &SearchLogsIndexResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SendMessageToConversationBody { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub card_block: std::collections::HashMap, + ///The ID of the Teams conversation/activity + pub conversation_id: String, + ///Used for styling the card conditionally + #[serde(default = "defaults::default_bool::")] + pub success: bool, + ///The message text to be sent in the Teams card + pub text: String, + } + impl From<&SendMessageToConversationBody> for SendMessageToConversationBody { + fn from(value: &SendMessageToConversationBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetAutomaticBillingBody { + pub automatic_billing: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub seats: Option, + } + impl From<&SetAutomaticBillingBody> for SetAutomaticBillingBody { + fn from(value: &SetAutomaticBillingBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetCaptureConfigBody { + pub is_flow: bool, + pub path: String, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub trigger_config: std::collections::HashMap, + pub trigger_kind: CaptureTriggerKind, + } + impl From<&SetCaptureConfigBody> for SetCaptureConfigBody { + fn from(value: &SetCaptureConfigBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetDefaultErrorOrRecoveryHandlerBody { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub extra_args: std::collections::HashMap, + pub handler_type: SetDefaultErrorOrRecoveryHandlerBodyHandlerType, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub number_of_occurence: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub number_of_occurence_exact: Option, + pub override_existing: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_handler_muted: Option, + } + impl From<&SetDefaultErrorOrRecoveryHandlerBody> + for SetDefaultErrorOrRecoveryHandlerBody { + fn from(value: &SetDefaultErrorOrRecoveryHandlerBody) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum SetDefaultErrorOrRecoveryHandlerBodyHandlerType { + #[serde(rename = "error")] + Error, + #[serde(rename = "recovery")] + Recovery, + #[serde(rename = "success")] + Success, + } + impl From<&SetDefaultErrorOrRecoveryHandlerBodyHandlerType> + for SetDefaultErrorOrRecoveryHandlerBodyHandlerType { + fn from(value: &SetDefaultErrorOrRecoveryHandlerBodyHandlerType) -> Self { + value.clone() + } + } + impl ToString for SetDefaultErrorOrRecoveryHandlerBodyHandlerType { + fn to_string(&self) -> String { + match *self { + Self::Error => "error".to_string(), + Self::Recovery => "recovery".to_string(), + Self::Success => "success".to_string(), + } + } + } + impl std::str::FromStr for SetDefaultErrorOrRecoveryHandlerBodyHandlerType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "error" => Ok(Self::Error), + "recovery" => Ok(Self::Recovery), + "success" => Ok(Self::Success), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> + for SetDefaultErrorOrRecoveryHandlerBodyHandlerType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> + for SetDefaultErrorOrRecoveryHandlerBodyHandlerType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom + for SetDefaultErrorOrRecoveryHandlerBodyHandlerType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetEnvironmentVariableBody { + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + } + impl From<&SetEnvironmentVariableBody> for SetEnvironmentVariableBody { + fn from(value: &SetEnvironmentVariableBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetGlobalBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + } + impl From<&SetGlobalBody> for SetGlobalBody { + fn from(value: &SetGlobalBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetJobProgressBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub flow_job_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub percent: Option, + } + impl From<&SetJobProgressBody> for SetJobProgressBody { + fn from(value: &SetJobProgressBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetKafkaTriggerEnabledBody { + pub enabled: bool, + } + impl From<&SetKafkaTriggerEnabledBody> for SetKafkaTriggerEnabledBody { + fn from(value: &SetKafkaTriggerEnabledBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetLoginTypeForUserBody { + pub login_type: String, + } + impl From<&SetLoginTypeForUserBody> for SetLoginTypeForUserBody { + fn from(value: &SetLoginTypeForUserBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetMqttTriggerEnabledBody { + pub enabled: bool, + } + impl From<&SetMqttTriggerEnabledBody> for SetMqttTriggerEnabledBody { + fn from(value: &SetMqttTriggerEnabledBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetNatsTriggerEnabledBody { + pub enabled: bool, + } + impl From<&SetNatsTriggerEnabledBody> for SetNatsTriggerEnabledBody { + fn from(value: &SetNatsTriggerEnabledBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetPasswordBody { + pub password: String, + } + impl From<&SetPasswordBody> for SetPasswordBody { + fn from(value: &SetPasswordBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetPasswordForUserBody { + pub password: String, + } + impl From<&SetPasswordForUserBody> for SetPasswordForUserBody { + fn from(value: &SetPasswordForUserBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetPostgresTriggerEnabledBody { + pub enabled: bool, + } + impl From<&SetPostgresTriggerEnabledBody> for SetPostgresTriggerEnabledBody { + fn from(value: &SetPostgresTriggerEnabledBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetScheduleEnabledBody { + pub enabled: bool, + } + impl From<&SetScheduleEnabledBody> for SetScheduleEnabledBody { + fn from(value: &SetScheduleEnabledBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetSqsTriggerEnabledBody { + pub enabled: bool, + } + impl From<&SetSqsTriggerEnabledBody> for SetSqsTriggerEnabledBody { + fn from(value: &SetSqsTriggerEnabledBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetThresholdAlertBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub threshold_alert_amount: Option, + } + impl From<&SetThresholdAlertBody> for SetThresholdAlertBody { + fn from(value: &SetThresholdAlertBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetWebsocketTriggerEnabledBody { + pub enabled: bool, + } + impl From<&SetWebsocketTriggerEnabledBody> for SetWebsocketTriggerEnabledBody { + fn from(value: &SetWebsocketTriggerEnabledBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetWorkspaceEncryptionKeyBody { + pub new_key: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub skip_reencrypt: Option, + } + impl From<&SetWorkspaceEncryptionKeyBody> for SetWorkspaceEncryptionKeyBody { + fn from(value: &SetWorkspaceEncryptionKeyBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SlackToken { + pub access_token: String, + pub bot: SlackTokenBot, + pub team_id: String, + pub team_name: String, + } + impl From<&SlackToken> for SlackToken { + fn from(value: &SlackToken) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SlackTokenBot { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub bot_access_token: Option, + } + impl From<&SlackTokenBot> for SlackTokenBot { + fn from(value: &SlackTokenBot) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Slot { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + } + impl From<&Slot> for Slot { + fn from(value: &Slot) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SlotList { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub active: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub slot_name: Option, + } + impl From<&SlotList> for SlotList { + fn from(value: &SlotList) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SqsTrigger { + pub aws_resource_path: String, + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub message_attributes: Vec, + pub queue_url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + } + impl From<&SqsTrigger> for SqsTrigger { + fn from(value: &SqsTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct StarBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub favorite_kind: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + } + impl From<&StarBody> for StarBody { + fn from(value: &StarBody) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum StarBodyFavoriteKind { + #[serde(rename = "flow")] + Flow, + #[serde(rename = "app")] + App, + #[serde(rename = "script")] + Script, + #[serde(rename = "raw_app")] + RawApp, + } + impl From<&StarBodyFavoriteKind> for StarBodyFavoriteKind { + fn from(value: &StarBodyFavoriteKind) -> Self { + value.clone() + } + } + impl ToString for StarBodyFavoriteKind { + fn to_string(&self) -> String { + match *self { + Self::Flow => "flow".to_string(), + Self::App => "app".to_string(), + Self::Script => "script".to_string(), + Self::RawApp => "raw_app".to_string(), + } + } + } + impl std::str::FromStr for StarBodyFavoriteKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "flow" => Ok(Self::Flow), + "app" => Ok(Self::App), + "script" => Ok(Self::Script), + "raw_app" => Ok(Self::RawApp), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for StarBodyFavoriteKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for StarBodyFavoriteKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for StarBodyFavoriteKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct StaticTransform { + #[serde(rename = "type")] + pub type_: StaticTransformType, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + } + impl From<&StaticTransform> for StaticTransform { + fn from(value: &StaticTransform) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum StaticTransformType { + #[serde(rename = "javascript")] + Javascript, + } + impl From<&StaticTransformType> for StaticTransformType { + fn from(value: &StaticTransformType) -> Self { + value.clone() + } + } + impl ToString for StaticTransformType { + fn to_string(&self) -> String { + match *self { + Self::Javascript => "javascript".to_string(), + } + } + } + impl std::str::FromStr for StaticTransformType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "javascript" => Ok(Self::Javascript), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for StaticTransformType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for StaticTransformType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for StaticTransformType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TableToTrack(pub Vec); + impl std::ops::Deref for TableToTrack { + type Target = Vec; + fn deref(&self) -> &Vec { + &self.0 + } + } + impl From for Vec { + fn from(value: TableToTrack) -> Self { + value.0 + } + } + impl From<&TableToTrack> for TableToTrack { + fn from(value: &TableToTrack) -> Self { + value.clone() + } + } + impl From> for TableToTrack { + fn from(value: Vec) -> Self { + Self(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TableToTrackItem { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub columns_name: Vec, + pub table_name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub where_clause: Option, + } + impl From<&TableToTrackItem> for TableToTrackItem { + fn from(value: &TableToTrackItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TeamInfo { + ///List of channels within the team + pub channels: Vec, + ///The unique identifier of the Microsoft Teams team + pub team_id: String, + ///The display name of the Microsoft Teams team + pub team_name: String, + } + impl From<&TeamInfo> for TeamInfo { + fn from(value: &TeamInfo) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TemplateScript { + pub language: Language, + pub postgres_resource_path: String, + pub relations: Vec, + } + impl From<&TemplateScript> for TemplateScript { + fn from(value: &TemplateScript) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TestCriticalChannelsBodyItem { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub email: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub slack_channel: Option, + } + impl From<&TestCriticalChannelsBodyItem> for TestCriticalChannelsBodyItem { + fn from(value: &TestCriticalChannelsBodyItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TestKafkaConnectionBody { + pub connection: std::collections::HashMap, + } + impl From<&TestKafkaConnectionBody> for TestKafkaConnectionBody { + fn from(value: &TestKafkaConnectionBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TestLicenseKeyBody { + pub license_key: String, + } + impl From<&TestLicenseKeyBody> for TestLicenseKeyBody { + fn from(value: &TestLicenseKeyBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TestMqttConnectionBody { + pub connection: std::collections::HashMap, + } + impl From<&TestMqttConnectionBody> for TestMqttConnectionBody { + fn from(value: &TestMqttConnectionBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TestNatsConnectionBody { + pub connection: std::collections::HashMap, + } + impl From<&TestNatsConnectionBody> for TestNatsConnectionBody { + fn from(value: &TestNatsConnectionBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TestPostgresConnectionBody { + pub database: String, + } + impl From<&TestPostgresConnectionBody> for TestPostgresConnectionBody { + fn from(value: &TestPostgresConnectionBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TestSmtpBody { + pub smtp: TestSmtpBodySmtp, + pub to: String, + } + impl From<&TestSmtpBody> for TestSmtpBody { + fn from(value: &TestSmtpBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TestSmtpBodySmtp { + pub disable_tls: bool, + pub from: String, + pub host: String, + pub password: String, + pub port: i64, + pub tls_implicit: bool, + pub username: String, + } + impl From<&TestSmtpBodySmtp> for TestSmtpBodySmtp { + fn from(value: &TestSmtpBodySmtp) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TestSqsConnectionBody { + pub connection: std::collections::HashMap, + } + impl From<&TestSqsConnectionBody> for TestSqsConnectionBody { + fn from(value: &TestSqsConnectionBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TestWebsocketConnectionBody { + pub can_return_message: bool, + pub url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url_runnable_args: Option, + } + impl From<&TestWebsocketConnectionBody> for TestWebsocketConnectionBody { + fn from(value: &TestWebsocketConnectionBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TimeseriesMetric { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub metric_id: Option, + pub values: Vec, + } + impl From<&TimeseriesMetric> for TimeseriesMetric { + fn from(value: &TimeseriesMetric) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ToggleWorkspaceErrorHandlerForFlowBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub muted: Option, + } + impl From<&ToggleWorkspaceErrorHandlerForFlowBody> + for ToggleWorkspaceErrorHandlerForFlowBody { + fn from(value: &ToggleWorkspaceErrorHandlerForFlowBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ToggleWorkspaceErrorHandlerForScriptBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub muted: Option, + } + impl From<&ToggleWorkspaceErrorHandlerForScriptBody> + for ToggleWorkspaceErrorHandlerForScriptBody { + fn from(value: &ToggleWorkspaceErrorHandlerForScriptBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TokenResponse { + pub access_token: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expires_in: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub refresh_token: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub scope: Vec, + } + impl From<&TokenResponse> for TokenResponse { + fn from(value: &TokenResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TriggerExtraProperty { + pub edited_at: chrono::DateTime, + pub edited_by: String, + pub email: String, + pub extra_perms: std::collections::HashMap, + pub is_flow: bool, + pub path: String, + pub script_path: String, + pub workspace_id: String, + } + impl From<&TriggerExtraProperty> for TriggerExtraProperty { + fn from(value: &TriggerExtraProperty) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TriggersCount { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub email_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub http_routes_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kafka_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mqtt_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub nats_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub postgres_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub primary_schedule: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schedule_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sqs_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub webhook_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub websocket_count: Option, + } + impl From<&TriggersCount> for TriggersCount { + fn from(value: &TriggersCount) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TriggersCountPrimarySchedule { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schedule: Option, + } + impl From<&TriggersCountPrimarySchedule> for TriggersCountPrimarySchedule { + fn from(value: &TriggersCountPrimarySchedule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TruncatedToken { + pub created_at: chrono::DateTime, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub email: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expiration: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub label: Option, + pub last_used_at: chrono::DateTime, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub scopes: Vec, + pub token_prefix: String, + } + impl From<&TruncatedToken> for TruncatedToken { + fn from(value: &TruncatedToken) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UnstarBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub favorite_kind: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + } + impl From<&UnstarBody> for UnstarBody { + fn from(value: &UnstarBody) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum UnstarBodyFavoriteKind { + #[serde(rename = "flow")] + Flow, + #[serde(rename = "app")] + App, + #[serde(rename = "script")] + Script, + #[serde(rename = "raw_app")] + RawApp, + } + impl From<&UnstarBodyFavoriteKind> for UnstarBodyFavoriteKind { + fn from(value: &UnstarBodyFavoriteKind) -> Self { + value.clone() + } + } + impl ToString for UnstarBodyFavoriteKind { + fn to_string(&self) -> String { + match *self { + Self::Flow => "flow".to_string(), + Self::App => "app".to_string(), + Self::Script => "script".to_string(), + Self::RawApp => "raw_app".to_string(), + } + } + } + impl std::str::FromStr for UnstarBodyFavoriteKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "flow" => Ok(Self::Flow), + "app" => Ok(Self::App), + "script" => Ok(Self::Script), + "raw_app" => Ok(Self::RawApp), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for UnstarBodyFavoriteKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for UnstarBodyFavoriteKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for UnstarBodyFavoriteKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UpdateAppBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub custom_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_message: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub policy: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + } + impl From<&UpdateAppBody> for UpdateAppBody { + fn from(value: &UpdateAppBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UpdateAppHistoryBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_msg: Option, + } + impl From<&UpdateAppHistoryBody> for UpdateAppHistoryBody { + fn from(value: &UpdateAppHistoryBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UpdateFlowBody { + #[serde(flatten)] + pub open_flow_w_path: OpenFlowWPath, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_message: Option, + } + impl From<&UpdateFlowBody> for UpdateFlowBody { + fn from(value: &UpdateFlowBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UpdateFlowHistoryBody { + pub deployment_msg: String, + } + impl From<&UpdateFlowHistoryBody> for UpdateFlowHistoryBody { + fn from(value: &UpdateFlowHistoryBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UpdateFolderBody { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub extra_perms: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub owners: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&UpdateFolderBody> for UpdateFolderBody { + fn from(value: &UpdateFolderBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UpdateGroupBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&UpdateGroupBody> for UpdateGroupBody { + fn from(value: &UpdateGroupBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UpdateInput { + pub id: String, + pub is_public: bool, + pub name: String, + } + impl From<&UpdateInput> for UpdateInput { + fn from(value: &UpdateInput) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UpdateInstanceGroupBody { + pub new_summary: String, + } + impl From<&UpdateInstanceGroupBody> for UpdateInstanceGroupBody { + fn from(value: &UpdateInstanceGroupBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UpdateRawAppBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + } + impl From<&UpdateRawAppBody> for UpdateRawAppBody { + fn from(value: &UpdateRawAppBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UpdateResourceValueBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + } + impl From<&UpdateResourceValueBody> for UpdateResourceValueBody { + fn from(value: &UpdateResourceValueBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UpdateScriptHistoryBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_msg: Option, + } + impl From<&UpdateScriptHistoryBody> for UpdateScriptHistoryBody { + fn from(value: &UpdateScriptHistoryBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UpdateTutorialProgressBody { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub progress: Option, + } + impl From<&UpdateTutorialProgressBody> for UpdateTutorialProgressBody { + fn from(value: &UpdateTutorialProgressBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UploadFilePart { + pub part_number: i64, + pub tag: String, + } + impl From<&UploadFilePart> for UploadFilePart { + fn from(value: &UploadFilePart) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UploadS3FileFromAppResponse { + pub delete_token: String, + pub file_key: String, + } + impl From<&UploadS3FileFromAppResponse> for UploadS3FileFromAppResponse { + fn from(value: &UploadS3FileFromAppResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct User { + pub created_at: chrono::DateTime, + pub disabled: bool, + pub email: String, + pub folders: Vec, + pub folders_owners: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub groups: Vec, + pub is_admin: bool, + pub is_super_admin: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + pub operator: bool, + pub username: String, + } + impl From<&User> for User { + fn from(value: &User) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UserUsage { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub email: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub executions: Option, + } + impl From<&UserUsage> for UserUsage { + fn from(value: &UserUsage) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UserWorkspaceList { + pub email: String, + pub workspaces: Vec, + } + impl From<&UserWorkspaceList> for UserWorkspaceList { + fn from(value: &UserWorkspaceList) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UserWorkspaceListWorkspacesItem { + pub color: String, + pub id: String, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub operator_settings: Option, + pub username: String, + } + impl From<&UserWorkspaceListWorkspacesItem> for UserWorkspaceListWorkspacesItem { + fn from(value: &UserWorkspaceListWorkspacesItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WebsocketTrigger { + pub can_return_message: bool, + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + pub filters: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub initial_messages: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + pub url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url_runnable_args: Option, + } + impl From<&WebsocketTrigger> for WebsocketTrigger { + fn from(value: &WebsocketTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WebsocketTriggerFiltersItem { + pub key: String, + pub value: serde_json::Value, + } + impl From<&WebsocketTriggerFiltersItem> for WebsocketTriggerFiltersItem { + fn from(value: &WebsocketTriggerFiltersItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub enum WebsocketTriggerInitialMessage { + #[serde(rename = "raw_message")] + RawMessage(String), + #[serde(rename = "runnable_result")] + RunnableResult { args: ScriptArgs, is_flow: bool, path: String }, + } + impl From<&WebsocketTriggerInitialMessage> for WebsocketTriggerInitialMessage { + fn from(value: &WebsocketTriggerInitialMessage) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WhileloopFlow { + pub modules: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parallel: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parallelism: Option, + pub skip_failures: bool, + #[serde(rename = "type")] + pub type_: WhileloopFlowType, + } + impl From<&WhileloopFlow> for WhileloopFlow { + fn from(value: &WhileloopFlow) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum WhileloopFlowType { + #[serde(rename = "forloopflow")] + Forloopflow, + } + impl From<&WhileloopFlowType> for WhileloopFlowType { + fn from(value: &WhileloopFlowType) -> Self { + value.clone() + } + } + impl ToString for WhileloopFlowType { + fn to_string(&self) -> String { + match *self { + Self::Forloopflow => "forloopflow".to_string(), + } + } + } + impl std::str::FromStr for WhileloopFlowType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "forloopflow" => Ok(Self::Forloopflow), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for WhileloopFlowType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for WhileloopFlowType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for WhileloopFlowType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WindmillFileMetadata { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expires: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_modified: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mime_type: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub size_in_bytes: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub version_id: Option, + } + impl From<&WindmillFileMetadata> for WindmillFileMetadata { + fn from(value: &WindmillFileMetadata) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WindmillFilePreview { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub content: Option, + pub content_type: WindmillFilePreviewContentType, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub msg: Option, + } + impl From<&WindmillFilePreview> for WindmillFilePreview { + fn from(value: &WindmillFilePreview) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum WindmillFilePreviewContentType { + RawText, + Csv, + Parquet, + Unknown, + } + impl From<&WindmillFilePreviewContentType> for WindmillFilePreviewContentType { + fn from(value: &WindmillFilePreviewContentType) -> Self { + value.clone() + } + } + impl ToString for WindmillFilePreviewContentType { + fn to_string(&self) -> String { + match *self { + Self::RawText => "RawText".to_string(), + Self::Csv => "Csv".to_string(), + Self::Parquet => "Parquet".to_string(), + Self::Unknown => "Unknown".to_string(), + } + } + } + impl std::str::FromStr for WindmillFilePreviewContentType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "RawText" => Ok(Self::RawText), + "Csv" => Ok(Self::Csv), + "Parquet" => Ok(Self::Parquet), + "Unknown" => Ok(Self::Unknown), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for WindmillFilePreviewContentType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for WindmillFilePreviewContentType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for WindmillFilePreviewContentType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WindmillLargeFile { + pub s3: String, + } + impl From<&WindmillLargeFile> for WindmillLargeFile { + fn from(value: &WindmillLargeFile) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkerPing { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub custom_tags: Vec, + pub ip: String, + pub jobs_executed: i64, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_job_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_job_workspace_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_ping: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub memory: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub memory_usage: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub occupancy_rate: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub occupancy_rate_15s: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub occupancy_rate_30m: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub occupancy_rate_5m: Option, + pub started_at: chrono::DateTime, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub vcpus: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub wm_memory_usage: Option, + pub wm_version: String, + pub worker: String, + pub worker_group: String, + pub worker_instance: String, + } + impl From<&WorkerPing> for WorkerPing { + fn from(value: &WorkerPing) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkflowStatus { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub duration_ms: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub scheduled_for: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub started_at: Option>, + } + impl From<&WorkflowStatus> for WorkflowStatus { + fn from(value: &WorkflowStatus) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkflowStatusRecord( + pub std::collections::HashMap, + ); + impl std::ops::Deref for WorkflowStatusRecord { + type Target = std::collections::HashMap; + fn deref(&self) -> &std::collections::HashMap { + &self.0 + } + } + impl From + for std::collections::HashMap { + fn from(value: WorkflowStatusRecord) -> Self { + value.0 + } + } + impl From<&WorkflowStatusRecord> for WorkflowStatusRecord { + fn from(value: &WorkflowStatusRecord) -> Self { + value.clone() + } + } + impl From> + for WorkflowStatusRecord { + fn from(value: std::collections::HashMap) -> Self { + Self(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkflowTask { + pub args: ScriptArgs, + } + impl From<&WorkflowTask> for WorkflowTask { + fn from(value: &WorkflowTask) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Workspace { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub color: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub domain: Option, + pub id: String, + pub name: String, + pub owner: String, + } + impl From<&Workspace> for Workspace { + fn from(value: &Workspace) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkspaceDefaultScripts { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub default_script_content: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub hidden: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub order: Vec, + } + impl From<&WorkspaceDefaultScripts> for WorkspaceDefaultScripts { + fn from(value: &WorkspaceDefaultScripts) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkspaceDeployUiSettings { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub include_path: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub include_type: Vec, + } + impl From<&WorkspaceDeployUiSettings> for WorkspaceDeployUiSettings { + fn from(value: &WorkspaceDeployUiSettings) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum WorkspaceDeployUiSettingsIncludeTypeItem { + #[serde(rename = "script")] + Script, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "app")] + App, + #[serde(rename = "resource")] + Resource, + #[serde(rename = "variable")] + Variable, + #[serde(rename = "secret")] + Secret, + #[serde(rename = "trigger")] + Trigger, + } + impl From<&WorkspaceDeployUiSettingsIncludeTypeItem> + for WorkspaceDeployUiSettingsIncludeTypeItem { + fn from(value: &WorkspaceDeployUiSettingsIncludeTypeItem) -> Self { + value.clone() + } + } + impl ToString for WorkspaceDeployUiSettingsIncludeTypeItem { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Flow => "flow".to_string(), + Self::App => "app".to_string(), + Self::Resource => "resource".to_string(), + Self::Variable => "variable".to_string(), + Self::Secret => "secret".to_string(), + Self::Trigger => "trigger".to_string(), + } + } + } + impl std::str::FromStr for WorkspaceDeployUiSettingsIncludeTypeItem { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "flow" => Ok(Self::Flow), + "app" => Ok(Self::App), + "resource" => Ok(Self::Resource), + "variable" => Ok(Self::Variable), + "secret" => Ok(Self::Secret), + "trigger" => Ok(Self::Trigger), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for WorkspaceDeployUiSettingsIncludeTypeItem { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for WorkspaceDeployUiSettingsIncludeTypeItem { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for WorkspaceDeployUiSettingsIncludeTypeItem { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkspaceGetCriticalAlertsResponse { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub alerts: Vec, + ///Total number of pages based on the page size. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub total_pages: Option, + ///Total number of rows matching the query. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub total_rows: Option, + } + impl From<&WorkspaceGetCriticalAlertsResponse> + for WorkspaceGetCriticalAlertsResponse { + fn from(value: &WorkspaceGetCriticalAlertsResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkspaceGitSyncSettings { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub include_path: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub include_type: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub repositories: Vec, + } + impl From<&WorkspaceGitSyncSettings> for WorkspaceGitSyncSettings { + fn from(value: &WorkspaceGitSyncSettings) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum WorkspaceGitSyncSettingsIncludeTypeItem { + #[serde(rename = "script")] + Script, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "app")] + App, + #[serde(rename = "folder")] + Folder, + #[serde(rename = "resource")] + Resource, + #[serde(rename = "variable")] + Variable, + #[serde(rename = "secret")] + Secret, + #[serde(rename = "resourcetype")] + Resourcetype, + #[serde(rename = "schedule")] + Schedule, + #[serde(rename = "user")] + User, + #[serde(rename = "group")] + Group, + } + impl From<&WorkspaceGitSyncSettingsIncludeTypeItem> + for WorkspaceGitSyncSettingsIncludeTypeItem { + fn from(value: &WorkspaceGitSyncSettingsIncludeTypeItem) -> Self { + value.clone() + } + } + impl ToString for WorkspaceGitSyncSettingsIncludeTypeItem { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Flow => "flow".to_string(), + Self::App => "app".to_string(), + Self::Folder => "folder".to_string(), + Self::Resource => "resource".to_string(), + Self::Variable => "variable".to_string(), + Self::Secret => "secret".to_string(), + Self::Resourcetype => "resourcetype".to_string(), + Self::Schedule => "schedule".to_string(), + Self::User => "user".to_string(), + Self::Group => "group".to_string(), + } + } + } + impl std::str::FromStr for WorkspaceGitSyncSettingsIncludeTypeItem { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "flow" => Ok(Self::Flow), + "app" => Ok(Self::App), + "folder" => Ok(Self::Folder), + "resource" => Ok(Self::Resource), + "variable" => Ok(Self::Variable), + "secret" => Ok(Self::Secret), + "resourcetype" => Ok(Self::Resourcetype), + "schedule" => Ok(Self::Schedule), + "user" => Ok(Self::User), + "group" => Ok(Self::Group), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for WorkspaceGitSyncSettingsIncludeTypeItem { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for WorkspaceGitSyncSettingsIncludeTypeItem { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for WorkspaceGitSyncSettingsIncludeTypeItem { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkspaceInvite { + pub email: String, + pub is_admin: bool, + pub operator: bool, + pub workspace_id: String, + } + impl From<&WorkspaceInvite> for WorkspaceInvite { + fn from(value: &WorkspaceInvite) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkspaceMuteCriticalAlertsUiBody { + ///Whether critical alerts should be muted. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mute_critical_alerts: Option, + } + impl From<&WorkspaceMuteCriticalAlertsUiBody> for WorkspaceMuteCriticalAlertsUiBody { + fn from(value: &WorkspaceMuteCriticalAlertsUiBody) -> Self { + value.clone() + } + } + pub mod defaults { + pub(super) fn default_bool() -> bool { + V + } + } +} +#[derive(Clone, Debug)] +/**Client for Windmill API + +Version: 1.478.1*/ +pub struct Client { + pub(crate) baseurl: String, + pub(crate) client: reqwest::Client, +} +impl Client { + /// Create a new client. + /// + /// `baseurl` is the base URL provided to the internal + /// `reqwest::Client`, and should include a scheme and hostname, + /// as well as port and a path stem if applicable. + pub fn new(baseurl: &str) -> Self { + #[cfg(not(target_arch = "wasm32"))] + let client = { + let dur = std::time::Duration::from_secs(15); + reqwest::ClientBuilder::new().connect_timeout(dur).timeout(dur) + }; + #[cfg(target_arch = "wasm32")] + let client = reqwest::ClientBuilder::new(); + Self::new_with_client(baseurl, client.build().unwrap()) + } + /// Construct a new client with an existing `reqwest::Client`, + /// allowing more control over its configuration. + /// + /// `baseurl` is the base URL provided to the internal + /// `reqwest::Client`, and should include a scheme and hostname, + /// as well as port and a path stem if applicable. + pub fn new_with_client(baseurl: &str, client: reqwest::Client) -> Self { + Self { + baseurl: baseurl.to_string(), + client, + } + } + /// Get the base URL to which requests are made. + pub fn baseurl(&self) -> &String { + &self.baseurl + } + /// Get the internal `reqwest::Client` used to make requests. + pub fn client(&self) -> &reqwest::Client { + &self.client + } + /// Get the version of this API. + /// + /// This string is pulled directly from the source OpenAPI + /// document and may be in any format the API selects. + pub fn api_version(&self) -> &'static str { + "1.478.1" + } +} +impl Client { + /**get backend version + +Sends a `GET` request to `/version` + +*/ + pub async fn backend_version<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/version", self.baseurl,); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**is backend up to date + +Sends a `GET` request to `/uptodate` + +*/ + pub async fn backend_uptodate<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/uptodate", self.baseurl,); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get license id + +Sends a `GET` request to `/ee_license` + +*/ + pub async fn get_license_id<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/ee_license", self.baseurl,); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get openapi yaml spec + +Sends a `GET` request to `/openapi.yaml` + +*/ + pub async fn get_open_api_yaml<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/openapi.yaml", self.baseurl,); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get audit log (requires admin privilege) + +Sends a `GET` request to `/w/{workspace}/audit/get/{id}` + +*/ + pub async fn get_audit_log<'a>( + &'a self, + workspace: &'a str, + id: i64, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/audit/get/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& id.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list audit logs (requires admin privilege) + +Sends a `GET` request to `/w/{workspace}/audit/list` + +Arguments: +- `workspace` +- `action_kind`: filter on type of operation +- `after`: filter on created after (exclusive) timestamp +- `all_workspaces`: get audit logs for all workspaces +- `before`: filter on started before (inclusive) timestamp +- `exclude_operations`: comma separated list of operations to exclude +- `operation`: filter on exact or prefix name of operation +- `operations`: comma separated list of exact operations to include +- `page`: which page to return (start at 1, default 1) +- `per_page`: number of items to return for a given page (default 30, max 100) +- `resource`: filter on exact or prefix name of resource +- `username`: filter on exact username of user +*/ + pub async fn list_audit_logs<'a>( + &'a self, + workspace: &'a str, + action_kind: Option, + after: Option<&'a chrono::DateTime>, + all_workspaces: Option, + before: Option<&'a chrono::DateTime>, + exclude_operations: Option<&'a str>, + operation: Option<&'a str>, + operations: Option<&'a str>, + page: Option, + per_page: Option, + resource: Option<&'a str>, + username: Option<&'a str>, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/audit/list", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(11usize); + if let Some(v) = &action_kind { + query.push(("action_kind", v.to_string())); + } + if let Some(v) = &after { + query.push(("after", v.to_string())); + } + if let Some(v) = &all_workspaces { + query.push(("all_workspaces", v.to_string())); + } + if let Some(v) = &before { + query.push(("before", v.to_string())); + } + if let Some(v) = &exclude_operations { + query.push(("exclude_operations", v.to_string())); + } + if let Some(v) = &operation { + query.push(("operation", v.to_string())); + } + if let Some(v) = &operations { + query.push(("operations", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &resource { + query.push(("resource", v.to_string())); + } + if let Some(v) = &username { + query.push(("username", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**login with password + +Sends a `POST` request to `/auth/login` + +Arguments: +- `body`: credentials +*/ + pub async fn login<'a>( + &'a self, + body: &'a types::Login, + ) -> Result, Error<()>> { + let url = format!("{}/auth/login", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**logout + +Sends a `POST` request to `/auth/logout` + +*/ + pub async fn logout<'a>(&'a self) -> Result, Error<()>> { + let url = format!("{}/auth/logout", self.baseurl,); + let request = self.client.post(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get user (require admin privilege) + +Sends a `GET` request to `/w/{workspace}/users/get/{username}` + +*/ + pub async fn get_user<'a>( + &'a self, + workspace: &'a str, + username: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/users/get/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& username.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update user (require admin privilege) + +Sends a `POST` request to `/w/{workspace}/users/update/{username}` + +Arguments: +- `workspace` +- `username` +- `body`: new user +*/ + pub async fn update_user<'a>( + &'a self, + workspace: &'a str, + username: &'a str, + body: &'a types::EditWorkspaceUser, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/users/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& username.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**is owner of path + +Sends a `GET` request to `/w/{workspace}/users/is_owner/{path}` + +*/ + pub async fn is_owner_of_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/users/is_owner/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set password + +Sends a `POST` request to `/users/setpassword` + +Arguments: +- `body`: set password +*/ + pub async fn set_password<'a>( + &'a self, + body: &'a types::SetPasswordBody, + ) -> Result, Error<()>> { + let url = format!("{}/users/setpassword", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set password for a specific user (require super admin) + +Sends a `POST` request to `/users/set_password_of/{user}` + +Arguments: +- `user` +- `body`: set password +*/ + pub async fn set_password_for_user<'a>( + &'a self, + user: &'a str, + body: &'a types::SetPasswordForUserBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/users/set_password_of/{}", self.baseurl, encode_path(& user.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set login type for a specific user (require super admin) + +Sends a `POST` request to `/users/set_login_type/{user}` + +Arguments: +- `user` +- `body`: set login type +*/ + pub async fn set_login_type_for_user<'a>( + &'a self, + user: &'a str, + body: &'a types::SetLoginTypeForUserBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/users/set_login_type/{}", self.baseurl, encode_path(& user.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create user + +Sends a `POST` request to `/users/create` + +Arguments: +- `body`: user info +*/ + pub async fn create_user_globally<'a>( + &'a self, + body: &'a types::CreateUserGloballyBody, + ) -> Result, Error<()>> { + let url = format!("{}/users/create", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**global update user (require super admin) + +Sends a `POST` request to `/users/update/{email}` + +Arguments: +- `email` +- `body`: new user info +*/ + pub async fn global_user_update<'a>( + &'a self, + email: &'a str, + body: &'a types::GlobalUserUpdateBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/users/update/{}", self.baseurl, encode_path(& email.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**global username info (require super admin) + +Sends a `GET` request to `/users/username_info/{email}` + +*/ + pub async fn global_username_info<'a>( + &'a self, + email: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/users/username_info/{}", self.baseurl, encode_path(& email.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**global rename user (require super admin) + +Sends a `POST` request to `/users/rename/{email}` + +Arguments: +- `email` +- `body`: new username +*/ + pub async fn global_user_rename<'a>( + &'a self, + email: &'a str, + body: &'a types::GlobalUserRenameBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/users/rename/{}", self.baseurl, encode_path(& email.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**global delete user (require super admin) + +Sends a `DELETE` request to `/users/delete/{email}` + +*/ + pub async fn global_user_delete<'a>( + &'a self, + email: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/users/delete/{}", self.baseurl, encode_path(& email.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**global overwrite users (require super admin and EE) + +Sends a `POST` request to `/users/overwrite` + +Arguments: +- `body`: List of users +*/ + pub async fn global_users_overwrite<'a>( + &'a self, + body: &'a Vec, + ) -> Result, Error<()>> { + let url = format!("{}/users/overwrite", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**global export users (require super admin and EE) + +Sends a `GET` request to `/users/export` + +*/ + pub async fn global_users_export<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/users/export", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete user (require admin privilege) + +Sends a `DELETE` request to `/w/{workspace}/users/delete/{username}` + +*/ + pub async fn delete_user<'a>( + &'a self, + workspace: &'a str, + username: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/users/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& username.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all workspaces visible to me + +Sends a `GET` request to `/workspaces/list` + +*/ + pub async fn list_workspaces<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/workspaces/list", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**is domain allowed for auto invi + +Sends a `GET` request to `/workspaces/allowed_domain_auto_invite` + +*/ + pub async fn is_domain_allowed<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/workspaces/allowed_domain_auto_invite", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all workspaces visible to me with user info + +Sends a `GET` request to `/workspaces/users` + +*/ + pub async fn list_user_workspaces<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/workspaces/users", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all workspaces as super admin (require to be super admin) + +Sends a `GET` request to `/workspaces/list_as_superadmin` + +Arguments: +- `page`: which page to return (start at 1, default 1) +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_workspaces_as_super_admin<'a>( + &'a self, + page: Option, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!("{}/workspaces/list_as_superadmin", self.baseurl,); + let mut query = Vec::with_capacity(2usize); + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create workspace + +Sends a `POST` request to `/workspaces/create` + +Arguments: +- `body`: new token +*/ + pub async fn create_workspace<'a>( + &'a self, + body: &'a types::CreateWorkspace, + ) -> Result, Error<()>> { + let url = format!("{}/workspaces/create", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**exists workspace + +Sends a `POST` request to `/workspaces/exists` + +Arguments: +- `body`: id of workspace +*/ + pub async fn exists_workspace<'a>( + &'a self, + body: &'a types::ExistsWorkspaceBody, + ) -> Result, Error<()>> { + let url = format!("{}/workspaces/exists", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**exists username + +Sends a `POST` request to `/workspaces/exists_username` + +*/ + pub async fn exists_username<'a>( + &'a self, + body: &'a types::ExistsUsernameBody, + ) -> Result, Error<()>> { + let url = format!("{}/workspaces/exists_username", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get global settings + +Sends a `GET` request to `/settings/global/{key}` + +*/ + pub async fn get_global<'a>( + &'a self, + key: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/settings/global/{}", self.baseurl, encode_path(& key.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**post global settings + +Sends a `POST` request to `/settings/global/{key}` + +Arguments: +- `key` +- `body`: value set +*/ + pub async fn set_global<'a>( + &'a self, + key: &'a str, + body: &'a types::SetGlobalBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/settings/global/{}", self.baseurl, encode_path(& key.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get local settings + +Sends a `GET` request to `/settings/local` + +*/ + pub async fn get_local<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/settings/local", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**test smtp + +Sends a `POST` request to `/settings/test_smtp` + +Arguments: +- `body`: test smtp payload +*/ + pub async fn test_smtp<'a>( + &'a self, + body: &'a types::TestSmtpBody, + ) -> Result, Error<()>> { + let url = format!("{}/settings/test_smtp", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**test critical channels + +Sends a `POST` request to `/settings/test_critical_channels` + +Arguments: +- `body`: test critical channel payload +*/ + pub async fn test_critical_channels<'a>( + &'a self, + body: &'a Vec, + ) -> Result, Error<()>> { + let url = format!("{}/settings/test_critical_channels", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Get all critical alerts + +Sends a `GET` request to `/settings/critical_alerts` + +*/ + pub async fn get_critical_alerts<'a>( + &'a self, + acknowledged: Option, + page: Option, + page_size: Option, + ) -> Result, Error<()>> { + let url = format!("{}/settings/critical_alerts", self.baseurl,); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &acknowledged { + query.push(("acknowledged", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &page_size { + query.push(("page_size", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Acknowledge a critical alert + +Sends a `POST` request to `/settings/critical_alerts/{id}/acknowledge` + +Arguments: +- `id`: The ID of the critical alert to acknowledge +*/ + pub async fn acknowledge_critical_alert<'a>( + &'a self, + id: i64, + ) -> Result, Error<()>> { + let url = format!( + "{}/settings/critical_alerts/{}/acknowledge", self.baseurl, encode_path(& id + .to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Acknowledge all unacknowledged critical alerts + +Sends a `POST` request to `/settings/critical_alerts/acknowledge_all` + +*/ + pub async fn acknowledge_all_critical_alerts<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/settings/critical_alerts/acknowledge_all", self.baseurl,); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**test license key + +Sends a `POST` request to `/settings/test_license_key` + +Arguments: +- `body`: test license key +*/ + pub async fn test_license_key<'a>( + &'a self, + body: &'a types::TestLicenseKeyBody, + ) -> Result, Error<()>> { + let url = format!("{}/settings/test_license_key", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**test object storage config + +Sends a `POST` request to `/settings/test_object_storage_config` + +Arguments: +- `body`: test object storage config +*/ + pub async fn test_object_storage_config<'a>( + &'a self, + body: &'a std::collections::HashMap, + ) -> Result, Error<()>> { + let url = format!("{}/settings/test_object_storage_config", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**send stats + +Sends a `POST` request to `/settings/send_stats` + +*/ + pub async fn send_stats<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/settings/send_stats", self.baseurl,); + let request = self.client.post(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get latest key renewal attempt + +Sends a `GET` request to `/settings/latest_key_renewal_attempt` + +*/ + pub async fn get_latest_key_renewal_attempt<'a>( + &'a self, + ) -> Result< + ResponseValue>, + Error<()>, + > { + let url = format!("{}/settings/latest_key_renewal_attempt", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**renew license key + +Sends a `POST` request to `/settings/renew_license_key` + +*/ + pub async fn renew_license_key<'a>( + &'a self, + license_key: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!("{}/settings/renew_license_key", self.baseurl,); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &license_key { + query.push(("license_key", v.to_string())); + } + let request = self.client.post(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create customer portal session + +Sends a `POST` request to `/settings/customer_portal` + +*/ + pub async fn create_customer_portal_session<'a>( + &'a self, + license_key: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!("{}/settings/customer_portal", self.baseurl,); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &license_key { + query.push(("license_key", v.to_string())); + } + let request = self.client.post(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**test metadata + +Sends a `POST` request to `/saml/test_metadata` + +Arguments: +- `body`: test metadata +*/ + pub async fn test_metadata<'a>( + &'a self, + body: &'a str, + ) -> Result, Error<()>> { + let url = format!("{}/saml/test_metadata", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list global settings + +Sends a `GET` request to `/settings/list_global` + +*/ + pub async fn list_global_settings<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/settings/list_global", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get current user email (if logged in) + +Sends a `GET` request to `/users/email` + +*/ + pub async fn get_current_email<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/users/email", self.baseurl,); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**refresh the current token + +Sends a `GET` request to `/users/refresh_token` + +*/ + pub async fn refresh_user_token<'a>( + &'a self, + if_expiring_in_less_than_s: Option, + ) -> Result, Error<()>> { + let url = format!("{}/users/refresh_token", self.baseurl,); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &if_expiring_in_less_than_s { + query.push(("if_expiring_in_less_than_s", v.to_string())); + } + let request = self.client.get(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get tutorial progress + +Sends a `GET` request to `/users/tutorial_progress` + +*/ + pub async fn get_tutorial_progress<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/users/tutorial_progress", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update tutorial progress + +Sends a `POST` request to `/users/tutorial_progress` + +Arguments: +- `body`: progress update +*/ + pub async fn update_tutorial_progress<'a>( + &'a self, + body: &'a types::UpdateTutorialProgressBody, + ) -> Result, Error<()>> { + let url = format!("{}/users/tutorial_progress", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**leave instance + +Sends a `POST` request to `/users/leave_instance` + +*/ + pub async fn leave_instance<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/users/leave_instance", self.baseurl,); + let request = self.client.post(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get current usage outside of premium workspaces + +Sends a `GET` request to `/users/usage` + +*/ + pub async fn get_usage<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/users/usage", self.baseurl,); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get all runnables in every workspace + +Sends a `GET` request to `/users/all_runnables` + +*/ + pub async fn get_runnable<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/users/all_runnables", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get current global whoami (if logged in) + +Sends a `GET` request to `/users/whoami` + +*/ + pub async fn global_whoami<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/users/whoami", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all workspace invites + +Sends a `GET` request to `/users/list_invites` + +*/ + pub async fn list_workspace_invites<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/users/list_invites", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**whoami + +Sends a `GET` request to `/w/{workspace}/users/whoami` + +*/ + pub async fn whoami<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/users/whoami", self.baseurl, encode_path(& workspace.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**accept invite to workspace + +Sends a `POST` request to `/users/accept_invite` + +Arguments: +- `body`: accept invite +*/ + pub async fn accept_invite<'a>( + &'a self, + body: &'a types::AcceptInviteBody, + ) -> Result, Error<()>> { + let url = format!("{}/users/accept_invite", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**decline invite to workspace + +Sends a `POST` request to `/users/decline_invite` + +Arguments: +- `body`: decline invite +*/ + pub async fn decline_invite<'a>( + &'a self, + body: &'a types::DeclineInviteBody, + ) -> Result, Error<()>> { + let url = format!("{}/users/decline_invite", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**invite user to workspace + +Sends a `POST` request to `/w/{workspace}/workspaces/invite_user` + +Arguments: +- `workspace` +- `body`: WorkspaceInvite +*/ + pub async fn invite_user<'a>( + &'a self, + workspace: &'a str, + body: &'a types::InviteUserBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/invite_user", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**add user to workspace + +Sends a `POST` request to `/w/{workspace}/workspaces/add_user` + +Arguments: +- `workspace` +- `body`: WorkspaceInvite +*/ + pub async fn add_user<'a>( + &'a self, + workspace: &'a str, + body: &'a types::AddUserBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/add_user", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete user invite + +Sends a `POST` request to `/w/{workspace}/workspaces/delete_invite` + +Arguments: +- `workspace` +- `body`: WorkspaceInvite +*/ + pub async fn delete_invite<'a>( + &'a self, + workspace: &'a str, + body: &'a types::DeleteInviteBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/delete_invite", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**archive workspace + +Sends a `POST` request to `/w/{workspace}/workspaces/archive` + +*/ + pub async fn archive_workspace<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/archive", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**unarchive workspace + +Sends a `POST` request to `/workspaces/unarchive/{workspace}` + +*/ + pub async fn unarchive_workspace<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/workspaces/unarchive/{}", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete workspace (require super admin) + +Sends a `DELETE` request to `/workspaces/delete/{workspace}` + +*/ + pub async fn delete_workspace<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/workspaces/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**leave workspace + +Sends a `POST` request to `/w/{workspace}/workspaces/leave` + +*/ + pub async fn leave_workspace<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/leave", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get workspace name + +Sends a `GET` request to `/w/{workspace}/workspaces/get_workspace_name` + +*/ + pub async fn get_workspace_name<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/get_workspace_name", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**change workspace name + +Sends a `POST` request to `/w/{workspace}/workspaces/change_workspace_name` + +*/ + pub async fn change_workspace_name<'a>( + &'a self, + workspace: &'a str, + body: &'a types::ChangeWorkspaceNameBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/change_workspace_name", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**change workspace id + +Sends a `POST` request to `/w/{workspace}/workspaces/change_workspace_id` + +*/ + pub async fn change_workspace_id<'a>( + &'a self, + workspace: &'a str, + body: &'a types::ChangeWorkspaceIdBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/change_workspace_id", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**change workspace id + +Sends a `POST` request to `/w/{workspace}/workspaces/change_workspace_color` + +*/ + pub async fn change_workspace_color<'a>( + &'a self, + workspace: &'a str, + body: &'a types::ChangeWorkspaceColorBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/change_workspace_color", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**whois + +Sends a `GET` request to `/w/{workspace}/users/whois/{username}` + +*/ + pub async fn whois<'a>( + &'a self, + workspace: &'a str, + username: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/users/whois/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& username.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Update operator settings for a workspace + +Updates the operator settings for a specific workspace. Requires workspace admin privileges. + +Sends a `POST` request to `/w/{workspace}/workspaces/operator_settings` + +*/ + pub async fn update_operator_settings<'a>( + &'a self, + workspace: &'a str, + body: &'a types::OperatorSettings, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/operator_settings", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**exists email + +Sends a `GET` request to `/users/exists/{email}` + +*/ + pub async fn exists_email<'a>( + &'a self, + email: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/users/exists/{}", self.baseurl, encode_path(& email.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all users as super admin (require to be super amdin) + +Sends a `GET` request to `/users/list_as_super_admin` + +Arguments: +- `active_only`: filter only active users +- `page`: which page to return (start at 1, default 1) +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_users_as_super_admin<'a>( + &'a self, + active_only: Option, + page: Option, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!("{}/users/list_as_super_admin", self.baseurl,); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &active_only { + query.push(("active_only", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list pending invites for a workspace + +Sends a `GET` request to `/w/{workspace}/workspaces/list_pending_invites` + +*/ + pub async fn list_pending_invites<'a>( + &'a self, + workspace: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/list_pending_invites", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get settings + +Sends a `GET` request to `/w/{workspace}/workspaces/get_settings` + +*/ + pub async fn get_settings<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/get_settings", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get deploy to + +Sends a `GET` request to `/w/{workspace}/workspaces/get_deploy_to` + +*/ + pub async fn get_deploy_to<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/get_deploy_to", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get if workspace is premium + +Sends a `GET` request to `/w/{workspace}/workspaces/is_premium` + +*/ + pub async fn get_is_premium<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/is_premium", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get premium info + +Sends a `GET` request to `/w/{workspace}/workspaces/premium_info` + +*/ + pub async fn get_premium_info<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/premium_info", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set automatic billing + +Sends a `POST` request to `/w/{workspace}/workspaces/set_automatic_billing` + +Arguments: +- `workspace` +- `body`: automatic billing +*/ + pub async fn set_automatic_billing<'a>( + &'a self, + workspace: &'a str, + body: &'a types::SetAutomaticBillingBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/set_automatic_billing", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get threshold alert info + +Sends a `GET` request to `/w/{workspace}/workspaces/threshold_alert` + +*/ + pub async fn get_threshold_alert<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/threshold_alert", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set threshold alert info + +Sends a `POST` request to `/w/{workspace}/workspaces/threshold_alert` + +Arguments: +- `workspace` +- `body`: threshold alert info +*/ + pub async fn set_threshold_alert<'a>( + &'a self, + workspace: &'a str, + body: &'a types::SetThresholdAlertBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/threshold_alert", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**edit slack command + +Sends a `POST` request to `/w/{workspace}/workspaces/edit_slack_command` + +Arguments: +- `workspace` +- `body`: WorkspaceInvite +*/ + pub async fn edit_slack_command<'a>( + &'a self, + workspace: &'a str, + body: &'a types::EditSlackCommandBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/edit_slack_command", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**edit teams command + +Sends a `POST` request to `/w/{workspace}/workspaces/edit_teams_command` + +Arguments: +- `workspace` +- `body`: WorkspaceInvite +*/ + pub async fn edit_teams_command<'a>( + &'a self, + workspace: &'a str, + body: &'a types::EditTeamsCommandBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/edit_teams_command", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list available teams ids + +Sends a `GET` request to `/w/{workspace}/workspaces/available_teams_ids` + +*/ + pub async fn list_available_teams_ids<'a>( + &'a self, + workspace: &'a str, + ) -> Result< + ResponseValue>, + Error<()>, + > { + let url = format!( + "{}/w/{}/workspaces/available_teams_ids", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list available teams channels + +Sends a `GET` request to `/w/{workspace}/workspaces/available_teams_channels` + +*/ + pub async fn list_available_teams_channels<'a>( + &'a self, + workspace: &'a str, + ) -> Result< + ResponseValue>, + Error<()>, + > { + let url = format!( + "{}/w/{}/workspaces/available_teams_channels", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**connect teams + +Sends a `POST` request to `/w/{workspace}/workspaces/connect_teams` + +Arguments: +- `workspace` +- `body`: connect teams +*/ + pub async fn connect_teams<'a>( + &'a self, + workspace: &'a str, + body: &'a types::ConnectTeamsBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/connect_teams", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run a job that sends a message to Slack + +Sends a `POST` request to `/w/{workspace}/workspaces/run_slack_message_test_job` + +Arguments: +- `workspace` +- `body`: path to hub script to run and its corresponding args +*/ + pub async fn run_slack_message_test_job<'a>( + &'a self, + workspace: &'a str, + body: &'a types::RunSlackMessageTestJobBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/run_slack_message_test_job", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run a job that sends a message to Teams + +Sends a `POST` request to `/w/{workspace}/workspaces/run_teams_message_test_job` + +Arguments: +- `workspace` +- `body`: path to hub script to run and its corresponding args +*/ + pub async fn run_teams_message_test_job<'a>( + &'a self, + workspace: &'a str, + body: &'a types::RunTeamsMessageTestJobBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/run_teams_message_test_job", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**edit deploy to + +Sends a `POST` request to `/w/{workspace}/workspaces/edit_deploy_to` + +*/ + pub async fn edit_deploy_to<'a>( + &'a self, + workspace: &'a str, + body: &'a types::EditDeployToBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/edit_deploy_to", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**edit auto invite + +Sends a `POST` request to `/w/{workspace}/workspaces/edit_auto_invite` + +Arguments: +- `workspace` +- `body`: WorkspaceInvite +*/ + pub async fn edit_auto_invite<'a>( + &'a self, + workspace: &'a str, + body: &'a types::EditAutoInviteBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/edit_auto_invite", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**edit webhook + +Sends a `POST` request to `/w/{workspace}/workspaces/edit_webhook` + +Arguments: +- `workspace` +- `body`: WorkspaceWebhook +*/ + pub async fn edit_webhook<'a>( + &'a self, + workspace: &'a str, + body: &'a types::EditWebhookBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/edit_webhook", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**edit copilot config + +Sends a `POST` request to `/w/{workspace}/workspaces/edit_copilot_config` + +Arguments: +- `workspace` +- `body`: WorkspaceCopilotConfig +*/ + pub async fn edit_copilot_config<'a>( + &'a self, + workspace: &'a str, + body: &'a types::EditCopilotConfigBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/edit_copilot_config", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get copilot info + +Sends a `GET` request to `/w/{workspace}/workspaces/get_copilot_info` + +*/ + pub async fn get_copilot_info<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/get_copilot_info", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**edit error handler + +Sends a `POST` request to `/w/{workspace}/workspaces/edit_error_handler` + +Arguments: +- `workspace` +- `body`: WorkspaceErrorHandler +*/ + pub async fn edit_error_handler<'a>( + &'a self, + workspace: &'a str, + body: &'a types::EditErrorHandlerBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/edit_error_handler", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**edit large file storage settings + +Sends a `POST` request to `/w/{workspace}/workspaces/edit_large_file_storage_config` + +Arguments: +- `workspace` +- `body`: LargeFileStorage info +*/ + pub async fn edit_large_file_storage_config<'a>( + &'a self, + workspace: &'a str, + body: &'a types::EditLargeFileStorageConfigBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/edit_large_file_storage_config", self.baseurl, + encode_path(& workspace.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**edit workspace git sync settings + +Sends a `POST` request to `/w/{workspace}/workspaces/edit_git_sync_config` + +Arguments: +- `workspace` +- `body`: Workspace Git sync settings +*/ + pub async fn edit_workspace_git_sync_config<'a>( + &'a self, + workspace: &'a str, + body: &'a types::EditWorkspaceGitSyncConfigBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/edit_git_sync_config", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**edit workspace deploy ui settings + +Sends a `POST` request to `/w/{workspace}/workspaces/edit_deploy_ui_config` + +Arguments: +- `workspace` +- `body`: Workspace deploy UI settings +*/ + pub async fn edit_workspace_deploy_ui_settings<'a>( + &'a self, + workspace: &'a str, + body: &'a types::EditWorkspaceDeployUiSettingsBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/edit_deploy_ui_config", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**edit default app for workspace + +Sends a `POST` request to `/w/{workspace}/workspaces/edit_default_app` + +Arguments: +- `workspace` +- `body`: Workspace default app +*/ + pub async fn edit_workspace_default_app<'a>( + &'a self, + workspace: &'a str, + body: &'a types::EditWorkspaceDefaultAppBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/edit_default_app", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get default scripts for workspace + +Sends a `GET` request to `/w/{workspace}/workspaces/default_scripts` + +*/ + pub async fn get_default_scripts<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/default_scripts", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**edit default scripts for workspace + +Sends a `POST` request to `/w/{workspace}/workspaces/default_scripts` + +Arguments: +- `workspace` +- `body`: Workspace default app +*/ + pub async fn edit_default_scripts<'a>( + &'a self, + workspace: &'a str, + body: &'a types::WorkspaceDefaultScripts, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/default_scripts", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set environment variable + +Sends a `POST` request to `/w/{workspace}/workspaces/set_environment_variable` + +Arguments: +- `workspace` +- `body`: Workspace default app +*/ + pub async fn set_environment_variable<'a>( + &'a self, + workspace: &'a str, + body: &'a types::SetEnvironmentVariableBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/set_environment_variable", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**retrieves the encryption key for this workspace + +Sends a `GET` request to `/w/{workspace}/workspaces/encryption_key` + +*/ + pub async fn get_workspace_encryption_key<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/encryption_key", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update the encryption key for this workspace + +Sends a `POST` request to `/w/{workspace}/workspaces/encryption_key` + +Arguments: +- `workspace` +- `body`: New encryption key +*/ + pub async fn set_workspace_encryption_key<'a>( + &'a self, + workspace: &'a str, + body: &'a types::SetWorkspaceEncryptionKeyBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/encryption_key", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get default app for workspace + +Sends a `GET` request to `/w/{workspace}/workspaces/default_app` + +*/ + pub async fn get_workspace_default_app<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/default_app", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get large file storage config + +Sends a `GET` request to `/w/{workspace}/workspaces/get_large_file_storage_config` + +*/ + pub async fn get_large_file_storage_config<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/get_large_file_storage_config", self.baseurl, + encode_path(& workspace.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get usage + +Sends a `GET` request to `/w/{workspace}/workspaces/usage` + +*/ + pub async fn get_workspace_usage<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/usage", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get used triggers + +Sends a `GET` request to `/w/{workspace}/workspaces/used_triggers` + +*/ + pub async fn get_used_triggers<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/used_triggers", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list users + +Sends a `GET` request to `/w/{workspace}/users/list` + +*/ + pub async fn list_users<'a>( + &'a self, + workspace: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/users/list", self.baseurl, encode_path(& workspace.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list users usage + +Sends a `GET` request to `/w/{workspace}/users/list_usage` + +*/ + pub async fn list_users_usage<'a>( + &'a self, + workspace: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/users/list_usage", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list usernames + +Sends a `GET` request to `/w/{workspace}/users/list_usernames` + +*/ + pub async fn list_usernames<'a>( + &'a self, + workspace: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/users/list_usernames", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get email from username + +Sends a `GET` request to `/w/{workspace}/users/username_to_email/{username}` + +*/ + pub async fn username_to_email<'a>( + &'a self, + workspace: &'a str, + username: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/users/username_to_email/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& username.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create token + +Sends a `POST` request to `/users/tokens/create` + +Arguments: +- `body`: new token +*/ + pub async fn create_token<'a>( + &'a self, + body: &'a types::NewToken, + ) -> Result, Error<()>> { + let url = format!("{}/users/tokens/create", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create token to impersonate a user (require superadmin) + +Sends a `POST` request to `/users/tokens/impersonate` + +Arguments: +- `body`: new token +*/ + pub async fn create_token_impersonate<'a>( + &'a self, + body: &'a types::NewTokenImpersonate, + ) -> Result, Error<()>> { + let url = format!("{}/users/tokens/impersonate", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete token + +Sends a `DELETE` request to `/users/tokens/delete/{token_prefix}` + +*/ + pub async fn delete_token<'a>( + &'a self, + token_prefix: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/users/tokens/delete/{}", self.baseurl, encode_path(& token_prefix + .to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list token + +Sends a `GET` request to `/users/tokens/list` + +Arguments: +- `exclude_ephemeral` +- `page`: which page to return (start at 1, default 1) +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_tokens<'a>( + &'a self, + exclude_ephemeral: Option, + page: Option, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!("{}/users/tokens/list", self.baseurl,); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &exclude_ephemeral { + query.push(("exclude_ephemeral", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get OIDC token (ee only) + +Sends a `POST` request to `/w/{workspace}/oidc/token/{audience}` + +*/ + pub async fn get_oidc_token<'a>( + &'a self, + workspace: &'a str, + audience: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/oidc/token/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& audience.to_string()), + ); + let request = self.client.post(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create variable + +Sends a `POST` request to `/w/{workspace}/variables/create` + +Arguments: +- `workspace` +- `already_encrypted` +- `body`: new variable +*/ + pub async fn create_variable<'a>( + &'a self, + workspace: &'a str, + already_encrypted: Option, + body: &'a types::CreateVariable, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/variables/create", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &already_encrypted { + query.push(("already_encrypted", v.to_string())); + } + let request = self.client.post(url).json(&body).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**encrypt value + +Sends a `POST` request to `/w/{workspace}/variables/encrypt` + +Arguments: +- `workspace` +- `body`: new variable +*/ + pub async fn encrypt_value<'a>( + &'a self, + workspace: &'a str, + body: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/variables/encrypt", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete variable + +Sends a `DELETE` request to `/w/{workspace}/variables/delete/{path}` + +*/ + pub async fn delete_variable<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/variables/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update variable + +Sends a `POST` request to `/w/{workspace}/variables/update/{path}` + +Arguments: +- `workspace` +- `path` +- `already_encrypted` +- `body`: updated variable +*/ + pub async fn update_variable<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + already_encrypted: Option, + body: &'a types::EditVariable, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/variables/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &already_encrypted { + query.push(("already_encrypted", v.to_string())); + } + let request = self.client.post(url).json(&body).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get variable + +Sends a `GET` request to `/w/{workspace}/variables/get/{path}` + +Arguments: +- `workspace` +- `path` +- `decrypt_secret`: ask to decrypt secret if this variable is secret +(if not secret no effect, default: true) + +- `include_encrypted`: ask to include the encrypted value if secret and decrypt secret is not true (default: false) + +*/ + pub async fn get_variable<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + decrypt_secret: Option, + include_encrypted: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/variables/get/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(2usize); + if let Some(v) = &decrypt_secret { + query.push(("decrypt_secret", v.to_string())); + } + if let Some(v) = &include_encrypted { + query.push(("include_encrypted", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get variable value + +Sends a `GET` request to `/w/{workspace}/variables/get_value/{path}` + +*/ + pub async fn get_variable_value<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/variables/get_value/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does variable exists at path + +Sends a `GET` request to `/w/{workspace}/variables/exists/{path}` + +*/ + pub async fn exists_variable<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/variables/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list variables + +Sends a `GET` request to `/w/{workspace}/variables/list` + +Arguments: +- `workspace` +- `page`: which page to return (start at 1, default 1) +- `path_start` +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_variable<'a>( + &'a self, + workspace: &'a str, + page: Option, + path_start: Option<&'a str>, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/variables/list", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list contextual variables + +Sends a `GET` request to `/w/{workspace}/variables/list_contextual` + +*/ + pub async fn list_contextual_variables<'a>( + &'a self, + workspace: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/variables/list_contextual", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Get all critical alerts for this workspace + +Sends a `GET` request to `/w/{workspace}/workspaces/critical_alerts` + +*/ + pub async fn workspace_get_critical_alerts<'a>( + &'a self, + workspace: &'a str, + acknowledged: Option, + page: Option, + page_size: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/critical_alerts", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &acknowledged { + query.push(("acknowledged", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &page_size { + query.push(("page_size", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Acknowledge a critical alert for this workspace + +Sends a `POST` request to `/w/{workspace}/workspaces/critical_alerts/{id}/acknowledge` + +Arguments: +- `workspace` +- `id`: The ID of the critical alert to acknowledge +*/ + pub async fn workspace_acknowledge_critical_alert<'a>( + &'a self, + workspace: &'a str, + id: i64, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/critical_alerts/{}/acknowledge", self.baseurl, + encode_path(& workspace.to_string()), encode_path(& id.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Acknowledge all unacknowledged critical alerts for this workspace + +Sends a `POST` request to `/w/{workspace}/workspaces/critical_alerts/acknowledge_all` + +*/ + pub async fn workspace_acknowledge_all_critical_alerts<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/critical_alerts/acknowledge_all", self.baseurl, + encode_path(& workspace.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Mute critical alert UI for this workspace + +Sends a `POST` request to `/w/{workspace}/workspaces/critical_alerts/mute` + +Arguments: +- `workspace` +- `body`: Boolean flag to mute critical alerts. +*/ + pub async fn workspace_mute_critical_alerts_ui<'a>( + &'a self, + workspace: &'a str, + body: &'a types::WorkspaceMuteCriticalAlertsUiBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/workspaces/critical_alerts/mute", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**login with oauth authorization flow + +Sends a `POST` request to `/oauth/login_callback/{client_name}` + +Arguments: +- `client_name` +- `body`: Partially filled script +*/ + pub async fn login_with_oauth<'a>( + &'a self, + client_name: &'a str, + body: &'a types::LoginWithOauthBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/oauth/login_callback/{}", self.baseurl, encode_path(& client_name + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**connect slack callback + +Sends a `POST` request to `/w/{workspace}/oauth/connect_slack_callback` + +Arguments: +- `workspace` +- `body`: code endpoint +*/ + pub async fn connect_slack_callback<'a>( + &'a self, + workspace: &'a str, + body: &'a types::ConnectSlackCallbackBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/oauth/connect_slack_callback", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**connect slack callback instance + +Sends a `POST` request to `/oauth/connect_slack_callback` + +Arguments: +- `body`: code endpoint +*/ + pub async fn connect_slack_callback_instance<'a>( + &'a self, + body: &'a types::ConnectSlackCallbackInstanceBody, + ) -> Result, Error<()>> { + let url = format!("{}/oauth/connect_slack_callback", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**connect callback + +Sends a `POST` request to `/oauth/connect_callback/{client_name}` + +Arguments: +- `client_name` +- `body`: code endpoint +*/ + pub async fn connect_callback<'a>( + &'a self, + client_name: &'a str, + body: &'a types::ConnectCallbackBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/oauth/connect_callback/{}", self.baseurl, encode_path(& client_name + .to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create OAuth account + +Sends a `POST` request to `/w/{workspace}/oauth/create_account` + +Arguments: +- `workspace` +- `body`: code endpoint +*/ + pub async fn create_account<'a>( + &'a self, + workspace: &'a str, + body: &'a types::CreateAccountBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/oauth/create_account", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**refresh token + +Sends a `POST` request to `/w/{workspace}/oauth/refresh_token/{id}` + +Arguments: +- `workspace` +- `id` +- `body`: variable path +*/ + pub async fn refresh_token<'a>( + &'a self, + workspace: &'a str, + id: i64, + body: &'a types::RefreshTokenBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/oauth/refresh_token/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**disconnect account + +Sends a `POST` request to `/w/{workspace}/oauth/disconnect/{id}` + +*/ + pub async fn disconnect_account<'a>( + &'a self, + workspace: &'a str, + id: i64, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/oauth/disconnect/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let request = self.client.post(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**disconnect slack + +Sends a `POST` request to `/w/{workspace}/oauth/disconnect_slack` + +*/ + pub async fn disconnect_slack<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/oauth/disconnect_slack", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**disconnect teams + +Sends a `POST` request to `/w/{workspace}/oauth/disconnect_teams` + +*/ + pub async fn disconnect_teams<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/oauth/disconnect_teams", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list oauth logins + +Sends a `GET` request to `/oauth/list_logins` + +*/ + pub async fn list_o_auth_logins<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/oauth/list_logins", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list oauth connects + +Sends a `GET` request to `/oauth/list_connects` + +*/ + pub async fn list_o_auth_connects<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/oauth/list_connects", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get oauth connect + +Sends a `GET` request to `/oauth/get_connect/{client}` + +Arguments: +- `client`: client name +*/ + pub async fn get_o_auth_connect<'a>( + &'a self, + client: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/oauth/get_connect/{}", self.baseurl, encode_path(& client.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**synchronize Microsoft Teams information (teams/channels) + +Sends a `POST` request to `/teams/sync` + +*/ + pub async fn sync_teams<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/teams/sync", self.baseurl,); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**send update to Microsoft Teams activity + +Respond to a Microsoft Teams activity after a workspace command is run + +Sends a `POST` request to `/teams/activities` + +*/ + pub async fn send_message_to_conversation<'a>( + &'a self, + body: &'a types::SendMessageToConversationBody, + ) -> Result, Error<()>> { + let url = format!("{}/teams/activities", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::empty(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create resource + +Sends a `POST` request to `/w/{workspace}/resources/create` + +Arguments: +- `workspace` +- `update_if_exists` +- `body`: new resource +*/ + pub async fn create_resource<'a>( + &'a self, + workspace: &'a str, + update_if_exists: Option, + body: &'a types::CreateResource, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/create", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &update_if_exists { + query.push(("update_if_exists", v.to_string())); + } + let request = self.client.post(url).json(&body).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete resource + +Sends a `DELETE` request to `/w/{workspace}/resources/delete/{path}` + +*/ + pub async fn delete_resource<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update resource + +Sends a `POST` request to `/w/{workspace}/resources/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated resource +*/ + pub async fn update_resource<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::EditResource, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update resource value + +Sends a `POST` request to `/w/{workspace}/resources/update_value/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated resource +*/ + pub async fn update_resource_value<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::UpdateResourceValueBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/update_value/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get resource + +Sends a `GET` request to `/w/{workspace}/resources/get/{path}` + +*/ + pub async fn get_resource<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/get/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get resource interpolated (variables and resources are fully unrolled) + +Sends a `GET` request to `/w/{workspace}/resources/get_value_interpolated/{path}` + +Arguments: +- `workspace` +- `path` +- `job_id`: job id +*/ + pub async fn get_resource_value_interpolated<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + job_id: Option<&'a uuid::Uuid>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/get_value_interpolated/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &job_id { + query.push(("job_id", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get resource value + +Sends a `GET` request to `/w/{workspace}/resources/get_value/{path}` + +*/ + pub async fn get_resource_value<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/get_value/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does resource exists + +Sends a `GET` request to `/w/{workspace}/resources/exists/{path}` + +*/ + pub async fn exists_resource<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list resources + +Sends a `GET` request to `/w/{workspace}/resources/list` + +Arguments: +- `workspace` +- `page`: which page to return (start at 1, default 1) +- `path_start` +- `per_page`: number of items to return for a given page (default 30, max 100) +- `resource_type`: resource_types to list from, separated by ',', +- `resource_type_exclude`: resource_types to not list from, separated by ',', +*/ + pub async fn list_resource<'a>( + &'a self, + workspace: &'a str, + page: Option, + path_start: Option<&'a str>, + per_page: Option, + resource_type: Option<&'a str>, + resource_type_exclude: Option<&'a str>, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/resources/list", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(5usize); + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &resource_type { + query.push(("resource_type", v.to_string())); + } + if let Some(v) = &resource_type_exclude { + query.push(("resource_type_exclude", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list resources for search + +Sends a `GET` request to `/w/{workspace}/resources/list_search` + +*/ + pub async fn list_search_resource<'a>( + &'a self, + workspace: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/resources/list_search", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list resource names + +Sends a `GET` request to `/w/{workspace}/resources/list_names/{name}` + +*/ + pub async fn list_resource_names<'a>( + &'a self, + workspace: &'a str, + name: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/resources/list_names/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& name.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create resource_type + +Sends a `POST` request to `/w/{workspace}/resources/type/create` + +Arguments: +- `workspace` +- `body`: new resource_type +*/ + pub async fn create_resource_type<'a>( + &'a self, + workspace: &'a str, + body: &'a types::ResourceType, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/type/create", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get map from resource type to format extension + +Sends a `GET` request to `/w/{workspace}/resources/file_resource_type_to_file_ext_map` + +*/ + pub async fn file_resource_type_to_file_ext_map<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/file_resource_type_to_file_ext_map", self.baseurl, + encode_path(& workspace.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete resource_type + +Sends a `DELETE` request to `/w/{workspace}/resources/type/delete/{path}` + +*/ + pub async fn delete_resource_type<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/type/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update resource_type + +Sends a `POST` request to `/w/{workspace}/resources/type/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated resource_type +*/ + pub async fn update_resource_type<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::EditResourceType, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/type/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get resource_type + +Sends a `GET` request to `/w/{workspace}/resources/type/get/{path}` + +*/ + pub async fn get_resource_type<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/type/get/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does resource_type exists + +Sends a `GET` request to `/w/{workspace}/resources/type/exists/{path}` + +*/ + pub async fn exists_resource_type<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/resources/type/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list resource_types + +Sends a `GET` request to `/w/{workspace}/resources/type/list` + +*/ + pub async fn list_resource_type<'a>( + &'a self, + workspace: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/resources/type/list", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list resource_types names + +Sends a `GET` request to `/w/{workspace}/resources/type/listnames` + +*/ + pub async fn list_resource_type_names<'a>( + &'a self, + workspace: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/resources/type/listnames", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**query resource types by similarity + +Sends a `GET` request to `/w/{workspace}/embeddings/query_resource_types` + +Arguments: +- `workspace` +- `limit`: query limit +- `text`: query text +*/ + pub async fn query_resource_types<'a>( + &'a self, + workspace: &'a str, + limit: Option, + text: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/embeddings/query_resource_types", self.baseurl, encode_path(& + workspace.to_string()), + ); + let mut query = Vec::with_capacity(2usize); + if let Some(v) = &limit { + query.push(("limit", v.to_string())); + } + query.push(("text", text.to_string())); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list hub integrations + +Sends a `GET` request to `/integrations/hub/list` + +Arguments: +- `kind`: query integrations kind +*/ + pub async fn list_hub_integrations<'a>( + &'a self, + kind: Option<&'a str>, + ) -> Result>, Error<()>> { + let url = format!("{}/integrations/hub/list", self.baseurl,); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &kind { + query.push(("kind", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all hub flows + +Sends a `GET` request to `/flows/hub/list` + +*/ + pub async fn list_hub_flows<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/flows/hub/list", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get hub flow by id + +Sends a `GET` request to `/flows/hub/get/{id}` + +*/ + pub async fn get_hub_flow_by_id<'a>( + &'a self, + id: i64, + ) -> Result, Error<()>> { + let url = format!( + "{}/flows/hub/get/{}", self.baseurl, encode_path(& id.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all hub apps + +Sends a `GET` request to `/apps/hub/list` + +*/ + pub async fn list_hub_apps<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/apps/hub/list", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get hub app by id + +Sends a `GET` request to `/apps/hub/get/{id}` + +*/ + pub async fn get_hub_app_by_id<'a>( + &'a self, + id: i64, + ) -> Result, Error<()>> { + let url = format!( + "{}/apps/hub/get/{}", self.baseurl, encode_path(& id.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get public app by custom path + +Sends a `GET` request to `/apps_u/public_app_by_custom_path/{custom_path}` + +*/ + pub async fn get_public_app_by_custom_path<'a>( + &'a self, + custom_path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/apps_u/public_app_by_custom_path/{}", self.baseurl, encode_path(& + custom_path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get hub script content by path + +Sends a `GET` request to `/scripts/hub/get/{path}` + +*/ + pub async fn get_hub_script_content_by_path<'a>( + &'a self, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/scripts/hub/get/{}", self.baseurl, encode_path(& path.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get full hub script by path + +Sends a `GET` request to `/scripts/hub/get_full/{path}` + +*/ + pub async fn get_hub_script_by_path<'a>( + &'a self, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/scripts/hub/get_full/{}", self.baseurl, encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get top hub scripts + +Sends a `GET` request to `/scripts/hub/top` + +Arguments: +- `app`: query scripts app +- `kind`: query scripts kind +- `limit`: query limit +*/ + pub async fn get_top_hub_scripts<'a>( + &'a self, + app: Option<&'a str>, + kind: Option<&'a str>, + limit: Option, + ) -> Result, Error<()>> { + let url = format!("{}/scripts/hub/top", self.baseurl,); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &app { + query.push(("app", v.to_string())); + } + if let Some(v) = &kind { + query.push(("kind", v.to_string())); + } + if let Some(v) = &limit { + query.push(("limit", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**query hub scripts by similarity + +Sends a `GET` request to `/embeddings/query_hub_scripts` + +Arguments: +- `app`: query scripts app +- `kind`: query scripts kind +- `limit`: query limit +- `text`: query text +*/ + pub async fn query_hub_scripts<'a>( + &'a self, + app: Option<&'a str>, + kind: Option<&'a str>, + limit: Option, + text: &'a str, + ) -> Result>, Error<()>> { + let url = format!("{}/embeddings/query_hub_scripts", self.baseurl,); + let mut query = Vec::with_capacity(4usize); + if let Some(v) = &app { + query.push(("app", v.to_string())); + } + if let Some(v) = &kind { + query.push(("kind", v.to_string())); + } + if let Some(v) = &limit { + query.push(("limit", v.to_string())); + } + query.push(("text", text.to_string())); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list scripts for search + +Sends a `GET` request to `/w/{workspace}/scripts/list_search` + +*/ + pub async fn list_search_script<'a>( + &'a self, + workspace: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/scripts/list_search", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all scripts + +Sends a `GET` request to `/w/{workspace}/scripts/list` + +Arguments: +- `workspace` +- `created_by`: mask to filter exact matching user creator +- `first_parent_hash`: mask to filter scripts whom first direct parent has exact hash +- `include_draft_only`: (default false) +include scripts that have no deployed version + +- `include_without_main`: (default false) +include scripts without an exported main function + +- `is_template`: (default regardless) +if true show only the templates +if false show only the non templates +if not defined, show all regardless of if the script is a template + +- `kinds`: (default regardless) +script kinds to filter, split by comma + +- `last_parent_hash`: mask to filter scripts whom last parent in the chain has exact hash. +Beware that each script stores only a limited number of parents. Hence +the last parent hash for a script is not necessarily its top-most parent. +To find the top-most parent you will have to jump from last to last hash + until finding the parent + +- `order_desc`: order by desc order (default true) +- `page`: which page to return (start at 1, default 1) +- `parent_hash`: is the hash present in the array of stored parent hashes for this script. +The same warning applies than for last_parent_hash. A script only store a +limited number of direct parent + +- `path_exact`: mask to filter exact matching path +- `path_start`: mask to filter matching starting path +- `per_page`: number of items to return for a given page (default 30, max 100) +- `show_archived`: (default false) +show only the archived files. +when multiple archived hash share the same path, only the ones with the latest create_at +are +ed. + +- `starred_only`: (default false) +show only the starred items + +- `with_deployment_msg`: (default false) +include deployment message + +*/ + pub async fn list_scripts<'a>( + &'a self, + workspace: &'a str, + created_by: Option<&'a str>, + first_parent_hash: Option<&'a str>, + include_draft_only: Option, + include_without_main: Option, + is_template: Option, + kinds: Option<&'a str>, + last_parent_hash: Option<&'a str>, + order_desc: Option, + page: Option, + parent_hash: Option<&'a str>, + path_exact: Option<&'a str>, + path_start: Option<&'a str>, + per_page: Option, + show_archived: Option, + starred_only: Option, + with_deployment_msg: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/scripts/list", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(16usize); + if let Some(v) = &created_by { + query.push(("created_by", v.to_string())); + } + if let Some(v) = &first_parent_hash { + query.push(("first_parent_hash", v.to_string())); + } + if let Some(v) = &include_draft_only { + query.push(("include_draft_only", v.to_string())); + } + if let Some(v) = &include_without_main { + query.push(("include_without_main", v.to_string())); + } + if let Some(v) = &is_template { + query.push(("is_template", v.to_string())); + } + if let Some(v) = &kinds { + query.push(("kinds", v.to_string())); + } + if let Some(v) = &last_parent_hash { + query.push(("last_parent_hash", v.to_string())); + } + if let Some(v) = &order_desc { + query.push(("order_desc", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &parent_hash { + query.push(("parent_hash", v.to_string())); + } + if let Some(v) = &path_exact { + query.push(("path_exact", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &show_archived { + query.push(("show_archived", v.to_string())); + } + if let Some(v) = &starred_only { + query.push(("starred_only", v.to_string())); + } + if let Some(v) = &with_deployment_msg { + query.push(("with_deployment_msg", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all scripts paths + +Sends a `GET` request to `/w/{workspace}/scripts/list_paths` + +*/ + pub async fn list_script_paths<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/list_paths", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create draft + +Sends a `POST` request to `/w/{workspace}/drafts/create` + +*/ + pub async fn create_draft<'a>( + &'a self, + workspace: &'a str, + body: &'a types::CreateDraftBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/drafts/create", self.baseurl, encode_path(& workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete draft + +Sends a `DELETE` request to `/w/{workspace}/drafts/delete/{kind}/{path}` + +*/ + pub async fn delete_draft<'a>( + &'a self, + workspace: &'a str, + kind: types::DeleteDraftKind, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/drafts/delete/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& kind.to_string()), encode_path(& path + .to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create script + +Sends a `POST` request to `/w/{workspace}/scripts/create` + +Arguments: +- `workspace` +- `body`: Partially filled script +*/ + pub async fn create_script<'a>( + &'a self, + workspace: &'a str, + body: &'a types::NewScript, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/create", self.baseurl, encode_path(& workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Toggle ON and OFF the workspace error handler for a given script + +Sends a `POST` request to `/w/{workspace}/scripts/toggle_workspace_error_handler/p/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: Workspace error handler enabled +*/ + pub async fn toggle_workspace_error_handler_for_script<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::ToggleWorkspaceErrorHandlerForScriptBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/toggle_workspace_error_handler/p/{}", self.baseurl, + encode_path(& workspace.to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get all instance custom tags (tags are used to dispatch jobs to different worker groups) + +Sends a `GET` request to `/workers/custom_tags` + +*/ + pub async fn get_custom_tags<'a>( + &'a self, + show_workspace_restriction: Option, + workspace: Option<&'a str>, + ) -> Result>, Error<()>> { + let url = format!("{}/workers/custom_tags", self.baseurl,); + let mut query = Vec::with_capacity(2usize); + if let Some(v) = &show_workspace_restriction { + query.push(("show_workspace_restriction", v.to_string())); + } + if let Some(v) = &workspace { + query.push(("workspace", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get all instance default tags + +Sends a `GET` request to `/workers/get_default_tags` + +*/ + pub async fn ge_default_tags<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/workers/get_default_tags", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**is default tags per workspace + +Sends a `GET` request to `/workers/is_default_tags_per_workspace` + +*/ + pub async fn is_default_tags_per_workspace<'a>( + &'a self, + ) -> Result, Error<()>> { + let url = format!("{}/workers/is_default_tags_per_workspace", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**archive script by path + +Sends a `POST` request to `/w/{workspace}/scripts/archive/p/{path}` + +*/ + pub async fn archive_script_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/archive/p/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**archive script by hash + +Sends a `POST` request to `/w/{workspace}/scripts/archive/h/{hash}` + +*/ + pub async fn archive_script_by_hash<'a>( + &'a self, + workspace: &'a str, + hash: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/archive/h/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& hash.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete script by hash (erase content but keep hash, require admin) + +Sends a `POST` request to `/w/{workspace}/scripts/delete/h/{hash}` + +*/ + pub async fn delete_script_by_hash<'a>( + &'a self, + workspace: &'a str, + hash: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/delete/h/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& hash.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete script at a given path (require admin) + +Sends a `POST` request to `/w/{workspace}/scripts/delete/p/{path}` + +Arguments: +- `workspace` +- `path` +- `keep_captures`: keep captures +*/ + pub async fn delete_script_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + keep_captures: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/delete/p/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &keep_captures { + query.push(("keep_captures", v.to_string())); + } + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get script by path + +Sends a `GET` request to `/w/{workspace}/scripts/get/p/{path}` + +*/ + pub async fn get_script_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + with_starred_info: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/get/p/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &with_starred_info { + query.push(("with_starred_info", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get triggers count of script + +Sends a `GET` request to `/w/{workspace}/scripts/get_triggers_count/{path}` + +*/ + pub async fn get_triggers_count_of_script<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/get_triggers_count/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get tokens with script scope + +Sends a `GET` request to `/w/{workspace}/scripts/list_tokens/{path}` + +*/ + pub async fn list_tokens_of_script<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/scripts/list_tokens/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get script by path with draft + +Sends a `GET` request to `/w/{workspace}/scripts/get/draft/{path}` + +*/ + pub async fn get_script_by_path_with_draft<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/get/draft/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get history of a script by path + +Sends a `GET` request to `/w/{workspace}/scripts/history/p/{path}` + +*/ + pub async fn get_script_history_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/scripts/history/p/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get scripts's latest version (hash) + +Sends a `GET` request to `/w/{workspace}/scripts/get_latest_version/{path}` + +*/ + pub async fn get_script_latest_version<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/get_latest_version/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update history of a script + +Sends a `POST` request to `/w/{workspace}/scripts/history_update/h/{hash}/p/{path}` + +Arguments: +- `workspace` +- `hash` +- `path` +- `body`: Script deployment message +*/ + pub async fn update_script_history<'a>( + &'a self, + workspace: &'a str, + hash: &'a str, + path: &'a str, + body: &'a types::UpdateScriptHistoryBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/history_update/h/{}/p/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& hash.to_string()), encode_path(& path + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**raw script by path + +Sends a `GET` request to `/w/{workspace}/scripts/raw/p/{path}` + +*/ + pub async fn raw_script_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/raw/p/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**raw script by path with a token (mostly used by lsp to be used with import maps to resolve scripts) + +Sends a `GET` request to `/scripts_u/tokened_raw/{workspace}/{token}/{path}` + +*/ + pub async fn raw_script_by_path_tokened<'a>( + &'a self, + workspace: &'a str, + token: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/scripts_u/tokened_raw/{}/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& token.to_string()), encode_path(& path + .to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**exists script by path + +Sends a `GET` request to `/w/{workspace}/scripts/exists/p/{path}` + +*/ + pub async fn exists_script_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/exists/p/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get script by hash + +Sends a `GET` request to `/w/{workspace}/scripts/get/h/{hash}` + +*/ + pub async fn get_script_by_hash<'a>( + &'a self, + workspace: &'a str, + hash: &'a str, + with_starred_info: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/get/h/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& hash.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &with_starred_info { + query.push(("with_starred_info", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**raw script by hash + +Sends a `GET` request to `/w/{workspace}/scripts/raw/h/{path}` + +*/ + pub async fn raw_script_by_hash<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/raw/h/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get script deployment status + +Sends a `GET` request to `/w/{workspace}/scripts/deployment_status/h/{hash}` + +*/ + pub async fn get_script_deployment_status<'a>( + &'a self, + workspace: &'a str, + hash: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/deployment_status/h/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& hash.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run script by path + +Sends a `POST` request to `/w/{workspace}/jobs/run/p/{path}` + +Arguments: +- `workspace` +- `path` +- `cache_ttl`: Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl +- `invisible_to_owner`: make the run invisible to the the script owner (default false) +- `job_id`: The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) +- `parent_job`: The parent job that is at the origin and responsible for the execution of this script if any +- `scheduled_for`: when to schedule this job (leave empty for immediate run) +- `scheduled_in_secs`: schedule the script to execute in the number of seconds starting now +- `skip_preprocessor`: skip the preprocessor +- `tag`: Override the tag to use +- `body`: script args +*/ + pub async fn run_script_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + cache_ttl: Option<&'a str>, + invisible_to_owner: Option, + job_id: Option<&'a uuid::Uuid>, + parent_job: Option<&'a uuid::Uuid>, + scheduled_for: Option<&'a chrono::DateTime>, + scheduled_in_secs: Option, + skip_preprocessor: Option, + tag: Option<&'a str>, + body: &'a types::ScriptArgs, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/run/p/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(8usize); + if let Some(v) = &cache_ttl { + query.push(("cache_ttl", v.to_string())); + } + if let Some(v) = &invisible_to_owner { + query.push(("invisible_to_owner", v.to_string())); + } + if let Some(v) = &job_id { + query.push(("job_id", v.to_string())); + } + if let Some(v) = &parent_job { + query.push(("parent_job", v.to_string())); + } + if let Some(v) = &scheduled_for { + query.push(("scheduled_for", v.to_string())); + } + if let Some(v) = &scheduled_in_secs { + query.push(("scheduled_in_secs", v.to_string())); + } + if let Some(v) = &skip_preprocessor { + query.push(("skip_preprocessor", v.to_string())); + } + if let Some(v) = &tag { + query.push(("tag", v.to_string())); + } + let request = self.client.post(url).json(&body).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run script by path in openai format + +Sends a `POST` request to `/w/{workspace}/jobs/openai_sync/p/{path}` + +Arguments: +- `workspace` +- `path` +- `include_header`: List of headers's keys (separated with ',') whove value are added to the args +Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + +- `job_id`: The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) +- `parent_job`: The parent job that is at the origin and responsible for the execution of this script if any +- `queue_limit`: The maximum size of the queue for which the request would get rejected if that job would push it above that limit + +- `body`: script args +*/ + pub async fn openai_sync_script_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + include_header: Option<&'a str>, + job_id: Option<&'a uuid::Uuid>, + parent_job: Option<&'a uuid::Uuid>, + queue_limit: Option<&'a str>, + body: &'a types::ScriptArgs, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/openai_sync/p/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(4usize); + if let Some(v) = &include_header { + query.push(("include_header", v.to_string())); + } + if let Some(v) = &job_id { + query.push(("job_id", v.to_string())); + } + if let Some(v) = &parent_job { + query.push(("parent_job", v.to_string())); + } + if let Some(v) = &queue_limit { + query.push(("queue_limit", v.to_string())); + } + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run script by path with get + +Sends a `GET` request to `/w/{workspace}/jobs/run_wait_result/p/{path}` + +Arguments: +- `workspace` +- `path` +- `cache_ttl`: Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl +- `include_header`: List of headers's keys (separated with ',') whove value are added to the args +Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + +- `job_id`: The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) +- `parent_job`: The parent job that is at the origin and responsible for the execution of this script if any +- `payload`: The base64 encoded payload that has been encoded as a JSON. e.g how to encode such payload encodeURIComponent +`encodeURIComponent(btoa(JSON.stringify({a: 2})))` + +- `queue_limit`: The maximum size of the queue for which the request would get rejected if that job would push it above that limit + +- `tag`: Override the tag to use +*/ + pub async fn run_wait_result_script_by_path_get<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + cache_ttl: Option<&'a str>, + include_header: Option<&'a str>, + job_id: Option<&'a uuid::Uuid>, + parent_job: Option<&'a uuid::Uuid>, + payload: Option<&'a str>, + queue_limit: Option<&'a str>, + tag: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/run_wait_result/p/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(7usize); + if let Some(v) = &cache_ttl { + query.push(("cache_ttl", v.to_string())); + } + if let Some(v) = &include_header { + query.push(("include_header", v.to_string())); + } + if let Some(v) = &job_id { + query.push(("job_id", v.to_string())); + } + if let Some(v) = &parent_job { + query.push(("parent_job", v.to_string())); + } + if let Some(v) = &payload { + query.push(("payload", v.to_string())); + } + if let Some(v) = &queue_limit { + query.push(("queue_limit", v.to_string())); + } + if let Some(v) = &tag { + query.push(("tag", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run script by path + +Sends a `POST` request to `/w/{workspace}/jobs/run_wait_result/p/{path}` + +Arguments: +- `workspace` +- `path` +- `cache_ttl`: Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl +- `include_header`: List of headers's keys (separated with ',') whove value are added to the args +Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + +- `job_id`: The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) +- `parent_job`: The parent job that is at the origin and responsible for the execution of this script if any +- `queue_limit`: The maximum size of the queue for which the request would get rejected if that job would push it above that limit + +- `tag`: Override the tag to use +- `body`: script args +*/ + pub async fn run_wait_result_script_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + cache_ttl: Option<&'a str>, + include_header: Option<&'a str>, + job_id: Option<&'a uuid::Uuid>, + parent_job: Option<&'a uuid::Uuid>, + queue_limit: Option<&'a str>, + tag: Option<&'a str>, + body: &'a types::ScriptArgs, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/run_wait_result/p/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(6usize); + if let Some(v) = &cache_ttl { + query.push(("cache_ttl", v.to_string())); + } + if let Some(v) = &include_header { + query.push(("include_header", v.to_string())); + } + if let Some(v) = &job_id { + query.push(("job_id", v.to_string())); + } + if let Some(v) = &parent_job { + query.push(("parent_job", v.to_string())); + } + if let Some(v) = &queue_limit { + query.push(("queue_limit", v.to_string())); + } + if let Some(v) = &tag { + query.push(("tag", v.to_string())); + } + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run flow by path and wait until completion in openai format + +Sends a `POST` request to `/w/{workspace}/jobs/openai_sync/f/{path}` + +Arguments: +- `workspace` +- `path` +- `include_header`: List of headers's keys (separated with ',') whove value are added to the args +Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + +- `job_id`: The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) +- `queue_limit`: The maximum size of the queue for which the request would get rejected if that job would push it above that limit + +- `body`: script args +*/ + pub async fn openai_sync_flow_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + include_header: Option<&'a str>, + job_id: Option<&'a uuid::Uuid>, + queue_limit: Option<&'a str>, + body: &'a types::ScriptArgs, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/openai_sync/f/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &include_header { + query.push(("include_header", v.to_string())); + } + if let Some(v) = &job_id { + query.push(("job_id", v.to_string())); + } + if let Some(v) = &queue_limit { + query.push(("queue_limit", v.to_string())); + } + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run flow by path and wait until completion + +Sends a `POST` request to `/w/{workspace}/jobs/run_wait_result/f/{path}` + +Arguments: +- `workspace` +- `path` +- `include_header`: List of headers's keys (separated with ',') whove value are added to the args +Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + +- `job_id`: The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) +- `queue_limit`: The maximum size of the queue for which the request would get rejected if that job would push it above that limit + +- `body`: script args +*/ + pub async fn run_wait_result_flow_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + include_header: Option<&'a str>, + job_id: Option<&'a uuid::Uuid>, + queue_limit: Option<&'a str>, + body: &'a types::ScriptArgs, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/run_wait_result/f/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &include_header { + query.push(("include_header", v.to_string())); + } + if let Some(v) = &job_id { + query.push(("job_id", v.to_string())); + } + if let Some(v) = &queue_limit { + query.push(("queue_limit", v.to_string())); + } + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get job result by id + +Sends a `GET` request to `/w/{workspace}/jobs/result_by_id/{flow_job_id}/{node_id}` + +*/ + pub async fn result_by_id<'a>( + &'a self, + workspace: &'a str, + flow_job_id: &'a str, + node_id: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/result_by_id/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& flow_job_id.to_string()), encode_path(& node_id + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all flow paths + +Sends a `GET` request to `/w/{workspace}/flows/list_paths` + +*/ + pub async fn list_flow_paths<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/list_paths", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list flows for search + +Sends a `GET` request to `/w/{workspace}/flows/list_search` + +*/ + pub async fn list_search_flow<'a>( + &'a self, + workspace: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/flows/list_search", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all flows + +Sends a `GET` request to `/w/{workspace}/flows/list` + +Arguments: +- `workspace` +- `created_by`: mask to filter exact matching user creator +- `include_draft_only`: (default false) +include items that have no deployed version + +- `order_desc`: order by desc order (default true) +- `page`: which page to return (start at 1, default 1) +- `path_exact`: mask to filter exact matching path +- `path_start`: mask to filter matching starting path +- `per_page`: number of items to return for a given page (default 30, max 100) +- `show_archived`: (default false) +show only the archived files. +when multiple archived hash share the same path, only the ones with the latest create_at +are displayed. + +- `starred_only`: (default false) +show only the starred items + +- `with_deployment_msg`: (default false) +include deployment message + +*/ + pub async fn list_flows<'a>( + &'a self, + workspace: &'a str, + created_by: Option<&'a str>, + include_draft_only: Option, + order_desc: Option, + page: Option, + path_exact: Option<&'a str>, + path_start: Option<&'a str>, + per_page: Option, + show_archived: Option, + starred_only: Option, + with_deployment_msg: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/flows/list", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(10usize); + if let Some(v) = &created_by { + query.push(("created_by", v.to_string())); + } + if let Some(v) = &include_draft_only { + query.push(("include_draft_only", v.to_string())); + } + if let Some(v) = &order_desc { + query.push(("order_desc", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &path_exact { + query.push(("path_exact", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &show_archived { + query.push(("show_archived", v.to_string())); + } + if let Some(v) = &starred_only { + query.push(("starred_only", v.to_string())); + } + if let Some(v) = &with_deployment_msg { + query.push(("with_deployment_msg", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get flow history by path + +Sends a `GET` request to `/w/{workspace}/flows/history/p/{path}` + +*/ + pub async fn get_flow_history<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/flows/history/p/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get flow's latest version + +Sends a `GET` request to `/w/{workspace}/flows/get_latest_version/{path}` + +*/ + pub async fn get_flow_latest_version<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/get_latest_version/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list flow paths from workspace runnable + +Sends a `GET` request to `/w/{workspace}/flows/list_paths_from_workspace_runnable/{runnable_kind}/{path}` + +*/ + pub async fn list_flow_paths_from_workspace_runnable<'a>( + &'a self, + workspace: &'a str, + runnable_kind: types::ListFlowPathsFromWorkspaceRunnableRunnableKind, + path: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/flows/list_paths_from_workspace_runnable/{}/{}", self.baseurl, + encode_path(& workspace.to_string()), encode_path(& runnable_kind + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get flow version + +Sends a `GET` request to `/w/{workspace}/flows/get/v/{version}/p/{path}` + +*/ + pub async fn get_flow_version<'a>( + &'a self, + workspace: &'a str, + version: f64, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/get/v/{}/p/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& version.to_string()), encode_path(& path + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update flow history + +Sends a `POST` request to `/w/{workspace}/flows/history_update/v/{version}/p/{path}` + +Arguments: +- `workspace` +- `version` +- `path` +- `body`: Flow deployment message +*/ + pub async fn update_flow_history<'a>( + &'a self, + workspace: &'a str, + version: f64, + path: &'a str, + body: &'a types::UpdateFlowHistoryBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/history_update/v/{}/p/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& version.to_string()), encode_path(& + path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get flow by path + +Sends a `GET` request to `/w/{workspace}/flows/get/{path}` + +*/ + pub async fn get_flow_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + with_starred_info: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/get/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &with_starred_info { + query.push(("with_starred_info", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get flow deployment status + +Sends a `GET` request to `/w/{workspace}/flows/deployment_status/p/{path}` + +*/ + pub async fn get_flow_deployment_status<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/deployment_status/p/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get triggers count of flow + +Sends a `GET` request to `/w/{workspace}/flows/get_triggers_count/{path}` + +*/ + pub async fn get_triggers_count_of_flow<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/get_triggers_count/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get tokens with flow scope + +Sends a `GET` request to `/w/{workspace}/flows/list_tokens/{path}` + +*/ + pub async fn list_tokens_of_flow<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/flows/list_tokens/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Toggle ON and OFF the workspace error handler for a given flow + +Sends a `POST` request to `/w/{workspace}/flows/toggle_workspace_error_handler/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: Workspace error handler enabled +*/ + pub async fn toggle_workspace_error_handler_for_flow<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::ToggleWorkspaceErrorHandlerForFlowBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/toggle_workspace_error_handler/{}", self.baseurl, + encode_path(& workspace.to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get flow by path with draft + +Sends a `GET` request to `/w/{workspace}/flows/get/draft/{path}` + +*/ + pub async fn get_flow_by_path_with_draft<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/get/draft/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**exists flow by path + +Sends a `GET` request to `/w/{workspace}/flows/exists/{path}` + +*/ + pub async fn exists_flow_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create flow + +Sends a `POST` request to `/w/{workspace}/flows/create` + +Arguments: +- `workspace` +- `body`: Partially filled flow +*/ + pub async fn create_flow<'a>( + &'a self, + workspace: &'a str, + body: &'a types::CreateFlowBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/create", self.baseurl, encode_path(& workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update flow + +Sends a `POST` request to `/w/{workspace}/flows/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: Partially filled flow +*/ + pub async fn update_flow<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::UpdateFlowBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**archive flow by path + +Sends a `POST` request to `/w/{workspace}/flows/archive/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: archiveFlow +*/ + pub async fn archive_flow_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::ArchiveFlowByPathBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/archive/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete flow by path + +Sends a `DELETE` request to `/w/{workspace}/flows/delete/{path}` + +Arguments: +- `workspace` +- `path` +- `keep_captures`: keep captures +*/ + pub async fn delete_flow_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + keep_captures: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &keep_captures { + query.push(("keep_captures", v.to_string())); + } + let request = self.client.delete(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all raw apps + +Sends a `GET` request to `/w/{workspace}/raw_apps/list` + +Arguments: +- `workspace` +- `created_by`: mask to filter exact matching user creator +- `order_desc`: order by desc order (default true) +- `page`: which page to return (start at 1, default 1) +- `path_exact`: mask to filter exact matching path +- `path_start`: mask to filter matching starting path +- `per_page`: number of items to return for a given page (default 30, max 100) +- `starred_only`: (default false) +show only the starred items + +*/ + pub async fn list_raw_apps<'a>( + &'a self, + workspace: &'a str, + created_by: Option<&'a str>, + order_desc: Option, + page: Option, + path_exact: Option<&'a str>, + path_start: Option<&'a str>, + per_page: Option, + starred_only: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/raw_apps/list", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(7usize); + if let Some(v) = &created_by { + query.push(("created_by", v.to_string())); + } + if let Some(v) = &order_desc { + query.push(("order_desc", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &path_exact { + query.push(("path_exact", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &starred_only { + query.push(("starred_only", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does an app exisst at path + +Sends a `GET` request to `/w/{workspace}/raw_apps/exists/{path}` + +*/ + pub async fn exists_raw_app<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/raw_apps/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get app by path + +Sends a `GET` request to `/w/{workspace}/apps/get_data/{version}/{path}` + +*/ + pub async fn get_raw_app_data<'a>( + &'a self, + workspace: &'a str, + version: f64, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps/get_data/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& version.to_string()), encode_path(& path + .to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list apps for search + +Sends a `GET` request to `/w/{workspace}/apps/list_search` + +*/ + pub async fn list_search_app<'a>( + &'a self, + workspace: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/apps/list_search", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all apps + +Sends a `GET` request to `/w/{workspace}/apps/list` + +Arguments: +- `workspace` +- `created_by`: mask to filter exact matching user creator +- `include_draft_only`: (default false) +include items that have no deployed version + +- `order_desc`: order by desc order (default true) +- `page`: which page to return (start at 1, default 1) +- `path_exact`: mask to filter exact matching path +- `path_start`: mask to filter matching starting path +- `per_page`: number of items to return for a given page (default 30, max 100) +- `starred_only`: (default false) +show only the starred items + +- `with_deployment_msg`: (default false) +include deployment message + +*/ + pub async fn list_apps<'a>( + &'a self, + workspace: &'a str, + created_by: Option<&'a str>, + include_draft_only: Option, + order_desc: Option, + page: Option, + path_exact: Option<&'a str>, + path_start: Option<&'a str>, + per_page: Option, + starred_only: Option, + with_deployment_msg: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/apps/list", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(9usize); + if let Some(v) = &created_by { + query.push(("created_by", v.to_string())); + } + if let Some(v) = &include_draft_only { + query.push(("include_draft_only", v.to_string())); + } + if let Some(v) = &order_desc { + query.push(("order_desc", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &path_exact { + query.push(("path_exact", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &starred_only { + query.push(("starred_only", v.to_string())); + } + if let Some(v) = &with_deployment_msg { + query.push(("with_deployment_msg", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create app + +Sends a `POST` request to `/w/{workspace}/apps/create` + +Arguments: +- `workspace` +- `body`: new app +*/ + pub async fn create_app<'a>( + &'a self, + workspace: &'a str, + body: &'a types::CreateAppBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps/create", self.baseurl, encode_path(& workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does an app exisst at path + +Sends a `GET` request to `/w/{workspace}/apps/exists/{path}` + +*/ + pub async fn exists_app<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps/exists/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get app by path + +Sends a `GET` request to `/w/{workspace}/apps/get/p/{path}` + +*/ + pub async fn get_app_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + with_starred_info: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps/get/p/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &with_starred_info { + query.push(("with_starred_info", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get app lite by path + +Sends a `GET` request to `/w/{workspace}/apps/get/lite/{path}` + +*/ + pub async fn get_app_lite_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps/get/lite/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get app by path with draft + +Sends a `GET` request to `/w/{workspace}/apps/get/draft/{path}` + +*/ + pub async fn get_app_by_path_with_draft<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps/get/draft/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get app history by path + +Sends a `GET` request to `/w/{workspace}/apps/history/p/{path}` + +*/ + pub async fn get_app_history_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/apps/history/p/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get apps's latest version + +Sends a `GET` request to `/w/{workspace}/apps/get_latest_version/{path}` + +*/ + pub async fn get_app_latest_version<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps/get_latest_version/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update app history + +Sends a `POST` request to `/w/{workspace}/apps/history_update/a/{id}/v/{version}` + +Arguments: +- `workspace` +- `id` +- `version` +- `body`: App deployment message +*/ + pub async fn update_app_history<'a>( + &'a self, + workspace: &'a str, + id: i64, + version: i64, + body: &'a types::UpdateAppHistoryBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps/history_update/a/{}/v/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& id.to_string()), encode_path(& version + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get public app by secret + +Sends a `GET` request to `/w/{workspace}/apps_u/public_app/{path}` + +*/ + pub async fn get_public_app_by_secret<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps_u/public_app/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get public resource + +Sends a `GET` request to `/w/{workspace}/apps_u/public_resource/{path}` + +*/ + pub async fn get_public_resource<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps_u/public_resource/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get public secret of app + +Sends a `GET` request to `/w/{workspace}/apps/secret_of/{path}` + +*/ + pub async fn get_public_secret_of_app<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps/secret_of/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get app by version + +Sends a `GET` request to `/w/{workspace}/apps/get/v/{id}` + +*/ + pub async fn get_app_by_version<'a>( + &'a self, + workspace: &'a str, + id: i64, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps/get/v/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& id.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create raw app + +Sends a `POST` request to `/w/{workspace}/raw_apps/create` + +Arguments: +- `workspace` +- `body`: new raw app +*/ + pub async fn create_raw_app<'a>( + &'a self, + workspace: &'a str, + body: &'a types::CreateRawAppBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/raw_apps/create", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update app + +Sends a `POST` request to `/w/{workspace}/raw_apps/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updateraw app +*/ + pub async fn update_raw_app<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::UpdateRawAppBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/raw_apps/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete raw app + +Sends a `DELETE` request to `/w/{workspace}/raw_apps/delete/{path}` + +*/ + pub async fn delete_raw_app<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/raw_apps/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete app + +Sends a `DELETE` request to `/w/{workspace}/apps/delete/{path}` + +*/ + pub async fn delete_app<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps/delete/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update app + +Sends a `POST` request to `/w/{workspace}/apps/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: update app +*/ + pub async fn update_app<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::UpdateAppBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps/update/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**check if custom path exists + +Sends a `GET` request to `/w/{workspace}/apps/custom_path_exists/{custom_path}` + +*/ + pub async fn custom_path_exists<'a>( + &'a self, + workspace: &'a str, + custom_path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps/custom_path_exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& custom_path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**executeComponent + +Sends a `POST` request to `/w/{workspace}/apps_u/execute_component/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: update app +*/ + pub async fn execute_component<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::ExecuteComponentBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps_u/execute_component/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**upload s3 file from app + +Sends a `POST` request to `/w/{workspace}/apps_u/upload_s3_file/{path}` + +Arguments: +- `workspace` +- `path` +- `content_disposition` +- `content_type` +- `file_extension` +- `file_key` +- `resource_type` +- `s3_resource_path` +- `storage` +- `body`: File content +*/ + pub async fn upload_s3_file_from_app<'a, B: Into>( + &'a self, + workspace: &'a str, + path: &'a str, + content_disposition: Option<&'a str>, + content_type: Option<&'a str>, + file_extension: Option<&'a str>, + file_key: Option<&'a str>, + resource_type: Option<&'a str>, + s3_resource_path: Option<&'a str>, + storage: Option<&'a str>, + body: B, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps_u/upload_s3_file/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(7usize); + if let Some(v) = &content_disposition { + query.push(("content_disposition", v.to_string())); + } + if let Some(v) = &content_type { + query.push(("content_type", v.to_string())); + } + if let Some(v) = &file_extension { + query.push(("file_extension", v.to_string())); + } + if let Some(v) = &file_key { + query.push(("file_key", v.to_string())); + } + if let Some(v) = &resource_type { + query.push(("resource_type", v.to_string())); + } + if let Some(v) = &s3_resource_path { + query.push(("s3_resource_path", v.to_string())); + } + if let Some(v) = &storage { + query.push(("storage", v.to_string())); + } + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .header( + reqwest::header::CONTENT_TYPE, + reqwest::header::HeaderValue::from_static("application/octet-stream"), + ) + .body(body) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete s3 file from app + +Sends a `DELETE` request to `/w/{workspace}/apps_u/delete_s3_file` + +*/ + pub async fn delete_s3_file_from_app<'a>( + &'a self, + workspace: &'a str, + delete_token: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/apps_u/delete_s3_file", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(1usize); + query.push(("delete_token", delete_token.to_string())); + let request = self.client.delete(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run flow by path + +Sends a `POST` request to `/w/{workspace}/jobs/run/f/{path}` + +Arguments: +- `workspace` +- `path` +- `include_header`: List of headers's keys (separated with ',') whove value are added to the args +Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + +- `invisible_to_owner`: make the run invisible to the the flow owner (default false) +- `job_id`: The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) +- `parent_job`: The parent job that is at the origin and responsible for the execution of this script if any +- `scheduled_for`: when to schedule this job (leave empty for immediate run) +- `scheduled_in_secs`: schedule the script to execute in the number of seconds starting now +- `skip_preprocessor`: skip the preprocessor +- `tag`: Override the tag to use +- `body`: flow args +*/ + pub async fn run_flow_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + include_header: Option<&'a str>, + invisible_to_owner: Option, + job_id: Option<&'a uuid::Uuid>, + parent_job: Option<&'a uuid::Uuid>, + scheduled_for: Option<&'a chrono::DateTime>, + scheduled_in_secs: Option, + skip_preprocessor: Option, + tag: Option<&'a str>, + body: &'a types::ScriptArgs, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/run/f/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(8usize); + if let Some(v) = &include_header { + query.push(("include_header", v.to_string())); + } + if let Some(v) = &invisible_to_owner { + query.push(("invisible_to_owner", v.to_string())); + } + if let Some(v) = &job_id { + query.push(("job_id", v.to_string())); + } + if let Some(v) = &parent_job { + query.push(("parent_job", v.to_string())); + } + if let Some(v) = &scheduled_for { + query.push(("scheduled_for", v.to_string())); + } + if let Some(v) = &scheduled_in_secs { + query.push(("scheduled_in_secs", v.to_string())); + } + if let Some(v) = &skip_preprocessor { + query.push(("skip_preprocessor", v.to_string())); + } + if let Some(v) = &tag { + query.push(("tag", v.to_string())); + } + let request = self.client.post(url).json(&body).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**restart a completed flow at a given step + +Sends a `POST` request to `/w/{workspace}/jobs/restart/f/{id}/from/{step_id}/{branch_or_iteration_n}` + +Arguments: +- `workspace` +- `id` +- `step_id`: step id to restart the flow from +- `branch_or_iteration_n`: for branchall or loop, the iteration at which the flow should restart +- `include_header`: List of headers's keys (separated with ',') whove value are added to the args +Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + +- `invisible_to_owner`: make the run invisible to the the flow owner (default false) +- `job_id`: The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) +- `parent_job`: The parent job that is at the origin and responsible for the execution of this script if any +- `scheduled_for`: when to schedule this job (leave empty for immediate run) +- `scheduled_in_secs`: schedule the script to execute in the number of seconds starting now +- `tag`: Override the tag to use +- `body`: flow args +*/ + pub async fn restart_flow_at_step<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + step_id: &'a str, + branch_or_iteration_n: i64, + include_header: Option<&'a str>, + invisible_to_owner: Option, + job_id: Option<&'a uuid::Uuid>, + parent_job: Option<&'a uuid::Uuid>, + scheduled_for: Option<&'a chrono::DateTime>, + scheduled_in_secs: Option, + tag: Option<&'a str>, + body: &'a types::ScriptArgs, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/restart/f/{}/from/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), encode_path(& step_id + .to_string()), encode_path(& branch_or_iteration_n.to_string()), + ); + let mut query = Vec::with_capacity(7usize); + if let Some(v) = &include_header { + query.push(("include_header", v.to_string())); + } + if let Some(v) = &invisible_to_owner { + query.push(("invisible_to_owner", v.to_string())); + } + if let Some(v) = &job_id { + query.push(("job_id", v.to_string())); + } + if let Some(v) = &parent_job { + query.push(("parent_job", v.to_string())); + } + if let Some(v) = &scheduled_for { + query.push(("scheduled_for", v.to_string())); + } + if let Some(v) = &scheduled_in_secs { + query.push(("scheduled_in_secs", v.to_string())); + } + if let Some(v) = &tag { + query.push(("tag", v.to_string())); + } + let request = self.client.post(url).json(&body).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run script by hash + +Sends a `POST` request to `/w/{workspace}/jobs/run/h/{hash}` + +Arguments: +- `workspace` +- `hash` +- `cache_ttl`: Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl +- `include_header`: List of headers's keys (separated with ',') whove value are added to the args +Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + +- `invisible_to_owner`: make the run invisible to the the script owner (default false) +- `job_id`: The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) +- `parent_job`: The parent job that is at the origin and responsible for the execution of this script if any +- `scheduled_for`: when to schedule this job (leave empty for immediate run) +- `scheduled_in_secs`: schedule the script to execute in the number of seconds starting now +- `skip_preprocessor`: skip the preprocessor +- `tag`: Override the tag to use +- `body`: Partially filled args +*/ + pub async fn run_script_by_hash<'a>( + &'a self, + workspace: &'a str, + hash: &'a str, + cache_ttl: Option<&'a str>, + include_header: Option<&'a str>, + invisible_to_owner: Option, + job_id: Option<&'a uuid::Uuid>, + parent_job: Option<&'a uuid::Uuid>, + scheduled_for: Option<&'a chrono::DateTime>, + scheduled_in_secs: Option, + skip_preprocessor: Option, + tag: Option<&'a str>, + body: &'a std::collections::HashMap, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/run/h/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& hash.to_string()), + ); + let mut query = Vec::with_capacity(9usize); + if let Some(v) = &cache_ttl { + query.push(("cache_ttl", v.to_string())); + } + if let Some(v) = &include_header { + query.push(("include_header", v.to_string())); + } + if let Some(v) = &invisible_to_owner { + query.push(("invisible_to_owner", v.to_string())); + } + if let Some(v) = &job_id { + query.push(("job_id", v.to_string())); + } + if let Some(v) = &parent_job { + query.push(("parent_job", v.to_string())); + } + if let Some(v) = &scheduled_for { + query.push(("scheduled_for", v.to_string())); + } + if let Some(v) = &scheduled_in_secs { + query.push(("scheduled_in_secs", v.to_string())); + } + if let Some(v) = &skip_preprocessor { + query.push(("skip_preprocessor", v.to_string())); + } + if let Some(v) = &tag { + query.push(("tag", v.to_string())); + } + let request = self.client.post(url).json(&body).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run script preview + +Sends a `POST` request to `/w/{workspace}/jobs/run/preview` + +Arguments: +- `workspace` +- `include_header`: List of headers's keys (separated with ',') whove value are added to the args +Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + +- `invisible_to_owner`: make the run invisible to the the script owner (default false) +- `job_id`: The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) +- `body`: preview +*/ + pub async fn run_script_preview<'a>( + &'a self, + workspace: &'a str, + include_header: Option<&'a str>, + invisible_to_owner: Option, + job_id: Option<&'a uuid::Uuid>, + body: &'a types::Preview, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/run/preview", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &include_header { + query.push(("include_header", v.to_string())); + } + if let Some(v) = &invisible_to_owner { + query.push(("invisible_to_owner", v.to_string())); + } + if let Some(v) = &job_id { + query.push(("job_id", v.to_string())); + } + let request = self.client.post(url).json(&body).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run code-workflow task + +Sends a `POST` request to `/w/{workspace}/jobs/workflow_as_code/{job_id}/{entrypoint}` + +Arguments: +- `workspace` +- `job_id` +- `entrypoint` +- `body`: preview +*/ + pub async fn run_code_workflow_task<'a>( + &'a self, + workspace: &'a str, + job_id: &'a str, + entrypoint: &'a str, + body: &'a types::WorkflowTask, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/workflow_as_code/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& job_id.to_string()), encode_path(& entrypoint + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run a one-off dependencies job + +Sends a `POST` request to `/w/{workspace}/jobs/run/dependencies` + +Arguments: +- `workspace` +- `body`: raw script content +*/ + pub async fn run_raw_script_dependencies<'a>( + &'a self, + workspace: &'a str, + body: &'a types::RunRawScriptDependenciesBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/run/dependencies", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**run flow preview + +Sends a `POST` request to `/w/{workspace}/jobs/run/preview_flow` + +Arguments: +- `workspace` +- `include_header`: List of headers's keys (separated with ',') whove value are added to the args +Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + +- `invisible_to_owner`: make the run invisible to the the script owner (default false) +- `job_id`: The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) +- `body`: preview +*/ + pub async fn run_flow_preview<'a>( + &'a self, + workspace: &'a str, + include_header: Option<&'a str>, + invisible_to_owner: Option, + job_id: Option<&'a uuid::Uuid>, + body: &'a types::FlowPreview, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/run/preview_flow", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &include_header { + query.push(("include_header", v.to_string())); + } + if let Some(v) = &invisible_to_owner { + query.push(("invisible_to_owner", v.to_string())); + } + if let Some(v) = &job_id { + query.push(("job_id", v.to_string())); + } + let request = self.client.post(url).json(&body).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all queued jobs + +Sends a `GET` request to `/w/{workspace}/jobs/queue/list` + +Arguments: +- `workspace` +- `all_workspaces`: get jobs from all workspaces (only valid if request come from the `admins` workspace) +- `args`: filter on jobs containing those args as a json subset (@> in postgres) +- `created_by`: mask to filter exact matching user creator +- `is_not_schedule`: is not a scheduled job +- `job_kinds`: filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, +- `order_desc`: order by desc order (default true) +- `page`: which page to return (start at 1, default 1) +- `parent_job`: The parent job that is at the origin and responsible for the execution of this script if any +- `per_page`: number of items to return for a given page (default 30, max 100) +- `result`: filter on jobs containing those result as a json subset (@> in postgres) +- `running`: filter on running jobs +- `schedule_path`: mask to filter by schedule path +- `scheduled_for_before_now`: filter on jobs scheduled_for before now (hence waitinf for a worker) +- `script_hash`: mask to filter exact matching path +- `script_path_exact`: mask to filter exact matching path +- `script_path_start`: mask to filter matching starting path +- `started_after`: filter on started after (exclusive) timestamp +- `started_before`: filter on started before (inclusive) timestamp +- `success`: filter on successful jobs +- `suspended`: filter on suspended jobs +- `tag`: filter on jobs with a given tag/worker group +- `worker`: worker this job was ran on +*/ + pub async fn list_queue<'a>( + &'a self, + workspace: &'a str, + all_workspaces: Option, + args: Option<&'a str>, + created_by: Option<&'a str>, + is_not_schedule: Option, + job_kinds: Option<&'a str>, + order_desc: Option, + page: Option, + parent_job: Option<&'a uuid::Uuid>, + per_page: Option, + result: Option<&'a str>, + running: Option, + schedule_path: Option<&'a str>, + scheduled_for_before_now: Option, + script_hash: Option<&'a str>, + script_path_exact: Option<&'a str>, + script_path_start: Option<&'a str>, + started_after: Option<&'a chrono::DateTime>, + started_before: Option<&'a chrono::DateTime>, + success: Option, + suspended: Option, + tag: Option<&'a str>, + worker: Option<&'a str>, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/jobs/queue/list", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(22usize); + if let Some(v) = &all_workspaces { + query.push(("all_workspaces", v.to_string())); + } + if let Some(v) = &args { + query.push(("args", v.to_string())); + } + if let Some(v) = &created_by { + query.push(("created_by", v.to_string())); + } + if let Some(v) = &is_not_schedule { + query.push(("is_not_schedule", v.to_string())); + } + if let Some(v) = &job_kinds { + query.push(("job_kinds", v.to_string())); + } + if let Some(v) = &order_desc { + query.push(("order_desc", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &parent_job { + query.push(("parent_job", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &result { + query.push(("result", v.to_string())); + } + if let Some(v) = &running { + query.push(("running", v.to_string())); + } + if let Some(v) = &schedule_path { + query.push(("schedule_path", v.to_string())); + } + if let Some(v) = &scheduled_for_before_now { + query.push(("scheduled_for_before_now", v.to_string())); + } + if let Some(v) = &script_hash { + query.push(("script_hash", v.to_string())); + } + if let Some(v) = &script_path_exact { + query.push(("script_path_exact", v.to_string())); + } + if let Some(v) = &script_path_start { + query.push(("script_path_start", v.to_string())); + } + if let Some(v) = &started_after { + query.push(("started_after", v.to_string())); + } + if let Some(v) = &started_before { + query.push(("started_before", v.to_string())); + } + if let Some(v) = &success { + query.push(("success", v.to_string())); + } + if let Some(v) = &suspended { + query.push(("suspended", v.to_string())); + } + if let Some(v) = &tag { + query.push(("tag", v.to_string())); + } + if let Some(v) = &worker { + query.push(("worker", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get queue count + +Sends a `GET` request to `/w/{workspace}/jobs/queue/count` + +Arguments: +- `workspace` +- `all_workspaces`: get jobs from all workspaces (only valid if request come from the `admins` workspace) +*/ + pub async fn get_queue_count<'a>( + &'a self, + workspace: &'a str, + all_workspaces: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/queue/count", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &all_workspaces { + query.push(("all_workspaces", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get completed count + +Sends a `GET` request to `/w/{workspace}/jobs/completed/count` + +*/ + pub async fn get_completed_count<'a>( + &'a self, + workspace: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/completed/count", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**count number of completed jobs with filter + +Sends a `GET` request to `/w/{workspace}/jobs/completed/count_jobs` + +*/ + pub async fn count_completed_jobs<'a>( + &'a self, + workspace: &'a str, + all_workspaces: Option, + completed_after_s_ago: Option, + success: Option, + tags: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/completed/count_jobs", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(4usize); + if let Some(v) = &all_workspaces { + query.push(("all_workspaces", v.to_string())); + } + if let Some(v) = &completed_after_s_ago { + query.push(("completed_after_s_ago", v.to_string())); + } + if let Some(v) = &success { + query.push(("success", v.to_string())); + } + if let Some(v) = &tags { + query.push(("tags", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get the ids of all jobs matching the given filters + +Sends a `GET` request to `/w/{workspace}/jobs/queue/list_filtered_uuids` + +Arguments: +- `workspace` +- `all_workspaces`: get jobs from all workspaces (only valid if request come from the `admins` workspace) +- `args`: filter on jobs containing those args as a json subset (@> in postgres) +- `concurrency_key` +- `created_by`: mask to filter exact matching user creator +- `is_not_schedule`: is not a scheduled job +- `job_kinds`: filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, +- `order_desc`: order by desc order (default true) +- `page`: which page to return (start at 1, default 1) +- `parent_job`: The parent job that is at the origin and responsible for the execution of this script if any +- `per_page`: number of items to return for a given page (default 30, max 100) +- `result`: filter on jobs containing those result as a json subset (@> in postgres) +- `running`: filter on running jobs +- `schedule_path`: mask to filter by schedule path +- `scheduled_for_before_now`: filter on jobs scheduled_for before now (hence waitinf for a worker) +- `script_hash`: mask to filter exact matching path +- `script_path_exact`: mask to filter exact matching path +- `script_path_start`: mask to filter matching starting path +- `started_after`: filter on started after (exclusive) timestamp +- `started_before`: filter on started before (inclusive) timestamp +- `success`: filter on successful jobs +- `suspended`: filter on suspended jobs +- `tag`: filter on jobs with a given tag/worker group +*/ + pub async fn list_filtered_uuids<'a>( + &'a self, + workspace: &'a str, + all_workspaces: Option, + args: Option<&'a str>, + concurrency_key: Option<&'a str>, + created_by: Option<&'a str>, + is_not_schedule: Option, + job_kinds: Option<&'a str>, + order_desc: Option, + page: Option, + parent_job: Option<&'a uuid::Uuid>, + per_page: Option, + result: Option<&'a str>, + running: Option, + schedule_path: Option<&'a str>, + scheduled_for_before_now: Option, + script_hash: Option<&'a str>, + script_path_exact: Option<&'a str>, + script_path_start: Option<&'a str>, + started_after: Option<&'a chrono::DateTime>, + started_before: Option<&'a chrono::DateTime>, + success: Option, + suspended: Option, + tag: Option<&'a str>, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/jobs/queue/list_filtered_uuids", self.baseurl, encode_path(& + workspace.to_string()), + ); + let mut query = Vec::with_capacity(22usize); + if let Some(v) = &all_workspaces { + query.push(("all_workspaces", v.to_string())); + } + if let Some(v) = &args { + query.push(("args", v.to_string())); + } + if let Some(v) = &concurrency_key { + query.push(("concurrency_key", v.to_string())); + } + if let Some(v) = &created_by { + query.push(("created_by", v.to_string())); + } + if let Some(v) = &is_not_schedule { + query.push(("is_not_schedule", v.to_string())); + } + if let Some(v) = &job_kinds { + query.push(("job_kinds", v.to_string())); + } + if let Some(v) = &order_desc { + query.push(("order_desc", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &parent_job { + query.push(("parent_job", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &result { + query.push(("result", v.to_string())); + } + if let Some(v) = &running { + query.push(("running", v.to_string())); + } + if let Some(v) = &schedule_path { + query.push(("schedule_path", v.to_string())); + } + if let Some(v) = &scheduled_for_before_now { + query.push(("scheduled_for_before_now", v.to_string())); + } + if let Some(v) = &script_hash { + query.push(("script_hash", v.to_string())); + } + if let Some(v) = &script_path_exact { + query.push(("script_path_exact", v.to_string())); + } + if let Some(v) = &script_path_start { + query.push(("script_path_start", v.to_string())); + } + if let Some(v) = &started_after { + query.push(("started_after", v.to_string())); + } + if let Some(v) = &started_before { + query.push(("started_before", v.to_string())); + } + if let Some(v) = &success { + query.push(("success", v.to_string())); + } + if let Some(v) = &suspended { + query.push(("suspended", v.to_string())); + } + if let Some(v) = &tag { + query.push(("tag", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**cancel jobs based on the given uuids + +Sends a `POST` request to `/w/{workspace}/jobs/queue/cancel_selection` + +Arguments: +- `workspace` +- `body`: uuids of the jobs to cancel +*/ + pub async fn cancel_selection<'a>( + &'a self, + workspace: &'a str, + body: &'a Vec, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/jobs/queue/cancel_selection", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all completed jobs + +Sends a `GET` request to `/w/{workspace}/jobs/completed/list` + +Arguments: +- `workspace` +- `args`: filter on jobs containing those args as a json subset (@> in postgres) +- `created_by`: mask to filter exact matching user creator +- `has_null_parent`: has null parent +- `is_flow_step`: is the job a flow step +- `is_not_schedule`: is not a scheduled job +- `is_skipped`: is the job skipped +- `job_kinds`: filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, +- `label`: mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') +- `order_desc`: order by desc order (default true) +- `page`: which page to return (start at 1, default 1) +- `parent_job`: The parent job that is at the origin and responsible for the execution of this script if any +- `per_page`: number of items to return for a given page (default 30, max 100) +- `result`: filter on jobs containing those result as a json subset (@> in postgres) +- `schedule_path`: mask to filter by schedule path +- `script_hash`: mask to filter exact matching path +- `script_path_exact`: mask to filter exact matching path +- `script_path_start`: mask to filter matching starting path +- `started_after`: filter on started after (exclusive) timestamp +- `started_before`: filter on started before (inclusive) timestamp +- `success`: filter on successful jobs +- `tag`: filter on jobs with a given tag/worker group +- `worker`: worker this job was ran on +*/ + pub async fn list_completed_jobs<'a>( + &'a self, + workspace: &'a str, + args: Option<&'a str>, + created_by: Option<&'a str>, + has_null_parent: Option, + is_flow_step: Option, + is_not_schedule: Option, + is_skipped: Option, + job_kinds: Option<&'a str>, + label: Option<&'a str>, + order_desc: Option, + page: Option, + parent_job: Option<&'a uuid::Uuid>, + per_page: Option, + result: Option<&'a str>, + schedule_path: Option<&'a str>, + script_hash: Option<&'a str>, + script_path_exact: Option<&'a str>, + script_path_start: Option<&'a str>, + started_after: Option<&'a chrono::DateTime>, + started_before: Option<&'a chrono::DateTime>, + success: Option, + tag: Option<&'a str>, + worker: Option<&'a str>, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/jobs/completed/list", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(22usize); + if let Some(v) = &args { + query.push(("args", v.to_string())); + } + if let Some(v) = &created_by { + query.push(("created_by", v.to_string())); + } + if let Some(v) = &has_null_parent { + query.push(("has_null_parent", v.to_string())); + } + if let Some(v) = &is_flow_step { + query.push(("is_flow_step", v.to_string())); + } + if let Some(v) = &is_not_schedule { + query.push(("is_not_schedule", v.to_string())); + } + if let Some(v) = &is_skipped { + query.push(("is_skipped", v.to_string())); + } + if let Some(v) = &job_kinds { + query.push(("job_kinds", v.to_string())); + } + if let Some(v) = &label { + query.push(("label", v.to_string())); + } + if let Some(v) = &order_desc { + query.push(("order_desc", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &parent_job { + query.push(("parent_job", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &result { + query.push(("result", v.to_string())); + } + if let Some(v) = &schedule_path { + query.push(("schedule_path", v.to_string())); + } + if let Some(v) = &script_hash { + query.push(("script_hash", v.to_string())); + } + if let Some(v) = &script_path_exact { + query.push(("script_path_exact", v.to_string())); + } + if let Some(v) = &script_path_start { + query.push(("script_path_start", v.to_string())); + } + if let Some(v) = &started_after { + query.push(("started_after", v.to_string())); + } + if let Some(v) = &started_before { + query.push(("started_before", v.to_string())); + } + if let Some(v) = &success { + query.push(("success", v.to_string())); + } + if let Some(v) = &tag { + query.push(("tag", v.to_string())); + } + if let Some(v) = &worker { + query.push(("worker", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list all jobs + +Sends a `GET` request to `/w/{workspace}/jobs/list` + +Arguments: +- `workspace` +- `all_workspaces`: get jobs from all workspaces (only valid if request come from the `admins` workspace) +- `args`: filter on jobs containing those args as a json subset (@> in postgres) +- `created_after`: filter on created after (exclusive) timestamp +- `created_before`: filter on created before (inclusive) timestamp +- `created_by`: mask to filter exact matching user creator +- `created_or_started_after`: filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp +- `created_or_started_after_completed_jobs`: filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp but only for the completed jobs +- `created_or_started_before`: filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp +- `has_null_parent`: has null parent +- `is_flow_step`: is the job a flow step +- `is_not_schedule`: is not a scheduled job +- `is_skipped`: is the job skipped +- `job_kinds`: filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, +- `label`: mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') +- `page`: which page to return (start at 1, default 1) +- `parent_job`: The parent job that is at the origin and responsible for the execution of this script if any +- `per_page`: number of items to return for a given page (default 30, max 100) +- `result`: filter on jobs containing those result as a json subset (@> in postgres) +- `running`: filter on running jobs +- `schedule_path`: mask to filter by schedule path +- `scheduled_for_before_now`: filter on jobs scheduled_for before now (hence waitinf for a worker) +- `script_hash`: mask to filter exact matching path +- `script_path_exact`: mask to filter exact matching path +- `script_path_start`: mask to filter matching starting path +- `started_after`: filter on started after (exclusive) timestamp +- `started_before`: filter on started before (inclusive) timestamp +- `success`: filter on successful jobs +- `suspended`: filter on suspended jobs +- `tag`: filter on jobs with a given tag/worker group +- `worker`: worker this job was ran on +*/ + pub async fn list_jobs<'a>( + &'a self, + workspace: &'a str, + all_workspaces: Option, + args: Option<&'a str>, + created_after: Option<&'a chrono::DateTime>, + created_before: Option<&'a chrono::DateTime>, + created_by: Option<&'a str>, + created_or_started_after: Option<&'a chrono::DateTime>, + created_or_started_after_completed_jobs: Option< + &'a chrono::DateTime, + >, + created_or_started_before: Option<&'a chrono::DateTime>, + has_null_parent: Option, + is_flow_step: Option, + is_not_schedule: Option, + is_skipped: Option, + job_kinds: Option<&'a str>, + label: Option<&'a str>, + page: Option, + parent_job: Option<&'a uuid::Uuid>, + per_page: Option, + result: Option<&'a str>, + running: Option, + schedule_path: Option<&'a str>, + scheduled_for_before_now: Option, + script_hash: Option<&'a str>, + script_path_exact: Option<&'a str>, + script_path_start: Option<&'a str>, + started_after: Option<&'a chrono::DateTime>, + started_before: Option<&'a chrono::DateTime>, + success: Option, + suspended: Option, + tag: Option<&'a str>, + worker: Option<&'a str>, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/jobs/list", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(30usize); + if let Some(v) = &all_workspaces { + query.push(("all_workspaces", v.to_string())); + } + if let Some(v) = &args { + query.push(("args", v.to_string())); + } + if let Some(v) = &created_after { + query.push(("created_after", v.to_string())); + } + if let Some(v) = &created_before { + query.push(("created_before", v.to_string())); + } + if let Some(v) = &created_by { + query.push(("created_by", v.to_string())); + } + if let Some(v) = &created_or_started_after { + query.push(("created_or_started_after", v.to_string())); + } + if let Some(v) = &created_or_started_after_completed_jobs { + query.push(("created_or_started_after_completed_jobs", v.to_string())); + } + if let Some(v) = &created_or_started_before { + query.push(("created_or_started_before", v.to_string())); + } + if let Some(v) = &has_null_parent { + query.push(("has_null_parent", v.to_string())); + } + if let Some(v) = &is_flow_step { + query.push(("is_flow_step", v.to_string())); + } + if let Some(v) = &is_not_schedule { + query.push(("is_not_schedule", v.to_string())); + } + if let Some(v) = &is_skipped { + query.push(("is_skipped", v.to_string())); + } + if let Some(v) = &job_kinds { + query.push(("job_kinds", v.to_string())); + } + if let Some(v) = &label { + query.push(("label", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &parent_job { + query.push(("parent_job", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &result { + query.push(("result", v.to_string())); + } + if let Some(v) = &running { + query.push(("running", v.to_string())); + } + if let Some(v) = &schedule_path { + query.push(("schedule_path", v.to_string())); + } + if let Some(v) = &scheduled_for_before_now { + query.push(("scheduled_for_before_now", v.to_string())); + } + if let Some(v) = &script_hash { + query.push(("script_hash", v.to_string())); + } + if let Some(v) = &script_path_exact { + query.push(("script_path_exact", v.to_string())); + } + if let Some(v) = &script_path_start { + query.push(("script_path_start", v.to_string())); + } + if let Some(v) = &started_after { + query.push(("started_after", v.to_string())); + } + if let Some(v) = &started_before { + query.push(("started_before", v.to_string())); + } + if let Some(v) = &success { + query.push(("success", v.to_string())); + } + if let Some(v) = &suspended { + query.push(("suspended", v.to_string())); + } + if let Some(v) = &tag { + query.push(("tag", v.to_string())); + } + if let Some(v) = &worker { + query.push(("worker", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get db clock + +Sends a `GET` request to `/jobs/db_clock` + +*/ + pub async fn get_db_clock<'a>(&'a self) -> Result, Error<()>> { + let url = format!("{}/jobs/db_clock", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Count jobs by tag + +Sends a `GET` request to `/jobs/completed/count_by_tag` + +Arguments: +- `horizon_secs`: Past Time horizon in seconds (when to start the count = now - horizon) (default is 3600) +- `workspace_id`: Specific workspace ID to filter results (optional) +*/ + pub async fn count_jobs_by_tag<'a>( + &'a self, + horizon_secs: Option, + workspace_id: Option<&'a str>, + ) -> Result>, Error<()>> { + let url = format!("{}/jobs/completed/count_by_tag", self.baseurl,); + let mut query = Vec::with_capacity(2usize); + if let Some(v) = &horizon_secs { + query.push(("horizon_secs", v.to_string())); + } + if let Some(v) = &workspace_id { + query.push(("workspace_id", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get job + +Sends a `GET` request to `/w/{workspace}/jobs_u/get/{id}` + +*/ + pub async fn get_job<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + no_logs: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/get/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& id.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &no_logs { + query.push(("no_logs", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get root job id + +Sends a `GET` request to `/w/{workspace}/jobs_u/get_root_job_id/{id}` + +*/ + pub async fn get_root_job_id<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/get_root_job_id/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get job logs + +Sends a `GET` request to `/w/{workspace}/jobs_u/get_logs/{id}` + +*/ + pub async fn get_job_logs<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/get_logs/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get job args + +Sends a `GET` request to `/w/{workspace}/jobs_u/get_args/{id}` + +*/ + pub async fn get_job_args<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/get_args/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get job updates + +Sends a `GET` request to `/w/{workspace}/jobs_u/getupdate/{id}` + +*/ + pub async fn get_job_updates<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + get_progress: Option, + log_offset: Option, + running: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/getupdate/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &get_progress { + query.push(("get_progress", v.to_string())); + } + if let Some(v) = &log_offset { + query.push(("log_offset", v.to_string())); + } + if let Some(v) = &running { + query.push(("running", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get log file from object store + +Sends a `GET` request to `/w/{workspace}/jobs_u/get_log_file/{path}` + +*/ + pub async fn get_log_file_from_store<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/get_log_file/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get flow debug info + +Sends a `GET` request to `/w/{workspace}/jobs_u/get_flow_debug_info/{id}` + +*/ + pub async fn get_flow_debug_info<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/get_flow_debug_info/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& id.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get completed job + +Sends a `GET` request to `/w/{workspace}/jobs_u/completed/get/{id}` + +*/ + pub async fn get_completed_job<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/completed/get/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get completed job result + +Sends a `GET` request to `/w/{workspace}/jobs_u/completed/get_result/{id}` + +*/ + pub async fn get_completed_job_result<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + approver: Option<&'a str>, + resume_id: Option, + secret: Option<&'a str>, + suspended_job: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/completed/get_result/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& id.to_string()), + ); + let mut query = Vec::with_capacity(4usize); + if let Some(v) = &approver { + query.push(("approver", v.to_string())); + } + if let Some(v) = &resume_id { + query.push(("resume_id", v.to_string())); + } + if let Some(v) = &secret { + query.push(("secret", v.to_string())); + } + if let Some(v) = &suspended_job { + query.push(("suspended_job", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get completed job result if job is completed + +Sends a `GET` request to `/w/{workspace}/jobs_u/completed/get_result_maybe/{id}` + +*/ + pub async fn get_completed_job_result_maybe<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + get_started: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/completed/get_result_maybe/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& id.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &get_started { + query.push(("get_started", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete completed job (erase content but keep run id) + +Sends a `POST` request to `/w/{workspace}/jobs/completed/delete/{id}` + +*/ + pub async fn delete_completed_job<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/completed/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**cancel queued or running job + +Sends a `POST` request to `/w/{workspace}/jobs_u/queue/cancel/{id}` + +Arguments: +- `workspace` +- `id` +- `body`: reason +*/ + pub async fn cancel_queued_job<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + body: &'a types::CancelQueuedJobBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/queue/cancel/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**cancel all queued jobs for persistent script + +Sends a `POST` request to `/w/{workspace}/jobs_u/queue/cancel_persistent/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: reason +*/ + pub async fn cancel_persistent_queued_jobs<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::CancelPersistentQueuedJobsBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/queue/cancel_persistent/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**force cancel queued job + +Sends a `POST` request to `/w/{workspace}/jobs_u/queue/force_cancel/{id}` + +Arguments: +- `workspace` +- `id` +- `body`: reason +*/ + pub async fn force_cancel_queued_job<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + body: &'a types::ForceCancelQueuedJobBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/queue/force_cancel/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create an HMac signature given a job id and a resume id + +Sends a `GET` request to `/w/{workspace}/jobs/job_signature/{id}/{resume_id}` + +*/ + pub async fn create_job_signature<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + resume_id: i64, + approver: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/job_signature/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), encode_path(& resume_id + .to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &approver { + query.push(("approver", v.to_string())); + } + let request = self.client.get(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get resume urls given a job_id, resume_id and a nonce to resume a flow + +Sends a `GET` request to `/w/{workspace}/jobs/resume_urls/{id}/{resume_id}` + +*/ + pub async fn get_resume_urls<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + resume_id: i64, + approver: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/resume_urls/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), encode_path(& resume_id + .to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &approver { + query.push(("approver", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**generate interactive slack approval for suspended job + +Sends a `GET` request to `/w/{workspace}/jobs/slack_approval/{id}` + +*/ + pub async fn get_slack_approval_payload<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + approver: Option<&'a str>, + channel_id: &'a str, + default_args_json: Option<&'a str>, + dynamic_enums_json: Option<&'a str>, + flow_step_id: &'a str, + message: Option<&'a str>, + slack_resource_path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/slack_approval/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let mut query = Vec::with_capacity(7usize); + if let Some(v) = &approver { + query.push(("approver", v.to_string())); + } + query.push(("channel_id", channel_id.to_string())); + if let Some(v) = &default_args_json { + query.push(("default_args_json", v.to_string())); + } + if let Some(v) = &dynamic_enums_json { + query.push(("dynamic_enums_json", v.to_string())); + } + query.push(("flow_step_id", flow_step_id.to_string())); + if let Some(v) = &message { + query.push(("message", v.to_string())); + } + query.push(("slack_resource_path", slack_resource_path.to_string())); + let request = self.client.get(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::empty(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**resume a job for a suspended flow + +Sends a `GET` request to `/w/{workspace}/jobs_u/resume/{id}/{resume_id}/{signature}` + +Arguments: +- `workspace` +- `id` +- `resume_id` +- `signature` +- `approver` +- `payload`: The base64 encoded payload that has been encoded as a JSON. e.g how to encode such payload encodeURIComponent +`encodeURIComponent(btoa(JSON.stringify({a: 2})))` + +*/ + pub async fn resume_suspended_job_get<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + resume_id: i64, + signature: &'a str, + approver: Option<&'a str>, + payload: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/resume/{}/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), encode_path(& resume_id + .to_string()), encode_path(& signature.to_string()), + ); + let mut query = Vec::with_capacity(2usize); + if let Some(v) = &approver { + query.push(("approver", v.to_string())); + } + if let Some(v) = &payload { + query.push(("payload", v.to_string())); + } + let request = self.client.get(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**resume a job for a suspended flow + +Sends a `POST` request to `/w/{workspace}/jobs_u/resume/{id}/{resume_id}/{signature}` + +*/ + pub async fn resume_suspended_job_post<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + resume_id: i64, + signature: &'a str, + approver: Option<&'a str>, + body: &'a std::collections::HashMap, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/resume/{}/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), encode_path(& resume_id + .to_string()), encode_path(& signature.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &approver { + query.push(("approver", v.to_string())); + } + let request = self.client.post(url).json(&body).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get flow user state at a given key + +Sends a `GET` request to `/w/{workspace}/jobs/flow/user_states/{id}/{key}` + +*/ + pub async fn get_flow_user_state<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + key: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/flow/user_states/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), encode_path(& key.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set flow user state at a given key + +Sends a `POST` request to `/w/{workspace}/jobs/flow/user_states/{id}/{key}` + +Arguments: +- `workspace` +- `id` +- `key` +- `body`: new value +*/ + pub async fn set_flow_user_state<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + key: &'a str, + body: &'a serde_json::Value, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/flow/user_states/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), encode_path(& key.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**resume a job for a suspended flow as an owner + +Sends a `POST` request to `/w/{workspace}/jobs/flow/resume/{id}` + +*/ + pub async fn resume_suspended_flow_as_owner<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + body: &'a std::collections::HashMap, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs/flow/resume/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**cancel a job for a suspended flow + +Sends a `GET` request to `/w/{workspace}/jobs_u/cancel/{id}/{resume_id}/{signature}` + +*/ + pub async fn cancel_suspended_job_get<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + resume_id: i64, + signature: &'a str, + approver: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/cancel/{}/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), encode_path(& resume_id + .to_string()), encode_path(& signature.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &approver { + query.push(("approver", v.to_string())); + } + let request = self.client.get(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**cancel a job for a suspended flow + +Sends a `POST` request to `/w/{workspace}/jobs_u/cancel/{id}/{resume_id}/{signature}` + +*/ + pub async fn cancel_suspended_job_post<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + resume_id: i64, + signature: &'a str, + approver: Option<&'a str>, + body: &'a std::collections::HashMap, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/cancel/{}/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), encode_path(& resume_id + .to_string()), encode_path(& signature.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &approver { + query.push(("approver", v.to_string())); + } + let request = self.client.post(url).json(&body).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get parent flow job of suspended job + +Sends a `GET` request to `/w/{workspace}/jobs_u/get_flow/{id}/{resume_id}/{signature}` + +*/ + pub async fn get_suspended_job_flow<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + resume_id: i64, + signature: &'a str, + approver: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/jobs_u/get_flow/{}/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), encode_path(& resume_id + .to_string()), encode_path(& signature.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &approver { + query.push(("approver", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**preview schedule + +Sends a `POST` request to `/schedules/preview` + +Arguments: +- `body`: schedule +*/ + pub async fn preview_schedule<'a>( + &'a self, + body: &'a types::PreviewScheduleBody, + ) -> Result>>, Error<()>> { + let url = format!("{}/schedules/preview", self.baseurl,); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create schedule + +Sends a `POST` request to `/w/{workspace}/schedules/create` + +Arguments: +- `workspace` +- `body`: new schedule +*/ + pub async fn create_schedule<'a>( + &'a self, + workspace: &'a str, + body: &'a types::NewSchedule, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/schedules/create", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update schedule + +Sends a `POST` request to `/w/{workspace}/schedules/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated schedule +*/ + pub async fn update_schedule<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::EditSchedule, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/schedules/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set enabled schedule + +Sends a `POST` request to `/w/{workspace}/schedules/setenabled/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated schedule enable +*/ + pub async fn set_schedule_enabled<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::SetScheduleEnabledBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/schedules/setenabled/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete schedule + +Sends a `DELETE` request to `/w/{workspace}/schedules/delete/{path}` + +*/ + pub async fn delete_schedule<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/schedules/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get schedule + +Sends a `GET` request to `/w/{workspace}/schedules/get/{path}` + +*/ + pub async fn get_schedule<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/schedules/get/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does schedule exists + +Sends a `GET` request to `/w/{workspace}/schedules/exists/{path}` + +*/ + pub async fn exists_schedule<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/schedules/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list schedules + +Sends a `GET` request to `/w/{workspace}/schedules/list` + +Arguments: +- `workspace` +- `args`: filter on jobs containing those args as a json subset (@> in postgres) +- `is_flow` +- `page`: which page to return (start at 1, default 1) +- `path`: filter by path +- `path_start` +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_schedules<'a>( + &'a self, + workspace: &'a str, + args: Option<&'a str>, + is_flow: Option, + page: Option, + path: Option<&'a str>, + path_start: Option<&'a str>, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/schedules/list", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(6usize); + if let Some(v) = &args { + query.push(("args", v.to_string())); + } + if let Some(v) = &is_flow { + query.push(("is_flow", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &path { + query.push(("path", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list schedules with last 20 jobs + +Sends a `GET` request to `/w/{workspace}/schedules/list_with_jobs` + +Arguments: +- `workspace` +- `page`: which page to return (start at 1, default 1) +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_schedules_with_jobs<'a>( + &'a self, + workspace: &'a str, + page: Option, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/schedules/list_with_jobs", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(2usize); + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Set default error or recoevery handler + +Sends a `POST` request to `/w/{workspace}/schedules/setdefaulthandler` + +Arguments: +- `workspace` +- `body`: Handler description +*/ + pub async fn set_default_error_or_recovery_handler<'a>( + &'a self, + workspace: &'a str, + body: &'a types::SetDefaultErrorOrRecoveryHandlerBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/schedules/setdefaulthandler", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::empty(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create http trigger + +Sends a `POST` request to `/w/{workspace}/http_triggers/create` + +Arguments: +- `workspace` +- `body`: new http trigger +*/ + pub async fn create_http_trigger<'a>( + &'a self, + workspace: &'a str, + body: &'a types::NewHttpTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/http_triggers/create", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update http trigger + +Sends a `POST` request to `/w/{workspace}/http_triggers/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated trigger +*/ + pub async fn update_http_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::EditHttpTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/http_triggers/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete http trigger + +Sends a `DELETE` request to `/w/{workspace}/http_triggers/delete/{path}` + +*/ + pub async fn delete_http_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/http_triggers/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get http trigger + +Sends a `GET` request to `/w/{workspace}/http_triggers/get/{path}` + +*/ + pub async fn get_http_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/http_triggers/get/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list http triggers + +Sends a `GET` request to `/w/{workspace}/http_triggers/list` + +Arguments: +- `workspace` +- `is_flow` +- `page`: which page to return (start at 1, default 1) +- `path`: filter by path +- `path_start` +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_http_triggers<'a>( + &'a self, + workspace: &'a str, + is_flow: Option, + page: Option, + path: Option<&'a str>, + path_start: Option<&'a str>, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/http_triggers/list", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(5usize); + if let Some(v) = &is_flow { + query.push(("is_flow", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &path { + query.push(("path", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does http trigger exists + +Sends a `GET` request to `/w/{workspace}/http_triggers/exists/{path}` + +*/ + pub async fn exists_http_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/http_triggers/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does route exists + +Sends a `POST` request to `/w/{workspace}/http_triggers/route_exists` + +Arguments: +- `workspace` +- `body`: route exists request +*/ + pub async fn exists_route<'a>( + &'a self, + workspace: &'a str, + body: &'a types::ExistsRouteBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/http_triggers/route_exists", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create websocket trigger + +Sends a `POST` request to `/w/{workspace}/websocket_triggers/create` + +Arguments: +- `workspace` +- `body`: new websocket trigger +*/ + pub async fn create_websocket_trigger<'a>( + &'a self, + workspace: &'a str, + body: &'a types::NewWebsocketTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/websocket_triggers/create", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update websocket trigger + +Sends a `POST` request to `/w/{workspace}/websocket_triggers/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated trigger +*/ + pub async fn update_websocket_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::EditWebsocketTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/websocket_triggers/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete websocket trigger + +Sends a `DELETE` request to `/w/{workspace}/websocket_triggers/delete/{path}` + +*/ + pub async fn delete_websocket_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/websocket_triggers/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get websocket trigger + +Sends a `GET` request to `/w/{workspace}/websocket_triggers/get/{path}` + +*/ + pub async fn get_websocket_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/websocket_triggers/get/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list websocket triggers + +Sends a `GET` request to `/w/{workspace}/websocket_triggers/list` + +Arguments: +- `workspace` +- `is_flow` +- `page`: which page to return (start at 1, default 1) +- `path`: filter by path +- `path_start` +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_websocket_triggers<'a>( + &'a self, + workspace: &'a str, + is_flow: Option, + page: Option, + path: Option<&'a str>, + path_start: Option<&'a str>, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/websocket_triggers/list", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(5usize); + if let Some(v) = &is_flow { + query.push(("is_flow", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &path { + query.push(("path", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does websocket trigger exists + +Sends a `GET` request to `/w/{workspace}/websocket_triggers/exists/{path}` + +*/ + pub async fn exists_websocket_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/websocket_triggers/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set enabled websocket trigger + +Sends a `POST` request to `/w/{workspace}/websocket_triggers/setenabled/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated websocket trigger enable +*/ + pub async fn set_websocket_trigger_enabled<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::SetWebsocketTriggerEnabledBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/websocket_triggers/setenabled/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**test websocket connection + +Sends a `POST` request to `/w/{workspace}/websocket_triggers/test` + +Arguments: +- `workspace` +- `body`: test websocket connection +*/ + pub async fn test_websocket_connection<'a>( + &'a self, + workspace: &'a str, + body: &'a types::TestWebsocketConnectionBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/websocket_triggers/test", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create kafka trigger + +Sends a `POST` request to `/w/{workspace}/kafka_triggers/create` + +Arguments: +- `workspace` +- `body`: new kafka trigger +*/ + pub async fn create_kafka_trigger<'a>( + &'a self, + workspace: &'a str, + body: &'a types::NewKafkaTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/kafka_triggers/create", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update kafka trigger + +Sends a `POST` request to `/w/{workspace}/kafka_triggers/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated trigger +*/ + pub async fn update_kafka_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::EditKafkaTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/kafka_triggers/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete kafka trigger + +Sends a `DELETE` request to `/w/{workspace}/kafka_triggers/delete/{path}` + +*/ + pub async fn delete_kafka_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/kafka_triggers/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get kafka trigger + +Sends a `GET` request to `/w/{workspace}/kafka_triggers/get/{path}` + +*/ + pub async fn get_kafka_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/kafka_triggers/get/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list kafka triggers + +Sends a `GET` request to `/w/{workspace}/kafka_triggers/list` + +Arguments: +- `workspace` +- `is_flow` +- `page`: which page to return (start at 1, default 1) +- `path`: filter by path +- `path_start` +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_kafka_triggers<'a>( + &'a self, + workspace: &'a str, + is_flow: Option, + page: Option, + path: Option<&'a str>, + path_start: Option<&'a str>, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/kafka_triggers/list", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(5usize); + if let Some(v) = &is_flow { + query.push(("is_flow", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &path { + query.push(("path", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does kafka trigger exists + +Sends a `GET` request to `/w/{workspace}/kafka_triggers/exists/{path}` + +*/ + pub async fn exists_kafka_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/kafka_triggers/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set enabled kafka trigger + +Sends a `POST` request to `/w/{workspace}/kafka_triggers/setenabled/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated kafka trigger enable +*/ + pub async fn set_kafka_trigger_enabled<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::SetKafkaTriggerEnabledBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/kafka_triggers/setenabled/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**test kafka connection + +Sends a `POST` request to `/w/{workspace}/kafka_triggers/test` + +Arguments: +- `workspace` +- `body`: test kafka connection +*/ + pub async fn test_kafka_connection<'a>( + &'a self, + workspace: &'a str, + body: &'a types::TestKafkaConnectionBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/kafka_triggers/test", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create nats trigger + +Sends a `POST` request to `/w/{workspace}/nats_triggers/create` + +Arguments: +- `workspace` +- `body`: new nats trigger +*/ + pub async fn create_nats_trigger<'a>( + &'a self, + workspace: &'a str, + body: &'a types::NewNatsTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/nats_triggers/create", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update nats trigger + +Sends a `POST` request to `/w/{workspace}/nats_triggers/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated trigger +*/ + pub async fn update_nats_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::EditNatsTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/nats_triggers/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete nats trigger + +Sends a `DELETE` request to `/w/{workspace}/nats_triggers/delete/{path}` + +*/ + pub async fn delete_nats_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/nats_triggers/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get nats trigger + +Sends a `GET` request to `/w/{workspace}/nats_triggers/get/{path}` + +*/ + pub async fn get_nats_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/nats_triggers/get/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list nats triggers + +Sends a `GET` request to `/w/{workspace}/nats_triggers/list` + +Arguments: +- `workspace` +- `is_flow` +- `page`: which page to return (start at 1, default 1) +- `path`: filter by path +- `path_start` +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_nats_triggers<'a>( + &'a self, + workspace: &'a str, + is_flow: Option, + page: Option, + path: Option<&'a str>, + path_start: Option<&'a str>, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/nats_triggers/list", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(5usize); + if let Some(v) = &is_flow { + query.push(("is_flow", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &path { + query.push(("path", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does nats trigger exists + +Sends a `GET` request to `/w/{workspace}/nats_triggers/exists/{path}` + +*/ + pub async fn exists_nats_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/nats_triggers/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set enabled nats trigger + +Sends a `POST` request to `/w/{workspace}/nats_triggers/setenabled/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated nats trigger enable +*/ + pub async fn set_nats_trigger_enabled<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::SetNatsTriggerEnabledBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/nats_triggers/setenabled/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**test NATS connection + +Sends a `POST` request to `/w/{workspace}/nats_triggers/test` + +Arguments: +- `workspace` +- `body`: test nats connection +*/ + pub async fn test_nats_connection<'a>( + &'a self, + workspace: &'a str, + body: &'a types::TestNatsConnectionBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/nats_triggers/test", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create sqs trigger + +Sends a `POST` request to `/w/{workspace}/sqs_triggers/create` + +Arguments: +- `workspace` +- `body`: new sqs trigger +*/ + pub async fn create_sqs_trigger<'a>( + &'a self, + workspace: &'a str, + body: &'a types::NewSqsTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/sqs_triggers/create", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update sqs trigger + +Sends a `POST` request to `/w/{workspace}/sqs_triggers/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated trigger +*/ + pub async fn update_sqs_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::EditSqsTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/sqs_triggers/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete sqs trigger + +Sends a `DELETE` request to `/w/{workspace}/sqs_triggers/delete/{path}` + +*/ + pub async fn delete_sqs_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/sqs_triggers/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get sqs trigger + +Sends a `GET` request to `/w/{workspace}/sqs_triggers/get/{path}` + +*/ + pub async fn get_sqs_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/sqs_triggers/get/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list sqs triggers + +Sends a `GET` request to `/w/{workspace}/sqs_triggers/list` + +Arguments: +- `workspace` +- `is_flow` +- `page`: which page to return (start at 1, default 1) +- `path`: filter by path +- `path_start` +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_sqs_triggers<'a>( + &'a self, + workspace: &'a str, + is_flow: Option, + page: Option, + path: Option<&'a str>, + path_start: Option<&'a str>, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/sqs_triggers/list", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(5usize); + if let Some(v) = &is_flow { + query.push(("is_flow", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &path { + query.push(("path", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does sqs trigger exists + +Sends a `GET` request to `/w/{workspace}/sqs_triggers/exists/{path}` + +*/ + pub async fn exists_sqs_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/sqs_triggers/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set enabled sqs trigger + +Sends a `POST` request to `/w/{workspace}/sqs_triggers/setenabled/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated sqs trigger enable +*/ + pub async fn set_sqs_trigger_enabled<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::SetSqsTriggerEnabledBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/sqs_triggers/setenabled/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**test sqs connection + +Sends a `POST` request to `/w/{workspace}/sqs_triggers/test` + +Arguments: +- `workspace` +- `body`: test sqs connection +*/ + pub async fn test_sqs_connection<'a>( + &'a self, + workspace: &'a str, + body: &'a types::TestSqsConnectionBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/sqs_triggers/test", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create mqtt trigger + +Sends a `POST` request to `/w/{workspace}/mqtt_triggers/create` + +Arguments: +- `workspace` +- `body`: new mqtt trigger +*/ + pub async fn create_mqtt_trigger<'a>( + &'a self, + workspace: &'a str, + body: &'a types::NewMqttTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/mqtt_triggers/create", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update mqtt trigger + +Sends a `POST` request to `/w/{workspace}/mqtt_triggers/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated trigger +*/ + pub async fn update_mqtt_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::EditMqttTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/mqtt_triggers/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete mqtt trigger + +Sends a `DELETE` request to `/w/{workspace}/mqtt_triggers/delete/{path}` + +*/ + pub async fn delete_mqtt_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/mqtt_triggers/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get mqtt trigger + +Sends a `GET` request to `/w/{workspace}/mqtt_triggers/get/{path}` + +*/ + pub async fn get_mqtt_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/mqtt_triggers/get/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list mqtt triggers + +Sends a `GET` request to `/w/{workspace}/mqtt_triggers/list` + +Arguments: +- `workspace` +- `is_flow` +- `page`: which page to return (start at 1, default 1) +- `path`: filter by path +- `path_start` +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_mqtt_triggers<'a>( + &'a self, + workspace: &'a str, + is_flow: Option, + page: Option, + path: Option<&'a str>, + path_start: Option<&'a str>, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/mqtt_triggers/list", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(5usize); + if let Some(v) = &is_flow { + query.push(("is_flow", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &path { + query.push(("path", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does mqtt trigger exists + +Sends a `GET` request to `/w/{workspace}/mqtt_triggers/exists/{path}` + +*/ + pub async fn exists_mqtt_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/mqtt_triggers/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set enabled mqtt trigger + +Sends a `POST` request to `/w/{workspace}/mqtt_triggers/setenabled/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated mqtt trigger enable +*/ + pub async fn set_mqtt_trigger_enabled<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::SetMqttTriggerEnabledBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/mqtt_triggers/setenabled/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**test mqtt connection + +Sends a `POST` request to `/w/{workspace}/mqtt_triggers/test` + +Arguments: +- `workspace` +- `body`: test mqtt connection +*/ + pub async fn test_mqtt_connection<'a>( + &'a self, + workspace: &'a str, + body: &'a types::TestMqttConnectionBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/mqtt_triggers/test", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**check if postgres configuration is set to logical + +Sends a `GET` request to `/w/{workspace}/postgres_triggers/is_valid_postgres_configuration/{path}` + +*/ + pub async fn is_valid_postgres_configuration<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/is_valid_postgres_configuration/{}", self.baseurl, + encode_path(& workspace.to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create template script + +Sends a `POST` request to `/w/{workspace}/postgres_triggers/create_template_script` + +Arguments: +- `workspace` +- `body`: template script +*/ + pub async fn create_template_script<'a>( + &'a self, + workspace: &'a str, + body: &'a types::TemplateScript, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/create_template_script", self.baseurl, + encode_path(& workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get template script + +Sends a `GET` request to `/w/{workspace}/postgres_triggers/get_template_script/{id}` + +*/ + pub async fn get_template_script<'a>( + &'a self, + workspace: &'a str, + id: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/get_template_script/{}", self.baseurl, + encode_path(& workspace.to_string()), encode_path(& id.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list postgres replication slot + +Sends a `GET` request to `/w/{workspace}/postgres_triggers/slot/list/{path}` + +*/ + pub async fn list_postgres_replication_slot<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/slot/list/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create replication slot for postgres + +Sends a `POST` request to `/w/{workspace}/postgres_triggers/slot/create/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: new slot for postgres +*/ + pub async fn create_postgres_replication_slot<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::Slot, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/slot/create/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete postgres replication slot + +Sends a `DELETE` request to `/w/{workspace}/postgres_triggers/slot/delete/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: replication slot of postgres +*/ + pub async fn delete_postgres_replication_slot<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::Slot, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/slot/delete/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& path.to_string()), + ); + let request = self.client.delete(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list postgres publication + +Sends a `GET` request to `/w/{workspace}/postgres_triggers/publication/list/{path}` + +*/ + pub async fn list_postgres_publication<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/publication/list/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get postgres publication + +Sends a `GET` request to `/w/{workspace}/postgres_triggers/publication/get/{publication}/{path}` + +*/ + pub async fn get_postgres_publication<'a>( + &'a self, + workspace: &'a str, + publication: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/publication/get/{}/{}", self.baseurl, + encode_path(& workspace.to_string()), encode_path(& publication.to_string()), + encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create publication for postgres + +Sends a `POST` request to `/w/{workspace}/postgres_triggers/publication/create/{publication}/{path}` + +Arguments: +- `workspace` +- `publication` +- `path` +- `body`: new publication for postgres +*/ + pub async fn create_postgres_publication<'a>( + &'a self, + workspace: &'a str, + publication: &'a str, + path: &'a str, + body: &'a types::PublicationData, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/publication/create/{}/{}", self.baseurl, + encode_path(& workspace.to_string()), encode_path(& publication.to_string()), + encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update publication for postgres + +Sends a `POST` request to `/w/{workspace}/postgres_triggers/publication/update/{publication}/{path}` + +Arguments: +- `workspace` +- `publication` +- `path` +- `body`: update publication for postgres +*/ + pub async fn update_postgres_publication<'a>( + &'a self, + workspace: &'a str, + publication: &'a str, + path: &'a str, + body: &'a types::PublicationData, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/publication/update/{}/{}", self.baseurl, + encode_path(& workspace.to_string()), encode_path(& publication.to_string()), + encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete postgres publication + +Sends a `DELETE` request to `/w/{workspace}/postgres_triggers/publication/delete/{publication}/{path}` + +*/ + pub async fn delete_postgres_publication<'a>( + &'a self, + workspace: &'a str, + publication: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/publication/delete/{}/{}", self.baseurl, + encode_path(& workspace.to_string()), encode_path(& publication.to_string()), + encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create postgres trigger + +Sends a `POST` request to `/w/{workspace}/postgres_triggers/create` + +Arguments: +- `workspace` +- `body`: new postgres trigger +*/ + pub async fn create_postgres_trigger<'a>( + &'a self, + workspace: &'a str, + body: &'a types::NewPostgresTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/create", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update postgres trigger + +Sends a `POST` request to `/w/{workspace}/postgres_triggers/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated trigger +*/ + pub async fn update_postgres_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::EditPostgresTrigger, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete postgres trigger + +Sends a `DELETE` request to `/w/{workspace}/postgres_triggers/delete/{path}` + +*/ + pub async fn delete_postgres_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get postgres trigger + +Sends a `GET` request to `/w/{workspace}/postgres_triggers/get/{path}` + +*/ + pub async fn get_postgres_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/get/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list postgres triggers + +Sends a `GET` request to `/w/{workspace}/postgres_triggers/list` + +Arguments: +- `workspace` +- `is_flow` +- `page`: which page to return (start at 1, default 1) +- `path`: filter by path +- `path_start` +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_postgres_triggers<'a>( + &'a self, + workspace: &'a str, + is_flow: Option, + page: Option, + path: Option<&'a str>, + path_start: Option<&'a str>, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/list", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(5usize); + if let Some(v) = &is_flow { + query.push(("is_flow", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &path { + query.push(("path", v.to_string())); + } + if let Some(v) = &path_start { + query.push(("path_start", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**does postgres trigger exists + +Sends a `GET` request to `/w/{workspace}/postgres_triggers/exists/{path}` + +*/ + pub async fn exists_postgres_trigger<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set enabled postgres trigger + +Sends a `POST` request to `/w/{workspace}/postgres_triggers/setenabled/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated postgres trigger enable +*/ + pub async fn set_postgres_trigger_enabled<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::SetPostgresTriggerEnabledBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/setenabled/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**test postgres connection + +Sends a `POST` request to `/w/{workspace}/postgres_triggers/test` + +Arguments: +- `workspace` +- `body`: test postgres connection +*/ + pub async fn test_postgres_connection<'a>( + &'a self, + workspace: &'a str, + body: &'a types::TestPostgresConnectionBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/postgres_triggers/test", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list instance groups + +Sends a `GET` request to `/groups/list` + +*/ + pub async fn list_instance_groups<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/groups/list", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get instance group + +Sends a `GET` request to `/groups/get/{name}` + +*/ + pub async fn get_instance_group<'a>( + &'a self, + name: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/groups/get/{}", self.baseurl, encode_path(& name.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create instance group + +Sends a `POST` request to `/groups/create` + +Arguments: +- `body`: create instance group +*/ + pub async fn create_instance_group<'a>( + &'a self, + body: &'a types::CreateInstanceGroupBody, + ) -> Result, Error<()>> { + let url = format!("{}/groups/create", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update instance group + +Sends a `POST` request to `/groups/update/{name}` + +Arguments: +- `name` +- `body`: update instance group +*/ + pub async fn update_instance_group<'a>( + &'a self, + name: &'a str, + body: &'a types::UpdateInstanceGroupBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/groups/update/{}", self.baseurl, encode_path(& name.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete instance group + +Sends a `DELETE` request to `/groups/delete/{name}` + +*/ + pub async fn delete_instance_group<'a>( + &'a self, + name: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/groups/delete/{}", self.baseurl, encode_path(& name.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**add user to instance group + +Sends a `POST` request to `/groups/adduser/{name}` + +Arguments: +- `name` +- `body`: user to add to instance group +*/ + pub async fn add_user_to_instance_group<'a>( + &'a self, + name: &'a str, + body: &'a types::AddUserToInstanceGroupBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/groups/adduser/{}", self.baseurl, encode_path(& name.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**remove user from instance group + +Sends a `POST` request to `/groups/removeuser/{name}` + +Arguments: +- `name` +- `body`: user to remove from instance group +*/ + pub async fn remove_user_from_instance_group<'a>( + &'a self, + name: &'a str, + body: &'a types::RemoveUserFromInstanceGroupBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/groups/removeuser/{}", self.baseurl, encode_path(& name.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**export instance groups + +Sends a `GET` request to `/groups/export` + +*/ + pub async fn export_instance_groups<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/groups/export", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**overwrite instance groups + +Sends a `POST` request to `/groups/overwrite` + +Arguments: +- `body`: overwrite instance groups +*/ + pub async fn overwrite_instance_groups<'a>( + &'a self, + body: &'a Vec, + ) -> Result, Error<()>> { + let url = format!("{}/groups/overwrite", self.baseurl,); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list groups + +Sends a `GET` request to `/w/{workspace}/groups/list` + +Arguments: +- `workspace` +- `page`: which page to return (start at 1, default 1) +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_groups<'a>( + &'a self, + workspace: &'a str, + page: Option, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/groups/list", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(2usize); + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list group names + +Sends a `GET` request to `/w/{workspace}/groups/listnames` + +Arguments: +- `workspace` +- `only_member_of`: only list the groups the user is member of (default false) +*/ + pub async fn list_group_names<'a>( + &'a self, + workspace: &'a str, + only_member_of: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/groups/listnames", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &only_member_of { + query.push(("only_member_of", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create group + +Sends a `POST` request to `/w/{workspace}/groups/create` + +Arguments: +- `workspace` +- `body`: create group +*/ + pub async fn create_group<'a>( + &'a self, + workspace: &'a str, + body: &'a types::CreateGroupBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/groups/create", self.baseurl, encode_path(& workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update group + +Sends a `POST` request to `/w/{workspace}/groups/update/{name}` + +Arguments: +- `workspace` +- `name` +- `body`: updated group +*/ + pub async fn update_group<'a>( + &'a self, + workspace: &'a str, + name: &'a str, + body: &'a types::UpdateGroupBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/groups/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& name.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete group + +Sends a `DELETE` request to `/w/{workspace}/groups/delete/{name}` + +*/ + pub async fn delete_group<'a>( + &'a self, + workspace: &'a str, + name: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/groups/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& name.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get group + +Sends a `GET` request to `/w/{workspace}/groups/get/{name}` + +*/ + pub async fn get_group<'a>( + &'a self, + workspace: &'a str, + name: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/groups/get/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& name.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**add user to group + +Sends a `POST` request to `/w/{workspace}/groups/adduser/{name}` + +Arguments: +- `workspace` +- `name` +- `body`: added user to group +*/ + pub async fn add_user_to_group<'a>( + &'a self, + workspace: &'a str, + name: &'a str, + body: &'a types::AddUserToGroupBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/groups/adduser/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& name.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**remove user to group + +Sends a `POST` request to `/w/{workspace}/groups/removeuser/{name}` + +Arguments: +- `workspace` +- `name` +- `body`: added user to group +*/ + pub async fn remove_user_to_group<'a>( + &'a self, + workspace: &'a str, + name: &'a str, + body: &'a types::RemoveUserToGroupBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/groups/removeuser/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& name.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list folders + +Sends a `GET` request to `/w/{workspace}/folders/list` + +Arguments: +- `workspace` +- `page`: which page to return (start at 1, default 1) +- `per_page`: number of items to return for a given page (default 30, max 100) +*/ + pub async fn list_folders<'a>( + &'a self, + workspace: &'a str, + page: Option, + per_page: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/folders/list", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(2usize); + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list folder names + +Sends a `GET` request to `/w/{workspace}/folders/listnames` + +Arguments: +- `workspace` +- `only_member_of`: only list the folders the user is member of (default false) +*/ + pub async fn list_folder_names<'a>( + &'a self, + workspace: &'a str, + only_member_of: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/folders/listnames", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &only_member_of { + query.push(("only_member_of", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create folder + +Sends a `POST` request to `/w/{workspace}/folders/create` + +Arguments: +- `workspace` +- `body`: create folder +*/ + pub async fn create_folder<'a>( + &'a self, + workspace: &'a str, + body: &'a types::CreateFolderBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/folders/create", self.baseurl, encode_path(& workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update folder + +Sends a `POST` request to `/w/{workspace}/folders/update/{name}` + +Arguments: +- `workspace` +- `name` +- `body`: update folder +*/ + pub async fn update_folder<'a>( + &'a self, + workspace: &'a str, + name: &'a str, + body: &'a types::UpdateFolderBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/folders/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& name.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete folder + +Sends a `DELETE` request to `/w/{workspace}/folders/delete/{name}` + +*/ + pub async fn delete_folder<'a>( + &'a self, + workspace: &'a str, + name: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/folders/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& name.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get folder + +Sends a `GET` request to `/w/{workspace}/folders/get/{name}` + +*/ + pub async fn get_folder<'a>( + &'a self, + workspace: &'a str, + name: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/folders/get/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& name.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**exists folder + +Sends a `GET` request to `/w/{workspace}/folders/exists/{name}` + +*/ + pub async fn exists_folder<'a>( + &'a self, + workspace: &'a str, + name: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/folders/exists/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& name.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get folder usage + +Sends a `GET` request to `/w/{workspace}/folders/getusage/{name}` + +*/ + pub async fn get_folder_usage<'a>( + &'a self, + workspace: &'a str, + name: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/folders/getusage/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& name.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**add owner to folder + +Sends a `POST` request to `/w/{workspace}/folders/addowner/{name}` + +Arguments: +- `workspace` +- `name` +- `body`: owner user to folder +*/ + pub async fn add_owner_to_folder<'a>( + &'a self, + workspace: &'a str, + name: &'a str, + body: &'a types::AddOwnerToFolderBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/folders/addowner/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& name.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**remove owner to folder + +Sends a `POST` request to `/w/{workspace}/folders/removeowner/{name}` + +Arguments: +- `workspace` +- `name` +- `body`: added owner to folder +*/ + pub async fn remove_owner_to_folder<'a>( + &'a self, + workspace: &'a str, + name: &'a str, + body: &'a types::RemoveOwnerToFolderBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/folders/removeowner/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& name.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list workers + +Sends a `GET` request to `/workers/list` + +Arguments: +- `page`: which page to return (start at 1, default 1) +- `per_page`: number of items to return for a given page (default 30, max 100) +- `ping_since`: number of seconds the worker must have had a last ping more recent of (default to 300) +*/ + pub async fn list_workers<'a>( + &'a self, + page: Option, + per_page: Option, + ping_since: Option, + ) -> Result>, Error<()>> { + let url = format!("{}/workers/list", self.baseurl,); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &ping_since { + query.push(("ping_since", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**exists worker with tag + +Sends a `GET` request to `/workers/exists_worker_with_tag` + +*/ + pub async fn exists_worker_with_tag<'a>( + &'a self, + tag: &'a str, + ) -> Result, Error<()>> { + let url = format!("{}/workers/exists_worker_with_tag", self.baseurl,); + let mut query = Vec::with_capacity(1usize); + query.push(("tag", tag.to_string())); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get queue metrics + +Sends a `GET` request to `/workers/queue_metrics` + +*/ + pub async fn get_queue_metrics<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/workers/queue_metrics", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get counts of jobs waiting for an executor per tag + +Sends a `GET` request to `/workers/queue_counts` + +*/ + pub async fn get_counts_of_jobs_waiting_per_tag<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/workers/queue_counts", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list worker groups + +Sends a `GET` request to `/configs/list_worker_groups` + +*/ + pub async fn list_worker_groups<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/configs/list_worker_groups", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get config + +Sends a `GET` request to `/configs/get/{name}` + +*/ + pub async fn get_config<'a>( + &'a self, + name: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/configs/get/{}", self.baseurl, encode_path(& name.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Update config + +Sends a `POST` request to `/configs/update/{name}` + +Arguments: +- `name` +- `body`: worker group +*/ + pub async fn update_config<'a>( + &'a self, + name: &'a str, + body: &'a serde_json::Value, + ) -> Result, Error<()>> { + let url = format!( + "{}/configs/update/{}", self.baseurl, encode_path(& name.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Delete Config + +Sends a `DELETE` request to `/configs/update/{name}` + +*/ + pub async fn delete_config<'a>( + &'a self, + name: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/configs/update/{}", self.baseurl, encode_path(& name.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list configs + +Sends a `GET` request to `/configs/list` + +*/ + pub async fn list_configs<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/configs/list", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**List autoscaling events + +Sends a `GET` request to `/configs/list_autoscaling_events/{worker_group}` + +*/ + pub async fn list_autoscaling_events<'a>( + &'a self, + worker_group: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/configs/list_autoscaling_events/{}", self.baseurl, encode_path(& + worker_group.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get granular acls + +Sends a `GET` request to `/w/{workspace}/acls/get/{kind}/{path}` + +*/ + pub async fn get_granular_acls<'a>( + &'a self, + workspace: &'a str, + kind: types::GetGranularAclsKind, + path: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/acls/get/{}/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& kind.to_string()), encode_path(& path.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**add granular acls + +Sends a `POST` request to `/w/{workspace}/acls/add/{kind}/{path}` + +Arguments: +- `workspace` +- `kind` +- `path` +- `body`: acl to add +*/ + pub async fn add_granular_acls<'a>( + &'a self, + workspace: &'a str, + kind: types::AddGranularAclsKind, + path: &'a str, + body: &'a types::AddGranularAclsBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/acls/add/{}/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& kind.to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**remove granular acls + +Sends a `POST` request to `/w/{workspace}/acls/remove/{kind}/{path}` + +Arguments: +- `workspace` +- `kind` +- `path` +- `body`: acl to add +*/ + pub async fn remove_granular_acls<'a>( + &'a self, + workspace: &'a str, + kind: types::RemoveGranularAclsKind, + path: &'a str, + body: &'a types::RemoveGranularAclsBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/acls/remove/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& kind.to_string()), encode_path(& path + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set capture config + +Sends a `POST` request to `/w/{workspace}/capture/set_config` + +Arguments: +- `workspace` +- `body`: capture config +*/ + pub async fn set_capture_config<'a>( + &'a self, + workspace: &'a str, + body: &'a types::SetCaptureConfigBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/capture/set_config", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::empty(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**ping capture config + +Sends a `POST` request to `/w/{workspace}/capture/ping_config/{trigger_kind}/{runnable_kind}/{path}` + +*/ + pub async fn ping_capture_config<'a>( + &'a self, + workspace: &'a str, + trigger_kind: types::CaptureTriggerKind, + runnable_kind: types::PingCaptureConfigRunnableKind, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/capture/ping_config/{}/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& trigger_kind.to_string()), encode_path(& + runnable_kind.to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::empty(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get capture configs for a script or flow + +Sends a `GET` request to `/w/{workspace}/capture/get_configs/{runnable_kind}/{path}` + +*/ + pub async fn get_capture_configs<'a>( + &'a self, + workspace: &'a str, + runnable_kind: types::GetCaptureConfigsRunnableKind, + path: &'a str, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/capture/get_configs/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& runnable_kind.to_string()), encode_path(& path + .to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list captures for a script or flow + +Sends a `GET` request to `/w/{workspace}/capture/list/{runnable_kind}/{path}` + +Arguments: +- `workspace` +- `runnable_kind` +- `path` +- `page`: which page to return (start at 1, default 1) +- `per_page`: number of items to return for a given page (default 30, max 100) +- `trigger_kind` +*/ + pub async fn list_captures<'a>( + &'a self, + workspace: &'a str, + runnable_kind: types::ListCapturesRunnableKind, + path: &'a str, + page: Option, + per_page: Option, + trigger_kind: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/capture/list/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& runnable_kind.to_string()), encode_path(& path + .to_string()), + ); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &trigger_kind { + query.push(("trigger_kind", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**move captures and configs for a script or flow + +Sends a `POST` request to `/w/{workspace}/capture/move/{runnable_kind}/{path}` + +Arguments: +- `workspace` +- `runnable_kind` +- `path` +- `body`: move captures and configs to a new path +*/ + pub async fn move_captures_and_configs<'a>( + &'a self, + workspace: &'a str, + runnable_kind: types::MoveCapturesAndConfigsRunnableKind, + path: &'a str, + body: &'a types::MoveCapturesAndConfigsBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/capture/move/{}/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& runnable_kind.to_string()), encode_path(& path + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get a capture + +Sends a `GET` request to `/w/{workspace}/capture/{id}` + +*/ + pub async fn get_capture<'a>( + &'a self, + workspace: &'a str, + id: i64, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/capture/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& id.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**delete a capture + +Sends a `DELETE` request to `/w/{workspace}/capture/{id}` + +*/ + pub async fn delete_capture<'a>( + &'a self, + workspace: &'a str, + id: i64, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/capture/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& id.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::empty(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**star item + +Sends a `POST` request to `/w/{workspace}/favorites/star` + +*/ + pub async fn star<'a>( + &'a self, + workspace: &'a str, + body: &'a types::StarBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/favorites/star", self.baseurl, encode_path(& workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::empty(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**unstar item + +Sends a `POST` request to `/w/{workspace}/favorites/unstar` + +*/ + pub async fn unstar<'a>( + &'a self, + workspace: &'a str, + body: &'a types::UnstarBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/favorites/unstar", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::empty(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**List Inputs used in previously completed jobs + +Sends a `GET` request to `/w/{workspace}/inputs/history` + +Arguments: +- `workspace` +- `args`: filter on jobs containing those args as a json subset (@> in postgres) +- `include_preview` +- `page`: which page to return (start at 1, default 1) +- `per_page`: number of items to return for a given page (default 30, max 100) +- `runnable_id` +- `runnable_type` +*/ + pub async fn get_input_history<'a>( + &'a self, + workspace: &'a str, + args: Option<&'a str>, + include_preview: Option, + page: Option, + per_page: Option, + runnable_id: Option<&'a str>, + runnable_type: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/inputs/history", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(6usize); + if let Some(v) = &args { + query.push(("args", v.to_string())); + } + if let Some(v) = &include_preview { + query.push(("include_preview", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &runnable_id { + query.push(("runnable_id", v.to_string())); + } + if let Some(v) = &runnable_type { + query.push(("runnable_type", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Get args from history or saved input + +Sends a `GET` request to `/w/{workspace}/inputs/{jobOrInputId}/args` + +*/ + pub async fn get_args_from_history_or_saved_input<'a>( + &'a self, + workspace: &'a str, + job_or_input_id: &'a str, + allow_large: Option, + input: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/inputs/{}/args", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& job_or_input_id.to_string()), + ); + let mut query = Vec::with_capacity(2usize); + if let Some(v) = &allow_large { + query.push(("allow_large", v.to_string())); + } + if let Some(v) = &input { + query.push(("input", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**List saved Inputs for a Runnable + +Sends a `GET` request to `/w/{workspace}/inputs/list` + +Arguments: +- `workspace` +- `page`: which page to return (start at 1, default 1) +- `per_page`: number of items to return for a given page (default 30, max 100) +- `runnable_id` +- `runnable_type` +*/ + pub async fn list_inputs<'a>( + &'a self, + workspace: &'a str, + page: Option, + per_page: Option, + runnable_id: Option<&'a str>, + runnable_type: Option, + ) -> Result>, Error<()>> { + let url = format!( + "{}/w/{}/inputs/list", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(4usize); + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &runnable_id { + query.push(("runnable_id", v.to_string())); + } + if let Some(v) = &runnable_type { + query.push(("runnable_type", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Create an Input for future use in a script or flow + +Sends a `POST` request to `/w/{workspace}/inputs/create` + +Arguments: +- `workspace` +- `runnable_id` +- `runnable_type` +- `body`: Input +*/ + pub async fn create_input<'a>( + &'a self, + workspace: &'a str, + runnable_id: Option<&'a str>, + runnable_type: Option, + body: &'a types::CreateInput, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/inputs/create", self.baseurl, encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(2usize); + if let Some(v) = &runnable_id { + query.push(("runnable_id", v.to_string())); + } + if let Some(v) = &runnable_type { + query.push(("runnable_type", v.to_string())); + } + let request = self.client.post(url).json(&body).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Update an Input + +Sends a `POST` request to `/w/{workspace}/inputs/update` + +Arguments: +- `workspace` +- `body`: UpdateInput +*/ + pub async fn update_input<'a>( + &'a self, + workspace: &'a str, + body: &'a types::UpdateInput, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/inputs/update", self.baseurl, encode_path(& workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Delete a Saved Input + +Sends a `POST` request to `/w/{workspace}/inputs/delete/{input}` + +*/ + pub async fn delete_input<'a>( + &'a self, + workspace: &'a str, + input: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/inputs/delete/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& input.to_string()), + ); + let request = self.client.post(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Converts an S3 resource to the set of instructions necessary to connect DuckDB to an S3 bucket + +Sends a `POST` request to `/w/{workspace}/job_helpers/duckdb_connection_settings` + +Arguments: +- `workspace` +- `body`: S3 resource to connect to +*/ + pub async fn duckdb_connection_settings<'a>( + &'a self, + workspace: &'a str, + body: &'a types::DuckdbConnectionSettingsBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/duckdb_connection_settings", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Converts an S3 resource to the set of instructions necessary to connect DuckDB to an S3 bucket + +Sends a `POST` request to `/w/{workspace}/job_helpers/v2/duckdb_connection_settings` + +Arguments: +- `workspace` +- `body`: S3 resource path to use to generate the connection settings. If empty, the S3 resource defined in the workspace settings will be used +*/ + pub async fn duckdb_connection_settings_v2<'a>( + &'a self, + workspace: &'a str, + body: &'a types::DuckdbConnectionSettingsV2Body, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/v2/duckdb_connection_settings", self.baseurl, + encode_path(& workspace.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Converts an S3 resource to the set of arguments necessary to connect Polars to an S3 bucket + +Sends a `POST` request to `/w/{workspace}/job_helpers/polars_connection_settings` + +Arguments: +- `workspace` +- `body`: S3 resource to connect to +*/ + pub async fn polars_connection_settings<'a>( + &'a self, + workspace: &'a str, + body: &'a types::PolarsConnectionSettingsBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/polars_connection_settings", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Converts an S3 resource to the set of arguments necessary to connect Polars to an S3 bucket + +Sends a `POST` request to `/w/{workspace}/job_helpers/v2/polars_connection_settings` + +Arguments: +- `workspace` +- `body`: S3 resource path to use to generate the connection settings. If empty, the S3 resource defined in the workspace settings will be used +*/ + pub async fn polars_connection_settings_v2<'a>( + &'a self, + workspace: &'a str, + body: &'a types::PolarsConnectionSettingsV2Body, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/v2/polars_connection_settings", self.baseurl, + encode_path(& workspace.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Returns the s3 resource associated to the provided path, or the workspace default S3 resource + +Sends a `POST` request to `/w/{workspace}/job_helpers/v2/s3_resource_info` + +Arguments: +- `workspace` +- `body`: S3 resource path to use. If empty, the S3 resource defined in the workspace settings will be used +*/ + pub async fn s3_resource_info<'a>( + &'a self, + workspace: &'a str, + body: &'a types::S3ResourceInfoBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/v2/s3_resource_info", self.baseurl, encode_path(& + workspace.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Test connection to the workspace object storage + +Sends a `GET` request to `/w/{workspace}/job_helpers/test_connection` + +*/ + pub async fn dataset_storage_test_connection<'a>( + &'a self, + workspace: &'a str, + storage: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/test_connection", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &storage { + query.push(("storage", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**List the file keys available in a workspace object storage + +Sends a `GET` request to `/w/{workspace}/job_helpers/list_stored_files` + +*/ + pub async fn list_stored_files<'a>( + &'a self, + workspace: &'a str, + marker: Option<&'a str>, + max_keys: i64, + prefix: Option<&'a str>, + storage: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/list_stored_files", self.baseurl, encode_path(& + workspace.to_string()), + ); + let mut query = Vec::with_capacity(4usize); + if let Some(v) = &marker { + query.push(("marker", v.to_string())); + } + query.push(("max_keys", max_keys.to_string())); + if let Some(v) = &prefix { + query.push(("prefix", v.to_string())); + } + if let Some(v) = &storage { + query.push(("storage", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Load metadata of the file + +Sends a `GET` request to `/w/{workspace}/job_helpers/load_file_metadata` + +*/ + pub async fn load_file_metadata<'a>( + &'a self, + workspace: &'a str, + file_key: &'a str, + storage: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/load_file_metadata", self.baseurl, encode_path(& + workspace.to_string()), + ); + let mut query = Vec::with_capacity(2usize); + query.push(("file_key", file_key.to_string())); + if let Some(v) = &storage { + query.push(("storage", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Load a preview of the file + +Sends a `GET` request to `/w/{workspace}/job_helpers/load_file_preview` + +*/ + pub async fn load_file_preview<'a>( + &'a self, + workspace: &'a str, + csv_has_header: Option, + csv_separator: Option<&'a str>, + file_key: &'a str, + file_mime_type: Option<&'a str>, + file_size_in_bytes: Option, + read_bytes_from: Option, + read_bytes_length: Option, + storage: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/load_file_preview", self.baseurl, encode_path(& + workspace.to_string()), + ); + let mut query = Vec::with_capacity(8usize); + if let Some(v) = &csv_has_header { + query.push(("csv_has_header", v.to_string())); + } + if let Some(v) = &csv_separator { + query.push(("csv_separator", v.to_string())); + } + query.push(("file_key", file_key.to_string())); + if let Some(v) = &file_mime_type { + query.push(("file_mime_type", v.to_string())); + } + if let Some(v) = &file_size_in_bytes { + query.push(("file_size_in_bytes", v.to_string())); + } + if let Some(v) = &read_bytes_from { + query.push(("read_bytes_from", v.to_string())); + } + if let Some(v) = &read_bytes_length { + query.push(("read_bytes_length", v.to_string())); + } + if let Some(v) = &storage { + query.push(("storage", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Load a preview of a parquet file + +Sends a `GET` request to `/w/{workspace}/job_helpers/load_parquet_preview/{path}` + +*/ + pub async fn load_parquet_preview<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + limit: Option, + offset: Option, + search_col: Option<&'a str>, + search_term: Option<&'a str>, + sort_col: Option<&'a str>, + sort_desc: Option, + storage: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/load_parquet_preview/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(7usize); + if let Some(v) = &limit { + query.push(("limit", v.to_string())); + } + if let Some(v) = &offset { + query.push(("offset", v.to_string())); + } + if let Some(v) = &search_col { + query.push(("search_col", v.to_string())); + } + if let Some(v) = &search_term { + query.push(("search_term", v.to_string())); + } + if let Some(v) = &sort_col { + query.push(("sort_col", v.to_string())); + } + if let Some(v) = &sort_desc { + query.push(("sort_desc", v.to_string())); + } + if let Some(v) = &storage { + query.push(("storage", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Load the table row count + +Sends a `GET` request to `/w/{workspace}/job_helpers/load_table_count/{path}` + +*/ + pub async fn load_table_row_count<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + search_col: Option<&'a str>, + search_term: Option<&'a str>, + storage: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/load_table_count/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &search_col { + query.push(("search_col", v.to_string())); + } + if let Some(v) = &search_term { + query.push(("search_term", v.to_string())); + } + if let Some(v) = &storage { + query.push(("storage", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Load a preview of a csv file + +Sends a `GET` request to `/w/{workspace}/job_helpers/load_csv_preview/{path}` + +*/ + pub async fn load_csv_preview<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + csv_separator: Option<&'a str>, + limit: Option, + offset: Option, + search_col: Option<&'a str>, + search_term: Option<&'a str>, + sort_col: Option<&'a str>, + sort_desc: Option, + storage: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/load_csv_preview/{}", self.baseurl, encode_path(& + workspace.to_string()), encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(8usize); + if let Some(v) = &csv_separator { + query.push(("csv_separator", v.to_string())); + } + if let Some(v) = &limit { + query.push(("limit", v.to_string())); + } + if let Some(v) = &offset { + query.push(("offset", v.to_string())); + } + if let Some(v) = &search_col { + query.push(("search_col", v.to_string())); + } + if let Some(v) = &search_term { + query.push(("search_term", v.to_string())); + } + if let Some(v) = &sort_col { + query.push(("sort_col", v.to_string())); + } + if let Some(v) = &sort_desc { + query.push(("sort_desc", v.to_string())); + } + if let Some(v) = &storage { + query.push(("storage", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Permanently delete file from S3 + +Sends a `DELETE` request to `/w/{workspace}/job_helpers/delete_s3_file` + +*/ + pub async fn delete_s3_file<'a>( + &'a self, + workspace: &'a str, + file_key: &'a str, + storage: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/delete_s3_file", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(2usize); + query.push(("file_key", file_key.to_string())); + if let Some(v) = &storage { + query.push(("storage", v.to_string())); + } + let request = self + .client + .delete(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Move a S3 file from one path to the other within the same bucket + +Sends a `GET` request to `/w/{workspace}/job_helpers/move_s3_file` + +*/ + pub async fn move_s3_file<'a>( + &'a self, + workspace: &'a str, + dest_file_key: &'a str, + src_file_key: &'a str, + storage: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/move_s3_file", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(3usize); + query.push(("dest_file_key", dest_file_key.to_string())); + query.push(("src_file_key", src_file_key.to_string())); + if let Some(v) = &storage { + query.push(("storage", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Upload file to S3 bucket + +Sends a `POST` request to `/w/{workspace}/job_helpers/upload_s3_file` + +Arguments: +- `workspace` +- `content_disposition` +- `content_type` +- `file_extension` +- `file_key` +- `resource_type` +- `s3_resource_path` +- `storage` +- `body`: File content +*/ + pub async fn file_upload<'a, B: Into>( + &'a self, + workspace: &'a str, + content_disposition: Option<&'a str>, + content_type: Option<&'a str>, + file_extension: Option<&'a str>, + file_key: Option<&'a str>, + resource_type: Option<&'a str>, + s3_resource_path: Option<&'a str>, + storage: Option<&'a str>, + body: B, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/upload_s3_file", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(7usize); + if let Some(v) = &content_disposition { + query.push(("content_disposition", v.to_string())); + } + if let Some(v) = &content_type { + query.push(("content_type", v.to_string())); + } + if let Some(v) = &file_extension { + query.push(("file_extension", v.to_string())); + } + if let Some(v) = &file_key { + query.push(("file_key", v.to_string())); + } + if let Some(v) = &resource_type { + query.push(("resource_type", v.to_string())); + } + if let Some(v) = &s3_resource_path { + query.push(("s3_resource_path", v.to_string())); + } + if let Some(v) = &storage { + query.push(("storage", v.to_string())); + } + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .header( + reqwest::header::CONTENT_TYPE, + reqwest::header::HeaderValue::from_static("application/octet-stream"), + ) + .body(body) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Download file from S3 bucket + +Sends a `GET` request to `/w/{workspace}/job_helpers/download_s3_file` + +*/ + pub async fn file_download<'a>( + &'a self, + workspace: &'a str, + file_key: &'a str, + resource_type: Option<&'a str>, + s3_resource_path: Option<&'a str>, + storage: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/download_s3_file", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(4usize); + query.push(("file_key", file_key.to_string())); + if let Some(v) = &resource_type { + query.push(("resource_type", v.to_string())); + } + if let Some(v) = &s3_resource_path { + query.push(("s3_resource_path", v.to_string())); + } + if let Some(v) = &storage { + query.push(("storage", v.to_string())); + } + let request = self.client.get(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Download file to S3 bucket + +Sends a `GET` request to `/w/{workspace}/job_helpers/download_s3_parquet_file_as_csv` + +*/ + pub async fn file_download_parquet_as_csv<'a>( + &'a self, + workspace: &'a str, + file_key: &'a str, + resource_type: Option<&'a str>, + s3_resource_path: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_helpers/download_s3_parquet_file_as_csv", self.baseurl, + encode_path(& workspace.to_string()), + ); + let mut query = Vec::with_capacity(3usize); + query.push(("file_key", file_key.to_string())); + if let Some(v) = &resource_type { + query.push(("resource_type", v.to_string())); + } + if let Some(v) = &s3_resource_path { + query.push(("s3_resource_path", v.to_string())); + } + let request = self.client.get(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get job metrics + +Sends a `POST` request to `/w/{workspace}/job_metrics/get/{id}` + +Arguments: +- `workspace` +- `id` +- `body`: parameters for statistics retrieval +*/ + pub async fn get_job_metrics<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + body: &'a types::GetJobMetricsBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_metrics/get/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**set job metrics + +Sends a `POST` request to `/w/{workspace}/job_metrics/set_progress/{id}` + +Arguments: +- `workspace` +- `id` +- `body`: parameters for statistics retrieval +*/ + pub async fn set_job_progress<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + body: &'a types::SetJobProgressBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_metrics/set_progress/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let request = self + .client + .post(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get job progress + +Sends a `GET` request to `/w/{workspace}/job_metrics/get_progress/{id}` + +*/ + pub async fn get_job_progress<'a>( + &'a self, + workspace: &'a str, + id: &'a uuid::Uuid, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/job_metrics/get_progress/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& id.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**list log files ordered by timestamp + +Sends a `GET` request to `/service_logs/list_files` + +Arguments: +- `after`: filter on created after (exclusive) timestamp +- `before`: filter on started before (inclusive) timestamp +- `with_error` +*/ + pub async fn list_log_files<'a>( + &'a self, + after: Option<&'a chrono::DateTime>, + before: Option<&'a chrono::DateTime>, + with_error: Option, + ) -> Result>, Error<()>> { + let url = format!("{}/service_logs/list_files", self.baseurl,); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &after { + query.push(("after", v.to_string())); + } + if let Some(v) = &before { + query.push(("before", v.to_string())); + } + if let Some(v) = &with_error { + query.push(("with_error", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get log file by path + +Sends a `GET` request to `/service_logs/get_log_file/{path}` + +*/ + pub async fn get_log_file<'a>( + &'a self, + path: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/service_logs/get_log_file/{}", self.baseurl, encode_path(& path + .to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**List all concurrency groups + +Sends a `GET` request to `/concurrency_groups/list` + +*/ + pub async fn list_concurrency_groups<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/concurrency_groups/list", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Delete concurrency group + +Sends a `DELETE` request to `/concurrency_groups/prune/{concurrency_id}` + +*/ + pub async fn delete_concurrency_group<'a>( + &'a self, + concurrency_id: &'a str, + ) -> Result< + ResponseValue>, + Error<()>, + > { + let url = format!( + "{}/concurrency_groups/prune/{}", self.baseurl, encode_path(& concurrency_id + .to_string()), + ); + let request = self + .client + .delete(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Get the concurrency key for a job that has concurrency limits enabled + +Sends a `GET` request to `/concurrency_groups/{id}/key` + +*/ + pub async fn get_concurrency_key<'a>( + &'a self, + id: &'a uuid::Uuid, + ) -> Result, Error<()>> { + let url = format!( + "{}/concurrency_groups/{}/key", self.baseurl, encode_path(& id.to_string()), + ); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Get intervals of job runtime concurrency + +Sends a `GET` request to `/w/{workspace}/concurrency_groups/list_jobs` + +Arguments: +- `workspace` +- `all_workspaces`: get jobs from all workspaces (only valid if request come from the `admins` workspace) +- `args`: filter on jobs containing those args as a json subset (@> in postgres) +- `concurrency_key` +- `created_by`: mask to filter exact matching user creator +- `created_or_started_after`: filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp +- `created_or_started_after_completed_jobs`: filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp but only for the completed jobs +- `created_or_started_before`: filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp +- `has_null_parent`: has null parent +- `is_flow_step`: is the job a flow step +- `is_not_schedule`: is not a scheduled job +- `is_skipped`: is the job skipped +- `job_kinds`: filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, +- `label`: mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') +- `page`: which page to return (start at 1, default 1) +- `parent_job`: The parent job that is at the origin and responsible for the execution of this script if any +- `per_page`: number of items to return for a given page (default 30, max 100) +- `result`: filter on jobs containing those result as a json subset (@> in postgres) +- `row_limit` +- `running`: filter on running jobs +- `schedule_path`: mask to filter by schedule path +- `scheduled_for_before_now`: filter on jobs scheduled_for before now (hence waitinf for a worker) +- `script_hash`: mask to filter exact matching path +- `script_path_exact`: mask to filter exact matching path +- `script_path_start`: mask to filter matching starting path +- `started_after`: filter on started after (exclusive) timestamp +- `started_before`: filter on started before (inclusive) timestamp +- `success`: filter on successful jobs +- `tag`: filter on jobs with a given tag/worker group +*/ + pub async fn list_extended_jobs<'a>( + &'a self, + workspace: &'a str, + all_workspaces: Option, + args: Option<&'a str>, + concurrency_key: Option<&'a str>, + created_by: Option<&'a str>, + created_or_started_after: Option<&'a chrono::DateTime>, + created_or_started_after_completed_jobs: Option< + &'a chrono::DateTime, + >, + created_or_started_before: Option<&'a chrono::DateTime>, + has_null_parent: Option, + is_flow_step: Option, + is_not_schedule: Option, + is_skipped: Option, + job_kinds: Option<&'a str>, + label: Option<&'a str>, + page: Option, + parent_job: Option<&'a uuid::Uuid>, + per_page: Option, + result: Option<&'a str>, + row_limit: Option, + running: Option, + schedule_path: Option<&'a str>, + scheduled_for_before_now: Option, + script_hash: Option<&'a str>, + script_path_exact: Option<&'a str>, + script_path_start: Option<&'a str>, + started_after: Option<&'a chrono::DateTime>, + started_before: Option<&'a chrono::DateTime>, + success: Option, + tag: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/concurrency_groups/list_jobs", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(28usize); + if let Some(v) = &all_workspaces { + query.push(("all_workspaces", v.to_string())); + } + if let Some(v) = &args { + query.push(("args", v.to_string())); + } + if let Some(v) = &concurrency_key { + query.push(("concurrency_key", v.to_string())); + } + if let Some(v) = &created_by { + query.push(("created_by", v.to_string())); + } + if let Some(v) = &created_or_started_after { + query.push(("created_or_started_after", v.to_string())); + } + if let Some(v) = &created_or_started_after_completed_jobs { + query.push(("created_or_started_after_completed_jobs", v.to_string())); + } + if let Some(v) = &created_or_started_before { + query.push(("created_or_started_before", v.to_string())); + } + if let Some(v) = &has_null_parent { + query.push(("has_null_parent", v.to_string())); + } + if let Some(v) = &is_flow_step { + query.push(("is_flow_step", v.to_string())); + } + if let Some(v) = &is_not_schedule { + query.push(("is_not_schedule", v.to_string())); + } + if let Some(v) = &is_skipped { + query.push(("is_skipped", v.to_string())); + } + if let Some(v) = &job_kinds { + query.push(("job_kinds", v.to_string())); + } + if let Some(v) = &label { + query.push(("label", v.to_string())); + } + if let Some(v) = &page { + query.push(("page", v.to_string())); + } + if let Some(v) = &parent_job { + query.push(("parent_job", v.to_string())); + } + if let Some(v) = &per_page { + query.push(("per_page", v.to_string())); + } + if let Some(v) = &result { + query.push(("result", v.to_string())); + } + if let Some(v) = &row_limit { + query.push(("row_limit", v.to_string())); + } + if let Some(v) = &running { + query.push(("running", v.to_string())); + } + if let Some(v) = &schedule_path { + query.push(("schedule_path", v.to_string())); + } + if let Some(v) = &scheduled_for_before_now { + query.push(("scheduled_for_before_now", v.to_string())); + } + if let Some(v) = &script_hash { + query.push(("script_hash", v.to_string())); + } + if let Some(v) = &script_path_exact { + query.push(("script_path_exact", v.to_string())); + } + if let Some(v) = &script_path_start { + query.push(("script_path_start", v.to_string())); + } + if let Some(v) = &started_after { + query.push(("started_after", v.to_string())); + } + if let Some(v) = &started_before { + query.push(("started_before", v.to_string())); + } + if let Some(v) = &success { + query.push(("success", v.to_string())); + } + if let Some(v) = &tag { + query.push(("tag", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Search through jobs with a string query + +Sends a `GET` request to `/srch/w/{workspace}/index/search/job` + +*/ + pub async fn search_jobs_index<'a>( + &'a self, + workspace: &'a str, + search_query: &'a str, + ) -> Result, Error<()>> { + let url = format!( + "{}/srch/w/{}/index/search/job", self.baseurl, encode_path(& workspace + .to_string()), + ); + let mut query = Vec::with_capacity(1usize); + query.push(("search_query", search_query.to_string())); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Search through service logs with a string query + +Sends a `GET` request to `/srch/index/search/service_logs` + +*/ + pub async fn search_logs_index<'a>( + &'a self, + hostname: &'a str, + max_ts: Option<&'a chrono::DateTime>, + min_ts: Option<&'a chrono::DateTime>, + mode: &'a str, + search_query: &'a str, + worker_group: Option<&'a str>, + ) -> Result, Error<()>> { + let url = format!("{}/srch/index/search/service_logs", self.baseurl,); + let mut query = Vec::with_capacity(6usize); + query.push(("hostname", hostname.to_string())); + if let Some(v) = &max_ts { + query.push(("max_ts", v.to_string())); + } + if let Some(v) = &min_ts { + query.push(("min_ts", v.to_string())); + } + query.push(("mode", mode.to_string())); + query.push(("search_query", search_query.to_string())); + if let Some(v) = &worker_group { + query.push(("worker_group", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Search and count the log line hits on every provided host + +Sends a `GET` request to `/srch/index/search/count_service_logs` + +*/ + pub async fn count_search_logs_index<'a>( + &'a self, + max_ts: Option<&'a chrono::DateTime>, + min_ts: Option<&'a chrono::DateTime>, + search_query: &'a str, + ) -> Result, Error<()>> { + let url = format!("{}/srch/index/search/count_service_logs", self.baseurl,); + let mut query = Vec::with_capacity(3usize); + if let Some(v) = &max_ts { + query.push(("max_ts", v.to_string())); + } + if let Some(v) = &min_ts { + query.push(("min_ts", v.to_string())); + } + query.push(("search_query", search_query.to_string())); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**Restart container and delete the index to recreate it + +Sends a `DELETE` request to `/srch/index/delete/{idx_name}` + +*/ + pub async fn clear_index<'a>( + &'a self, + idx_name: types::ClearIndexIdxName, + ) -> Result, Error<()>> { + let url = format!( + "{}/srch/index/delete/{}", self.baseurl, encode_path(& idx_name.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } +} +pub mod prelude { + pub use super::Client; +} diff --git a/backend/windmill-api-client/src/codegen.rs b/backend/windmill-api-client/src/codegen.rs new file mode 100644 index 0000000000..9bc26262d5 --- /dev/null +++ b/backend/windmill-api-client/src/codegen.rs @@ -0,0 +1,6904 @@ +pub use progenitor_client::{ByteStream, Error, ResponseValue}; +#[allow(unused_imports)] +use progenitor_client::{encode_path, RequestBuilderExt}; +#[allow(unused_imports)] +use reqwest::header::{HeaderMap, HeaderValue}; +pub mod types { + use serde::{Deserialize, Serialize}; + #[allow(unused_imports)] + use std::convert::TryFrom; + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum AiProvider { + #[serde(rename = "openai")] + Openai, + #[serde(rename = "anthropic")] + Anthropic, + #[serde(rename = "mistral")] + Mistral, + #[serde(rename = "deepseek")] + Deepseek, + #[serde(rename = "googleai")] + Googleai, + #[serde(rename = "groq")] + Groq, + #[serde(rename = "openrouter")] + Openrouter, + #[serde(rename = "customai")] + Customai, + } + impl From<&AiProvider> for AiProvider { + fn from(value: &AiProvider) -> Self { + value.clone() + } + } + impl ToString for AiProvider { + fn to_string(&self) -> String { + match *self { + Self::Openai => "openai".to_string(), + Self::Anthropic => "anthropic".to_string(), + Self::Mistral => "mistral".to_string(), + Self::Deepseek => "deepseek".to_string(), + Self::Googleai => "googleai".to_string(), + Self::Groq => "groq".to_string(), + Self::Openrouter => "openrouter".to_string(), + Self::Customai => "customai".to_string(), + } + } + } + impl std::str::FromStr for AiProvider { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "openai" => Ok(Self::Openai), + "anthropic" => Ok(Self::Anthropic), + "mistral" => Ok(Self::Mistral), + "deepseek" => Ok(Self::Deepseek), + "googleai" => Ok(Self::Googleai), + "groq" => Ok(Self::Groq), + "openrouter" => Ok(Self::Openrouter), + "customai" => Ok(Self::Customai), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for AiProvider { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for AiProvider { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for AiProvider { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AiResource { + pub path: String, + pub provider: AiProvider, + } + impl From<&AiResource> for AiResource { + fn from(value: &AiResource) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AppHistory { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_msg: Option, + pub version: i64, + } + impl From<&AppHistory> for AppHistory { + fn from(value: &AppHistory) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AppWithLastVersion { + pub created_at: chrono::DateTime, + pub created_by: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub custom_path: Option, + pub execution_mode: AppWithLastVersionExecutionMode, + pub extra_perms: std::collections::HashMap, + pub id: i64, + pub path: String, + pub policy: Policy, + pub summary: String, + pub value: std::collections::HashMap, + pub versions: Vec, + pub workspace_id: String, + } + impl From<&AppWithLastVersion> for AppWithLastVersion { + fn from(value: &AppWithLastVersion) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum AppWithLastVersionExecutionMode { + #[serde(rename = "viewer")] + Viewer, + #[serde(rename = "publisher")] + Publisher, + #[serde(rename = "anonymous")] + Anonymous, + } + impl From<&AppWithLastVersionExecutionMode> for AppWithLastVersionExecutionMode { + fn from(value: &AppWithLastVersionExecutionMode) -> Self { + value.clone() + } + } + impl ToString for AppWithLastVersionExecutionMode { + fn to_string(&self) -> String { + match *self { + Self::Viewer => "viewer".to_string(), + Self::Publisher => "publisher".to_string(), + Self::Anonymous => "anonymous".to_string(), + } + } + } + impl std::str::FromStr for AppWithLastVersionExecutionMode { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "viewer" => Ok(Self::Viewer), + "publisher" => Ok(Self::Publisher), + "anonymous" => Ok(Self::Anonymous), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for AppWithLastVersionExecutionMode { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for AppWithLastVersionExecutionMode { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for AppWithLastVersionExecutionMode { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AppWithLastVersionWDraft { + #[serde(flatten)] + pub app_with_last_version: AppWithLastVersion, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft_only: Option, + } + impl From<&AppWithLastVersionWDraft> for AppWithLastVersionWDraft { + fn from(value: &AppWithLastVersionWDraft) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AuditLog { + pub action_kind: AuditLogActionKind, + pub id: i64, + pub operation: AuditLogOperation, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub parameters: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub resource: Option, + pub timestamp: chrono::DateTime, + pub username: String, + } + impl From<&AuditLog> for AuditLog { + fn from(value: &AuditLog) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum AuditLogActionKind { + Created, + Updated, + Delete, + Execute, + } + impl From<&AuditLogActionKind> for AuditLogActionKind { + fn from(value: &AuditLogActionKind) -> Self { + value.clone() + } + } + impl ToString for AuditLogActionKind { + fn to_string(&self) -> String { + match *self { + Self::Created => "Created".to_string(), + Self::Updated => "Updated".to_string(), + Self::Delete => "Delete".to_string(), + Self::Execute => "Execute".to_string(), + } + } + } + impl std::str::FromStr for AuditLogActionKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "Created" => Ok(Self::Created), + "Updated" => Ok(Self::Updated), + "Delete" => Ok(Self::Delete), + "Execute" => Ok(Self::Execute), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for AuditLogActionKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for AuditLogActionKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for AuditLogActionKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum AuditLogOperation { + #[serde(rename = "jobs.run")] + JobsRun, + #[serde(rename = "jobs.run.script")] + JobsRunScript, + #[serde(rename = "jobs.run.preview")] + JobsRunPreview, + #[serde(rename = "jobs.run.flow")] + JobsRunFlow, + #[serde(rename = "jobs.run.flow_preview")] + JobsRunFlowPreview, + #[serde(rename = "jobs.run.script_hub")] + JobsRunScriptHub, + #[serde(rename = "jobs.run.dependencies")] + JobsRunDependencies, + #[serde(rename = "jobs.run.identity")] + JobsRunIdentity, + #[serde(rename = "jobs.run.noop")] + JobsRunNoop, + #[serde(rename = "jobs.flow_dependencies")] + JobsFlowDependencies, + #[serde(rename = "jobs")] + Jobs, + #[serde(rename = "jobs.cancel")] + JobsCancel, + #[serde(rename = "jobs.force_cancel")] + JobsForceCancel, + #[serde(rename = "jobs.disapproval")] + JobsDisapproval, + #[serde(rename = "jobs.delete")] + JobsDelete, + #[serde(rename = "account.delete")] + AccountDelete, + #[serde(rename = "ai.request")] + AiRequest, + #[serde(rename = "resources.create")] + ResourcesCreate, + #[serde(rename = "resources.update")] + ResourcesUpdate, + #[serde(rename = "resources.delete")] + ResourcesDelete, + #[serde(rename = "resource_types.create")] + ResourceTypesCreate, + #[serde(rename = "resource_types.update")] + ResourceTypesUpdate, + #[serde(rename = "resource_types.delete")] + ResourceTypesDelete, + #[serde(rename = "schedule.create")] + ScheduleCreate, + #[serde(rename = "schedule.setenabled")] + ScheduleSetenabled, + #[serde(rename = "schedule.edit")] + ScheduleEdit, + #[serde(rename = "schedule.delete")] + ScheduleDelete, + #[serde(rename = "scripts.create")] + ScriptsCreate, + #[serde(rename = "scripts.update")] + ScriptsUpdate, + #[serde(rename = "scripts.archive")] + ScriptsArchive, + #[serde(rename = "scripts.delete")] + ScriptsDelete, + #[serde(rename = "users.create")] + UsersCreate, + #[serde(rename = "users.delete")] + UsersDelete, + #[serde(rename = "users.update")] + UsersUpdate, + #[serde(rename = "users.login")] + UsersLogin, + #[serde(rename = "users.login_failure")] + UsersLoginFailure, + #[serde(rename = "users.logout")] + UsersLogout, + #[serde(rename = "users.accept_invite")] + UsersAcceptInvite, + #[serde(rename = "users.decline_invite")] + UsersDeclineInvite, + #[serde(rename = "users.token.create")] + UsersTokenCreate, + #[serde(rename = "users.token.delete")] + UsersTokenDelete, + #[serde(rename = "users.add_to_workspace")] + UsersAddToWorkspace, + #[serde(rename = "users.add_global")] + UsersAddGlobal, + #[serde(rename = "users.setpassword")] + UsersSetpassword, + #[serde(rename = "users.impersonate")] + UsersImpersonate, + #[serde(rename = "users.leave_workspace")] + UsersLeaveWorkspace, + #[serde(rename = "oauth.login")] + OauthLogin, + #[serde(rename = "oauth.login_failure")] + OauthLoginFailure, + #[serde(rename = "oauth.signup")] + OauthSignup, + #[serde(rename = "variables.create")] + VariablesCreate, + #[serde(rename = "variables.delete")] + VariablesDelete, + #[serde(rename = "variables.update")] + VariablesUpdate, + #[serde(rename = "flows.create")] + FlowsCreate, + #[serde(rename = "flows.update")] + FlowsUpdate, + #[serde(rename = "flows.delete")] + FlowsDelete, + #[serde(rename = "flows.archive")] + FlowsArchive, + #[serde(rename = "apps.create")] + AppsCreate, + #[serde(rename = "apps.update")] + AppsUpdate, + #[serde(rename = "apps.delete")] + AppsDelete, + #[serde(rename = "folder.create")] + FolderCreate, + #[serde(rename = "folder.update")] + FolderUpdate, + #[serde(rename = "folder.delete")] + FolderDelete, + #[serde(rename = "folder.add_owner")] + FolderAddOwner, + #[serde(rename = "folder.remove_owner")] + FolderRemoveOwner, + #[serde(rename = "group.create")] + GroupCreate, + #[serde(rename = "group.delete")] + GroupDelete, + #[serde(rename = "group.edit")] + GroupEdit, + #[serde(rename = "group.adduser")] + GroupAdduser, + #[serde(rename = "group.removeuser")] + GroupRemoveuser, + #[serde(rename = "igroup.create")] + IgroupCreate, + #[serde(rename = "igroup.delete")] + IgroupDelete, + #[serde(rename = "igroup.adduser")] + IgroupAdduser, + #[serde(rename = "igroup.removeuser")] + IgroupRemoveuser, + #[serde(rename = "variables.decrypt_secret")] + VariablesDecryptSecret, + #[serde(rename = "workspaces.edit_command_script")] + WorkspacesEditCommandScript, + #[serde(rename = "workspaces.edit_deploy_to")] + WorkspacesEditDeployTo, + #[serde(rename = "workspaces.edit_auto_invite_domain")] + WorkspacesEditAutoInviteDomain, + #[serde(rename = "workspaces.edit_webhook")] + WorkspacesEditWebhook, + #[serde(rename = "workspaces.edit_copilot_config")] + WorkspacesEditCopilotConfig, + #[serde(rename = "workspaces.edit_error_handler")] + WorkspacesEditErrorHandler, + #[serde(rename = "workspaces.create")] + WorkspacesCreate, + #[serde(rename = "workspaces.update")] + WorkspacesUpdate, + #[serde(rename = "workspaces.archive")] + WorkspacesArchive, + #[serde(rename = "workspaces.unarchive")] + WorkspacesUnarchive, + #[serde(rename = "workspaces.delete")] + WorkspacesDelete, + } + impl From<&AuditLogOperation> for AuditLogOperation { + fn from(value: &AuditLogOperation) -> Self { + value.clone() + } + } + impl ToString for AuditLogOperation { + fn to_string(&self) -> String { + match *self { + Self::JobsRun => "jobs.run".to_string(), + Self::JobsRunScript => "jobs.run.script".to_string(), + Self::JobsRunPreview => "jobs.run.preview".to_string(), + Self::JobsRunFlow => "jobs.run.flow".to_string(), + Self::JobsRunFlowPreview => "jobs.run.flow_preview".to_string(), + Self::JobsRunScriptHub => "jobs.run.script_hub".to_string(), + Self::JobsRunDependencies => "jobs.run.dependencies".to_string(), + Self::JobsRunIdentity => "jobs.run.identity".to_string(), + Self::JobsRunNoop => "jobs.run.noop".to_string(), + Self::JobsFlowDependencies => "jobs.flow_dependencies".to_string(), + Self::Jobs => "jobs".to_string(), + Self::JobsCancel => "jobs.cancel".to_string(), + Self::JobsForceCancel => "jobs.force_cancel".to_string(), + Self::JobsDisapproval => "jobs.disapproval".to_string(), + Self::JobsDelete => "jobs.delete".to_string(), + Self::AccountDelete => "account.delete".to_string(), + Self::AiRequest => "ai.request".to_string(), + Self::ResourcesCreate => "resources.create".to_string(), + Self::ResourcesUpdate => "resources.update".to_string(), + Self::ResourcesDelete => "resources.delete".to_string(), + Self::ResourceTypesCreate => "resource_types.create".to_string(), + Self::ResourceTypesUpdate => "resource_types.update".to_string(), + Self::ResourceTypesDelete => "resource_types.delete".to_string(), + Self::ScheduleCreate => "schedule.create".to_string(), + Self::ScheduleSetenabled => "schedule.setenabled".to_string(), + Self::ScheduleEdit => "schedule.edit".to_string(), + Self::ScheduleDelete => "schedule.delete".to_string(), + Self::ScriptsCreate => "scripts.create".to_string(), + Self::ScriptsUpdate => "scripts.update".to_string(), + Self::ScriptsArchive => "scripts.archive".to_string(), + Self::ScriptsDelete => "scripts.delete".to_string(), + Self::UsersCreate => "users.create".to_string(), + Self::UsersDelete => "users.delete".to_string(), + Self::UsersUpdate => "users.update".to_string(), + Self::UsersLogin => "users.login".to_string(), + Self::UsersLoginFailure => "users.login_failure".to_string(), + Self::UsersLogout => "users.logout".to_string(), + Self::UsersAcceptInvite => "users.accept_invite".to_string(), + Self::UsersDeclineInvite => "users.decline_invite".to_string(), + Self::UsersTokenCreate => "users.token.create".to_string(), + Self::UsersTokenDelete => "users.token.delete".to_string(), + Self::UsersAddToWorkspace => "users.add_to_workspace".to_string(), + Self::UsersAddGlobal => "users.add_global".to_string(), + Self::UsersSetpassword => "users.setpassword".to_string(), + Self::UsersImpersonate => "users.impersonate".to_string(), + Self::UsersLeaveWorkspace => "users.leave_workspace".to_string(), + Self::OauthLogin => "oauth.login".to_string(), + Self::OauthLoginFailure => "oauth.login_failure".to_string(), + Self::OauthSignup => "oauth.signup".to_string(), + Self::VariablesCreate => "variables.create".to_string(), + Self::VariablesDelete => "variables.delete".to_string(), + Self::VariablesUpdate => "variables.update".to_string(), + Self::FlowsCreate => "flows.create".to_string(), + Self::FlowsUpdate => "flows.update".to_string(), + Self::FlowsDelete => "flows.delete".to_string(), + Self::FlowsArchive => "flows.archive".to_string(), + Self::AppsCreate => "apps.create".to_string(), + Self::AppsUpdate => "apps.update".to_string(), + Self::AppsDelete => "apps.delete".to_string(), + Self::FolderCreate => "folder.create".to_string(), + Self::FolderUpdate => "folder.update".to_string(), + Self::FolderDelete => "folder.delete".to_string(), + Self::FolderAddOwner => "folder.add_owner".to_string(), + Self::FolderRemoveOwner => "folder.remove_owner".to_string(), + Self::GroupCreate => "group.create".to_string(), + Self::GroupDelete => "group.delete".to_string(), + Self::GroupEdit => "group.edit".to_string(), + Self::GroupAdduser => "group.adduser".to_string(), + Self::GroupRemoveuser => "group.removeuser".to_string(), + Self::IgroupCreate => "igroup.create".to_string(), + Self::IgroupDelete => "igroup.delete".to_string(), + Self::IgroupAdduser => "igroup.adduser".to_string(), + Self::IgroupRemoveuser => "igroup.removeuser".to_string(), + Self::VariablesDecryptSecret => "variables.decrypt_secret".to_string(), + Self::WorkspacesEditCommandScript => { + "workspaces.edit_command_script".to_string() + } + Self::WorkspacesEditDeployTo => "workspaces.edit_deploy_to".to_string(), + Self::WorkspacesEditAutoInviteDomain => { + "workspaces.edit_auto_invite_domain".to_string() + } + Self::WorkspacesEditWebhook => "workspaces.edit_webhook".to_string(), + Self::WorkspacesEditCopilotConfig => { + "workspaces.edit_copilot_config".to_string() + } + Self::WorkspacesEditErrorHandler => { + "workspaces.edit_error_handler".to_string() + } + Self::WorkspacesCreate => "workspaces.create".to_string(), + Self::WorkspacesUpdate => "workspaces.update".to_string(), + Self::WorkspacesArchive => "workspaces.archive".to_string(), + Self::WorkspacesUnarchive => "workspaces.unarchive".to_string(), + Self::WorkspacesDelete => "workspaces.delete".to_string(), + } + } + } + impl std::str::FromStr for AuditLogOperation { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "jobs.run" => Ok(Self::JobsRun), + "jobs.run.script" => Ok(Self::JobsRunScript), + "jobs.run.preview" => Ok(Self::JobsRunPreview), + "jobs.run.flow" => Ok(Self::JobsRunFlow), + "jobs.run.flow_preview" => Ok(Self::JobsRunFlowPreview), + "jobs.run.script_hub" => Ok(Self::JobsRunScriptHub), + "jobs.run.dependencies" => Ok(Self::JobsRunDependencies), + "jobs.run.identity" => Ok(Self::JobsRunIdentity), + "jobs.run.noop" => Ok(Self::JobsRunNoop), + "jobs.flow_dependencies" => Ok(Self::JobsFlowDependencies), + "jobs" => Ok(Self::Jobs), + "jobs.cancel" => Ok(Self::JobsCancel), + "jobs.force_cancel" => Ok(Self::JobsForceCancel), + "jobs.disapproval" => Ok(Self::JobsDisapproval), + "jobs.delete" => Ok(Self::JobsDelete), + "account.delete" => Ok(Self::AccountDelete), + "ai.request" => Ok(Self::AiRequest), + "resources.create" => Ok(Self::ResourcesCreate), + "resources.update" => Ok(Self::ResourcesUpdate), + "resources.delete" => Ok(Self::ResourcesDelete), + "resource_types.create" => Ok(Self::ResourceTypesCreate), + "resource_types.update" => Ok(Self::ResourceTypesUpdate), + "resource_types.delete" => Ok(Self::ResourceTypesDelete), + "schedule.create" => Ok(Self::ScheduleCreate), + "schedule.setenabled" => Ok(Self::ScheduleSetenabled), + "schedule.edit" => Ok(Self::ScheduleEdit), + "schedule.delete" => Ok(Self::ScheduleDelete), + "scripts.create" => Ok(Self::ScriptsCreate), + "scripts.update" => Ok(Self::ScriptsUpdate), + "scripts.archive" => Ok(Self::ScriptsArchive), + "scripts.delete" => Ok(Self::ScriptsDelete), + "users.create" => Ok(Self::UsersCreate), + "users.delete" => Ok(Self::UsersDelete), + "users.update" => Ok(Self::UsersUpdate), + "users.login" => Ok(Self::UsersLogin), + "users.login_failure" => Ok(Self::UsersLoginFailure), + "users.logout" => Ok(Self::UsersLogout), + "users.accept_invite" => Ok(Self::UsersAcceptInvite), + "users.decline_invite" => Ok(Self::UsersDeclineInvite), + "users.token.create" => Ok(Self::UsersTokenCreate), + "users.token.delete" => Ok(Self::UsersTokenDelete), + "users.add_to_workspace" => Ok(Self::UsersAddToWorkspace), + "users.add_global" => Ok(Self::UsersAddGlobal), + "users.setpassword" => Ok(Self::UsersSetpassword), + "users.impersonate" => Ok(Self::UsersImpersonate), + "users.leave_workspace" => Ok(Self::UsersLeaveWorkspace), + "oauth.login" => Ok(Self::OauthLogin), + "oauth.login_failure" => Ok(Self::OauthLoginFailure), + "oauth.signup" => Ok(Self::OauthSignup), + "variables.create" => Ok(Self::VariablesCreate), + "variables.delete" => Ok(Self::VariablesDelete), + "variables.update" => Ok(Self::VariablesUpdate), + "flows.create" => Ok(Self::FlowsCreate), + "flows.update" => Ok(Self::FlowsUpdate), + "flows.delete" => Ok(Self::FlowsDelete), + "flows.archive" => Ok(Self::FlowsArchive), + "apps.create" => Ok(Self::AppsCreate), + "apps.update" => Ok(Self::AppsUpdate), + "apps.delete" => Ok(Self::AppsDelete), + "folder.create" => Ok(Self::FolderCreate), + "folder.update" => Ok(Self::FolderUpdate), + "folder.delete" => Ok(Self::FolderDelete), + "folder.add_owner" => Ok(Self::FolderAddOwner), + "folder.remove_owner" => Ok(Self::FolderRemoveOwner), + "group.create" => Ok(Self::GroupCreate), + "group.delete" => Ok(Self::GroupDelete), + "group.edit" => Ok(Self::GroupEdit), + "group.adduser" => Ok(Self::GroupAdduser), + "group.removeuser" => Ok(Self::GroupRemoveuser), + "igroup.create" => Ok(Self::IgroupCreate), + "igroup.delete" => Ok(Self::IgroupDelete), + "igroup.adduser" => Ok(Self::IgroupAdduser), + "igroup.removeuser" => Ok(Self::IgroupRemoveuser), + "variables.decrypt_secret" => Ok(Self::VariablesDecryptSecret), + "workspaces.edit_command_script" => Ok(Self::WorkspacesEditCommandScript), + "workspaces.edit_deploy_to" => Ok(Self::WorkspacesEditDeployTo), + "workspaces.edit_auto_invite_domain" => { + Ok(Self::WorkspacesEditAutoInviteDomain) + } + "workspaces.edit_webhook" => Ok(Self::WorkspacesEditWebhook), + "workspaces.edit_copilot_config" => Ok(Self::WorkspacesEditCopilotConfig), + "workspaces.edit_error_handler" => Ok(Self::WorkspacesEditErrorHandler), + "workspaces.create" => Ok(Self::WorkspacesCreate), + "workspaces.update" => Ok(Self::WorkspacesUpdate), + "workspaces.archive" => Ok(Self::WorkspacesArchive), + "workspaces.unarchive" => Ok(Self::WorkspacesUnarchive), + "workspaces.delete" => Ok(Self::WorkspacesDelete), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for AuditLogOperation { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for AuditLogOperation { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for AuditLogOperation { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AutoscalingEvent { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub applied_at: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub desired_workers: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub event_type: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub reason: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub worker_group: Option, + } + impl From<&AutoscalingEvent> for AutoscalingEvent { + fn from(value: &AutoscalingEvent) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct BranchAll { + pub branches: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parallel: Option, + #[serde(rename = "type")] + pub type_: BranchAllType, + } + impl From<&BranchAll> for BranchAll { + fn from(value: &BranchAll) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct BranchAllBranchesItem { + pub modules: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub skip_failure: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&BranchAllBranchesItem> for BranchAllBranchesItem { + fn from(value: &BranchAllBranchesItem) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum BranchAllType { + #[serde(rename = "branchall")] + Branchall, + } + impl From<&BranchAllType> for BranchAllType { + fn from(value: &BranchAllType) -> Self { + value.clone() + } + } + impl ToString for BranchAllType { + fn to_string(&self) -> String { + match *self { + Self::Branchall => "branchall".to_string(), + } + } + } + impl std::str::FromStr for BranchAllType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "branchall" => Ok(Self::Branchall), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for BranchAllType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for BranchAllType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for BranchAllType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct BranchOne { + pub branches: Vec, + pub default: Vec, + #[serde(rename = "type")] + pub type_: BranchOneType, + } + impl From<&BranchOne> for BranchOne { + fn from(value: &BranchOne) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct BranchOneBranchesItem { + pub expr: String, + pub modules: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&BranchOneBranchesItem> for BranchOneBranchesItem { + fn from(value: &BranchOneBranchesItem) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum BranchOneType { + #[serde(rename = "branchone")] + Branchone, + } + impl From<&BranchOneType> for BranchOneType { + fn from(value: &BranchOneType) -> Self { + value.clone() + } + } + impl ToString for BranchOneType { + fn to_string(&self) -> String { + match *self { + Self::Branchone => "branchone".to_string(), + } + } + } + impl std::str::FromStr for BranchOneType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "branchone" => Ok(Self::Branchone), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for BranchOneType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for BranchOneType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for BranchOneType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Capture { + pub created_at: chrono::DateTime, + pub id: i64, + pub payload: serde_json::Value, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub trigger_extra: Option, + pub trigger_kind: CaptureTriggerKind, + } + impl From<&Capture> for Capture { + fn from(value: &Capture) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CaptureConfig { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub trigger_config: Option, + pub trigger_kind: CaptureTriggerKind, + } + impl From<&CaptureConfig> for CaptureConfig { + fn from(value: &CaptureConfig) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum CaptureTriggerKind { + #[serde(rename = "webhook")] + Webhook, + #[serde(rename = "http")] + Http, + #[serde(rename = "websocket")] + Websocket, + #[serde(rename = "kafka")] + Kafka, + #[serde(rename = "email")] + Email, + #[serde(rename = "nats")] + Nats, + #[serde(rename = "postgres")] + Postgres, + #[serde(rename = "sqs")] + Sqs, + #[serde(rename = "mqtt")] + Mqtt, + } + impl From<&CaptureTriggerKind> for CaptureTriggerKind { + fn from(value: &CaptureTriggerKind) -> Self { + value.clone() + } + } + impl ToString for CaptureTriggerKind { + fn to_string(&self) -> String { + match *self { + Self::Webhook => "webhook".to_string(), + Self::Http => "http".to_string(), + Self::Websocket => "websocket".to_string(), + Self::Kafka => "kafka".to_string(), + Self::Email => "email".to_string(), + Self::Nats => "nats".to_string(), + Self::Postgres => "postgres".to_string(), + Self::Sqs => "sqs".to_string(), + Self::Mqtt => "mqtt".to_string(), + } + } + } + impl std::str::FromStr for CaptureTriggerKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "webhook" => Ok(Self::Webhook), + "http" => Ok(Self::Http), + "websocket" => Ok(Self::Websocket), + "kafka" => Ok(Self::Kafka), + "email" => Ok(Self::Email), + "nats" => Ok(Self::Nats), + "postgres" => Ok(Self::Postgres), + "sqs" => Ok(Self::Sqs), + "mqtt" => Ok(Self::Mqtt), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for CaptureTriggerKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for CaptureTriggerKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for CaptureTriggerKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ChannelInfo { + ///The unique identifier of the channel + pub channel_id: String, + ///The display name of the channel + pub channel_name: String, + ///The service URL for the channel + pub service_url: String, + ///The Microsoft Teams tenant identifier + pub tenant_id: String, + } + impl From<&ChannelInfo> for ChannelInfo { + fn from(value: &ChannelInfo) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CompletedJob { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub aggregate_wait_time_ms: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub args: Option, + pub canceled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub canceled_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub canceled_reason: Option, + pub created_at: chrono::DateTime, + pub created_by: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deleted: Option, + pub duration_ms: i64, + pub email: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub flow_status: Option, + pub id: uuid::Uuid, + pub is_flow_step: bool, + pub is_skipped: bool, + pub job_kind: CompletedJobJobKind, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub labels: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub language: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub logs: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mem_peak: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parent_job: Option, + /**The user (u/userfoo) or group (g/groupfoo) whom +the execution of this script will be permissioned_as and by extension its DT_TOKEN. +*/ + pub permissioned_as: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub preprocessed: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_code: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_flow: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub result: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schedule_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub script_hash: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub script_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub self_wait_time_ms: Option, + pub started_at: chrono::DateTime, + pub success: bool, + pub tag: String, + pub visible_to_owner: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&CompletedJob> for CompletedJob { + fn from(value: &CompletedJob) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum CompletedJobJobKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "preview")] + Preview, + #[serde(rename = "dependencies")] + Dependencies, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "flowdependencies")] + Flowdependencies, + #[serde(rename = "appdependencies")] + Appdependencies, + #[serde(rename = "flowpreview")] + Flowpreview, + #[serde(rename = "script_hub")] + ScriptHub, + #[serde(rename = "identity")] + Identity, + #[serde(rename = "deploymentcallback")] + Deploymentcallback, + #[serde(rename = "singlescriptflow")] + Singlescriptflow, + #[serde(rename = "flowscript")] + Flowscript, + #[serde(rename = "flownode")] + Flownode, + #[serde(rename = "appscript")] + Appscript, + } + impl From<&CompletedJobJobKind> for CompletedJobJobKind { + fn from(value: &CompletedJobJobKind) -> Self { + value.clone() + } + } + impl ToString for CompletedJobJobKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Preview => "preview".to_string(), + Self::Dependencies => "dependencies".to_string(), + Self::Flow => "flow".to_string(), + Self::Flowdependencies => "flowdependencies".to_string(), + Self::Appdependencies => "appdependencies".to_string(), + Self::Flowpreview => "flowpreview".to_string(), + Self::ScriptHub => "script_hub".to_string(), + Self::Identity => "identity".to_string(), + Self::Deploymentcallback => "deploymentcallback".to_string(), + Self::Singlescriptflow => "singlescriptflow".to_string(), + Self::Flowscript => "flowscript".to_string(), + Self::Flownode => "flownode".to_string(), + Self::Appscript => "appscript".to_string(), + } + } + } + impl std::str::FromStr for CompletedJobJobKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "preview" => Ok(Self::Preview), + "dependencies" => Ok(Self::Dependencies), + "flow" => Ok(Self::Flow), + "flowdependencies" => Ok(Self::Flowdependencies), + "appdependencies" => Ok(Self::Appdependencies), + "flowpreview" => Ok(Self::Flowpreview), + "script_hub" => Ok(Self::ScriptHub), + "identity" => Ok(Self::Identity), + "deploymentcallback" => Ok(Self::Deploymentcallback), + "singlescriptflow" => Ok(Self::Singlescriptflow), + "flowscript" => Ok(Self::Flowscript), + "flownode" => Ok(Self::Flownode), + "appscript" => Ok(Self::Appscript), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for CompletedJobJobKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for CompletedJobJobKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for CompletedJobJobKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ConcurrencyGroup { + pub concurrency_key: String, + pub total_running: f64, + } + impl From<&ConcurrencyGroup> for ConcurrencyGroup { + fn from(value: &ConcurrencyGroup) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Config { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub config: std::collections::HashMap, + pub name: String, + } + impl From<&Config> for Config { + fn from(value: &Config) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ContextualVariable { + pub description: String, + pub is_custom: bool, + pub name: String, + pub value: String, + } + impl From<&ContextualVariable> for ContextualVariable { + fn from(value: &ContextualVariable) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateFlowBody { + #[serde(flatten)] + pub open_flow_w_path: OpenFlowWPath, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_message: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft_only: Option, + } + impl From<&CreateFlowBody> for CreateFlowBody { + fn from(value: &CreateFlowBody) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateInput { + pub args: std::collections::HashMap, + pub name: String, + } + impl From<&CreateInput> for CreateInput { + fn from(value: &CreateInput) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateResource { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + pub path: String, + pub resource_type: String, + pub value: serde_json::Value, + } + impl From<&CreateResource> for CreateResource { + fn from(value: &CreateResource) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateVariable { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub account: Option, + pub description: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expires_at: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_oauth: Option, + pub is_secret: bool, + pub path: String, + pub value: String, + } + impl From<&CreateVariable> for CreateVariable { + fn from(value: &CreateVariable) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateWorkspace { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub color: Option, + pub id: String, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub username: Option, + } + impl From<&CreateWorkspace> for CreateWorkspace { + fn from(value: &CreateWorkspace) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CriticalAlert { + ///Acknowledgment status of the alert, can be true, false, or null if not set + #[serde(default, skip_serializing_if = "Option::is_none")] + pub acknowledged: Option, + ///Type of alert (e.g., critical_error) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub alert_type: Option, + ///Time when the alert was created + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_at: Option>, + ///Unique identifier for the alert + #[serde(default, skip_serializing_if = "Option::is_none")] + pub id: Option, + ///The message content of the alert + #[serde(default, skip_serializing_if = "Option::is_none")] + pub message: Option, + ///Workspace id if the alert is in the scope of a workspace + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&CriticalAlert> for CriticalAlert { + fn from(value: &CriticalAlert) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditHttpTrigger { + pub http_method: EditHttpTriggerHttpMethod, + pub is_async: bool, + pub is_flow: bool, + pub is_static_website: bool, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_string: Option, + pub requires_auth: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub route_path: Option, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub static_asset_config: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspaced_route: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub wrap_body: Option, + } + impl From<&EditHttpTrigger> for EditHttpTrigger { + fn from(value: &EditHttpTrigger) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum EditHttpTriggerHttpMethod { + #[serde(rename = "get")] + Get, + #[serde(rename = "post")] + Post, + #[serde(rename = "put")] + Put, + #[serde(rename = "delete")] + Delete, + #[serde(rename = "patch")] + Patch, + } + impl From<&EditHttpTriggerHttpMethod> for EditHttpTriggerHttpMethod { + fn from(value: &EditHttpTriggerHttpMethod) -> Self { + value.clone() + } + } + impl ToString for EditHttpTriggerHttpMethod { + fn to_string(&self) -> String { + match *self { + Self::Get => "get".to_string(), + Self::Post => "post".to_string(), + Self::Put => "put".to_string(), + Self::Delete => "delete".to_string(), + Self::Patch => "patch".to_string(), + } + } + } + impl std::str::FromStr for EditHttpTriggerHttpMethod { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "get" => Ok(Self::Get), + "post" => Ok(Self::Post), + "put" => Ok(Self::Put), + "delete" => Ok(Self::Delete), + "patch" => Ok(Self::Patch), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for EditHttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for EditHttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for EditHttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditHttpTriggerStaticAssetConfig { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub filename: Option, + pub s3: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub storage: Option, + } + impl From<&EditHttpTriggerStaticAssetConfig> for EditHttpTriggerStaticAssetConfig { + fn from(value: &EditHttpTriggerStaticAssetConfig) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditKafkaTrigger { + pub group_id: String, + pub is_flow: bool, + pub kafka_resource_path: String, + pub path: String, + pub script_path: String, + pub topics: Vec, + } + impl From<&EditKafkaTrigger> for EditKafkaTrigger { + fn from(value: &EditKafkaTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditMqttTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub client_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub client_version: Option, + pub enabled: bool, + pub is_flow: bool, + pub mqtt_resource_path: String, + pub path: String, + pub script_path: String, + pub subscribe_topics: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub v3_config: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub v5_config: Option, + } + impl From<&EditMqttTrigger> for EditMqttTrigger { + fn from(value: &EditMqttTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditNatsTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub consumer_name: Option, + pub is_flow: bool, + pub nats_resource_path: String, + pub path: String, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stream_name: Option, + pub subjects: Vec, + pub use_jetstream: bool, + } + impl From<&EditNatsTrigger> for EditNatsTrigger { + fn from(value: &EditNatsTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditPostgresTrigger { + pub enabled: bool, + pub is_flow: bool, + pub path: String, + pub postgres_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub publication: Option, + pub publication_name: String, + pub replication_slot_name: String, + pub script_path: String, + } + impl From<&EditPostgresTrigger> for EditPostgresTrigger { + fn from(value: &EditPostgresTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditResource { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + } + impl From<&EditResource> for EditResource { + fn from(value: &EditResource) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditResourceType { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schema: Option, + } + impl From<&EditResourceType> for EditResourceType { + fn from(value: &EditResourceType) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditSchedule { + pub args: ScriptArgs, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cron_version: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub no_flow_overlap: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_exact: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_times: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery_times: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_success: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_success_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub paused_until: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub retry: Option, + pub schedule: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + pub timezone: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&EditSchedule> for EditSchedule { + fn from(value: &EditSchedule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditSqsTrigger { + pub aws_resource_path: String, + pub enabled: bool, + pub is_flow: bool, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub message_attributes: Vec, + pub path: String, + pub queue_url: String, + pub script_path: String, + } + impl From<&EditSqsTrigger> for EditSqsTrigger { + fn from(value: &EditSqsTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditVariable { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_secret: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + } + impl From<&EditVariable> for EditVariable { + fn from(value: &EditVariable) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditWebsocketTrigger { + pub can_return_message: bool, + pub filters: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub initial_messages: Vec, + pub is_flow: bool, + pub path: String, + pub script_path: String, + pub url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url_runnable_args: Option, + } + impl From<&EditWebsocketTrigger> for EditWebsocketTrigger { + fn from(value: &EditWebsocketTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditWebsocketTriggerFiltersItem { + pub key: String, + pub value: serde_json::Value, + } + impl From<&EditWebsocketTriggerFiltersItem> for EditWebsocketTriggerFiltersItem { + fn from(value: &EditWebsocketTriggerFiltersItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct EditWorkspaceUser { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub disabled: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_admin: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub operator: Option, + } + impl From<&EditWorkspaceUser> for EditWorkspaceUser { + fn from(value: &EditWorkspaceUser) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ExportedInstanceGroup { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub emails: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub external_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub id: Option, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub scim_display_name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&ExportedInstanceGroup> for ExportedInstanceGroup { + fn from(value: &ExportedInstanceGroup) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ExportedUser { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub company: Option, + pub email: String, + pub first_time_user: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub password_hash: Option, + pub super_admin: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub username: Option, + pub verified: bool, + } + impl From<&ExportedUser> for ExportedUser { + fn from(value: &ExportedUser) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ExtendedJobs { + pub jobs: Vec, + pub obscured_jobs: Vec, + ///Obscured jobs omitted for security because of too specific filtering + #[serde(default, skip_serializing_if = "Option::is_none")] + pub omitted_obscured_jobs: Option, + } + impl From<&ExtendedJobs> for ExtendedJobs { + fn from(value: &ExtendedJobs) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ExtraPerms(pub std::collections::HashMap); + impl std::ops::Deref for ExtraPerms { + type Target = std::collections::HashMap; + fn deref(&self) -> &std::collections::HashMap { + &self.0 + } + } + impl From for std::collections::HashMap { + fn from(value: ExtraPerms) -> Self { + value.0 + } + } + impl From<&ExtraPerms> for ExtraPerms { + fn from(value: &ExtraPerms) -> Self { + value.clone() + } + } + impl From> for ExtraPerms { + fn from(value: std::collections::HashMap) -> Self { + Self(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Flow { + #[serde(flatten)] + pub open_flow: OpenFlow, + #[serde(flatten)] + pub flow_metadata: FlowMetadata, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock_error_logs: Option, + } + impl From<&Flow> for Flow { + fn from(value: &Flow) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowMetadata { + pub archived: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dedicated_worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft_only: Option, + pub edited_at: chrono::DateTime, + pub edited_by: String, + pub extra_perms: ExtraPerms, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_behalf_of_email: Option, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub starred: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub visible_to_runner_only: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&FlowMetadata> for FlowMetadata { + fn from(value: &FlowMetadata) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModule { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cache_ttl: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub continue_on_error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub delete_after_use: Option, + pub id: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mock: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub retry: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub skip_if: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sleep: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stop_after_all_iters_if: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stop_after_if: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub suspend: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeout: Option, + pub value: FlowModuleValue, + } + impl From<&FlowModule> for FlowModule { + fn from(value: &FlowModule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModuleMock { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub return_value: Option, + } + impl From<&FlowModuleMock> for FlowModuleMock { + fn from(value: &FlowModuleMock) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModuleSkipIf { + pub expr: String, + } + impl From<&FlowModuleSkipIf> for FlowModuleSkipIf { + fn from(value: &FlowModuleSkipIf) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModuleStopAfterAllItersIf { + pub expr: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub skip_if_stopped: Option, + } + impl From<&FlowModuleStopAfterAllItersIf> for FlowModuleStopAfterAllItersIf { + fn from(value: &FlowModuleStopAfterAllItersIf) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModuleStopAfterIf { + pub expr: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub skip_if_stopped: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error_message: Option + } + impl From<&FlowModuleStopAfterIf> for FlowModuleStopAfterIf { + fn from(value: &FlowModuleStopAfterIf) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModuleSuspend { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub continue_on_disapprove_timeout: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hide_cancel: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub required_events: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub resume_form: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub self_approval_disabled: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub user_auth_required: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub user_groups_required: Option, + } + impl From<&FlowModuleSuspend> for FlowModuleSuspend { + fn from(value: &FlowModuleSuspend) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowModuleSuspendResumeForm { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub schema: std::collections::HashMap, + } + impl From<&FlowModuleSuspendResumeForm> for FlowModuleSuspendResumeForm { + fn from(value: &FlowModuleSuspendResumeForm) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum FlowModuleValue { + RawScript(RawScript), + PathScript(PathScript), + PathFlow(PathFlow), + ForloopFlow(ForloopFlow), + WhileloopFlow(WhileloopFlow), + BranchOne(BranchOne), + BranchAll(BranchAll), + Identity(Identity), + } + impl From<&FlowModuleValue> for FlowModuleValue { + fn from(value: &FlowModuleValue) -> Self { + value.clone() + } + } + impl From for FlowModuleValue { + fn from(value: RawScript) -> Self { + Self::RawScript(value) + } + } + impl From for FlowModuleValue { + fn from(value: PathScript) -> Self { + Self::PathScript(value) + } + } + impl From for FlowModuleValue { + fn from(value: PathFlow) -> Self { + Self::PathFlow(value) + } + } + impl From for FlowModuleValue { + fn from(value: ForloopFlow) -> Self { + Self::ForloopFlow(value) + } + } + impl From for FlowModuleValue { + fn from(value: WhileloopFlow) -> Self { + Self::WhileloopFlow(value) + } + } + impl From for FlowModuleValue { + fn from(value: BranchOne) -> Self { + Self::BranchOne(value) + } + } + impl From for FlowModuleValue { + fn from(value: BranchAll) -> Self { + Self::BranchAll(value) + } + } + impl From for FlowModuleValue { + fn from(value: Identity) -> Self { + Self::Identity(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowPreview { + pub args: ScriptArgs, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub restarted_from: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + pub value: FlowValue, + } + impl From<&FlowPreview> for FlowPreview { + fn from(value: &FlowPreview) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatus { + pub failure_module: FlowStatusFailureModule, + pub modules: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub preprocessor_module: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub retry: Option, + pub step: i64, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub user_states: std::collections::HashMap, + } + impl From<&FlowStatus> for FlowStatus { + fn from(value: &FlowStatus) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusFailureModule { + #[serde(flatten)] + pub flow_status_module: FlowStatusModule, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parent_module: Option, + } + impl From<&FlowStatusFailureModule> for FlowStatusFailureModule { + fn from(value: &FlowStatusFailureModule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusModule { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub approvers: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub branch_chosen: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub branchall: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub count: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub failed_retries: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub flow_jobs: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub flow_jobs_success: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub iterator: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub job: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub progress: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub skipped: Option, + #[serde(rename = "type")] + pub type_: FlowStatusModuleType, + } + impl From<&FlowStatusModule> for FlowStatusModule { + fn from(value: &FlowStatusModule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusModuleApproversItem { + pub approver: String, + pub resume_id: i64, + } + impl From<&FlowStatusModuleApproversItem> for FlowStatusModuleApproversItem { + fn from(value: &FlowStatusModuleApproversItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusModuleBranchChosen { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub branch: Option, + #[serde(rename = "type")] + pub type_: FlowStatusModuleBranchChosenType, + } + impl From<&FlowStatusModuleBranchChosen> for FlowStatusModuleBranchChosen { + fn from(value: &FlowStatusModuleBranchChosen) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum FlowStatusModuleBranchChosenType { + #[serde(rename = "branch")] + Branch, + #[serde(rename = "default")] + Default, + } + impl From<&FlowStatusModuleBranchChosenType> for FlowStatusModuleBranchChosenType { + fn from(value: &FlowStatusModuleBranchChosenType) -> Self { + value.clone() + } + } + impl ToString for FlowStatusModuleBranchChosenType { + fn to_string(&self) -> String { + match *self { + Self::Branch => "branch".to_string(), + Self::Default => "default".to_string(), + } + } + } + impl std::str::FromStr for FlowStatusModuleBranchChosenType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "branch" => Ok(Self::Branch), + "default" => Ok(Self::Default), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for FlowStatusModuleBranchChosenType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for FlowStatusModuleBranchChosenType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for FlowStatusModuleBranchChosenType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusModuleBranchall { + pub branch: i64, + pub len: i64, + } + impl From<&FlowStatusModuleBranchall> for FlowStatusModuleBranchall { + fn from(value: &FlowStatusModuleBranchall) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusModuleIterator { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub index: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub itered: Vec, + } + impl From<&FlowStatusModuleIterator> for FlowStatusModuleIterator { + fn from(value: &FlowStatusModuleIterator) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum FlowStatusModuleType { + WaitingForPriorSteps, + WaitingForEvents, + WaitingForExecutor, + InProgress, + Success, + Failure, + } + impl From<&FlowStatusModuleType> for FlowStatusModuleType { + fn from(value: &FlowStatusModuleType) -> Self { + value.clone() + } + } + impl ToString for FlowStatusModuleType { + fn to_string(&self) -> String { + match *self { + Self::WaitingForPriorSteps => "WaitingForPriorSteps".to_string(), + Self::WaitingForEvents => "WaitingForEvents".to_string(), + Self::WaitingForExecutor => "WaitingForExecutor".to_string(), + Self::InProgress => "InProgress".to_string(), + Self::Success => "Success".to_string(), + Self::Failure => "Failure".to_string(), + } + } + } + impl std::str::FromStr for FlowStatusModuleType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "WaitingForPriorSteps" => Ok(Self::WaitingForPriorSteps), + "WaitingForEvents" => Ok(Self::WaitingForEvents), + "WaitingForExecutor" => Ok(Self::WaitingForExecutor), + "InProgress" => Ok(Self::InProgress), + "Success" => Ok(Self::Success), + "Failure" => Ok(Self::Failure), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for FlowStatusModuleType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for FlowStatusModuleType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for FlowStatusModuleType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowStatusRetry { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub fail_count: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub failed_jobs: Vec, + } + impl From<&FlowStatusRetry> for FlowStatusRetry { + fn from(value: &FlowStatusRetry) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowValue { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cache_ttl: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_key: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_time_window_s: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrent_limit: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub early_return: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub failure_module: Option, + pub modules: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub preprocessor_module: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub same_worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub skip_expr: Option, + } + impl From<&FlowValue> for FlowValue { + fn from(value: &FlowValue) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FlowVersion { + pub created_at: chrono::DateTime, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_msg: Option, + pub id: i64, + } + impl From<&FlowVersion> for FlowVersion { + fn from(value: &FlowVersion) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Folder { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub edited_at: Option>, + pub extra_perms: std::collections::HashMap, + pub name: String, + pub owners: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&Folder> for Folder { + fn from(value: &Folder) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ForloopFlow { + pub iterator: InputTransform, + pub modules: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parallel: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parallelism: Option, + pub skip_failures: bool, + #[serde(rename = "type")] + pub type_: ForloopFlowType, + } + impl From<&ForloopFlow> for ForloopFlow { + fn from(value: &ForloopFlow) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum ForloopFlowType { + #[serde(rename = "forloopflow")] + Forloopflow, + } + impl From<&ForloopFlowType> for ForloopFlowType { + fn from(value: &ForloopFlowType) -> Self { + value.clone() + } + } + impl ToString for ForloopFlowType { + fn to_string(&self) -> String { + match *self { + Self::Forloopflow => "forloopflow".to_string(), + } + } + } + impl std::str::FromStr for ForloopFlowType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "forloopflow" => Ok(Self::Forloopflow), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for ForloopFlowType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for ForloopFlowType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for ForloopFlowType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GitRepositorySettings { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub exclude_types_override: Vec, + pub git_repo_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub group_by_folder: Option, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub use_individual_branch: Option, + } + impl From<&GitRepositorySettings> for GitRepositorySettings { + fn from(value: &GitRepositorySettings) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum GitRepositorySettingsExcludeTypesOverrideItem { + #[serde(rename = "script")] + Script, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "app")] + App, + #[serde(rename = "folder")] + Folder, + #[serde(rename = "resource")] + Resource, + #[serde(rename = "variable")] + Variable, + #[serde(rename = "secret")] + Secret, + #[serde(rename = "resourcetype")] + Resourcetype, + #[serde(rename = "schedule")] + Schedule, + #[serde(rename = "user")] + User, + #[serde(rename = "group")] + Group, + } + impl From<&GitRepositorySettingsExcludeTypesOverrideItem> + for GitRepositorySettingsExcludeTypesOverrideItem { + fn from(value: &GitRepositorySettingsExcludeTypesOverrideItem) -> Self { + value.clone() + } + } + impl ToString for GitRepositorySettingsExcludeTypesOverrideItem { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Flow => "flow".to_string(), + Self::App => "app".to_string(), + Self::Folder => "folder".to_string(), + Self::Resource => "resource".to_string(), + Self::Variable => "variable".to_string(), + Self::Secret => "secret".to_string(), + Self::Resourcetype => "resourcetype".to_string(), + Self::Schedule => "schedule".to_string(), + Self::User => "user".to_string(), + Self::Group => "group".to_string(), + } + } + } + impl std::str::FromStr for GitRepositorySettingsExcludeTypesOverrideItem { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "flow" => Ok(Self::Flow), + "app" => Ok(Self::App), + "folder" => Ok(Self::Folder), + "resource" => Ok(Self::Resource), + "variable" => Ok(Self::Variable), + "secret" => Ok(Self::Secret), + "resourcetype" => Ok(Self::Resourcetype), + "schedule" => Ok(Self::Schedule), + "user" => Ok(Self::User), + "group" => Ok(Self::Group), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for GitRepositorySettingsExcludeTypesOverrideItem { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> + for GitRepositorySettingsExcludeTypesOverrideItem { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom + for GitRepositorySettingsExcludeTypesOverrideItem { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GlobalSetting { + pub name: String, + pub value: std::collections::HashMap, + } + impl From<&GlobalSetting> for GlobalSetting { + fn from(value: &GlobalSetting) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GlobalUserInfo { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub company: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub devops: Option, + pub email: String, + pub login_type: GlobalUserInfoLoginType, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub operator_only: Option, + pub super_admin: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub username: Option, + pub verified: bool, + } + impl From<&GlobalUserInfo> for GlobalUserInfo { + fn from(value: &GlobalUserInfo) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum GlobalUserInfoLoginType { + #[serde(rename = "password")] + Password, + #[serde(rename = "github")] + Github, + } + impl From<&GlobalUserInfoLoginType> for GlobalUserInfoLoginType { + fn from(value: &GlobalUserInfoLoginType) -> Self { + value.clone() + } + } + impl ToString for GlobalUserInfoLoginType { + fn to_string(&self) -> String { + match *self { + Self::Password => "password".to_string(), + Self::Github => "github".to_string(), + } + } + } + impl std::str::FromStr for GlobalUserInfoLoginType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "password" => Ok(Self::Password), + "github" => Ok(Self::Github), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for GlobalUserInfoLoginType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for GlobalUserInfoLoginType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for GlobalUserInfoLoginType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Group { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub extra_perms: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub members: Vec, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&Group> for Group { + fn from(value: &Group) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct HttpTrigger { + pub http_method: HttpTriggerHttpMethod, + pub is_async: bool, + pub is_static_website: bool, + pub raw_string: bool, + pub requires_auth: bool, + pub route_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub static_asset_config: Option, + pub workspaced_route: bool, + pub wrap_body: bool, + } + impl From<&HttpTrigger> for HttpTrigger { + fn from(value: &HttpTrigger) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum HttpTriggerHttpMethod { + #[serde(rename = "get")] + Get, + #[serde(rename = "post")] + Post, + #[serde(rename = "put")] + Put, + #[serde(rename = "delete")] + Delete, + #[serde(rename = "patch")] + Patch, + } + impl From<&HttpTriggerHttpMethod> for HttpTriggerHttpMethod { + fn from(value: &HttpTriggerHttpMethod) -> Self { + value.clone() + } + } + impl ToString for HttpTriggerHttpMethod { + fn to_string(&self) -> String { + match *self { + Self::Get => "get".to_string(), + Self::Post => "post".to_string(), + Self::Put => "put".to_string(), + Self::Delete => "delete".to_string(), + Self::Patch => "patch".to_string(), + } + } + } + impl std::str::FromStr for HttpTriggerHttpMethod { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "get" => Ok(Self::Get), + "post" => Ok(Self::Post), + "put" => Ok(Self::Put), + "delete" => Ok(Self::Delete), + "patch" => Ok(Self::Patch), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for HttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for HttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for HttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct HttpTriggerStaticAssetConfig { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub filename: Option, + pub s3: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub storage: Option, + } + impl From<&HttpTriggerStaticAssetConfig> for HttpTriggerStaticAssetConfig { + fn from(value: &HttpTriggerStaticAssetConfig) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct HubScriptKind(pub serde_json::Value); + impl std::ops::Deref for HubScriptKind { + type Target = serde_json::Value; + fn deref(&self) -> &serde_json::Value { + &self.0 + } + } + impl From for serde_json::Value { + fn from(value: HubScriptKind) -> Self { + value.0 + } + } + impl From<&HubScriptKind> for HubScriptKind { + fn from(value: &HubScriptKind) -> Self { + value.clone() + } + } + impl From for HubScriptKind { + fn from(value: serde_json::Value) -> Self { + Self(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Identity { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub flow: Option, + #[serde(rename = "type")] + pub type_: IdentityType, + } + impl From<&Identity> for Identity { + fn from(value: &Identity) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum IdentityType { + #[serde(rename = "identity")] + Identity, + } + impl From<&IdentityType> for IdentityType { + fn from(value: &IdentityType) -> Self { + value.clone() + } + } + impl ToString for IdentityType { + fn to_string(&self) -> String { + match *self { + Self::Identity => "identity".to_string(), + } + } + } + impl std::str::FromStr for IdentityType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "identity" => Ok(Self::Identity), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for IdentityType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for IdentityType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for IdentityType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Input { + pub created_at: chrono::DateTime, + pub created_by: String, + pub id: String, + pub is_public: bool, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub success: Option, + } + impl From<&Input> for Input { + fn from(value: &Input) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum InputTransform { + StaticTransform(StaticTransform), + JavascriptTransform(JavascriptTransform), + } + impl From<&InputTransform> for InputTransform { + fn from(value: &InputTransform) -> Self { + value.clone() + } + } + impl From for InputTransform { + fn from(value: StaticTransform) -> Self { + Self::StaticTransform(value) + } + } + impl From for InputTransform { + fn from(value: JavascriptTransform) -> Self { + Self::JavascriptTransform(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct InstanceGroup { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub emails: Vec, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + } + impl From<&InstanceGroup> for InstanceGroup { + fn from(value: &InstanceGroup) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct JavascriptTransform { + pub expr: String, + #[serde(rename = "type")] + pub type_: JavascriptTransformType, + } + impl From<&JavascriptTransform> for JavascriptTransform { + fn from(value: &JavascriptTransform) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum JavascriptTransformType { + #[serde(rename = "javascript")] + Javascript, + } + impl From<&JavascriptTransformType> for JavascriptTransformType { + fn from(value: &JavascriptTransformType) -> Self { + value.clone() + } + } + impl ToString for JavascriptTransformType { + fn to_string(&self) -> String { + match *self { + Self::Javascript => "javascript".to_string(), + } + } + } + impl std::str::FromStr for JavascriptTransformType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "javascript" => Ok(Self::Javascript), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for JavascriptTransformType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for JavascriptTransformType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for JavascriptTransformType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum Job { + Variant0(JobVariant0), + Variant1(JobVariant1), + } + impl From<&Job> for Job { + fn from(value: &Job) -> Self { + value.clone() + } + } + impl From for Job { + fn from(value: JobVariant0) -> Self { + Self::Variant0(value) + } + } + impl From for Job { + fn from(value: JobVariant1) -> Self { + Self::Variant1(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct JobSearchHit { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dancer: Option, + } + impl From<&JobSearchHit> for JobSearchHit { + fn from(value: &JobSearchHit) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct JobVariant0 { + #[serde(flatten)] + pub completed_job: CompletedJob, + #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")] + pub type_: Option, + } + impl From<&JobVariant0> for JobVariant0 { + fn from(value: &JobVariant0) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum JobVariant0Type { + CompletedJob, + } + impl From<&JobVariant0Type> for JobVariant0Type { + fn from(value: &JobVariant0Type) -> Self { + value.clone() + } + } + impl ToString for JobVariant0Type { + fn to_string(&self) -> String { + match *self { + Self::CompletedJob => "CompletedJob".to_string(), + } + } + } + impl std::str::FromStr for JobVariant0Type { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "CompletedJob" => Ok(Self::CompletedJob), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for JobVariant0Type { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for JobVariant0Type { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for JobVariant0Type { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct JobVariant1 { + #[serde(flatten)] + pub queued_job: QueuedJob, + #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")] + pub type_: Option, + } + impl From<&JobVariant1> for JobVariant1 { + fn from(value: &JobVariant1) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum JobVariant1Type { + QueuedJob, + } + impl From<&JobVariant1Type> for JobVariant1Type { + fn from(value: &JobVariant1Type) -> Self { + value.clone() + } + } + impl ToString for JobVariant1Type { + fn to_string(&self) -> String { + match *self { + Self::QueuedJob => "QueuedJob".to_string(), + } + } + } + impl std::str::FromStr for JobVariant1Type { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "QueuedJob" => Ok(Self::QueuedJob), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for JobVariant1Type { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for JobVariant1Type { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for JobVariant1Type { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct KafkaTrigger { + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + pub group_id: String, + pub kafka_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + pub topics: Vec, + } + impl From<&KafkaTrigger> for KafkaTrigger { + fn from(value: &KafkaTrigger) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum Language { + Typescript, + } + impl From<&Language> for Language { + fn from(value: &Language) -> Self { + value.clone() + } + } + impl ToString for Language { + fn to_string(&self) -> String { + match *self { + Self::Typescript => "Typescript".to_string(), + } + } + } + impl std::str::FromStr for Language { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "Typescript" => Ok(Self::Typescript), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for Language { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for Language { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for Language { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct LargeFileStorage { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub azure_blob_resource_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub public_resource: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub s3_resource_path: Option, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub secondary_storage: std::collections::HashMap< + String, + LargeFileStorageSecondaryStorageValue, + >, + #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")] + pub type_: Option, + } + impl From<&LargeFileStorage> for LargeFileStorage { + fn from(value: &LargeFileStorage) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct LargeFileStorageSecondaryStorageValue { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub azure_blob_resource_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub public_resource: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub s3_resource_path: Option, + #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")] + pub type_: Option, + } + impl From<&LargeFileStorageSecondaryStorageValue> + for LargeFileStorageSecondaryStorageValue { + fn from(value: &LargeFileStorageSecondaryStorageValue) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum LargeFileStorageSecondaryStorageValueType { + S3Storage, + AzureBlobStorage, + AzureWorkloadIdentity, + S3AwsOidc, + } + impl From<&LargeFileStorageSecondaryStorageValueType> + for LargeFileStorageSecondaryStorageValueType { + fn from(value: &LargeFileStorageSecondaryStorageValueType) -> Self { + value.clone() + } + } + impl ToString for LargeFileStorageSecondaryStorageValueType { + fn to_string(&self) -> String { + match *self { + Self::S3Storage => "S3Storage".to_string(), + Self::AzureBlobStorage => "AzureBlobStorage".to_string(), + Self::AzureWorkloadIdentity => "AzureWorkloadIdentity".to_string(), + Self::S3AwsOidc => "S3AwsOidc".to_string(), + } + } + } + impl std::str::FromStr for LargeFileStorageSecondaryStorageValueType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "S3Storage" => Ok(Self::S3Storage), + "AzureBlobStorage" => Ok(Self::AzureBlobStorage), + "AzureWorkloadIdentity" => Ok(Self::AzureWorkloadIdentity), + "S3AwsOidc" => Ok(Self::S3AwsOidc), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for LargeFileStorageSecondaryStorageValueType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for LargeFileStorageSecondaryStorageValueType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for LargeFileStorageSecondaryStorageValueType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum LargeFileStorageType { + S3Storage, + AzureBlobStorage, + AzureWorkloadIdentity, + S3AwsOidc, + } + impl From<&LargeFileStorageType> for LargeFileStorageType { + fn from(value: &LargeFileStorageType) -> Self { + value.clone() + } + } + impl ToString for LargeFileStorageType { + fn to_string(&self) -> String { + match *self { + Self::S3Storage => "S3Storage".to_string(), + Self::AzureBlobStorage => "AzureBlobStorage".to_string(), + Self::AzureWorkloadIdentity => "AzureWorkloadIdentity".to_string(), + Self::S3AwsOidc => "S3AwsOidc".to_string(), + } + } + } + impl std::str::FromStr for LargeFileStorageType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "S3Storage" => Ok(Self::S3Storage), + "AzureBlobStorage" => Ok(Self::AzureBlobStorage), + "AzureWorkloadIdentity" => Ok(Self::AzureWorkloadIdentity), + "S3AwsOidc" => Ok(Self::S3AwsOidc), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for LargeFileStorageType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for LargeFileStorageType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for LargeFileStorageType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListableApp { + pub edited_at: chrono::DateTime, + pub execution_mode: ListableAppExecutionMode, + pub extra_perms: std::collections::HashMap, + pub id: i64, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub starred: Option, + pub summary: String, + pub version: i64, + pub workspace_id: String, + } + impl From<&ListableApp> for ListableApp { + fn from(value: &ListableApp) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum ListableAppExecutionMode { + #[serde(rename = "viewer")] + Viewer, + #[serde(rename = "publisher")] + Publisher, + #[serde(rename = "anonymous")] + Anonymous, + } + impl From<&ListableAppExecutionMode> for ListableAppExecutionMode { + fn from(value: &ListableAppExecutionMode) -> Self { + value.clone() + } + } + impl ToString for ListableAppExecutionMode { + fn to_string(&self) -> String { + match *self { + Self::Viewer => "viewer".to_string(), + Self::Publisher => "publisher".to_string(), + Self::Anonymous => "anonymous".to_string(), + } + } + } + impl std::str::FromStr for ListableAppExecutionMode { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "viewer" => Ok(Self::Viewer), + "publisher" => Ok(Self::Publisher), + "anonymous" => Ok(Self::Anonymous), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for ListableAppExecutionMode { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for ListableAppExecutionMode { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for ListableAppExecutionMode { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListableRawApp { + pub edited_at: chrono::DateTime, + pub extra_perms: std::collections::HashMap, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub starred: Option, + pub summary: String, + pub version: f64, + pub workspace_id: String, + } + impl From<&ListableRawApp> for ListableRawApp { + fn from(value: &ListableRawApp) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListableResource { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub account: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub edited_at: Option>, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub extra_perms: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_expired: Option, + pub is_linked: bool, + pub is_oauth: bool, + pub is_refreshed: bool, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub refresh_error: Option, + pub resource_type: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&ListableResource> for ListableResource { + fn from(value: &ListableResource) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListableVariable { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub account: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expires_at: Option>, + pub extra_perms: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_expired: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_linked: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_oauth: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_refreshed: Option, + pub is_secret: bool, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub refresh_error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + pub workspace_id: String, + } + impl From<&ListableVariable> for ListableVariable { + fn from(value: &ListableVariable) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct LogSearchHit { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dancer: Option, + } + impl From<&LogSearchHit> for LogSearchHit { + fn from(value: &LogSearchHit) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Login { + pub email: String, + pub password: String, + } + impl From<&Login> for Login { + fn from(value: &Login) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MainArgSignature { + pub args: Vec, + pub error: String, + pub has_preprocessor: Option, + pub no_main_func: Option, + pub star_args: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub star_kwargs: Option, + #[serde(rename = "type")] + pub type_: MainArgSignatureType, + } + impl From<&MainArgSignature> for MainArgSignature { + fn from(value: &MainArgSignature) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MainArgSignatureArgsItem { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub default: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub has_default: Option, + pub name: String, + pub typ: MainArgSignatureArgsItemTyp, + } + impl From<&MainArgSignatureArgsItem> for MainArgSignatureArgsItem { + fn from(value: &MainArgSignatureArgsItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub enum MainArgSignatureArgsItemTyp { + #[serde(rename = "float")] + Float, + #[serde(rename = "int")] + Int, + #[serde(rename = "bool")] + Bool, + #[serde(rename = "email")] + Email, + #[serde(rename = "unknown")] + Unknown, + #[serde(rename = "bytes")] + Bytes, + #[serde(rename = "dict")] + Dict, + #[serde(rename = "datetime")] + Datetime, + #[serde(rename = "sql")] + Sql, + #[serde(rename = "resource")] + Resource(Option), + #[serde(rename = "str")] + Str(Option>), + #[serde(rename = "object")] + Object(Vec), + #[serde(rename = "list")] + List(MainArgSignatureArgsItemTypList), + } + impl From<&MainArgSignatureArgsItemTyp> for MainArgSignatureArgsItemTyp { + fn from(value: &MainArgSignatureArgsItemTyp) -> Self { + value.clone() + } + } + impl From> for MainArgSignatureArgsItemTyp { + fn from(value: Option) -> Self { + Self::Resource(value) + } + } + impl From>> for MainArgSignatureArgsItemTyp { + fn from(value: Option>) -> Self { + Self::Str(value) + } + } + impl From> + for MainArgSignatureArgsItemTyp { + fn from(value: Vec) -> Self { + Self::Object(value) + } + } + impl From for MainArgSignatureArgsItemTyp { + fn from(value: MainArgSignatureArgsItemTypList) -> Self { + Self::List(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub enum MainArgSignatureArgsItemTypList { + #[serde(rename = "float")] + Float, + #[serde(rename = "int")] + Int, + #[serde(rename = "bool")] + Bool, + #[serde(rename = "email")] + Email, + #[serde(rename = "unknown")] + Unknown, + #[serde(rename = "bytes")] + Bytes, + #[serde(rename = "dict")] + Dict, + #[serde(rename = "datetime")] + Datetime, + #[serde(rename = "sql")] + Sql, + #[serde(rename = "str")] + Str(serde_json::Value), + } + impl From<&MainArgSignatureArgsItemTypList> for MainArgSignatureArgsItemTypList { + fn from(value: &MainArgSignatureArgsItemTypList) -> Self { + value.clone() + } + } + impl From for MainArgSignatureArgsItemTypList { + fn from(value: serde_json::Value) -> Self { + Self::Str(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MainArgSignatureArgsItemTypObjectItem { + pub key: String, + pub typ: MainArgSignatureArgsItemTypObjectItemTyp, + } + impl From<&MainArgSignatureArgsItemTypObjectItem> + for MainArgSignatureArgsItemTypObjectItem { + fn from(value: &MainArgSignatureArgsItemTypObjectItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub enum MainArgSignatureArgsItemTypObjectItemTyp { + #[serde(rename = "float")] + Float, + #[serde(rename = "int")] + Int, + #[serde(rename = "bool")] + Bool, + #[serde(rename = "email")] + Email, + #[serde(rename = "unknown")] + Unknown, + #[serde(rename = "bytes")] + Bytes, + #[serde(rename = "dict")] + Dict, + #[serde(rename = "datetime")] + Datetime, + #[serde(rename = "sql")] + Sql, + #[serde(rename = "str")] + Str(serde_json::Value), + } + impl From<&MainArgSignatureArgsItemTypObjectItemTyp> + for MainArgSignatureArgsItemTypObjectItemTyp { + fn from(value: &MainArgSignatureArgsItemTypObjectItemTyp) -> Self { + value.clone() + } + } + impl From for MainArgSignatureArgsItemTypObjectItemTyp { + fn from(value: serde_json::Value) -> Self { + Self::Str(value) + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum MainArgSignatureType { + Valid, + Invalid, + } + impl From<&MainArgSignatureType> for MainArgSignatureType { + fn from(value: &MainArgSignatureType) -> Self { + value.clone() + } + } + impl ToString for MainArgSignatureType { + fn to_string(&self) -> String { + match *self { + Self::Valid => "Valid".to_string(), + Self::Invalid => "Invalid".to_string(), + } + } + } + impl std::str::FromStr for MainArgSignatureType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "Valid" => Ok(Self::Valid), + "Invalid" => Ok(Self::Invalid), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for MainArgSignatureType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for MainArgSignatureType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for MainArgSignatureType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MetricDataPoint { + pub timestamp: chrono::DateTime, + pub value: f64, + } + impl From<&MetricDataPoint> for MetricDataPoint { + fn from(value: &MetricDataPoint) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MetricMetadata { + pub id: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + } + impl From<&MetricMetadata> for MetricMetadata { + fn from(value: &MetricMetadata) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum MqttClientVersion { + #[serde(rename = "v3")] + V3, + #[serde(rename = "v5")] + V5, + } + impl From<&MqttClientVersion> for MqttClientVersion { + fn from(value: &MqttClientVersion) -> Self { + value.clone() + } + } + impl ToString for MqttClientVersion { + fn to_string(&self) -> String { + match *self { + Self::V3 => "v3".to_string(), + Self::V5 => "v5".to_string(), + } + } + } + impl std::str::FromStr for MqttClientVersion { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "v3" => Ok(Self::V3), + "v5" => Ok(Self::V5), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for MqttClientVersion { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for MqttClientVersion { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for MqttClientVersion { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum MqttQoS { + #[serde(rename = "qos0")] + Qos0, + #[serde(rename = "qos1")] + Qos1, + #[serde(rename = "qos2")] + Qos2, + } + impl From<&MqttQoS> for MqttQoS { + fn from(value: &MqttQoS) -> Self { + value.clone() + } + } + impl ToString for MqttQoS { + fn to_string(&self) -> String { + match *self { + Self::Qos0 => "qos0".to_string(), + Self::Qos1 => "qos1".to_string(), + Self::Qos2 => "qos2".to_string(), + } + } + } + impl std::str::FromStr for MqttQoS { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "qos0" => Ok(Self::Qos0), + "qos1" => Ok(Self::Qos1), + "qos2" => Ok(Self::Qos2), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for MqttQoS { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for MqttQoS { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for MqttQoS { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MqttSubscribeTopic { + pub qos: MqttQoS, + pub topic: String, + } + impl From<&MqttSubscribeTopic> for MqttSubscribeTopic { + fn from(value: &MqttSubscribeTopic) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MqttTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub client_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub client_version: Option, + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + pub mqtt_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + pub subscribe_topics: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub v3_config: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub v5_config: Option, + } + impl From<&MqttTrigger> for MqttTrigger { + fn from(value: &MqttTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MqttV3Config { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub clean_session: Option, + } + impl From<&MqttV3Config> for MqttV3Config { + fn from(value: &MqttV3Config) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct MqttV5Config { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub clean_start: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub session_expiry_interval: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub topic_alias: Option, + } + impl From<&MqttV5Config> for MqttV5Config { + fn from(value: &MqttV5Config) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NatsTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub consumer_name: Option, + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + pub nats_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stream_name: Option, + pub subjects: Vec, + pub use_jetstream: bool, + } + impl From<&NatsTrigger> for NatsTrigger { + fn from(value: &NatsTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewHttpTrigger { + pub http_method: NewHttpTriggerHttpMethod, + pub is_async: bool, + pub is_flow: bool, + pub is_static_website: bool, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_string: Option, + pub requires_auth: bool, + pub route_path: String, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub static_asset_config: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspaced_route: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub wrap_body: Option, + } + impl From<&NewHttpTrigger> for NewHttpTrigger { + fn from(value: &NewHttpTrigger) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum NewHttpTriggerHttpMethod { + #[serde(rename = "get")] + Get, + #[serde(rename = "post")] + Post, + #[serde(rename = "put")] + Put, + #[serde(rename = "delete")] + Delete, + #[serde(rename = "patch")] + Patch, + } + impl From<&NewHttpTriggerHttpMethod> for NewHttpTriggerHttpMethod { + fn from(value: &NewHttpTriggerHttpMethod) -> Self { + value.clone() + } + } + impl ToString for NewHttpTriggerHttpMethod { + fn to_string(&self) -> String { + match *self { + Self::Get => "get".to_string(), + Self::Post => "post".to_string(), + Self::Put => "put".to_string(), + Self::Delete => "delete".to_string(), + Self::Patch => "patch".to_string(), + } + } + } + impl std::str::FromStr for NewHttpTriggerHttpMethod { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "get" => Ok(Self::Get), + "post" => Ok(Self::Post), + "put" => Ok(Self::Put), + "delete" => Ok(Self::Delete), + "patch" => Ok(Self::Patch), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for NewHttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for NewHttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for NewHttpTriggerHttpMethod { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewHttpTriggerStaticAssetConfig { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub filename: Option, + pub s3: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub storage: Option, + } + impl From<&NewHttpTriggerStaticAssetConfig> for NewHttpTriggerStaticAssetConfig { + fn from(value: &NewHttpTriggerStaticAssetConfig) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewKafkaTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + pub group_id: String, + pub is_flow: bool, + pub kafka_resource_path: String, + pub path: String, + pub script_path: String, + pub topics: Vec, + } + impl From<&NewKafkaTrigger> for NewKafkaTrigger { + fn from(value: &NewKafkaTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewMqttTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub client_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub client_version: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + pub is_flow: bool, + pub mqtt_resource_path: String, + pub path: String, + pub script_path: String, + pub subscribe_topics: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub v3_config: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub v5_config: Option, + } + impl From<&NewMqttTrigger> for NewMqttTrigger { + fn from(value: &NewMqttTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewNatsTrigger { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub consumer_name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + pub is_flow: bool, + pub nats_resource_path: String, + pub path: String, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stream_name: Option, + pub subjects: Vec, + pub use_jetstream: bool, + } + impl From<&NewNatsTrigger> for NewNatsTrigger { + fn from(value: &NewNatsTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewPostgresTrigger { + pub enabled: bool, + pub is_flow: bool, + pub path: String, + pub postgres_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub publication: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub publication_name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub replication_slot_name: Option, + pub script_path: String, + } + impl From<&NewPostgresTrigger> for NewPostgresTrigger { + fn from(value: &NewPostgresTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewSchedule { + pub args: ScriptArgs, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cron_version: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + pub is_flow: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub no_flow_overlap: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_exact: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_times: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery_times: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_success: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_success_extra_args: Option, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub paused_until: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub retry: Option, + pub schedule: String, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + pub timezone: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&NewSchedule> for NewSchedule { + fn from(value: &NewSchedule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewScript { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cache_ttl: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub codebase: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_key: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_time_window_s: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrent_limit: Option, + pub content: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dedicated_worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub delete_after_use: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_message: Option, + pub description: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft_only: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub envs: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub has_preprocessor: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_template: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + pub language: ScriptLang, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub no_main_func: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_behalf_of_email: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parent_hash: Option, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub restart_unless_cancelled: Option, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub schema: std::collections::HashMap, + pub summary: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub visible_to_runner_only: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&NewScript> for NewScript { + fn from(value: &NewScript) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum NewScriptKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "failure")] + Failure, + #[serde(rename = "trigger")] + Trigger, + #[serde(rename = "command")] + Command, + #[serde(rename = "approval")] + Approval, + #[serde(rename = "preprocessor")] + Preprocessor, + } + impl From<&NewScriptKind> for NewScriptKind { + fn from(value: &NewScriptKind) -> Self { + value.clone() + } + } + impl ToString for NewScriptKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Failure => "failure".to_string(), + Self::Trigger => "trigger".to_string(), + Self::Command => "command".to_string(), + Self::Approval => "approval".to_string(), + Self::Preprocessor => "preprocessor".to_string(), + } + } + } + impl std::str::FromStr for NewScriptKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "failure" => Ok(Self::Failure), + "trigger" => Ok(Self::Trigger), + "command" => Ok(Self::Command), + "approval" => Ok(Self::Approval), + "preprocessor" => Ok(Self::Preprocessor), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for NewScriptKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for NewScriptKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for NewScriptKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewScriptWithDraft { + #[serde(flatten)] + pub new_script: NewScript, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft: Option, + pub hash: String, + } + impl From<&NewScriptWithDraft> for NewScriptWithDraft { + fn from(value: &NewScriptWithDraft) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewSqsTrigger { + pub aws_resource_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + pub is_flow: bool, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub message_attributes: Vec, + pub path: String, + pub queue_url: String, + pub script_path: String, + } + impl From<&NewSqsTrigger> for NewSqsTrigger { + fn from(value: &NewSqsTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewToken { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expiration: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub scopes: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&NewToken> for NewToken { + fn from(value: &NewToken) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewTokenImpersonate { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expiration: Option>, + pub impersonate_email: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&NewTokenImpersonate> for NewTokenImpersonate { + fn from(value: &NewTokenImpersonate) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewWebsocketTrigger { + pub can_return_message: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option, + pub filters: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub initial_messages: Vec, + pub is_flow: bool, + pub path: String, + pub script_path: String, + pub url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url_runnable_args: Option, + } + impl From<&NewWebsocketTrigger> for NewWebsocketTrigger { + fn from(value: &NewWebsocketTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewWebsocketTriggerFiltersItem { + pub key: String, + pub value: serde_json::Value, + } + impl From<&NewWebsocketTriggerFiltersItem> for NewWebsocketTriggerFiltersItem { + fn from(value: &NewWebsocketTriggerFiltersItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ObscuredJob { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub duration_ms: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub started_at: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub typ: Option, + } + impl From<&ObscuredJob> for ObscuredJob { + fn from(value: &ObscuredJob) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct OpenFlow { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub schema: std::collections::HashMap, + pub summary: String, + pub value: FlowValue, + } + impl From<&OpenFlow> for OpenFlow { + fn from(value: &OpenFlow) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct OpenFlowWPath { + #[serde(flatten)] + pub open_flow: OpenFlow, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dedicated_worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_behalf_of_email: Option, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub visible_to_runner_only: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&OpenFlowWPath> for OpenFlowWPath { + fn from(value: &OpenFlowWPath) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct OperatorSettings(pub Option); + impl std::ops::Deref for OperatorSettings { + type Target = Option; + fn deref(&self) -> &Option { + &self.0 + } + } + impl From for Option { + fn from(value: OperatorSettings) -> Self { + value.0 + } + } + impl From<&OperatorSettings> for OperatorSettings { + fn from(value: &OperatorSettings) -> Self { + value.clone() + } + } + impl From> for OperatorSettings { + fn from(value: Option) -> Self { + Self(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct OperatorSettingsInner { + ///Whether operators can view audit logs + pub audit_logs: bool, + ///Whether operators can view folders page + pub folders: bool, + ///Whether operators can view groups page + pub groups: bool, + ///Whether operators can view resources + pub resources: bool, + ///Whether operators can view runs + pub runs: bool, + ///Whether operators can view schedules + pub schedules: bool, + ///Whether operators can view triggers + pub triggers: bool, + ///Whether operators can view variables + pub variables: bool, + ///Whether operators can view workers page + pub workers: bool, + } + impl From<&OperatorSettingsInner> for OperatorSettingsInner { + fn from(value: &OperatorSettingsInner) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PathFlow { + pub input_transforms: std::collections::HashMap, + pub path: String, + #[serde(rename = "type")] + pub type_: PathFlowType, + } + impl From<&PathFlow> for PathFlow { + fn from(value: &PathFlow) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum PathFlowType { + #[serde(rename = "flow")] + Flow, + } + impl From<&PathFlowType> for PathFlowType { + fn from(value: &PathFlowType) -> Self { + value.clone() + } + } + impl ToString for PathFlowType { + fn to_string(&self) -> String { + match *self { + Self::Flow => "flow".to_string(), + } + } + } + impl std::str::FromStr for PathFlowType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "flow" => Ok(Self::Flow), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for PathFlowType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for PathFlowType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for PathFlowType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PathScript { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hash: Option, + pub input_transforms: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_trigger: Option, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag_override: Option, + #[serde(rename = "type")] + pub type_: PathScriptType, + } + impl From<&PathScript> for PathScript { + fn from(value: &PathScript) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum PathScriptType { + #[serde(rename = "script")] + Script, + } + impl From<&PathScriptType> for PathScriptType { + fn from(value: &PathScriptType) -> Self { + value.clone() + } + } + impl ToString for PathScriptType { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + } + } + } + impl std::str::FromStr for PathScriptType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for PathScriptType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for PathScriptType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for PathScriptType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PolarsClientKwargs { + pub region_name: String, + } + impl From<&PolarsClientKwargs> for PolarsClientKwargs { + fn from(value: &PolarsClientKwargs) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Policy { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub allowed_s3_keys: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub execution_mode: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_behalf_of: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_behalf_of_email: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub s3_inputs: Vec>, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub triggerables: std::collections::HashMap< + String, + std::collections::HashMap, + >, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub triggerables_v2: std::collections::HashMap< + String, + std::collections::HashMap, + >, + } + impl From<&Policy> for Policy { + fn from(value: &Policy) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PolicyAllowedS3KeysItem { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub resource: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub s3_path: Option, + } + impl From<&PolicyAllowedS3KeysItem> for PolicyAllowedS3KeysItem { + fn from(value: &PolicyAllowedS3KeysItem) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum PolicyExecutionMode { + #[serde(rename = "viewer")] + Viewer, + #[serde(rename = "publisher")] + Publisher, + #[serde(rename = "anonymous")] + Anonymous, + } + impl From<&PolicyExecutionMode> for PolicyExecutionMode { + fn from(value: &PolicyExecutionMode) -> Self { + value.clone() + } + } + impl ToString for PolicyExecutionMode { + fn to_string(&self) -> String { + match *self { + Self::Viewer => "viewer".to_string(), + Self::Publisher => "publisher".to_string(), + Self::Anonymous => "anonymous".to_string(), + } + } + } + impl std::str::FromStr for PolicyExecutionMode { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "viewer" => Ok(Self::Viewer), + "publisher" => Ok(Self::Publisher), + "anonymous" => Ok(Self::Anonymous), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for PolicyExecutionMode { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for PolicyExecutionMode { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for PolicyExecutionMode { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PostgresTrigger { + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + pub postgres_resource_path: String, + pub publication_name: String, + pub replication_slot_name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + } + impl From<&PostgresTrigger> for PostgresTrigger { + fn from(value: &PostgresTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Preview { + pub args: ScriptArgs, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub content: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dedicated_worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub language: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + } + impl From<&Preview> for Preview { + fn from(value: &Preview) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum PreviewKind { + #[serde(rename = "code")] + Code, + #[serde(rename = "identity")] + Identity, + #[serde(rename = "http")] + Http, + } + impl From<&PreviewKind> for PreviewKind { + fn from(value: &PreviewKind) -> Self { + value.clone() + } + } + impl ToString for PreviewKind { + fn to_string(&self) -> String { + match *self { + Self::Code => "code".to_string(), + Self::Identity => "identity".to_string(), + Self::Http => "http".to_string(), + } + } + } + impl std::str::FromStr for PreviewKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "code" => Ok(Self::Code), + "identity" => Ok(Self::Identity), + "http" => Ok(Self::Http), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for PreviewKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for PreviewKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for PreviewKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PublicationData { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub table_to_track: Vec, + pub transaction_to_track: Vec, + } + impl From<&PublicationData> for PublicationData { + fn from(value: &PublicationData) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct QueuedJob { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub aggregate_wait_time_ms: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub args: Option, + pub canceled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub canceled_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub canceled_reason: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_at: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_by: Option, + pub email: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub flow_status: Option, + pub id: uuid::Uuid, + pub is_flow_step: bool, + pub job_kind: QueuedJobJobKind, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub language: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_ping: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub logs: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mem_peak: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parent_job: Option, + /**The user (u/userfoo) or group (g/groupfoo) whom +the execution of this script will be permissioned_as and by extension its DT_TOKEN. +*/ + pub permissioned_as: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub preprocessed: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_code: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_flow: Option, + pub running: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schedule_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub scheduled_for: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub script_hash: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub script_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub self_wait_time_ms: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub started_at: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub suspend: Option, + pub tag: String, + pub visible_to_owner: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&QueuedJob> for QueuedJob { + fn from(value: &QueuedJob) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum QueuedJobJobKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "preview")] + Preview, + #[serde(rename = "dependencies")] + Dependencies, + #[serde(rename = "flowdependencies")] + Flowdependencies, + #[serde(rename = "appdependencies")] + Appdependencies, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "flowpreview")] + Flowpreview, + #[serde(rename = "script_hub")] + ScriptHub, + #[serde(rename = "identity")] + Identity, + #[serde(rename = "deploymentcallback")] + Deploymentcallback, + #[serde(rename = "singlescriptflow")] + Singlescriptflow, + #[serde(rename = "flowscript")] + Flowscript, + #[serde(rename = "flownode")] + Flownode, + #[serde(rename = "appscript")] + Appscript, + } + impl From<&QueuedJobJobKind> for QueuedJobJobKind { + fn from(value: &QueuedJobJobKind) -> Self { + value.clone() + } + } + impl ToString for QueuedJobJobKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Preview => "preview".to_string(), + Self::Dependencies => "dependencies".to_string(), + Self::Flowdependencies => "flowdependencies".to_string(), + Self::Appdependencies => "appdependencies".to_string(), + Self::Flow => "flow".to_string(), + Self::Flowpreview => "flowpreview".to_string(), + Self::ScriptHub => "script_hub".to_string(), + Self::Identity => "identity".to_string(), + Self::Deploymentcallback => "deploymentcallback".to_string(), + Self::Singlescriptflow => "singlescriptflow".to_string(), + Self::Flowscript => "flowscript".to_string(), + Self::Flownode => "flownode".to_string(), + Self::Appscript => "appscript".to_string(), + } + } + } + impl std::str::FromStr for QueuedJobJobKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "preview" => Ok(Self::Preview), + "dependencies" => Ok(Self::Dependencies), + "flowdependencies" => Ok(Self::Flowdependencies), + "appdependencies" => Ok(Self::Appdependencies), + "flow" => Ok(Self::Flow), + "flowpreview" => Ok(Self::Flowpreview), + "script_hub" => Ok(Self::ScriptHub), + "identity" => Ok(Self::Identity), + "deploymentcallback" => Ok(Self::Deploymentcallback), + "singlescriptflow" => Ok(Self::Singlescriptflow), + "flowscript" => Ok(Self::Flowscript), + "flownode" => Ok(Self::Flownode), + "appscript" => Ok(Self::Appscript), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for QueuedJobJobKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for QueuedJobJobKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for QueuedJobJobKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RawScript { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_time_window_s: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrent_limit: Option, + pub content: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub custom_concurrency_key: Option, + pub input_transforms: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub is_trigger: Option, + pub language: RawScriptLanguage, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + #[serde(rename = "type")] + pub type_: RawScriptType, + } + impl From<&RawScript> for RawScript { + fn from(value: &RawScript) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RawScriptForDependencies { + pub language: ScriptLang, + pub path: String, + pub raw_code: String, + } + impl From<&RawScriptForDependencies> for RawScriptForDependencies { + fn from(value: &RawScriptForDependencies) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum RawScriptLanguage { + #[serde(rename = "deno")] + Deno, + #[serde(rename = "bun")] + Bun, + #[serde(rename = "python3")] + Python3, + #[serde(rename = "go")] + Go, + #[serde(rename = "bash")] + Bash, + #[serde(rename = "powershell")] + Powershell, + #[serde(rename = "postgresql")] + Postgresql, + #[serde(rename = "mysql")] + Mysql, + #[serde(rename = "bigquery")] + Bigquery, + #[serde(rename = "snowflake")] + Snowflake, + #[serde(rename = "mssql")] + Mssql, + #[serde(rename = "oracledb")] + Oracledb, + #[serde(rename = "graphql")] + Graphql, + #[serde(rename = "nativets")] + Nativets, + #[serde(rename = "php")] + Php, + } + impl From<&RawScriptLanguage> for RawScriptLanguage { + fn from(value: &RawScriptLanguage) -> Self { + value.clone() + } + } + impl ToString for RawScriptLanguage { + fn to_string(&self) -> String { + match *self { + Self::Deno => "deno".to_string(), + Self::Bun => "bun".to_string(), + Self::Python3 => "python3".to_string(), + Self::Go => "go".to_string(), + Self::Bash => "bash".to_string(), + Self::Powershell => "powershell".to_string(), + Self::Postgresql => "postgresql".to_string(), + Self::Mysql => "mysql".to_string(), + Self::Bigquery => "bigquery".to_string(), + Self::Snowflake => "snowflake".to_string(), + Self::Mssql => "mssql".to_string(), + Self::Oracledb => "oracledb".to_string(), + Self::Graphql => "graphql".to_string(), + Self::Nativets => "nativets".to_string(), + Self::Php => "php".to_string(), + } + } + } + impl std::str::FromStr for RawScriptLanguage { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "deno" => Ok(Self::Deno), + "bun" => Ok(Self::Bun), + "python3" => Ok(Self::Python3), + "go" => Ok(Self::Go), + "bash" => Ok(Self::Bash), + "powershell" => Ok(Self::Powershell), + "postgresql" => Ok(Self::Postgresql), + "mysql" => Ok(Self::Mysql), + "bigquery" => Ok(Self::Bigquery), + "snowflake" => Ok(Self::Snowflake), + "mssql" => Ok(Self::Mssql), + "oracledb" => Ok(Self::Oracledb), + "graphql" => Ok(Self::Graphql), + "nativets" => Ok(Self::Nativets), + "php" => Ok(Self::Php), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for RawScriptLanguage { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for RawScriptLanguage { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for RawScriptLanguage { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum RawScriptType { + #[serde(rename = "rawscript")] + Rawscript, + } + impl From<&RawScriptType> for RawScriptType { + fn from(value: &RawScriptType) -> Self { + value.clone() + } + } + impl ToString for RawScriptType { + fn to_string(&self) -> String { + match *self { + Self::Rawscript => "rawscript".to_string(), + } + } + } + impl std::str::FromStr for RawScriptType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "rawscript" => Ok(Self::Rawscript), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for RawScriptType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for RawScriptType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for RawScriptType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Relations { + pub schema_name: String, + pub table_to_track: TableToTrack, + } + impl From<&Relations> for Relations { + fn from(value: &Relations) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Resource { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub edited_at: Option>, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub extra_perms: std::collections::HashMap, + pub is_oauth: bool, + pub path: String, + pub resource_type: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&Resource> for Resource { + fn from(value: &Resource) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ResourceType { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_by: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub edited_at: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub format_extension: Option, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schema: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + } + impl From<&ResourceType> for ResourceType { + fn from(value: &ResourceType) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RestartedFrom { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub branch_or_iteration_n: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub flow_job_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub step_id: Option, + } + impl From<&RestartedFrom> for RestartedFrom { + fn from(value: &RestartedFrom) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Retry { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub constant: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub exponential: Option, + } + impl From<&Retry> for Retry { + fn from(value: &Retry) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RetryConstant { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub attempts: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub seconds: Option, + } + impl From<&RetryConstant> for RetryConstant { + fn from(value: &RetryConstant) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct RetryExponential { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub attempts: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub multiplier: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub random_factor: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub seconds: Option, + } + impl From<&RetryExponential> for RetryExponential { + fn from(value: &RetryExponential) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum RunnableType { + ScriptHash, + ScriptPath, + FlowPath, + } + impl From<&RunnableType> for RunnableType { + fn from(value: &RunnableType) -> Self { + value.clone() + } + } + impl ToString for RunnableType { + fn to_string(&self) -> String { + match *self { + Self::ScriptHash => "ScriptHash".to_string(), + Self::ScriptPath => "ScriptPath".to_string(), + Self::FlowPath => "FlowPath".to_string(), + } + } + } + impl std::str::FromStr for RunnableType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "ScriptHash" => Ok(Self::ScriptHash), + "ScriptPath" => Ok(Self::ScriptPath), + "FlowPath" => Ok(Self::FlowPath), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for RunnableType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for RunnableType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for RunnableType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct S3Resource { + #[serde(rename = "accessKey", default, skip_serializing_if = "Option::is_none")] + pub access_key: Option, + pub bucket: String, + #[serde(rename = "endPoint")] + pub end_point: String, + #[serde(rename = "pathStyle")] + pub path_style: bool, + pub region: String, + #[serde(rename = "secretKey", default, skip_serializing_if = "Option::is_none")] + pub secret_key: Option, + #[serde(rename = "useSSL")] + pub use_ssl: bool, + } + impl From<&S3Resource> for S3Resource { + fn from(value: &S3Resource) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ScalarMetric { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub metric_id: Option, + pub value: f64, + } + impl From<&ScalarMetric> for ScalarMetric { + fn from(value: &ScalarMetric) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Schedule { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cron_version: Option, + pub edited_at: chrono::DateTime, + pub edited_by: String, + pub email: String, + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + pub extra_perms: std::collections::HashMap, + pub is_flow: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub no_flow_overlap: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_exact: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_failure_times: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery_extra_args: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_recovery_times: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_success: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_success_extra_args: Option, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub paused_until: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub retry: Option, + pub schedule: String, + pub script_path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + pub timezone: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&Schedule> for Schedule { + fn from(value: &Schedule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ScheduleWJobs { + #[serde(flatten)] + pub schedule: Schedule, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub jobs: Vec, + } + impl From<&ScheduleWJobs> for ScheduleWJobs { + fn from(value: &ScheduleWJobs) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ScheduleWJobsJobsItem { + pub duration_ms: f64, + pub id: String, + pub success: bool, + } + impl From<&ScheduleWJobsJobsItem> for ScheduleWJobsJobsItem { + fn from(value: &ScheduleWJobsJobsItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Script { + pub archived: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cache_ttl: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub codebase: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_key: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrency_time_window_s: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub concurrent_limit: Option, + pub content: String, + pub created_at: chrono::DateTime, + pub created_by: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dedicated_worker: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub delete_after_use: Option, + pub deleted: bool, + pub description: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub draft_only: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub envs: Vec, + pub extra_perms: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub has_draft: Option, + pub has_preprocessor: bool, + pub hash: String, + pub is_template: bool, + pub kind: ScriptKind, + pub language: ScriptLang, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub lock_error_logs: Option, + pub no_main_func: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub on_behalf_of_email: Option, + /**The first element is the direct parent of the script, the second is the parent of the first, etc +*/ + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub parent_hashes: Vec, + pub path: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub restart_unless_cancelled: Option, + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub schema: std::collections::HashMap, + pub starred: bool, + pub summary: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub visible_to_runner_only: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ws_error_handler_muted: Option, + } + impl From<&Script> for Script { + fn from(value: &Script) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ScriptArgs(pub std::collections::HashMap); + impl std::ops::Deref for ScriptArgs { + type Target = std::collections::HashMap; + fn deref(&self) -> &std::collections::HashMap { + &self.0 + } + } + impl From for std::collections::HashMap { + fn from(value: ScriptArgs) -> Self { + value.0 + } + } + impl From<&ScriptArgs> for ScriptArgs { + fn from(value: &ScriptArgs) -> Self { + value.clone() + } + } + impl From> for ScriptArgs { + fn from(value: std::collections::HashMap) -> Self { + Self(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ScriptHistory { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployment_msg: Option, + pub script_hash: String, + } + impl From<&ScriptHistory> for ScriptHistory { + fn from(value: &ScriptHistory) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum ScriptKind { + #[serde(rename = "script")] + Script, + #[serde(rename = "failure")] + Failure, + #[serde(rename = "trigger")] + Trigger, + #[serde(rename = "command")] + Command, + #[serde(rename = "approval")] + Approval, + #[serde(rename = "preprocessor")] + Preprocessor, + } + impl From<&ScriptKind> for ScriptKind { + fn from(value: &ScriptKind) -> Self { + value.clone() + } + } + impl ToString for ScriptKind { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Failure => "failure".to_string(), + Self::Trigger => "trigger".to_string(), + Self::Command => "command".to_string(), + Self::Approval => "approval".to_string(), + Self::Preprocessor => "preprocessor".to_string(), + } + } + } + impl std::str::FromStr for ScriptKind { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "failure" => Ok(Self::Failure), + "trigger" => Ok(Self::Trigger), + "command" => Ok(Self::Command), + "approval" => Ok(Self::Approval), + "preprocessor" => Ok(Self::Preprocessor), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for ScriptKind { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for ScriptKind { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for ScriptKind { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum ScriptLang { + #[serde(rename = "python3")] + Python3, + #[serde(rename = "deno")] + Deno, + #[serde(rename = "go")] + Go, + #[serde(rename = "bash")] + Bash, + #[serde(rename = "powershell")] + Powershell, + #[serde(rename = "postgresql")] + Postgresql, + #[serde(rename = "mysql")] + Mysql, + #[serde(rename = "bigquery")] + Bigquery, + #[serde(rename = "snowflake")] + Snowflake, + #[serde(rename = "mssql")] + Mssql, + #[serde(rename = "oracledb")] + Oracledb, + #[serde(rename = "graphql")] + Graphql, + #[serde(rename = "nativets")] + Nativets, + #[serde(rename = "bun")] + Bun, + #[serde(rename = "php")] + Php, + #[serde(rename = "rust")] + Rust, + #[serde(rename = "ansible")] + Ansible, + #[serde(rename = "csharp")] + Csharp, + #[serde(rename = "nu")] + Nu, + } + impl From<&ScriptLang> for ScriptLang { + fn from(value: &ScriptLang) -> Self { + value.clone() + } + } + impl ToString for ScriptLang { + fn to_string(&self) -> String { + match *self { + Self::Python3 => "python3".to_string(), + Self::Deno => "deno".to_string(), + Self::Go => "go".to_string(), + Self::Bash => "bash".to_string(), + Self::Powershell => "powershell".to_string(), + Self::Postgresql => "postgresql".to_string(), + Self::Mysql => "mysql".to_string(), + Self::Bigquery => "bigquery".to_string(), + Self::Snowflake => "snowflake".to_string(), + Self::Mssql => "mssql".to_string(), + Self::Oracledb => "oracledb".to_string(), + Self::Graphql => "graphql".to_string(), + Self::Nativets => "nativets".to_string(), + Self::Bun => "bun".to_string(), + Self::Php => "php".to_string(), + Self::Rust => "rust".to_string(), + Self::Ansible => "ansible".to_string(), + Self::Csharp => "csharp".to_string(), + Self::Nu => "nu".to_string(), + } + } + } + impl std::str::FromStr for ScriptLang { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "python3" => Ok(Self::Python3), + "deno" => Ok(Self::Deno), + "go" => Ok(Self::Go), + "bash" => Ok(Self::Bash), + "powershell" => Ok(Self::Powershell), + "postgresql" => Ok(Self::Postgresql), + "mysql" => Ok(Self::Mysql), + "bigquery" => Ok(Self::Bigquery), + "snowflake" => Ok(Self::Snowflake), + "mssql" => Ok(Self::Mssql), + "oracledb" => Ok(Self::Oracledb), + "graphql" => Ok(Self::Graphql), + "nativets" => Ok(Self::Nativets), + "bun" => Ok(Self::Bun), + "php" => Ok(Self::Php), + "rust" => Ok(Self::Rust), + "ansible" => Ok(Self::Ansible), + "csharp" => Ok(Self::Csharp), + "nu" => Ok(Self::Nu), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for ScriptLang { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for ScriptLang { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for ScriptLang { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SlackToken { + pub access_token: String, + pub bot: SlackTokenBot, + pub team_id: String, + pub team_name: String, + } + impl From<&SlackToken> for SlackToken { + fn from(value: &SlackToken) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SlackTokenBot { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub bot_access_token: Option, + } + impl From<&SlackTokenBot> for SlackTokenBot { + fn from(value: &SlackTokenBot) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Slot { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + } + impl From<&Slot> for Slot { + fn from(value: &Slot) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SlotList { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub active: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub slot_name: Option, + } + impl From<&SlotList> for SlotList { + fn from(value: &SlotList) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SqsTrigger { + pub aws_resource_path: String, + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub message_attributes: Vec, + pub queue_url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + } + impl From<&SqsTrigger> for SqsTrigger { + fn from(value: &SqsTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct StaticTransform { + #[serde(rename = "type")] + pub type_: StaticTransformType, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + } + impl From<&StaticTransform> for StaticTransform { + fn from(value: &StaticTransform) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum StaticTransformType { + #[serde(rename = "javascript")] + Javascript, + } + impl From<&StaticTransformType> for StaticTransformType { + fn from(value: &StaticTransformType) -> Self { + value.clone() + } + } + impl ToString for StaticTransformType { + fn to_string(&self) -> String { + match *self { + Self::Javascript => "javascript".to_string(), + } + } + } + impl std::str::FromStr for StaticTransformType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "javascript" => Ok(Self::Javascript), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for StaticTransformType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for StaticTransformType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for StaticTransformType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TableToTrack(pub Vec); + impl std::ops::Deref for TableToTrack { + type Target = Vec; + fn deref(&self) -> &Vec { + &self.0 + } + } + impl From for Vec { + fn from(value: TableToTrack) -> Self { + value.0 + } + } + impl From<&TableToTrack> for TableToTrack { + fn from(value: &TableToTrack) -> Self { + value.clone() + } + } + impl From> for TableToTrack { + fn from(value: Vec) -> Self { + Self(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TableToTrackItem { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub columns_name: Vec, + pub table_name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub where_clause: Option, + } + impl From<&TableToTrackItem> for TableToTrackItem { + fn from(value: &TableToTrackItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TeamInfo { + ///List of channels within the team + pub channels: Vec, + ///The unique identifier of the Microsoft Teams team + pub team_id: String, + ///The display name of the Microsoft Teams team + pub team_name: String, + } + impl From<&TeamInfo> for TeamInfo { + fn from(value: &TeamInfo) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TemplateScript { + pub language: Language, + pub postgres_resource_path: String, + pub relations: Vec, + } + impl From<&TemplateScript> for TemplateScript { + fn from(value: &TemplateScript) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TimeseriesMetric { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub metric_id: Option, + pub values: Vec, + } + impl From<&TimeseriesMetric> for TimeseriesMetric { + fn from(value: &TimeseriesMetric) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TokenResponse { + pub access_token: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expires_in: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub refresh_token: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub scope: Vec, + } + impl From<&TokenResponse> for TokenResponse { + fn from(value: &TokenResponse) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TriggerExtraProperty { + pub edited_at: chrono::DateTime, + pub edited_by: String, + pub email: String, + pub extra_perms: std::collections::HashMap, + pub is_flow: bool, + pub path: String, + pub script_path: String, + pub workspace_id: String, + } + impl From<&TriggerExtraProperty> for TriggerExtraProperty { + fn from(value: &TriggerExtraProperty) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TriggersCount { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub email_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub http_routes_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kafka_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mqtt_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub nats_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub postgres_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub primary_schedule: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schedule_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sqs_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub webhook_count: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub websocket_count: Option, + } + impl From<&TriggersCount> for TriggersCount { + fn from(value: &TriggersCount) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TriggersCountPrimarySchedule { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schedule: Option, + } + impl From<&TriggersCountPrimarySchedule> for TriggersCountPrimarySchedule { + fn from(value: &TriggersCountPrimarySchedule) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TruncatedToken { + pub created_at: chrono::DateTime, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub email: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expiration: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub label: Option, + pub last_used_at: chrono::DateTime, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub scopes: Vec, + pub token_prefix: String, + } + impl From<&TruncatedToken> for TruncatedToken { + fn from(value: &TruncatedToken) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UpdateInput { + pub id: String, + pub is_public: bool, + pub name: String, + } + impl From<&UpdateInput> for UpdateInput { + fn from(value: &UpdateInput) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UploadFilePart { + pub part_number: i64, + pub tag: String, + } + impl From<&UploadFilePart> for UploadFilePart { + fn from(value: &UploadFilePart) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct User { + pub created_at: chrono::DateTime, + pub disabled: bool, + pub email: String, + pub folders: Vec, + pub folders_owners: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub groups: Vec, + pub is_admin: bool, + pub is_super_admin: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + pub operator: bool, + pub username: String, + } + impl From<&User> for User { + fn from(value: &User) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UserUsage { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub email: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub executions: Option, + } + impl From<&UserUsage> for UserUsage { + fn from(value: &UserUsage) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UserWorkspaceList { + pub email: String, + pub workspaces: Vec, + } + impl From<&UserWorkspaceList> for UserWorkspaceList { + fn from(value: &UserWorkspaceList) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UserWorkspaceListWorkspacesItem { + pub color: String, + pub id: String, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub operator_settings: Option, + pub username: String, + } + impl From<&UserWorkspaceListWorkspacesItem> for UserWorkspaceListWorkspacesItem { + fn from(value: &UserWorkspaceListWorkspacesItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WebsocketTrigger { + pub can_return_message: bool, + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + pub filters: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub initial_messages: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + pub url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url_runnable_args: Option, + } + impl From<&WebsocketTrigger> for WebsocketTrigger { + fn from(value: &WebsocketTrigger) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WebsocketTriggerFiltersItem { + pub key: String, + pub value: serde_json::Value, + } + impl From<&WebsocketTriggerFiltersItem> for WebsocketTriggerFiltersItem { + fn from(value: &WebsocketTriggerFiltersItem) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub enum WebsocketTriggerInitialMessage { + #[serde(rename = "raw_message")] + RawMessage(String), + #[serde(rename = "runnable_result")] + RunnableResult { args: ScriptArgs, is_flow: bool, path: String }, + } + impl From<&WebsocketTriggerInitialMessage> for WebsocketTriggerInitialMessage { + fn from(value: &WebsocketTriggerInitialMessage) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WhileloopFlow { + pub modules: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parallel: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parallelism: Option, + pub skip_failures: bool, + #[serde(rename = "type")] + pub type_: WhileloopFlowType, + } + impl From<&WhileloopFlow> for WhileloopFlow { + fn from(value: &WhileloopFlow) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum WhileloopFlowType { + #[serde(rename = "forloopflow")] + Forloopflow, + } + impl From<&WhileloopFlowType> for WhileloopFlowType { + fn from(value: &WhileloopFlowType) -> Self { + value.clone() + } + } + impl ToString for WhileloopFlowType { + fn to_string(&self) -> String { + match *self { + Self::Forloopflow => "forloopflow".to_string(), + } + } + } + impl std::str::FromStr for WhileloopFlowType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "forloopflow" => Ok(Self::Forloopflow), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for WhileloopFlowType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for WhileloopFlowType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for WhileloopFlowType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WindmillFileMetadata { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub expires: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_modified: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mime_type: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub size_in_bytes: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub version_id: Option, + } + impl From<&WindmillFileMetadata> for WindmillFileMetadata { + fn from(value: &WindmillFileMetadata) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WindmillFilePreview { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub content: Option, + pub content_type: WindmillFilePreviewContentType, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub msg: Option, + } + impl From<&WindmillFilePreview> for WindmillFilePreview { + fn from(value: &WindmillFilePreview) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum WindmillFilePreviewContentType { + RawText, + Csv, + Parquet, + Unknown, + } + impl From<&WindmillFilePreviewContentType> for WindmillFilePreviewContentType { + fn from(value: &WindmillFilePreviewContentType) -> Self { + value.clone() + } + } + impl ToString for WindmillFilePreviewContentType { + fn to_string(&self) -> String { + match *self { + Self::RawText => "RawText".to_string(), + Self::Csv => "Csv".to_string(), + Self::Parquet => "Parquet".to_string(), + Self::Unknown => "Unknown".to_string(), + } + } + } + impl std::str::FromStr for WindmillFilePreviewContentType { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "RawText" => Ok(Self::RawText), + "Csv" => Ok(Self::Csv), + "Parquet" => Ok(Self::Parquet), + "Unknown" => Ok(Self::Unknown), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for WindmillFilePreviewContentType { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for WindmillFilePreviewContentType { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for WindmillFilePreviewContentType { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WindmillLargeFile { + pub s3: String, + } + impl From<&WindmillLargeFile> for WindmillLargeFile { + fn from(value: &WindmillLargeFile) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkerPing { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub custom_tags: Vec, + pub ip: String, + pub jobs_executed: i64, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_job_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_job_workspace_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub last_ping: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub memory: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub memory_usage: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub occupancy_rate: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub occupancy_rate_15s: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub occupancy_rate_30m: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub occupancy_rate_5m: Option, + pub started_at: chrono::DateTime, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub vcpus: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub wm_memory_usage: Option, + pub wm_version: String, + pub worker: String, + pub worker_group: String, + pub worker_instance: String, + } + impl From<&WorkerPing> for WorkerPing { + fn from(value: &WorkerPing) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkflowStatus { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub duration_ms: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub scheduled_for: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub started_at: Option>, + } + impl From<&WorkflowStatus> for WorkflowStatus { + fn from(value: &WorkflowStatus) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkflowStatusRecord( + pub std::collections::HashMap, + ); + impl std::ops::Deref for WorkflowStatusRecord { + type Target = std::collections::HashMap; + fn deref(&self) -> &std::collections::HashMap { + &self.0 + } + } + impl From + for std::collections::HashMap { + fn from(value: WorkflowStatusRecord) -> Self { + value.0 + } + } + impl From<&WorkflowStatusRecord> for WorkflowStatusRecord { + fn from(value: &WorkflowStatusRecord) -> Self { + value.clone() + } + } + impl From> + for WorkflowStatusRecord { + fn from(value: std::collections::HashMap) -> Self { + Self(value) + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkflowTask { + pub args: ScriptArgs, + } + impl From<&WorkflowTask> for WorkflowTask { + fn from(value: &WorkflowTask) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Workspace { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub color: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub domain: Option, + pub id: String, + pub name: String, + pub owner: String, + } + impl From<&Workspace> for Workspace { + fn from(value: &Workspace) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkspaceDefaultScripts { + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub default_script_content: std::collections::HashMap, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub hidden: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub order: Vec, + } + impl From<&WorkspaceDefaultScripts> for WorkspaceDefaultScripts { + fn from(value: &WorkspaceDefaultScripts) -> Self { + value.clone() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkspaceDeployUiSettings { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub include_path: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub include_type: Vec, + } + impl From<&WorkspaceDeployUiSettings> for WorkspaceDeployUiSettings { + fn from(value: &WorkspaceDeployUiSettings) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum WorkspaceDeployUiSettingsIncludeTypeItem { + #[serde(rename = "script")] + Script, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "app")] + App, + #[serde(rename = "resource")] + Resource, + #[serde(rename = "variable")] + Variable, + #[serde(rename = "secret")] + Secret, + #[serde(rename = "trigger")] + Trigger, + } + impl From<&WorkspaceDeployUiSettingsIncludeTypeItem> + for WorkspaceDeployUiSettingsIncludeTypeItem { + fn from(value: &WorkspaceDeployUiSettingsIncludeTypeItem) -> Self { + value.clone() + } + } + impl ToString for WorkspaceDeployUiSettingsIncludeTypeItem { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Flow => "flow".to_string(), + Self::App => "app".to_string(), + Self::Resource => "resource".to_string(), + Self::Variable => "variable".to_string(), + Self::Secret => "secret".to_string(), + Self::Trigger => "trigger".to_string(), + } + } + } + impl std::str::FromStr for WorkspaceDeployUiSettingsIncludeTypeItem { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "flow" => Ok(Self::Flow), + "app" => Ok(Self::App), + "resource" => Ok(Self::Resource), + "variable" => Ok(Self::Variable), + "secret" => Ok(Self::Secret), + "trigger" => Ok(Self::Trigger), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for WorkspaceDeployUiSettingsIncludeTypeItem { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for WorkspaceDeployUiSettingsIncludeTypeItem { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for WorkspaceDeployUiSettingsIncludeTypeItem { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkspaceGitSyncSettings { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub include_path: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub include_type: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub repositories: Vec, + } + impl From<&WorkspaceGitSyncSettings> for WorkspaceGitSyncSettings { + fn from(value: &WorkspaceGitSyncSettings) -> Self { + value.clone() + } + } + #[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize + )] + pub enum WorkspaceGitSyncSettingsIncludeTypeItem { + #[serde(rename = "script")] + Script, + #[serde(rename = "flow")] + Flow, + #[serde(rename = "app")] + App, + #[serde(rename = "folder")] + Folder, + #[serde(rename = "resource")] + Resource, + #[serde(rename = "variable")] + Variable, + #[serde(rename = "secret")] + Secret, + #[serde(rename = "resourcetype")] + Resourcetype, + #[serde(rename = "schedule")] + Schedule, + #[serde(rename = "user")] + User, + #[serde(rename = "group")] + Group, + } + impl From<&WorkspaceGitSyncSettingsIncludeTypeItem> + for WorkspaceGitSyncSettingsIncludeTypeItem { + fn from(value: &WorkspaceGitSyncSettingsIncludeTypeItem) -> Self { + value.clone() + } + } + impl ToString for WorkspaceGitSyncSettingsIncludeTypeItem { + fn to_string(&self) -> String { + match *self { + Self::Script => "script".to_string(), + Self::Flow => "flow".to_string(), + Self::App => "app".to_string(), + Self::Folder => "folder".to_string(), + Self::Resource => "resource".to_string(), + Self::Variable => "variable".to_string(), + Self::Secret => "secret".to_string(), + Self::Resourcetype => "resourcetype".to_string(), + Self::Schedule => "schedule".to_string(), + Self::User => "user".to_string(), + Self::Group => "group".to_string(), + } + } + } + impl std::str::FromStr for WorkspaceGitSyncSettingsIncludeTypeItem { + type Err = &'static str; + fn from_str(value: &str) -> Result { + match value { + "script" => Ok(Self::Script), + "flow" => Ok(Self::Flow), + "app" => Ok(Self::App), + "folder" => Ok(Self::Folder), + "resource" => Ok(Self::Resource), + "variable" => Ok(Self::Variable), + "secret" => Ok(Self::Secret), + "resourcetype" => Ok(Self::Resourcetype), + "schedule" => Ok(Self::Schedule), + "user" => Ok(Self::User), + "group" => Ok(Self::Group), + _ => Err("invalid value"), + } + } + } + impl std::convert::TryFrom<&str> for WorkspaceGitSyncSettingsIncludeTypeItem { + type Error = &'static str; + fn try_from(value: &str) -> Result { + value.parse() + } + } + impl std::convert::TryFrom<&String> for WorkspaceGitSyncSettingsIncludeTypeItem { + type Error = &'static str; + fn try_from(value: &String) -> Result { + value.parse() + } + } + impl std::convert::TryFrom for WorkspaceGitSyncSettingsIncludeTypeItem { + type Error = &'static str; + fn try_from(value: String) -> Result { + value.parse() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WorkspaceInvite { + pub email: String, + pub is_admin: bool, + pub operator: bool, + pub workspace_id: String, + } + impl From<&WorkspaceInvite> for WorkspaceInvite { + fn from(value: &WorkspaceInvite) -> Self { + value.clone() + } + } +} +#[derive(Clone, Debug)] +/**Client for Windmill API + +Version: 1.478.1*/ +pub struct Client { + pub(crate) baseurl: String, + pub(crate) client: reqwest::Client, +} +impl Client { + /// Create a new client. + /// + /// `baseurl` is the base URL provided to the internal + /// `reqwest::Client`, and should include a scheme and hostname, + /// as well as port and a path stem if applicable. + pub fn new(baseurl: &str) -> Self { + #[cfg(not(target_arch = "wasm32"))] + let client = { + let dur = std::time::Duration::from_secs(15); + reqwest::ClientBuilder::new().connect_timeout(dur).timeout(dur) + }; + #[cfg(target_arch = "wasm32")] + let client = reqwest::ClientBuilder::new(); + Self::new_with_client(baseurl, client.build().unwrap()) + } + /// Construct a new client with an existing `reqwest::Client`, + /// allowing more control over its configuration. + /// + /// `baseurl` is the base URL provided to the internal + /// `reqwest::Client`, and should include a scheme and hostname, + /// as well as port and a path stem if applicable. + pub fn new_with_client(baseurl: &str, client: reqwest::Client) -> Self { + Self { + baseurl: baseurl.to_string(), + client, + } + } + /// Get the base URL to which requests are made. + pub fn baseurl(&self) -> &String { + &self.baseurl + } + /// Get the internal `reqwest::Client` used to make requests. + pub fn client(&self) -> &reqwest::Client { + &self.client + } + /// Get the version of this API. + /// + /// This string is pulled directly from the source OpenAPI + /// document and may be in any format the API selects. + pub fn api_version(&self) -> &'static str { + "1.478.1" + } +} +impl Client { + /**list all workspaces visible to me + +Sends a `GET` request to `/workspaces/list` + +*/ + pub async fn list_workspaces<'a>( + &'a self, + ) -> Result>, Error<()>> { + let url = format!("{}/workspaces/list", self.baseurl,); + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create script + +Sends a `POST` request to `/w/{workspace}/scripts/create` + +Arguments: +- `workspace` +- `body`: Partially filled script +*/ + pub async fn create_script<'a>( + &'a self, + workspace: &'a str, + body: &'a types::NewScript, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/scripts/create", self.baseurl, encode_path(& workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**get flow by path + +Sends a `GET` request to `/w/{workspace}/flows/get/{path}` + +*/ + pub async fn get_flow_by_path<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + with_starred_info: Option, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/get/{}", self.baseurl, encode_path(& workspace.to_string()), + encode_path(& path.to_string()), + ); + let mut query = Vec::with_capacity(1usize); + if let Some(v) = &with_starred_info { + query.push(("with_starred_info", v.to_string())); + } + let request = self + .client + .get(url) + .header( + reqwest::header::ACCEPT, + reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&query) + .build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create flow + +Sends a `POST` request to `/w/{workspace}/flows/create` + +Arguments: +- `workspace` +- `body`: Partially filled flow +*/ + pub async fn create_flow<'a>( + &'a self, + workspace: &'a str, + body: &'a types::CreateFlowBody, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/flows/create", self.baseurl, encode_path(& workspace.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**create schedule + +Sends a `POST` request to `/w/{workspace}/schedules/create` + +Arguments: +- `workspace` +- `body`: new schedule +*/ + pub async fn create_schedule<'a>( + &'a self, + workspace: &'a str, + body: &'a types::NewSchedule, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/schedules/create", self.baseurl, encode_path(& workspace + .to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } + /**update schedule + +Sends a `POST` request to `/w/{workspace}/schedules/update/{path}` + +Arguments: +- `workspace` +- `path` +- `body`: updated schedule +*/ + pub async fn update_schedule<'a>( + &'a self, + workspace: &'a str, + path: &'a str, + body: &'a types::EditSchedule, + ) -> Result, Error<()>> { + let url = format!( + "{}/w/{}/schedules/update/{}", self.baseurl, encode_path(& workspace + .to_string()), encode_path(& path.to_string()), + ); + let request = self.client.post(url).json(&body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => Ok(ResponseValue::stream(response)), + _ => Err(Error::UnexpectedResponse(response)), + } + } +} +pub mod prelude { + pub use super::Client; +} diff --git a/backend/windmill-api-client/src/lib.rs b/backend/windmill-api-client/src/lib.rs index 0b7222c5a5..3b879e0d98 100644 --- a/backend/windmill-api-client/src/lib.rs +++ b/backend/windmill-api-client/src/lib.rs @@ -1,4 +1,4 @@ -include!(concat!(env!("OUT_DIR"), "/codegen.rs")); +include!("./codegen.rs"); pub fn create_client(base_url: &str, token: String) -> Client { let mut val = reqwest::header::HeaderValue::from_str(&format!("Bearer {token}")) diff --git a/backend/windmill-api/Cargo.toml b/backend/windmill-api/Cargo.toml index cbecf865de..233fbeec54 100644 --- a/backend/windmill-api/Cargo.toml +++ b/backend/windmill-api/Cargo.toml @@ -10,13 +10,14 @@ path = "src/lib.rs" [features] default = [] -enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise"] -stripe = ["dep:async-stripe"] -enterprise_saml = ["dep:samael"] +enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker/enterprise"] +stripe = [] +agent_worker_server = [] +enterprise_saml = ["dep:samael", "dep:libxml"] benchmark = [] embedding = ["dep:tinyvector", "dep:hf-hub", "dep:tokenizers", "dep:candle-core", "dep:candle-transformers", "dep:candle-nn"] -parquet = ["dep:datafusion", "dep:object_store", "dep:url", "windmill-common/parquet"] -prometheus = ["windmill-common/prometheus", "windmill-queue/prometheus", "dep:prometheus"] +parquet = ["dep:datafusion", "dep:object_store", "dep:url", "windmill-common/parquet", "windmill-worker/parquet"] +prometheus = ["windmill-common/prometheus", "windmill-queue/prometheus", "dep:prometheus", "windmill-worker/prometheus"] openidconnect = ["dep:openidconnect"] tantivy = ["dep:windmill-indexer"] kafka = ["dep:rdkafka"] @@ -26,19 +27,30 @@ smtp = ["dep:mail-parser", "dep:openssl", "windmill-common/smtp"] license = ["dep:rsa"] zip = ["dep:async_zip"] oauth2 = ["dep:async-oauth2"] -http_trigger = ["dep:matchit"] +http_trigger = ["dep:matchit", "dep:thiserror", "dep:sha1", "dep:constant_time_eq"] static_frontend = ["dep:rust-embed"] postgres_trigger = ["dep:rust-postgres", "dep:pg_escape", "dep:byteorder", "dep:thiserror", "dep:rust_decimal", "dep:rust-postgres-native-tls"] +mqtt_trigger = ["dep:thiserror", "dep:rumqttc"] +sqs_trigger = ["dep:aws-sdk-sqs", "dep:thiserror", "dep:aws-config"] +deno_core = ["dep:deno_core", "dep:deno_error"] +gcp_trigger = ["dep:thiserror", "dep:google-cloud-pubsub", "dep:google-cloud-googleapis", "dep:tonic"] +cloud = ["windmill-common/cloud"] +mcp = ["dep:rmcp"] [dependencies] +rmcp = { git = "https://github.com/windmill-labs/rust-sdk", features = ["transport-sse-server"], optional = true } windmill-queue.workspace = true windmill-common = { workspace = true, default-features = false } windmill-audit.workspace = true windmill-parser.workspace = true windmill-parser-ts.workspace = true +windmill-parser-py.workspace = true +windmill-parser-py-imports.workspace = true windmill-git-sync.workspace = true windmill-indexer = { workspace = true, optional = true } +windmill-worker.workspace = true tokio.workspace = true +tokio-stream.workspace = true anyhow.workspace = true argon2.workspace = true axum.workspace = true @@ -80,14 +92,16 @@ tokio-tar.workspace = true hmac.workspace = true cookie.workspace = true sha2.workspace = true +sha1 = { workspace = true, optional = true } +constant_time_eq = { workspace = true, optional = true } urlencoding.workspace = true -async-stripe = { workspace = true, optional = true } lazy_static.workspace = true prometheus = { workspace = true, optional = true } async_zip = { workspace = true, optional = true } regex.workspace = true bytes.workspace = true samael = { workspace = true, optional = true } +libxml = { workspace = true, optional = true } async-recursion.workspace = true rsa = { workspace = true, optional = true} uuid.workspace = true @@ -117,4 +131,16 @@ pg_escape = { workspace = true, optional = true } byteorder = { workspace = true, optional = true } thiserror = { workspace = true, optional = true } rust_decimal = { workspace = true, optional = true } -rust-postgres-native-tls = { workspace = true, optional = true} \ No newline at end of file +rust-postgres-native-tls = { workspace = true, optional = true} +rumqttc = { workspace = true, optional = true } +aws-sdk-sqs = { workspace = true, optional = true } +aws-config = { workspace = true, optional = true } +aws-sdk-sts = { workspace = true, optional = true } +google-cloud-pubsub = { workspace = true, optional = true } +google-cloud-googleapis = { workspace = true , optional = true } +tonic = { workspace = true, optional = true } +deno_error = { workspace = true, optional = true } +deno_core = { workspace = true, optional = true } + +[build-dependencies] +deno_core = { workspace = true, optional = true } \ No newline at end of file diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 47f4d9eaa3..06ecb4f3b4 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.458.1 + version: 1.488.0 title: Windmill API contact: @@ -126,6 +126,11 @@ paths: type: string - $ref: "#/components/parameters/ResourceName" - $ref: "#/components/parameters/ActionKind" + - name: all_workspaces + in: query + description: get audit logs for all workspaces + schema: + type: boolean responses: "200": @@ -568,6 +573,20 @@ paths: schema: type: string + /github_app/connected_repositories: + get: + summary: get connected repositories + operationId: getGlobalConnectedRepositories + tags: + - git_sync + responses: + "200": + description: connected repositories + content: + application/json: + schema: + $ref: "#/components/schemas/GithubInstallations" + /workspaces/list: get: summary: list all workspaces visible to me @@ -1281,6 +1300,144 @@ paths: schema: $ref: "#/components/schemas/User" + /w/{workspace}/github_app/token: + post: + summary: get github app token + operationId: getGithubAppToken + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: jwt job token + required: true + content: + application/json: + schema: + type: object + properties: + job_token: + type: string + required: + - job_token + responses: + "200": + description: github app token + content: + application/json: + schema: + type: object + properties: + token: + type: string + required: + - token + + /w/{workspace}/github_app/install_from_workspace: + post: + tags: + - Git Sync + summary: Install a GitHub installation from another workspace + operationId: installFromWorkspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + source_workspace_id: + type: string + description: The ID of the workspace containing the installation to copy + installation_id: + type: number + description: The ID of the GitHub installation to copy + required: + - source_workspace_id + - installation_id + responses: + "200": + description: Installation successfully copied + + /w/{workspace}/github_app/installation/{installation_id}: + delete: + summary: Delete a GitHub installation from a workspace + operationId: deleteFromWorkspace + description: Removes a GitHub installation from the specified workspace. Requires admin privileges. + tags: + - Git Sync + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: installation_id + in: path + required: true + schema: + type: integer + format: int64 + description: The ID of the GitHub installation to delete + responses: + "200": + description: Installation successfully deleted + + /w/{workspace}/github_app/export/{installationId}: + get: + summary: Export GitHub installation JWT token + description: Exports the JWT token for a specific GitHub installation in the workspace + operationId: exportInstallation + tags: + - Git Sync + parameters: + - name: workspace + in: path + required: true + schema: + type: string + - name: installationId + in: path + required: true + schema: + type: integer + responses: + "200": + description: Successfully exported the JWT token + content: + application/json: + schema: + type: object + properties: + jwt_token: + type: string + + /w/{workspace}/github_app/import: + post: + summary: Import GitHub installation from JWT token + description: Imports a GitHub installation from a JWT token exported from another instance + operationId: importInstallation + tags: + - Git Sync + parameters: + - name: workspace + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - jwt_token + properties: + jwt_token: + type: string + responses: + "200": + description: Successfully imported the installation + /users/accept_invite: post: summary: accept invite to workspace @@ -1634,7 +1791,7 @@ paths: schema: $ref: "#/components/schemas/OperatorSettings" responses: - '200': + "200": description: Operator settings updated successfully content: text/plain: @@ -1742,22 +1899,14 @@ paths: type: boolean plan: type: string - automatic_billing: - type: boolean customer_id: type: string webhook: type: string deploy_to: type: string - ai_resource: - $ref: "#/components/schemas/AIResource" - code_completion_model: - type: string - ai_models: - type: array - items: - type: string + ai_config: + $ref: "#/components/schemas/AIConfig" error_handler: type: string error_handler_extra_args: @@ -1781,8 +1930,6 @@ paths: operator_settings: $ref: "#/components/schemas/OperatorSettings" required: - - ai_models - - automatic_billing - error_handler_muted_on_cancel /w/{workspace}/workspaces/get_deploy_to: @@ -1843,48 +1990,14 @@ paths: type: boolean usage: type: number - seats: - type: number - automatic_billing: - type: boolean owner: type: string + status: + type: string required: - premium - - automatic_billing - owner - /w/{workspace}/workspaces/set_automatic_billing: - post: - summary: set automatic billing - operationId: setAutomaticBilling - tags: - - workspace - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: automatic billing - required: true - content: - application/json: - schema: - type: object - properties: - automatic_billing: - type: boolean - seats: - type: number - required: - - automatic_billing - responses: - "200": - description: status - content: - text/plain: - schema: - type: string - - /w/{workspace}/workspaces/threshold_alert: get: summary: get threshold alert info @@ -2008,7 +2121,7 @@ paths: type: string team_id: type: string - + /w/{workspace}/workspaces/available_teams_channels: get: summary: list available teams channels @@ -2097,7 +2210,41 @@ paths: properties: job_uuid: type: string - + + /w/{workspace}/workspaces/run_teams_message_test_job: + post: + summary: run a job that sends a message to Teams + operationId: runTeamsMessageTestJob + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: path to hub script to run and its corresponding args + required: true + content: + application/json: + schema: + type: object + properties: + hub_script_path: + type: string + channel: + type: string + test_msg: + type: string + + responses: + "200": + description: status + content: + text/json: + schema: + type: object + properties: + job_uuid: + type: string + /w/{workspace}/workspaces/run_teams_message_test_job: post: summary: run a job that sends a message to Teams @@ -2230,18 +2377,7 @@ paths: content: application/json: schema: - type: object - required: - - ai_models - properties: - ai_resource: - $ref: "#/components/schemas/AIResource" - code_completion_model: - type: string - ai_models: - type: array - items: - type: string + $ref: "#/components/schemas/AIConfig" responses: "200": description: status @@ -2263,23 +2399,9 @@ paths: "200": description: status content: - text/plain: + application/json: schema: - type: object - properties: - ai_provider: - $ref: "#/components/schemas/AIProvider" - exists_ai_resource: - type: boolean - code_completion_model: - type: string - ai_models: - type: array - items: - type: string - required: - - exists_ai_resource - - ai_models + $ref: "#/components/schemas/AIConfig" /w/{workspace}/workspaces/edit_error_handler: post: @@ -2611,12 +2733,21 @@ paths: type: boolean postgres_used: type: boolean + mqtt_used: + type: boolean + gcp_used: + type: boolean + sqs_used: + type: boolean required: - http_routes_used - websocket_used - kafka_used - nats_used - postgres_used + - mqtt_used + - gcp_used + - sqs_used /w/{workspace}/users/list: get: summary: list users @@ -3432,7 +3563,7 @@ paths: type: array items: type: string - + /teams/sync: post: operationId: syncTeams @@ -3440,14 +3571,49 @@ paths: tags: - teams responses: - '200': + "200": description: Teams information successfully synchronized content: application/json: schema: type: array items: - $ref: '#/components/schemas/TeamInfo' + $ref: "#/components/schemas/TeamInfo" + + /teams/activities: + post: + summary: send update to Microsoft Teams activity + description: Respond to a Microsoft Teams activity after a workspace command is run + operationId: sendMessageToConversation + tags: + - teams + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - conversation_id + - text + properties: + conversation_id: + type: string + description: The ID of the Teams conversation/activity + success: + type: boolean + description: Used for styling the card conditionally + default: true + text: + type: string + description: The message text to be sent in the Teams card + card_block: + type: object + description: The card block to be sent in the Teams card + + responses: + "200": + description: Activity processed successfully /teams/activities: post: @@ -4331,7 +4497,7 @@ paths: type: string - name: last_parent_hash description: | - mask to filter scripts whom last parent in the chain has exact hash. + mask to filter scripts whom last parent in the chain has exact hash. Beware that each script stores only a limited number of parents. Hence the last parent hash for a script is not necessarily its top-most parent. To find the top-most parent you will have to jump from last to last hash @@ -4352,7 +4518,7 @@ paths: (default false) show only the archived files. when multiple archived hash share the same path, only the ones with the latest create_at - are + are ed. in: query schema: @@ -4657,6 +4823,11 @@ paths: parameters: - $ref: "#/components/parameters/WorkspaceId" - $ref: "#/components/parameters/ScriptPath" + - name: keep_captures + description: keep captures + in: query + schema: + type: boolean responses: "200": description: script path @@ -4758,6 +4929,25 @@ paths: items: $ref: "#/components/schemas/ScriptHistory" + /w/{workspace}/scripts/list_paths_from_workspace_runnable/{path}: + get: + summary: list script paths using provided script as a relative import + operationId: listScriptPathsFromWorkspaceRunnable + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: list of script paths + content: + application/json: + schema: + type: array + items: + type: string + /w/{workspace}/scripts/get_latest_version/{path}: get: summary: get scripts's latest version (hash) @@ -4824,8 +5014,7 @@ paths: /scripts_u/tokened_raw/{workspace}/{token}/{path}: get: - summary: - raw script by path with a token (mostly used by lsp to be used with + summary: raw script by path with a token (mostly used by lsp to be used with import maps to resolve scripts) operationId: rawScriptByPathTokened tags: @@ -4919,6 +5108,63 @@ paths: lock_error_logs: type: string + /w/{workspace}/jobs/list_selected_job_groups: + # We use post because sending a huge array as a query param can produce + # URLs that may be too long + post: + summary: list selected jobs script/flow schemas grouped by (kind, path) + operationId: listSelectedJobGroups + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: script args + required: true + content: + application/json: + schema: + type: array + items: + type: string + format: uuid + responses: + "200": + description: result + content: + text/plain: + schema: + type: array + items: + type: object + properties: + kind: + type: string + enum: ["script", "flow"] + script_path: + type: string + latest_schema: + type: object + schemas: + type: array + items: + type: object + properties: + schema: + type: object + script_hash: + type: string + job_ids: + type: array + items: + type: string + required: [schema, script_hash, job_ids] + required: + - kind + - script_path + - latest_schema + - schemas + /w/{workspace}/jobs/run/p/{path}: post: summary: run script by path @@ -5284,6 +5530,26 @@ paths: schema: $ref: "#/components/schemas/FlowVersion" + /w/{workspace}/flows/list_paths_from_workspace_runnable/{runnable_kind}/{path}: + get: + summary: list flow paths from workspace runnable + operationId: listFlowPathsFromWorkspaceRunnable + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/RunnableKind" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: list of flow paths + content: + application/json: + schema: + type: array + items: + type: string + /w/{workspace}/flows/get/v/{version}/p/{path}: get: summary: get flow version @@ -5363,6 +5629,26 @@ paths: schema: $ref: "#/components/schemas/Flow" + /w/{workspace}/flows/deployment_status/p/{path}: + get: + summary: get flow deployment status + operationId: getFlowDeploymentStatus + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: flow status + content: + application/json: + schema: + type: object + properties: + lock_error_logs: + type: string + /w/{workspace}/flows/get_triggers_count/{path}: get: summary: get triggers count of flow @@ -5561,6 +5847,11 @@ paths: parameters: - $ref: "#/components/parameters/WorkspaceId" - $ref: "#/components/parameters/ScriptPath" + - name: keep_captures + description: keep captures + in: query + schema: + type: boolean responses: "200": description: flow delete @@ -5763,6 +6054,55 @@ paths: schema: type: string + /w/{workspace}/apps/create_raw: + post: + summary: create app raw + operationId: createAppRaw + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new app + required: true + content: + multipart/form-data: + schema: + type: object + properties: + app: + type: object + properties: + path: + type: string + value: {} + summary: + type: string + policy: + $ref: "#/components/schemas/Policy" + draft_only: + type: boolean + deployment_message: + type: string + custom_path: + type: string + required: + - path + - value + - summary + - policy + js: + type: string + css: + type: string + responses: + "201": + description: app created + content: + text/plain: + schema: + type: string + /w/{workspace}/apps/exists/{path}: get: summary: does an app exisst at path @@ -5872,6 +6212,26 @@ paths: schema: $ref: "#/components/schemas/AppHistory" + /w/{workspace}/apps/list_paths_from_workspace_runnable/{runnable_kind}/{path}: + get: + summary: list app paths from workspace runnable + operationId: listAppPathsFromWorkspaceRunnable + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/RunnableKind" + - $ref: "#/components/parameters/ScriptPath" + responses: + "200": + description: list of app paths + content: + application/json: + schema: + type: array + items: + type: string + /w/{workspace}/apps/history_update/a/{id}/v/{version}: post: summary: update app history @@ -6102,6 +6462,49 @@ paths: schema: type: string + /w/{workspace}/apps/update_raw/{path}: + post: + summary: update app + operationId: updateAppRaw + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/ScriptPath" + requestBody: + description: update app + required: true + content: + multipart/form-data: + schema: + type: object + properties: + app: + type: object + properties: + path: + type: string + summary: + type: string + value: {} + policy: + $ref: "#/components/schemas/Policy" + deployment_message: + type: string + custom_path: + type: string + js: + type: string + css: + type: string + responses: + "200": + description: app updated + content: + text/plain: + schema: + type: string + /w/{workspace}/apps/custom_path_exists/{custom_path}: get: summary: check if custom path exists @@ -6119,6 +6522,38 @@ paths: schema: type: boolean + /w/{workspace}/apps/sign_s3_objects: + post: + summary: sign s3 objects, to be used by anonymous users in public apps + operationId: signS3Objects + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: s3 objects to sign + required: true + content: + application/json: + schema: + type: object + properties: + s3_objects: + type: array + items: + $ref: "#/components/schemas/S3Object" + required: + - s3_objects + responses: + "200": + description: signed s3 objects + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/S3Object" + /w/{workspace}/apps_u/execute_component/{path}: post: summary: executeComponent @@ -6183,6 +6618,92 @@ paths: schema: type: string + /w/{workspace}/apps_u/upload_s3_file/{path}: + post: + summary: upload s3 file from app + operationId: uploadS3FileFromApp + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + - name: file_key + in: query + required: false + schema: + type: string + - name: file_extension + in: query + required: false + schema: + type: string + - name: s3_resource_path + in: query + required: false + schema: + type: string + - name: resource_type + in: query + required: false + schema: + type: string + - name: storage + in: query + schema: + type: string + - name: content_type + in: query + schema: + type: string + - name: content_disposition + in: query + schema: + type: string + requestBody: + description: File content + required: true + content: + application/octet-stream: + schema: + type: string + format: binary + responses: + "200": + description: file uploaded + content: + application/json: + schema: + type: object + properties: + file_key: + type: string + delete_token: + type: string + required: + - file_key + - delete_token + + /w/{workspace}/apps_u/delete_s3_file: + delete: + summary: delete s3 file from app + operationId: deleteS3FileFromApp + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: delete_token + in: query + required: true + schema: + type: string + responses: + "200": + description: file deleted + content: + text/plain: + schema: + type: string + /w/{workspace}/jobs/run/f/{path}: post: summary: run flow by path @@ -6217,7 +6738,6 @@ paths: in: query schema: type: boolean - requestBody: description: flow args required: true @@ -6225,7 +6745,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ScriptArgs" - responses: "201": description: job created @@ -6235,6 +6754,62 @@ paths: type: string format: uuid + /w/{workspace}/jobs/run/batch_rerun_jobs: + post: + summary: re-run multiple jobs + operationId: batchReRunJobs + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: list of job ids to re run and arg tranforms + required: true + content: + application/json: + schema: + type: object + required: [job_ids, script_options_by_path, flow_options_by_path] + properties: + job_ids: + type: array + items: + type: string + script_options_by_path: + type: object + additionalProperties: + type: object + properties: + input_transforms: + type: object + additionalProperties: + $ref: "#/components/schemas/InputTransform" + use_latest_version: + type: boolean + flow_options_by_path: + type: object + additionalProperties: + type: object + properties: + input_transforms: + type: object + additionalProperties: + $ref: "#/components/schemas/InputTransform" + use_latest_version: + type: boolean + responses: + "201": + description: stream of created job uuids separated by \n. Lines may start with 'Error:' + example: | + a1a74c0d-708e-4539-9768-e8b3d37996bd + f0949132-5b30-48fe-bac8-873f047df810 + Error: Could not re-run 0b885808-ae89-4458-af95-c1ca3a13b0a5 + 52b9c01d-1125-4bbb-8bee-d41f26b70066 + content: + text/event-stream: + schema: + type: string + /w/{workspace}/jobs/restart/f/{id}/from/{step_id}/{branch_or_iteration_n}: post: summary: restart a completed flow at a given step @@ -6251,8 +6826,7 @@ paths: schema: type: string - name: branch_or_iteration_n - description: - for branchall or loop, the iteration at which the flow should + description: for branchall or loop, the iteration at which the flow should restart required: true in: path @@ -6501,6 +7075,7 @@ paths: - $ref: "#/components/parameters/OrderDesc" - $ref: "#/components/parameters/CreatedBy" - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/Worker" - $ref: "#/components/parameters/ScriptExactPath" - $ref: "#/components/parameters/ScriptStartPath" - $ref: "#/components/parameters/SchedulePath" @@ -6514,6 +7089,7 @@ paths: - $ref: "#/components/parameters/Running" - $ref: "#/components/parameters/ArgsFilter" - $ref: "#/components/parameters/ResultFilter" + - $ref: "#/components/parameters/AllowWildcards" - $ref: "#/components/parameters/Tag" - $ref: "#/components/parameters/Page" - $ref: "#/components/parameters/PerPage" @@ -6618,10 +7194,82 @@ paths: schema: type: integer - /w/{workspace}/jobs/queue/list_filtered_uuids: + /w/{workspace}/jobs/list_filtered_uuids: get: summary: get the ids of all jobs matching the given filters - operationId: listFilteredUuids + operationId: listFilteredJobsUuids + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/CreatedBy" + - $ref: "#/components/parameters/Label" + - $ref: "#/components/parameters/Worker" + - $ref: "#/components/parameters/ParentJob" + - $ref: "#/components/parameters/ScriptExactPath" + - $ref: "#/components/parameters/ScriptStartPath" + - $ref: "#/components/parameters/SchedulePath" + - $ref: "#/components/parameters/ScriptExactHash" + - $ref: "#/components/parameters/StartedBefore" + - $ref: "#/components/parameters/StartedAfter" + - $ref: "#/components/parameters/CreatedBefore" + - $ref: "#/components/parameters/CreatedAfter" + - $ref: "#/components/parameters/CreatedOrStartedBefore" + - $ref: "#/components/parameters/Running" + - $ref: "#/components/parameters/ScheduledForBeforeNow" + - $ref: "#/components/parameters/CreatedOrStartedAfter" + - $ref: "#/components/parameters/CreatedOrStartedAfterCompletedJob" + - $ref: "#/components/parameters/JobKinds" + - $ref: "#/components/parameters/Suspended" + - $ref: "#/components/parameters/ArgsFilter" + - $ref: "#/components/parameters/Tag" + - $ref: "#/components/parameters/ResultFilter" + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: is_skipped + description: is the job skipped + in: query + schema: + type: boolean + - name: is_flow_step + description: is the job a flow step + in: query + schema: + type: boolean + - name: has_null_parent + description: has null parent + in: query + schema: + type: boolean + - name: success + description: filter on successful jobs + in: query + schema: + type: boolean + - name: all_workspaces + description: get jobs from all workspaces (only valid if request come from the `admins` workspace) + in: query + schema: + type: boolean + - name: is_not_schedule + description: is not a scheduled job + in: query + schema: + type: boolean + responses: + "200": + description: uuids of jobs + content: + application/json: + schema: + type: array + items: + type: string + + /w/{workspace}/jobs/queue/list_filtered_uuids: + get: + summary: get the ids of all queued jobs matching the given filters + operationId: listFilteredQueueUuids tags: - job parameters: @@ -6642,6 +7290,7 @@ paths: - $ref: "#/components/parameters/Running" - $ref: "#/components/parameters/ArgsFilter" - $ref: "#/components/parameters/ResultFilter" + - $ref: "#/components/parameters/AllowWildcards" - $ref: "#/components/parameters/Tag" - $ref: "#/components/parameters/Page" - $ref: "#/components/parameters/PerPage" @@ -6708,6 +7357,7 @@ paths: - $ref: "#/components/parameters/OrderDesc" - $ref: "#/components/parameters/CreatedBy" - $ref: "#/components/parameters/Label" + - $ref: "#/components/parameters/Worker" - $ref: "#/components/parameters/ParentJob" - $ref: "#/components/parameters/ScriptExactPath" - $ref: "#/components/parameters/ScriptStartPath" @@ -6719,6 +7369,7 @@ paths: - $ref: "#/components/parameters/JobKinds" - $ref: "#/components/parameters/ArgsFilter" - $ref: "#/components/parameters/ResultFilter" + - $ref: "#/components/parameters/AllowWildcards" - $ref: "#/components/parameters/Tag" - $ref: "#/components/parameters/Page" - $ref: "#/components/parameters/PerPage" @@ -6762,6 +7413,7 @@ paths: - $ref: "#/components/parameters/WorkspaceId" - $ref: "#/components/parameters/CreatedBy" - $ref: "#/components/parameters/Label" + - $ref: "#/components/parameters/Worker" - $ref: "#/components/parameters/ParentJob" - $ref: "#/components/parameters/ScriptExactPath" - $ref: "#/components/parameters/ScriptStartPath" @@ -6781,6 +7433,7 @@ paths: - $ref: "#/components/parameters/ArgsFilter" - $ref: "#/components/parameters/Tag" - $ref: "#/components/parameters/ResultFilter" + - $ref: "#/components/parameters/AllowWildcards" - $ref: "#/components/parameters/Page" - $ref: "#/components/parameters/PerPage" - name: is_skipped @@ -8024,6 +8677,8 @@ paths: enum: ["get", "post", "put", "delete", "patch"] trigger_path: type: string + workspaced_route: + type: boolean required: - route_path - http_method @@ -8470,7 +9125,7 @@ paths: summary: delete nats trigger operationId: deleteNatsTrigger tags: - - nats_trigger + - nats_trigger parameters: - $ref: "#/components/parameters/WorkspaceId" - $ref: "#/components/parameters/Path" @@ -8499,7 +9154,6 @@ paths: schema: $ref: "#/components/schemas/NatsTrigger" - /w/{workspace}/nats_triggers/list: get: summary: list nats triggers @@ -8534,7 +9188,6 @@ paths: items: $ref: "#/components/schemas/NatsTrigger" - /w/{workspace}/nats_triggers/exists/{path}: get: summary: does nats trigger exists @@ -8581,7 +9234,6 @@ paths: schema: type: string - /w/{workspace}/nats_triggers/test: post: summary: test NATS connection @@ -8610,6 +9262,643 @@ paths: schema: type: string + /w/{workspace}/sqs_triggers/create: + post: + summary: create sqs trigger + operationId: createSqsTrigger + tags: + - sqs_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new sqs trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewSqsTrigger" + responses: + "201": + description: sqs trigger created + content: + text/plain: + schema: + type: string + + /w/{workspace}/sqs_triggers/update/{path}: + post: + summary: update sqs trigger + operationId: updateSqsTrigger + tags: + - sqs_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EditSqsTrigger" + responses: + "200": + description: sqs trigger updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/sqs_triggers/delete/{path}: + delete: + summary: delete sqs trigger + operationId: deleteSqsTrigger + tags: + - sqs_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: sqs trigger deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/sqs_triggers/get/{path}: + get: + summary: get sqs trigger + operationId: getSqsTrigger + tags: + - sqs_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: sqs trigger deleted + content: + application/json: + schema: + $ref: "#/components/schemas/SqsTrigger" + + /w/{workspace}/sqs_triggers/list: + get: + summary: list sqs triggers + operationId: listSqsTriggers + tags: + - sqs_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + required: true + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: path + description: filter by path + in: query + schema: + type: string + - name: is_flow + in: query + schema: + type: boolean + - name: path_start + in: query + schema: + type: string + responses: + "200": + description: sqs trigger list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/SqsTrigger" + + /w/{workspace}/sqs_triggers/exists/{path}: + get: + summary: does sqs trigger exists + operationId: existsSqsTrigger + tags: + - sqs_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: sqs trigger exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/sqs_triggers/setenabled/{path}: + post: + summary: set enabled sqs trigger + operationId: setSqsTriggerEnabled + tags: + - sqs_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated sqs trigger enable + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + required: + - enabled + responses: + "200": + description: sqs trigger enabled set + content: + text/plain: + schema: + type: string + + /w/{workspace}/sqs_triggers/test: + post: + summary: test sqs connection + operationId: testSqsConnection + tags: + - sqs_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: test sqs connection + required: true + content: + application/json: + schema: + type: object + properties: + connection: + type: object + required: + - connection + responses: + "200": + description: successfuly connected to sqs + content: + text/plain: + schema: + type: string + + /w/{workspace}/mqtt_triggers/create: + post: + summary: create mqtt trigger + operationId: createMqttTrigger + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new mqtt trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewMqttTrigger" + responses: + "201": + description: mqtt trigger created + content: + text/plain: + schema: + type: string + + /w/{workspace}/mqtt_triggers/update/{path}: + post: + summary: update mqtt trigger + operationId: updateMqttTrigger + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EditMqttTrigger" + responses: + "200": + description: mqtt trigger updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/mqtt_triggers/delete/{path}: + delete: + summary: delete mqtt trigger + operationId: deleteMqttTrigger + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: mqtt trigger deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/mqtt_triggers/get/{path}: + get: + summary: get mqtt trigger + operationId: getMqttTrigger + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: mqtt trigger deleted + content: + application/json: + schema: + $ref: "#/components/schemas/MqttTrigger" + + /w/{workspace}/mqtt_triggers/list: + get: + summary: list mqtt triggers + operationId: listMqttTriggers + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + required: true + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: path + description: filter by path + in: query + schema: + type: string + - name: is_flow + in: query + schema: + type: boolean + - name: path_start + in: query + schema: + type: string + responses: + "200": + description: mqtt trigger list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/MqttTrigger" + + /w/{workspace}/mqtt_triggers/exists/{path}: + get: + summary: does mqtt trigger exists + operationId: existsMqttTrigger + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: mqtt trigger exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/mqtt_triggers/setenabled/{path}: + post: + summary: set enabled mqtt trigger + operationId: setMqttTriggerEnabled + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated mqtt trigger enable + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + required: + - enabled + responses: + "200": + description: mqtt trigger enabled set + content: + text/plain: + schema: + type: string + + /w/{workspace}/mqtt_triggers/test: + post: + summary: test mqtt connection + operationId: testMqttConnection + tags: + - mqtt_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: test mqtt connection + required: true + content: + application/json: + schema: + type: object + properties: + connection: + type: object + required: + - connection + responses: + "200": + description: successfully connected to mqtt + content: + text/plain: + schema: + type: string + + /w/{workspace}/gcp_triggers/create: + post: + summary: create gcp trigger + operationId: createGcpTrigger + tags: + - gcp_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: new gcp trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/GcpTriggerData" + responses: + "201": + description: gcp trigger created + content: + text/plain: + schema: + type: string + + /w/{workspace}/gcp_triggers/update/{path}: + post: + summary: update gcp trigger + operationId: updateGcpTrigger + tags: + - gcp_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated trigger + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/GcpTriggerData" + responses: + "200": + description: gcp trigger updated + content: + text/plain: + schema: + type: string + + /w/{workspace}/gcp_triggers/delete/{path}: + delete: + summary: delete gcp trigger + operationId: deleteGcpTrigger + tags: + - gcp_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: gcp trigger deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/gcp_triggers/get/{path}: + get: + summary: get gcp trigger + operationId: getGcpTrigger + tags: + - gcp_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: gcp trigger deleted + content: + application/json: + schema: + $ref: "#/components/schemas/GcpTrigger" + + /w/{workspace}/gcp_triggers/list: + get: + summary: list gcp triggers + operationId: listGcpTriggers + tags: + - gcp_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + required: true + - $ref: "#/components/parameters/Page" + - $ref: "#/components/parameters/PerPage" + - name: path + description: filter by path + in: query + schema: + type: string + - name: is_flow + in: query + schema: + type: boolean + - name: path_start + in: query + schema: + type: string + responses: + "200": + description: gcp trigger list + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/GcpTrigger" + + /w/{workspace}/gcp_triggers/exists/{path}: + get: + summary: does gcp trigger exists + operationId: existsGcpTrigger + tags: + - gcp_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: gcp trigger exists + content: + application/json: + schema: + type: boolean + + /w/{workspace}/gcp_triggers/setenabled/{path}: + post: + summary: set enabled gcp trigger + operationId: setGcpTriggerEnabled + tags: + - gcp_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: updated gcp trigger enable + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + required: + - enabled + responses: + "200": + description: gcp trigger enabled set + content: + text/plain: + schema: + type: string + + /w/{workspace}/gcp_triggers/test: + post: + summary: test gcp connection + operationId: testGcpConnection + tags: + - gcp_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: test gcp connection + required: true + content: + application/json: + schema: + type: object + properties: + connection: + type: object + required: + - connection + responses: + "200": + description: try to connect to a gcp broker + content: + text/plain: + schema: + type: string + + + /w/{workspace}/gcp_triggers/subscriptions/delete/{path}: + delete: + summary: delete gcp trigger + operationId: deleteGcpSubscription + tags: + - gcp_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: args to delete subscription from google cloud + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/DeleteGcpSubscription" + responses: + "200": + description: gcp trigger deleted + content: + text/plain: + schema: + type: string + + /w/{workspace}/gcp_triggers/topics/list/{path}: + get: + summary: list all topics of google cloud service + operationId: listGoogleTopics + tags: + - gcp_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + responses: + "200": + description: get all google topics + content: + application/json: + schema: + type: array + items: + type: string + + /w/{workspace}/gcp_triggers/subscriptions/list/{path}: + post: + summary: list all subscription of a give topic from google cloud service + operationId: listAllTGoogleTopicSubscriptions + tags: + - gcp_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" + requestBody: + description: args to get subscription's topic from google cloud + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/GetAllTopicSubscription" + responses: + "200": + description: get all google topic subscriptions name + content: + application/json: + schema: + type: array + items: + type: string + /w/{workspace}/postgres_triggers/is_valid_postgres_configuration/{path}: get: summary: check if postgres configuration is set to logical @@ -8617,8 +9906,8 @@ paths: tags: - postgres_trigger parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Path" responses: "200": description: boolean that indicates if postgres is set to logical level or not @@ -8688,7 +9977,7 @@ paths: /w/{workspace}/postgres_triggers/slot/create/{path}: post: - summary: create replication slot for postgres + summary: create replication slot for postgres operationId: createPostgresReplicationSlot tags: - postgres_trigger @@ -8821,7 +10110,6 @@ paths: schema: type: string - /w/{workspace}/postgres_triggers/publication/delete/{publication}/{path}: delete: summary: delete postgres publication @@ -9001,6 +10289,34 @@ paths: schema: type: string + /w/{workspace}/postgres_triggers/test: + post: + summary: test postgres connection + operationId: testPostgresConnection + tags: + - postgres_trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: test postgres connection + required: true + content: + application/json: + schema: + type: object + properties: + database: + type: string + required: + - database + responses: + "200": + description: successfuly connected to postgres + content: + text/plain: + schema: + type: string + /groups/list: get: summary: list instance groups @@ -9535,6 +10851,23 @@ paths: schema: $ref: "#/components/schemas/Folder" + /w/{workspace}/folders/exists/{name}: + get: + summary: exists folder + operationId: existsFolder + tags: + - folder + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/Name" + responses: + "200": + description: folder exists + content: + application/json: + schema: + type: boolean + /w/{workspace}/folders/getusage/{name}: get: summary: get folder usage @@ -9840,6 +11173,41 @@ paths: items: $ref: "#/components/schemas/AutoscalingEvent" + /agent_workers/create_agent_token: + post: + summary: create agent token + operationId: createAgentToken + tags: + - agent_workers + requestBody: + description: agent token + required: true + content: + application/json: + schema: + type: object + properties: + worker_group: + type: string + tags: + type: array + items: + type: string + exp: + type: integer + required: + - worker_group + - tags + - exp + responses: + "200": + description: agent token created + content: + application/json: + schema: + type: string + + /w/{workspace}/acls/get/{kind}/{path}: get: summary: get granular acls @@ -9854,7 +11222,8 @@ paths: required: true schema: type: string - enum: [ + enum: + [ script, group_, resource, @@ -9869,6 +11238,9 @@ paths: kafka_trigger, nats_trigger, postgres_trigger, + mqtt_trigger, + gcp_trigger, + sqs_trigger ] responses: "200": @@ -9910,6 +11282,9 @@ paths: kafka_trigger, nats_trigger, postgres_trigger, + mqtt_trigger, + gcp_trigger, + sqs_trigger ] requestBody: description: acl to add @@ -9962,6 +11337,9 @@ paths: kafka_trigger, nats_trigger, postgres_trigger, + mqtt_trigger, + gcp_trigger, + sqs_trigger ] requestBody: description: acl to add @@ -10013,6 +11391,10 @@ paths: responses: "200": description: capture config set + content: + application/json: + schema: + type: object /w/{workspace}/capture/ping_config/{trigger_kind}/{runnable_kind}/{path}: post: @@ -10079,6 +11461,34 @@ paths: items: $ref: "#/components/schemas/Capture" + /w/{workspace}/capture/move/{runnable_kind}/{path}: + post: + summary: move captures and configs for a script or flow + operationId: moveCapturesAndConfigs + tags: + - capture + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/RunnableKind" + - $ref: "#/components/parameters/Path" + requestBody: + description: move captures and configs to a new path + required: true + content: + application/json: + schema: + type: object + properties: + new_path: + type: string + responses: + "200": + description: captures and configs moved + content: + text/plain: + schema: + type: string + /w/{workspace}/capture/{id}: get: summary: get a capture @@ -10173,6 +11583,7 @@ paths: - $ref: "#/components/parameters/RunnableTypeQuery" - $ref: "#/components/parameters/Page" - $ref: "#/components/parameters/PerPage" + - $ref: "#/components/parameters/ArgsFilter" - name: include_preview in: query schema: @@ -10307,8 +11718,7 @@ paths: /w/{workspace}/job_helpers/duckdb_connection_settings: post: - summary: - Converts an S3 resource to the set of instructions necessary to connect + summary: Converts an S3 resource to the set of instructions necessary to connect DuckDB to an S3 bucket operationId: duckdbConnectionSettings tags: @@ -10337,8 +11747,7 @@ paths: type: string /w/{workspace}/job_helpers/v2/duckdb_connection_settings: post: - summary: - Converts an S3 resource to the set of instructions necessary to connect + summary: Converts an S3 resource to the set of instructions necessary to connect DuckDB to an S3 bucket operationId: duckdbConnectionSettingsV2 tags: @@ -10374,8 +11783,7 @@ paths: /w/{workspace}/job_helpers/polars_connection_settings: post: - summary: - Converts an S3 resource to the set of arguments necessary to connect + summary: Converts an S3 resource to the set of arguments necessary to connect Polars to an S3 bucket operationId: polarsConnectionSettings tags: @@ -10419,8 +11827,7 @@ paths: - client_kwargs /w/{workspace}/job_helpers/v2/polars_connection_settings: post: - summary: - Converts an S3 resource to the set of arguments necessary to connect + summary: Converts an S3 resource to the set of arguments necessary to connect Polars to an S3 bucket operationId: polarsConnectionSettingsV2 tags: @@ -10497,8 +11904,7 @@ paths: parameters: - $ref: "#/components/parameters/WorkspaceId" requestBody: - description: - S3 resource path to use. If empty, the S3 resource defined in the + description: S3 resource path to use. If empty, the S3 resource defined in the workspace settings will be used required: true content: @@ -10893,7 +12299,7 @@ paths: /w/{workspace}/job_helpers/download_s3_file: get: - summary: Download file to S3 bucket + summary: Download file from S3 bucket operationId: fileDownload tags: - helpers @@ -11198,6 +12604,7 @@ paths: - $ref: "#/components/parameters/ArgsFilter" - $ref: "#/components/parameters/Tag" - $ref: "#/components/parameters/ResultFilter" + - $ref: "#/components/parameters/AllowWildcards" - $ref: "#/components/parameters/Page" - $ref: "#/components/parameters/PerPage" - name: is_skipped @@ -11533,10 +12940,15 @@ components: in: query schema: type: string + Worker: + name: worker + description: worker this job was ran on + in: query + schema: + type: string ParentJob: name: parent_job - description: - The parent job that is at the origin and responsible for the execution + description: The parent job that is at the origin and responsible for the execution of this script if any in: query schema: @@ -11556,8 +12968,7 @@ components: type: string NewJobId: name: job_id - description: - The job id to assign to the created job. if missing, job is chosen + description: The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) in: query @@ -11648,8 +13059,7 @@ components: format: date-time CreatedOrStartedAfter: name: created_or_started_after - description: - filter on created_at for non non started job and started_at otherwise + description: filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp in: query schema: @@ -11657,8 +13067,7 @@ components: format: date-time CreatedOrStartedAfterCompletedJob: name: created_or_started_after_completed_jobs - description: - filter on created_at for non non started job and started_at otherwise + description: filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp but only for the completed jobs in: query schema: @@ -11666,8 +13075,7 @@ components: format: date-time CreatedOrStartedBefore: name: created_or_started_before - description: - filter on created_at for non non started job and started_at otherwise + description: filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp in: query schema: @@ -11697,6 +13105,12 @@ components: in: query schema: type: boolean + AllowWildcards: + name: allow_wildcards + description: allow wildcards (*) in the filter of label, tag, worker + in: query + schema: + type: boolean ArgsFilter: name: args description: filter on jobs containing those args as a json subset (@> in postgres) @@ -11749,8 +13163,7 @@ components: enum: [Create, Update, Delete, Execute] JobKinds: name: job_kinds - description: - filter on job kind (values 'preview', 'script', 'dependencies', 'flow') + description: filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, in: query schema: @@ -11801,19 +13214,44 @@ components: AIProvider: type: string - enum: [openai, anthropic, mistral, deepseek, groq, openrouter, customai] + enum: [openai, azure_openai, anthropic, mistral, deepseek, googleai, groq, openrouter, togetherai, customai] - AIResource: + AIProviderModel: type: object properties: - path: + model: type: string provider: $ref: "#/components/schemas/AIProvider" required: - - path + - model - provider + AIProviderConfig: + type: object + properties: + resource_path: + type: string + models: + type: array + items: + type: string + required: + - resource_path + - models + + AIConfig: + type: object + properties: + providers: + type: object + additionalProperties: + $ref: "#/components/schemas/AIProviderConfig" + default_model: + $ref: "#/components/schemas/AIProviderModel" + code_completion_model: + $ref: "#/components/schemas/AIProviderModel" + Script: type: object properties: @@ -12133,14 +13571,14 @@ components: "singlescriptflow", "flowscript", "flownode", - "appscript", + "appscript" ] schedule_path: type: string permissioned_as: type: string description: | - The user (u/userfoo) or group (g/groupfoo) whom + The user (u/userfoo) or group (g/groupfoo) whom the execution of this script will be permissioned_as and by extension its DT_TOKEN. flow_status: $ref: "../../openflow.openapi.yaml#/components/schemas/FlowStatus" @@ -12168,6 +13606,8 @@ components: type: number preprocessed: type: boolean + worker: + type: string required: - id - running @@ -12238,14 +13678,14 @@ components: "singlescriptflow", "flowscript", "flownode", - "appscript", + "appscript" ] schedule_path: type: string permissioned_as: type: string description: | - The user (u/userfoo) or group (g/groupfoo) whom + The user (u/userfoo) or group (g/groupfoo) whom the execution of this script will be permissioned_as and by extension its DT_TOKEN. flow_status: $ref: "../../openflow.openapi.yaml#/components/schemas/FlowStatus" @@ -12277,6 +13717,8 @@ components: type: number preprocessed: type: boolean + worker: + type: string required: - id - created_by @@ -12686,7 +14128,7 @@ components: "bytes", "dict", "datetime", - "sql", + "sql" ] - type: object properties: @@ -12726,7 +14168,7 @@ components: "bytes", "dict", "datetime", - "sql", + "sql" ] - type: object properties: @@ -12752,7 +14194,7 @@ components: "bytes", "dict", "datetime", - "sql", + "sql" ] - type: object properties: @@ -12784,8 +14226,7 @@ components: ScriptLang: type: string - enum: - [ + enum: [ python3, deno, go, @@ -12803,7 +14244,10 @@ components: php, rust, ansible, - csharp + csharp, + nu, + java + # for related places search: ADD_NEW_LANG ] Preview: @@ -12813,6 +14257,8 @@ components: type: string path: type: string + script_hash: + type: string args: $ref: "#/components/schemas/ScriptArgs" language: @@ -13030,6 +14476,8 @@ components: $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" summary: type: string + description: + type: string no_flow_overlap: type: boolean tag: @@ -13116,6 +14564,8 @@ components: type: boolean summary: type: string + description: + type: string tag: type: string paused_until: @@ -13167,6 +14617,8 @@ components: type: boolean summary: type: string + description: + type: string tag: type: string paused_until: @@ -13184,6 +14636,10 @@ components: TriggerExtraProperty: type: object properties: + path: + type: string + script_path: + type: string email: type: string extra_perms: @@ -13197,22 +14653,33 @@ components: edited_at: type: string format: date-time + is_flow: + type: boolean required: + - path + - script_path - email - extra_perms - workspace_id - edited_by - edited_at + - is_flow + + AuthenticationMethod: + type: string + enum: + - none + - windmill + - api_key + - basic_http + - custom_script + - signature HttpTrigger: allOf: - $ref: "#/components/schemas/TriggerExtraProperty" type: object properties: - path: - type: string - script_path: - type: string route_path: type: string static_asset_config: @@ -13226,8 +14693,6 @@ components: type: string required: - s3 - is_flow: - type: boolean http_method: type: string enum: @@ -13236,27 +14701,30 @@ components: - put - delete - patch + authentication_resource_path: + type: string is_async: type: boolean - requires_auth: - type: boolean + authentication_method: + $ref: "#/components/schemas/AuthenticationMethod" is_static_website: type: boolean + workspaced_route: + type: boolean + wrap_body: + type: boolean + raw_string: + type: boolean required: - - path - - edited_by - - edited_at - - script_path - route_path - - extra_perms - - is_flow - - email - - workspace_id - is_async - - requires_auth + - authentication_method - http_method - is_static_website + - workspaced_route + - wrap_body + - raw_string NewHttpTrigger: type: object @@ -13267,6 +14735,8 @@ components: type: string route_path: type: string + workspaced_route: + type: boolean static_asset_config: type: object properties: @@ -13288,12 +14758,18 @@ components: - put - delete - patch + authentication_resource_path: + type: string is_async: type: boolean - requires_auth: - type: boolean + authentication_method: + $ref: "#/components/schemas/AuthenticationMethod" is_static_website: type: boolean + wrap_body: + type: boolean + raw_string: + type: boolean required: - path @@ -13301,7 +14777,7 @@ components: - route_path - is_flow - is_async - - requires_auth + - authentication_method - http_method - is_static_website @@ -13314,6 +14790,8 @@ components: type: string route_path: type: string + workspaced_route: + type: boolean static_asset_config: type: object properties: @@ -13325,6 +14803,8 @@ components: type: string required: - s3 + authentication_resource_path: + type: string is_flow: type: boolean http_method: @@ -13337,17 +14817,21 @@ components: - patch is_async: type: boolean - requires_auth: - type: boolean + authentication_method: + $ref: "#/components/schemas/AuthenticationMethod" is_static_website: type: boolean + wrap_body: + type: boolean + raw_string: + type: boolean required: - path - script_path - is_flow - kind - is_async - - requires_auth + - authentication_method - http_method - is_static_website @@ -13375,20 +14859,20 @@ components: type: number nats_count: type: number + mqtt_count: + type: number + gcp_count: + type: number + sqs_count: + type: number WebsocketTrigger: allOf: - $ref: "#/components/schemas/TriggerExtraProperty" type: object properties: - path: - type: string - script_path: - type: string url: type: string - is_flow: - type: boolean server_id: type: string last_server_ping: @@ -13419,15 +14903,7 @@ components: type: boolean required: - - path - - edited_by - - edited_at - - script_path - url - - extra_perms - - is_flow - - email - - workspace_id - enabled - filters - can_return_message @@ -13536,7 +15012,347 @@ components: - is_flow required: - runnable_result - + + MqttQoS: + type: string + enum: ["qos0", "qos1", "qos2"] + + MqttV3Config: + type: object + properties: + clean_session: + type: boolean + + MqttV5Config: + type: object + properties: + clean_start: + type: boolean + topic_alias: + type: number + session_expiry_interval: + type: number + + MqttSubscribeTopic: + type: object + properties: + qos: + $ref: "#/components/schemas/MqttQoS" + topic: + type: string + required: + - qos + - topic + + MqttClientVersion: + type: string + enum: ["v3", "v5"] + + MqttTrigger: + allOf: + - $ref: "#/components/schemas/TriggerExtraProperty" + type: object + properties: + mqtt_resource_path: + type: string + subscribe_topics: + type: array + items: + $ref: "#/components/schemas/MqttSubscribeTopic" + v3_config: + $ref: "#/components/schemas/MqttV3Config" + v5_config: + $ref: "#/components/schemas/MqttV5Config" + client_id: + type: string + client_version: + $ref: "#/components/schemas/MqttClientVersion" + server_id: + type: string + last_server_ping: + type: string + format: date-time + error: + type: string + enabled: + type: boolean + + required: + - enabled + - subscribe_topics + - mqtt_resource_path + + NewMqttTrigger: + type: object + properties: + mqtt_resource_path: + type: string + subscribe_topics: + type: array + items: + $ref: "#/components/schemas/MqttSubscribeTopic" + client_id: + type: string + v3_config: + $ref: "#/components/schemas/MqttV3Config" + v5_config: + $ref: "#/components/schemas/MqttV5Config" + client_version: + $ref: "#/components/schemas/MqttClientVersion" + path: + type: string + script_path: + type: string + is_flow: + type: boolean + enabled: + type: boolean + required: + - path + - script_path + - is_flow + - subscribe_topics + - mqtt_resource_path + + EditMqttTrigger: + type: object + properties: + mqtt_resource_path: + type: string + subscribe_topics: + type: array + items: + $ref: "#/components/schemas/MqttSubscribeTopic" + client_id: + type: string + v3_config: + $ref: "#/components/schemas/MqttV3Config" + v5_config: + $ref: "#/components/schemas/MqttV5Config" + client_version: + $ref: "#/components/schemas/MqttClientVersion" + path: + type: string + script_path: + type: string + is_flow: + type: boolean + enabled: + type: boolean + required: + - path + - script_path + - is_flow + - enabled + - subscribe_topics + - mqtt_resource_path + + DeliveryType: + type: string + enum: + - push + - pull + + PushConfig: + type: object + properties: + audience: + type: string + authenticate: + type: boolean + required: + - authenticate + - base_endpoint + + GcpTrigger: + allOf: + - $ref: "#/components/schemas/TriggerExtraProperty" + type: object + properties: + gcp_resource_path: + type: string + topic_id: + type: string + subscription_id: + type: string + server_id: + type: string + delivery_type: + $ref: "#/components/schemas/DeliveryType" + delivery_config: + $ref: "#/components/schemas/PushConfig" + subscription_mode: + $ref: "#/components/schemas/SubscriptionMode" + last_server_ping: + type: string + format: date-time + error: + type: string + enabled: + type: boolean + required: + - gcp_resource_path + - topic_id + - subscription_id + - enabled + - delivery_type + - subscription_mode + + + SubscriptionMode: + type: string + enum: + - existing + - create_update + description: "The mode of subscription. 'existing' means using an existing GCP subscription, while 'create_update' involves creating or updating a new subscription." + + + GcpTriggerData: + type: object + properties: + gcp_resource_path: + type: string + subscription_mode: + $ref: "#/components/schemas/SubscriptionMode" + topic_id: + type: string + subscription_id: + type: string + base_endpoint: + type: string + delivery_type: + $ref: "#/components/schemas/DeliveryType" + delivery_config: + $ref: "#/components/schemas/PushConfig" + path: + type: string + script_path: + type: string + is_flow: + type: boolean + enabled: + type: boolean + required: + - path + - script_path + - is_flow + - gcp_resource_path + - topic_id + - subscription_mode + + GetAllTopicSubscription: + type: object + properties: + topic_id: + type: string + required: + - topic_id + + + DeleteGcpSubscription: + type: object + properties: + subscription_id: + type: string + required: + - subscription_id + + AwsAuthResourceType: + type: string + enum: + - oidc + - credentials + + SqsTrigger: + allOf: + - $ref: "#/components/schemas/TriggerExtraProperty" + type: object + properties: + queue_url: + type: string + aws_auth_resource_type: + $ref: "#/components/schemas/AwsAuthResourceType" + aws_resource_path: + type: string + message_attributes: + type: array + items: + type: string + server_id: + type: string + last_server_ping: + type: string + format: date-time + error: + type: string + enabled: + type: boolean + + required: + - queue_url + - aws_resource_path + - enabled + - aws_auth_resource_type + + NewSqsTrigger: + type: object + properties: + queue_url: + type: string + aws_auth_resource_type: + $ref: "#/components/schemas/AwsAuthResourceType" + aws_resource_path: + type: string + message_attributes: + type: array + items: + type: string + path: + type: string + script_path: + type: string + is_flow: + type: boolean + enabled: + type: boolean + required: + - queue_url + - aws_resource_path + - path + - script_path + - is_flow + - aws_auth_resource_type + + EditSqsTrigger: + type: object + properties: + queue_url: + type: string + aws_auth_resource_type: + $ref: "#/components/schemas/AwsAuthResourceType" + aws_resource_path: + type: string + message_attributes: + type: array + items: + type: string + path: + type: string + script_path: + type: string + is_flow: + type: boolean + enabled: + type: boolean + required: + - queue_url + - aws_resource_path + - path + - script_path + - is_flow + - enabled + - aws_auth_resource_type + Slot: type: object properties: @@ -13612,18 +15428,12 @@ components: - postgres_resource_path - relations - language - + PostgresTrigger: allOf: - $ref: "#/components/schemas/TriggerExtraProperty" type: object properties: - path: - type: string - script_path: - type: string - is_flow: - type: boolean enabled: type: boolean postgres_resource_path: @@ -13637,12 +15447,9 @@ components: error: type: string last_server_ping: - type: string - format: date-time + type: string + format: date-time required: - - path - - script_path - - is_flow - enabled - postgres_resource_path - replication_slot_name @@ -13673,7 +15480,7 @@ components: - is_flow - enabled - postgres_resource_path - + EditPostgresTrigger: type: object properties: @@ -13703,17 +15510,10 @@ components: - replication_slot_name KafkaTrigger: + allOf: + - $ref: "#/components/schemas/TriggerExtraProperty" type: object properties: - path: - type: string - edited_by: - type: string - edited_at: - type: string - format: date-time - script_path: - type: string kafka_resource_path: type: string group_id: @@ -13722,16 +15522,6 @@ components: type: array items: type: string - is_flow: - type: boolean - extra_perms: - type: object - additionalProperties: - type: boolean - email: - type: string - workspace_id: - type: string server_id: type: string last_server_ping: @@ -13743,17 +15533,9 @@ components: type: boolean required: - - path - - edited_by - - edited_at - - script_path - kafka_resource_path - group_id - topics - - extra_perms - - is_flow - - email - - workspace_id - enabled NewKafkaTrigger: @@ -13811,17 +15593,10 @@ components: - is_flow NatsTrigger: + allOf: + - $ref: "#/components/schemas/TriggerExtraProperty" type: object properties: - path: - type: string - edited_by: - type: string - edited_at: - type: string - format: date-time - script_path: - type: string nats_resource_path: type: string use_jetstream: @@ -13834,16 +15609,6 @@ components: type: array items: type: string - is_flow: - type: boolean - extra_perms: - type: object - additionalProperties: - type: boolean - email: - type: string - workspace_id: - type: string server_id: type: string last_server_ping: @@ -13853,19 +15618,11 @@ components: type: string enabled: type: boolean - + required: - - path - - edited_by - - edited_at - - script_path - nats_resource_path - use_jetstream - subjects - - extra_perms - - is_flow - - email - - workspace_id - enabled NewNatsTrigger: @@ -13891,7 +15648,7 @@ components: type: string enabled: type: boolean - + required: - path - script_path @@ -13921,7 +15678,7 @@ components: type: string is_flow: type: boolean - + required: - path - script_path @@ -14152,6 +15909,10 @@ components: allOf: - $ref: "../../openflow.openapi.yaml#/components/schemas/OpenFlow" - $ref: "#/components/schemas/FlowMetadata" + - type: object + properties: + lock_error_logs: + type: string ExtraPerms: type: object @@ -14268,6 +16029,15 @@ components: type: array items: type: object + allowed_s3_keys: + type: array + items: + type: object + properties: + s3_path: + type: string + resource: + type: string execution_mode: type: string enum: [viewer, publisher, anonymous] @@ -14301,6 +16071,8 @@ components: execution_mode: type: string enum: [viewer, publisher, anonymous] + raw_app: + type: boolean required: - id - workspace_id @@ -14474,13 +16246,7 @@ components: properties: type: type: string - enum: - [ - "S3Storage", - "AzureBlobStorage", - "AzureWorkloadIdentity", - "S3AwsOidc", - ] + enum: ["S3Storage", "AzureBlobStorage", "AzureWorkloadIdentity", "S3AwsOidc"] s3_resource_path: type: string azure_blob_resource_path: @@ -14495,12 +16261,7 @@ components: type: type: string enum: - [ - "S3Storage", - "AzureBlobStorage", - "AzureWorkloadIdentity", - "S3AwsOidc", - ] + ["S3Storage", "AzureBlobStorage", "AzureWorkloadIdentity", "S3AwsOidc"] s3_resource_path: type: string azure_blob_resource_path: @@ -14615,6 +16376,7 @@ components: - resource - variable - secret + - trigger WorkspaceDefaultScripts: type: object @@ -14885,7 +16647,7 @@ components: CaptureTriggerKind: type: string - enum: [webhook, http, websocket, kafka, email, nats] + enum: [webhook, http, websocket, kafka, email, nats, postgres, sqs, mqtt, gcp] Capture: type: object @@ -14979,7 +16741,7 @@ components: type: array description: List of channels within the team items: - $ref: '#/components/schemas/ChannelInfo' + $ref: "#/components/schemas/ChannelInfo" ChannelInfo: type: object @@ -15005,3 +16767,81 @@ components: type: string description: The service URL for the channel example: "https://smba.trafficmanager.net/amer/12345678-1234-1234-1234-123456789012/" + + GithubInstallations: + type: array + items: + type: object + properties: + workspace_id: + type: string + installation_id: + type: number + account_id: + type: string + repositories: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + required: + - name + - url + required: + - installation_id + - account_id + - repositories + + WorkspaceGithubInstallation: + type: object + properties: + account_id: + type: string + installation_id: + type: number + required: + - account_id + - installation_id + + S3Object: + type: object + properties: + s3: + type: string + filename: + type: string + storage: + type: string + presigned: + type: string + required: + - s3 + + TeamsChannel: + type: object + required: + - team_id + - team_name + - channel_id + - channel_name + properties: + team_id: + type: string + description: Microsoft Teams team ID + minLength: 1 + team_name: + type: string + description: Microsoft Teams team name + minLength: 1 + channel_id: + type: string + description: Microsoft Teams channel ID + minLength: 1 + channel_name: + type: string + description: Microsoft Teams channel name + minLength: 1 \ No newline at end of file diff --git a/backend/windmill-api/src/ai.rs b/backend/windmill-api/src/ai.rs index d77943df9d..6b33a70730 100644 --- a/backend/windmill-api/src/ai.rs +++ b/backend/windmill-api/src/ai.rs @@ -3,192 +3,109 @@ use crate::{ variables::get_variable_or_self, }; -use anthropic::AnthropicCache; -use anyhow::Context; use axum::{body::Bytes, extract::Path, response::IntoResponse, routing::post, Extension, Router}; use http::HeaderMap; -use lazy_static::lazy_static; use quick_cache::sync::Cache; use reqwest::{Client, RequestBuilder}; use serde::{Deserialize, Serialize}; -use serde_json::value::{RawValue, Value}; +use serde_json::value::RawValue; use std::collections::HashMap; use windmill_audit::{audit_ee::audit_log, ActionKind}; use windmill_common::error::{to_anyhow, Error, Result}; -use mistral::MistralCache; -use openai::OpenaiCache; -use openai_api_compatible::OpenaiApiCompatibleCache; - lazy_static::lazy_static! { static ref HTTP_CLIENT: Client = reqwest::ClientBuilder::new() .timeout(std::time::Duration::from_secs(60 * 5)) .user_agent("windmill/beta") .build().unwrap(); + + static ref OPENAI_AZURE_BASE_PATH: Option = std::env::var("OPENAI_AZURE_BASE_PATH").ok(); + + pub static ref AI_REQUEST_CACHE: Cache<(String, AIProvider), ExpiringAIRequestConfig> = Cache::new(500); } -mod openai_api_compatible { - use super::*; +const AZURE_API_VERSION: &str = "2024-10-21"; +const OPENAI_BASE_URL: &str = "https://api.openai.com/v1"; - #[derive(Deserialize, Clone, Debug)] - pub struct OpenaiApiCompatibleCache { - pub base_url: String, - pub api_key: Option, - } +#[derive(Deserialize, Debug)] +struct AIOAuthResource { + client_id: String, + client_secret: String, + token_url: String, + user: Option, +} - impl OpenaiApiCompatibleCache { - pub fn prepare_request(self, path: &str, body: Bytes) -> Result { - let url = format!("{}/{}", self.base_url, path); +#[derive(Deserialize, Debug)] +struct AIStandardResource { + #[serde(alias = "baseUrl")] + base_url: Option, + #[serde(alias = "apiKey")] + api_key: Option, + organization_id: Option, +} - let mut request = HTTP_CLIENT - .post(url) - .header("content-type", "application/json") - .body(body); +#[derive(Deserialize, Debug)] +struct OAuthTokens { + access_token: String, +} - if let Some(api_key) = self.api_key { - request = request.header("Authorization", format!("Bearer {}", api_key)); - } +#[derive(Deserialize, Debug)] +#[serde(untagged)] +enum AIResource { + OAuth(AIOAuthResource), + Standard(AIStandardResource), +} - Ok(request) - } - } +#[derive(Deserialize, Clone, Debug)] +struct AIRequestConfig { + pub base_url: String, + pub api_key: Option, + pub access_token: Option, + pub organization_id: Option, + pub user: Option, +} - pub async fn get_cached_value( +impl AIRequestConfig { + pub async fn new( + provider: &AIProvider, db: &DB, w_id: &str, - resource: Value, - base_url: Option, - ) -> Result { - let mut resource: OpenaiApiCompatibleCache = if let Some(base_url) = base_url { - let api_key = match resource { - Value::Object(mut obj) => obj - .remove("api_key") - .map(|v| serde_json::from_value::(v.clone()).ok()) - .flatten(), - _ => None, - }; - OpenaiApiCompatibleCache { base_url, api_key } - } else { - serde_json::from_value(resource).with_context(|| "validating custom AI resource")? + resource: AIResource, + ) -> Result { + let (api_key, access_token, organization_id, base_url, user) = match resource { + AIResource::Standard(resource) => { + let base_url = provider.get_base_url(resource.base_url, db).await?; + let api_key = if let Some(api_key) = resource.api_key { + Some(get_variable_or_self(api_key, db, w_id).await?) + } else { + None + }; + let organization_id = if let Some(organization_id) = resource.organization_id { + Some(get_variable_or_self(organization_id, db, w_id).await?) + } else { + None + }; + + (api_key, None, organization_id, base_url, None) + } + AIResource::OAuth(resource) => { + let user = if let Some(user) = resource.user.clone() { + Some(get_variable_or_self(user, db, w_id).await?) + } else { + None + }; + let token = Self::get_token_using_oauth(resource, db, w_id).await?; + let base_url = provider.get_base_url(None, db).await?; + + (None, Some(token), None, base_url, user) + } }; - if let Some(api_key) = resource.api_key { - resource.api_key = Some(get_variable_or_self(api_key, db, w_id).await?); - } - - Ok(KeyCache::OpenaiApiCompatible(resource)) - } -} - -mod openai { - use super::*; - - const API_VERSION: &str = "2024-10-21"; - - #[derive(Deserialize, Debug)] - struct OpenaiResource { - api_key: String, - organization_id: Option, + Ok(Self { base_url, organization_id, api_key, access_token, user }) } - #[derive(Deserialize, Debug)] - struct OpenaiClientCredentialsOauthResource { - client_id: String, - client_secret: String, - token_url: String, - user: Option, - } - - #[derive(Deserialize, Debug)] - #[serde(untagged, rename_all = "snake_case")] - enum OpenaiConfig { - Resource(OpenaiResource), - ClientCredentialsOauthResource(OpenaiClientCredentialsOauthResource), - } - - lazy_static::lazy_static! { - pub static ref OPENAI_AZURE_BASE_PATH: Option = std::env::var("OPENAI_AZURE_BASE_PATH").ok(); - } - - #[derive(Deserialize, Debug)] - struct OpenaiCredentials { - access_token: String, - } - - #[derive(Clone, Debug, Deserialize)] - pub struct OpenaiCache { - api_key: String, - organization_id: Option, - azure_base_path: Option, - user: Option, - } - - impl OpenaiCache { - pub fn new( - api_key: String, - organization_id: Option, - azure_base_path: Option, - user: Option, - ) -> Self { - Self { api_key, organization_id, azure_base_path, user } - } - } - - const BASE_URL: &str = "https://api.openai.com/v1"; - impl OpenaiCache { - pub fn prepare_request(self, openai_path: &str, mut body: Bytes) -> Result { - let OpenaiCache { api_key, azure_base_path, organization_id, user } = self; - if user.is_some() { - tracing::debug!("Adding user to request body"); - let mut json_body: HashMap> = serde_json::from_slice(&body) - .map_err(|e| { - Error::internal_err(format!("Failed to parse request body: {}", e)) - })?; - - let user_json_string = serde_json::Value::String(user.unwrap()).to_string(); // makes sure to escape characters - - json_body.insert( - "user".to_string(), - RawValue::from_string(user_json_string) - .map_err(|e| Error::internal_err(format!("Failed to parse user: {}", e)))?, - ); - - body = serde_json::to_vec(&json_body) - .map_err(|e| { - Error::internal_err(format!("Failed to reserialize request body: {}", e)) - })? - .into(); - } - - let base_url = if let Some(base_url) = azure_base_path { - base_url - } else { - BASE_URL.to_string() - }; - let url = format!("{}/{}", base_url, openai_path); - let mut request = HTTP_CLIENT - .post(url) - .header("content-type", "application/json") - .body(body); - - if base_url != BASE_URL { - request = request - .header("api-key", api_key) - .query(&[("api-version", API_VERSION)]) - } else { - request = request.header("authorization", format!("Bearer {}", api_key)) - } - - if let Some(org_id) = organization_id { - request = request.header("OpenAI-Organization", org_id); - } - - Ok(request) - } - } - - async fn get_openai_key_using_credentials_flow( - mut resource: OpenaiClientCredentialsOauthResource, + async fn get_token_using_oauth( + mut resource: AIOAuthResource, db: &DB, w_id: &str, ) -> Result { @@ -197,204 +114,167 @@ mod openai { resource.token_url = get_variable_or_self(resource.token_url, db, w_id).await?; let mut params = HashMap::new(); params.insert("grant_type", "client_credentials"); + params.insert("scope", "https://cognitiveservices.azure.com/.default"); let response = HTTP_CLIENT .post(resource.token_url) .form(¶ms) .basic_auth(resource.client_id, Some(resource.client_secret)) .send() .await + .and_then(|r| r.error_for_status()) .map_err(|err| { Error::internal_err(format!( - "Failed to get OpenAI credentials using credentials flow: {}", + "Failed to get access token using credentials flow: {}", err )) })?; - let response = response.json::().await.map_err(|err| { + let response = response.json::().await.map_err(|err| { Error::internal_err(format!( - "Failed to parse OpenAI credentials from credentials flow: {}", + "Failed to parse access token from credentials flow: {}", err )) })?; Ok(response.access_token) } - pub async fn get_cached_value(db: &DB, w_id: &str, resource: Value) -> Result { - let config = serde_json::from_value(resource) - .map_err(|e| Error::internal_err(format!("validating openai resource {e:#}")))?; + pub fn prepare_request( + self, + provider: &AIProvider, + path: &str, + body: Bytes, + ) -> Result { + let url = format!("{}/{}", self.base_url, path); - let mut user = None::; - let mut resource = match config { - OpenaiConfig::Resource(resource) => { - tracing::debug!("Getting OpenAI key from static resource"); - resource - } - OpenaiConfig::ClientCredentialsOauthResource(resource) => { - tracing::debug!("Getting OpenAI key with client credentials flow"); - user = resource.user.clone(); - let token = get_openai_key_using_credentials_flow(resource, db, w_id).await?; - OpenaiResource { api_key: token, organization_id: None } - } - }; - - resource.api_key = get_variable_or_self(resource.api_key, db, w_id).await?; - - if let Some(organization_id) = resource.organization_id { - resource.organization_id = Some(get_variable_or_self(organization_id, db, w_id).await?); - } - - if user.is_some() { - user = Some(get_variable_or_self(user.unwrap(), db, w_id).await?); - } - - let azure_base_path = sqlx::query_scalar!( - "SELECT value - FROM global_settings - WHERE name = 'openai_azure_base_path'", - ) - .fetch_optional(db) - .await?; - - let azure_base_path = if let Some(azure_base_path) = azure_base_path { - Some( - serde_json::from_value::(azure_base_path).map_err(|e| { - Error::internal_err(format!("validating openai azure base path {e:#}")) - })?, - ) + let body = if let Some(user) = self.user { + Self::add_user_to_body(body, user)? } else { - OPENAI_AZURE_BASE_PATH.clone() + body }; - let workspace_cache = OpenaiCache::new( - resource.api_key.clone(), - resource.organization_id.clone(), - azure_base_path.clone(), - user.clone(), + let is_azure = matches!(provider, AIProvider::OpenAI) && self.base_url != OPENAI_BASE_URL + || matches!(provider, AIProvider::AzureOpenAI); + + let mut request = HTTP_CLIENT + .post(url) + .header("content-type", "application/json") + .body(body); + + if is_azure { + request = request.query(&[("api-version", AZURE_API_VERSION)]) + } + + if let Some(api_key) = self.api_key { + if is_azure { + request = request.header("api-key", api_key) + } else { + request = request.header("authorization", format!("Bearer {}", api_key)) + } + } + + if let Some(access_token) = self.access_token { + request = request.header("authorization", format!("Bearer {}", access_token)) + } + + if let Some(org_id) = self.organization_id { + request = request.header("OpenAI-Organization", org_id); + } + + Ok(request) + } + + fn add_user_to_body(body: Bytes, user: String) -> Result { + tracing::debug!("Adding user to request body"); + let mut json_body: HashMap> = serde_json::from_slice(&body) + .map_err(|e| Error::internal_err(format!("Failed to parse request body: {}", e)))?; + + let user_json_string = serde_json::Value::String(user).to_string(); // makes sure to escape characters + + json_body.insert( + "user".to_string(), + RawValue::from_string(user_json_string) + .map_err(|e| Error::internal_err(format!("Failed to parse user: {}", e)))?, ); - Ok(KeyCache::Openai(workspace_cache)) - } -} -mod anthropic { - - use super::*; - - #[derive(Clone, Deserialize, Debug)] - pub struct AnthropicCache { - #[serde(rename = "apiKey")] - pub api_key: String, - } - - const API_VERSION: &str = "2023-06-01"; - - const BASE_URL: &str = "https://api.anthropic.com"; - impl AnthropicCache { - pub fn prepare_request(self, anthropic_path: &str, body: Bytes) -> Result { - let AnthropicCache { api_key } = self; - let url = format!("{}/{}", BASE_URL, anthropic_path); - let request = HTTP_CLIENT - .post(url) - .header("x-api-key", api_key) - .header("anthropic-version", API_VERSION) - .header("content-type", "application/json") - .body(body); - Ok(request) - } - } - - pub async fn get_cached_value(db: &DB, w_id: &str, resource: Value) -> Result { - let mut resource: AnthropicCache = serde_json::from_value(resource) - .map_err(|e| Error::internal_err(format!("validating anthropic resource {e:#}")))?; - resource.api_key = get_variable_or_self(resource.api_key, db, w_id).await?; - Ok(KeyCache::Anthropic(resource)) - } -} - -mod mistral { - use super::*; - #[derive(Deserialize, Clone, Debug)] - pub struct MistralCache { - #[serde(rename = "apiKey")] - pub api_key: String, - } - - const BASE_URL: &str = "https://api.mistral.ai"; - impl MistralCache { - pub fn prepare_request(self, mistral_path: &str, body: Bytes) -> Result { - let MistralCache { api_key } = self; - - let url = format!("{}/{}", BASE_URL, mistral_path); - let request = HTTP_CLIENT - .post(url) - .header("content-type", "application/json") - .header("Accept", "application/json") - .header("authorization", format!("Bearer {}", api_key)) - .body(body); - Ok(request) - } - } - - pub async fn get_cached_value(db: &DB, w_id: &str, resource: Value) -> Result { - let mut resource: MistralCache = serde_json::from_value(resource) - .map_err(|e| Error::internal_err(format!("validating mistral resource {e:#}")))?; - resource.api_key = get_variable_or_self(resource.api_key, db, w_id).await?; - Ok(KeyCache::Mistral(resource)) + Ok(serde_json::to_vec(&json_body) + .map_err(|e| Error::internal_err(format!("Failed to reserialize request body: {}", e)))? + .into()) } } #[derive(Clone, Debug)] -pub enum KeyCache { - Openai(OpenaiCache), - Anthropic(AnthropicCache), - Mistral(MistralCache), - OpenaiApiCompatible(OpenaiApiCompatibleCache), +pub struct ExpiringAIRequestConfig { + config: AIRequestConfig, + expires_at: std::time::Instant, } -#[derive(Clone, Debug)] -pub struct AICache { - pub path: String, - pub cached_key: KeyCache, - pub expires_at: std::time::Instant, -} - -impl AICache { - pub fn new(path: String, cached_key: KeyCache) -> Self { - Self { - path, - cached_key, - expires_at: std::time::Instant::now() + std::time::Duration::from_secs(60), - } +impl ExpiringAIRequestConfig { + fn new(config: AIRequestConfig) -> Self { + Self { config, expires_at: std::time::Instant::now() + std::time::Duration::from_secs(60) } } fn is_expired(&self) -> bool { self.expires_at < std::time::Instant::now() } } -lazy_static! { - pub static ref AI_KEY_CACHE: Cache = Cache::new(500); -} - -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Hash, Clone)] #[serde(rename_all = "lowercase")] pub enum AIProvider { OpenAI, + #[serde(rename = "azure_openai")] + AzureOpenAI, Anthropic, Mistral, DeepSeek, + GoogleAI, Groq, OpenRouter, + TogetherAI, CustomAI, } impl AIProvider { - pub fn get_openai_compatible_base_url(&self) -> Result> { + pub async fn get_base_url(&self, resource_base_url: Option, db: &DB) -> Result { match self { - AIProvider::DeepSeek => Ok(Some("https://api.deepseek.com/v1".to_string())), - AIProvider::Groq => Ok(Some("https://api.groq.com/openai/v1".to_string())), - AIProvider::OpenRouter => Ok(Some("https://openrouter.ai/api/v1".to_string())), - AIProvider::CustomAI => Ok(None), - _ => Err(Error::BadRequest( - "Please use the specific provider instead of the OpenAI compatible one".to_string(), - )), + AIProvider::OpenAI => { + let azure_base_path = sqlx::query_scalar!( + "SELECT value + FROM global_settings + WHERE name = 'openai_azure_base_path'", + ) + .fetch_optional(db) + .await?; + + let azure_base_path = if let Some(azure_base_path) = azure_base_path { + Some( + serde_json::from_value::(azure_base_path).map_err(|e| { + Error::internal_err(format!("validating openai azure base path {e:#}")) + })?, + ) + } else { + OPENAI_AZURE_BASE_PATH.clone() + }; + + Ok(azure_base_path.unwrap_or(OPENAI_BASE_URL.to_string())) + } + AIProvider::DeepSeek => Ok("https://api.deepseek.com/v1".to_string()), + AIProvider::GoogleAI => { + Ok("https://generativelanguage.googleapis.com/v1beta/openai".to_string()) + } + AIProvider::Groq => Ok("https://api.groq.com/openai/v1".to_string()), + AIProvider::OpenRouter => Ok("https://openrouter.ai/api/v1".to_string()), + AIProvider::TogetherAI => Ok("https://api.together.xyz/v1".to_string()), + AIProvider::Anthropic => Ok("https://api.anthropic.com/v1".to_string()), + AIProvider::Mistral => Ok("https://api.mistral.ai/v1".to_string()), + p @ (AIProvider::CustomAI | AIProvider::AzureOpenAI) => { + if let Some(base_url) = resource_base_url { + Ok(base_url) + } else { + Err(Error::BadRequest(format!( + "{:?} provider requires a base URL in the resource", + p + ))) + } + } } } } @@ -402,29 +282,100 @@ impl AIProvider { impl TryFrom<&str> for AIProvider { type Error = Error; fn try_from(s: &str) -> Result { - match s { - "openai" => Ok(AIProvider::OpenAI), - "anthropic" => Ok(AIProvider::Anthropic), - "mistral" => Ok(AIProvider::Mistral), - "groq" => Ok(AIProvider::Groq), - "openrouter" => Ok(AIProvider::OpenRouter), - "deepseek" => Ok(AIProvider::DeepSeek), - "customai" => Ok(AIProvider::CustomAI), - _ => Err(Error::BadRequest(format!("Invalid AI provider: {}", s))), - } + let s = serde_json::from_value::(serde_json::Value::String(s.to_string())) + .map_err(|e| Error::BadRequest(format!("Invalid AI provider: {}", e)))?; + Ok(s) } } -#[derive(Deserialize, Debug)] -pub struct AIResource { - pub path: String, +#[derive(Serialize, Deserialize, Debug)] +pub struct ProviderConfig { + pub resource_path: String, + pub models: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ProviderModel { + pub model: String, pub provider: AIProvider, } -pub fn workspaced_service() -> Router { - let router = Router::new().route("/proxy/*ai", post(proxy)); +#[derive(Serialize, Deserialize, Debug)] +pub struct AIConfig { + #[serde(skip_serializing_if = "Option::is_none")] + pub providers: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub default_model: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub code_completion_model: Option, +} - router +pub fn global_service() -> Router { + Router::new().route("/proxy/*ai", post(global_proxy)) +} + +pub fn workspaced_service() -> Router { + Router::new().route("/proxy/*ai", post(proxy)) +} + +async fn global_proxy( + authed: ApiAuthed, + Extension(db): Extension, + Path(ai_path): Path, + headers: HeaderMap, + body: Bytes, +) -> impl IntoResponse { + let provider = headers + .get("X-Provider") + .map(|v| v.to_str().unwrap_or("").to_string()); + let api_key = headers + .get("X-API-Key") + .map(|v| v.to_str().unwrap_or("").to_string()); + + let provider = match provider { + Some(provider) => AIProvider::try_from(provider.as_str())?, + None => return Err(Error::BadRequest("Provider is required".to_string())), + }; + + let Some(api_key) = api_key else { + return Err(Error::BadRequest("API key is required".to_string())); + }; + + let base_url = provider.get_base_url(None, &db).await?; + + let url = format!("{}/{}", base_url, ai_path); + + let request = HTTP_CLIENT + .post(url) + .header("content-type", "application/json") + .header("Authorization", format!("Bearer {}", api_key)) + .body(body); + + let response = request.send().await.map_err(to_anyhow)?; + + let mut tx = db.begin().await?; + + audit_log( + &mut *tx, + &authed, + "ai.global_request", + ActionKind::Execute, + "global", + Some(&authed.email), + None, + ) + .await?; + tx.commit().await?; + + if response.error_for_status_ref().is_err() { + let err_msg = response.text().await.unwrap_or("".to_string()); + return Err(Error::AIError(err_msg)); + } + + let status_code = response.status(); + let headers = response.headers().clone(); + let stream = response.bytes_stream(); + Ok((status_code, headers, axum::body::Body::from_stream(stream))) } async fn proxy( @@ -434,105 +385,88 @@ async fn proxy( headers: HeaderMap, body: Bytes, ) -> impl IntoResponse { - let workspace_cache = AI_KEY_CACHE.get(&w_id); + let provider = headers + .get("X-Provider") + .map(|v| v.to_str().unwrap_or("").to_string()); + + let provider = match provider { + Some(provider) => AIProvider::try_from(provider.as_str())?, + None => return Err(Error::BadRequest("Provider is required".to_string())), + }; + + let workspace_cache = AI_REQUEST_CACHE.get(&(w_id.clone(), provider.clone())); + let forced_resource_path = headers .get("X-Resource-Path") .map(|v| v.to_str().unwrap_or("").to_string()); - let ai_cache = match workspace_cache { - Some(cache) if !cache.is_expired() && forced_resource_path.is_none() => cache.cached_key, + let request_config = match workspace_cache { + Some(request_cache) if !request_cache.is_expired() && forced_resource_path.is_none() => { + request_cache.config + } _ => { - let (resource, resource_path, ai_provider) = if let Some(resource_path) = - forced_resource_path - { - // guess the provider from the resource type - let record = sqlx::query!( - "SELECT value, resource_type FROM resource WHERE path = $1 AND workspace_id = $2", - &resource_path, - &w_id - ) - .fetch_optional(&db) - .await? - .ok_or_else(|| { - Error::NotFound(format!( - "Could not find the resource {}, update the resource path in the workspace settings", resource_path - )) - })?; - - ( - record.value, - resource_path, - AIProvider::try_from(record.resource_type.as_str())?, - ) + let (resource_path, save_to_cache) = if let Some(resource_path) = forced_resource_path { + // forced resource path + (resource_path, false) } else { - let ai_resource = sqlx::query_scalar!( - "SELECT ai_resource FROM workspace_settings WHERE workspace_id = $1", + let ai_config = sqlx::query_scalar!( + "SELECT ai_config FROM workspace_settings WHERE workspace_id = $1", &w_id ) .fetch_one(&db) .await?; - if ai_resource.is_none() { - return Err(Error::internal_err("AI resource not configured".to_string())); + if ai_config.is_none() { + return Err(Error::internal_err( + "AI resource not configured".to_string(), + )); } - let ai_resource = serde_json::from_value::(ai_resource.unwrap()) + let mut ai_config = serde_json::from_value::(ai_config.unwrap()) .map_err(|e| Error::BadRequest(e.to_string()))?; - let resource = sqlx::query_scalar!( - "SELECT value - FROM resource - WHERE path = $1 AND workspace_id = $2", - &ai_resource.path, - &w_id - ) - .fetch_optional(&db) - .await? - .ok_or_else(|| { - Error::NotFound(format!( - "Could not find the {:?} resource at path {}, update the resource path in the workspace settings", ai_resource.provider, ai_resource.path - )) - })?; + let provider_config = ai_config + .providers + .as_mut() + .map(|providers| providers.remove(&provider)) + .flatten() + .ok_or_else(|| { + Error::BadRequest(format!("Provider {:?} not configured", provider)) + })?; - (resource, ai_resource.path, ai_resource.provider) - }; - - if resource.is_none() { - return Err(Error::internal_err(format!( - "{:?} resource missing value", - ai_provider - ))); - } - - let resource = resource.unwrap(); - - let ai_cache = match ai_provider { - AIProvider::OpenAI => openai::get_cached_value(&db, &w_id, resource).await, - AIProvider::Anthropic => anthropic::get_cached_value(&db, &w_id, resource).await, - AIProvider::Mistral => mistral::get_cached_value(&db, &w_id, resource).await, - _ => { - openai_api_compatible::get_cached_value( - &db, - &w_id, - resource, - ai_provider.get_openai_compatible_base_url()?, - ) - .await + if provider_config.resource_path.is_empty() { + return Err(Error::BadRequest("Resource path is empty".to_string())); } + + (provider_config.resource_path, true) }; - let ai_cache = ai_cache?; - AI_KEY_CACHE.insert(w_id.clone(), AICache::new(resource_path, ai_cache.clone())); - ai_cache + + let resource= sqlx::query_scalar!( + "SELECT value as \"value: sqlx::types::Json>\" FROM resource WHERE path = $1 AND workspace_id = $2", + &resource_path, + &w_id + ) + .fetch_optional(&db) + .await? + .ok_or_else(|| Error::NotFound(format!("Could not find the resource {}, update the resource path in the workspace settings", resource_path)))? + .ok_or_else(|| Error::BadRequest(format!("Empty resource value for {}", resource_path)))?; + + let resource = serde_json::from_str::(resource.0.get()) + .map_err(|e| Error::BadRequest(e.to_string()))?; + + let request_config = AIRequestConfig::new(&provider, &db, &w_id, resource).await?; + if save_to_cache { + AI_REQUEST_CACHE.insert( + (w_id.clone(), provider.clone()), + ExpiringAIRequestConfig::new(request_config.clone()), + ); + } + request_config } }; - let request = match ai_cache { - KeyCache::Openai(cached) => cached.prepare_request(&ai_path, body), - KeyCache::Anthropic(cached) => cached.prepare_request(&ai_path, body), - KeyCache::Mistral(cached) => cached.prepare_request(&ai_path, body), - KeyCache::OpenaiApiCompatible(cached) => cached.prepare_request(&ai_path, body), - }; + let request = request_config.prepare_request(&provider, &ai_path, body)?; - let response = request?.send().await.map_err(to_anyhow)?; + let response = request.send().await.map_err(to_anyhow)?; let mut tx = db.begin().await?; @@ -543,14 +477,14 @@ async fn proxy( ActionKind::Execute, &w_id, Some(&authed.email), - Some([("ai_resource_path", &format!("{:?}", ai_path)[..])].into()), + Some([("ai_config_path", &format!("{:?}", ai_path)[..])].into()), ) .await?; tx.commit().await?; if response.error_for_status_ref().is_err() { let err_msg = response.text().await.unwrap_or("".to_string()); - return Err(Error::AiError(err_msg)); + return Err(Error::AIError(err_msg)); } let status_code = response.status(); diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index 952ff6d77e..533da11d1c 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -12,7 +12,7 @@ use crate::{ db::{ApiAuthed, DB}, resources::get_resource_value_interpolated_internal, users::{require_owner_of_path, OptAuthed}, - utils::WithStarredInfoQuery, + utils::{RunnableKind, WithStarredInfoQuery}, webhook_util::{WebhookMessage, WebhookShared}, HTTP_CLIENT, }; @@ -20,15 +20,14 @@ use crate::{ use crate::{ job_helpers_ee::{ download_s3_file_internal, get_random_file_name, get_s3_resource, - get_workspace_s3_resource, load_image_preview_internal, upload_file_from_req, - DownloadFileQuery, LoadImagePreviewQuery, UploadFileResponse, + get_workspace_s3_resource, upload_file_from_req, DownloadFileQuery, }, users::fetch_api_authed_from_permissioned_as, }; -#[cfg(feature = "parquet")] use axum::response::Response; use axum::{ - extract::{Extension, Json, Path, Query}, + body::Body, + extract::{Extension, Json, Multipart, Path, Query}, response::IntoResponse, routing::{delete, get, post}, Router, @@ -51,9 +50,6 @@ use sqlx::{types::Uuid, FromRow}; use std::str; use windmill_audit::audit_ee::audit_log; use windmill_audit::ActionKind; -#[cfg(feature = "parquet")] -use windmill_common::s3_helpers::build_object_store_client; -use windmill_common::variables::encrypt; use windmill_common::{ apps::{AppScriptId, ListAppQuery}, cache::{self, future::FutureCachedExt}, @@ -65,7 +61,7 @@ use windmill_common::{ http_get_from_hub, not_found_if_none, paginate, query_elems_from_hub, require_admin, Pagination, StripPath, }, - variables::{build_crypt, build_crypt_with_key_suffix}, + variables::{build_crypt, build_crypt_with_key_suffix, encrypt}, worker::{to_raw_value, CLOUD_HOSTED}, HUB_BASE_URL, }; @@ -73,6 +69,16 @@ use windmill_common::{ use windmill_git_sync::{handle_deployment_metadata, DeployedObject}; use windmill_queue::{push, PushArgs, PushArgsOwned, PushIsolationLevel}; +#[cfg(feature = "parquet")] +use hmac::Mac; +#[cfg(feature = "parquet")] +use windmill_common::{ + jwt, + oauth2::HmacSha256, + s3_helpers::{build_object_store_client, S3Object}, + variables::get_workspace_key, +}; + pub fn workspaced_service() -> Router { Router::new() .route("/list", get(list_apps)) @@ -82,25 +88,30 @@ pub fn workspaced_service() -> Router { .route("/get/draft/*path", get(get_app_w_draft)) .route("/secret_of/*path", get(get_secret_id)) .route("/get/v/*id", get(get_app_by_id)) + .route("/get_data/v/*id", get(get_raw_app_data)) .route("/exists/*path", get(exists_app)) .route("/update/*path", post(update_app)) + .route("/update_raw/*path", post(update_app_raw)) .route("/delete/*path", delete(delete_app)) .route("/create", post(create_app)) + .route("/create_raw", post(create_app_raw)) .route("/history/p/*path", get(get_app_history)) .route("/get_latest_version/*path", get(get_latest_version)) .route("/history_update/a/:id/v/:version", post(update_app_history)) + .route( + "/list_paths_from_workspace_runnable/:runnable_kind/*path", + get(list_paths_from_workspace_runnable), + ) .route("/custom_path_exists/*custom_path", get(custom_path_exists)) + .route("/sign_s3_objects", post(sign_s3_objects)) } pub fn unauthed_service() -> Router { Router::new() .route("/execute_component/*path", post(execute_component)) .route("/upload_s3_file/*path", post(upload_s3_file_from_app)) + .route("/delete_s3_file", delete(delete_s3_file_from_app)) .route("/download_s3_file/*path", get(download_s3_file_from_app)) - .route( - "/load_image_preview/*path", - get(load_s3_file_image_preview_from_app), - ) .route("/public_app/:secret", get(get_public_app_by_secret)) .route("/public_resource/*path", get(get_public_resource)) } @@ -127,6 +138,12 @@ pub struct ListableApp { #[sqlx(default)] #[serde(skip_serializing_if = "Option::is_none")] pub deployment_msg: Option, + #[serde(skip_serializing_if = "is_false")] + pub raw_app: bool, +} + +fn is_false(b: &bool) -> bool { + !b } #[derive(FromRow, Serialize, Deserialize)] @@ -224,6 +241,13 @@ pub struct S3Input { file_key_regex: String, } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct S3Key { + s3_path: String, + #[serde(skip_serializing_if = "Option::is_none")] + storage: Option, +} + #[derive(Serialize, Deserialize, Debug, Clone, Default)] pub struct Policy { pub on_behalf_of: Option, @@ -238,6 +262,7 @@ pub struct Policy { pub triggerables_v2: Option>, pub execution_mode: ExecutionMode, pub s3_inputs: Option>, + pub allowed_s3_keys: Option>, } #[derive(Deserialize)] @@ -312,7 +337,8 @@ async fn list_apps( "app.extra_perms", "favorite.path IS NOT NULL as starred", "draft.path IS NOT NULL as has_draft", - "draft_only" + "draft_only", + "app_version.raw_app", ]) .left() .join("favorite") @@ -371,6 +397,44 @@ async fn list_apps( Ok(Json(rows)) } +async fn get_raw_app_data(Path((w_id, version_id)): Path<(String, String)>) -> Result { + let file_path = format!("/tmp/wmill/{}/{}", w_id, version_id); + let file = tokio::fs::File::open(file_path).await?; + let stream = tokio_util::io::ReaderStream::new(file); + let res = Response::builder().header( + http::header::CONTENT_TYPE, + if version_id.ends_with(".css") { + "text/css" + } else { + "text/javascript" + }, + ); + Ok(res.body(Body::from_stream(stream)).unwrap()) +} + +// async fn get_app_version( +// authed: ApiAuthed, +// Extension(user_db): Extension, +// Path((w_id, path)): Path<(String, StripPath)>, +// ) -> JsonResult { +// let path = path.to_path(); +// let mut tx = user_db.begin(&authed).await?; + +// let version_o = sqlx::query_scalar!( +// "SELECT app.versions[array_upper(app.versions, 1)] as version FROM app +// WHERE app.path = $1 AND app.workspace_id = $2", +// path, +// &w_id, +// ) +// .fetch_optional(&mut *tx) +// .await? +// .flatten(); +// tx.commit().await?; + +// let version = not_found_if_none(version_o, "App", path)?; +// Ok(Json(version)) +// } + async fn get_app( authed: ApiAuthed, Extension(user_db): Extension, @@ -711,23 +775,155 @@ async fn get_secret_id( Ok(hx) } +macro_rules! process_app_multipart { + ($authed:expr, $user_db:expr, $db:expr, $w_id:expr, $path:expr, $multipart:expr, $internal_fn:expr) => { + async { + let mut saved_app = None; + let mut uploaded_js = false; + + //todo: use s3 instead + let file_path = format!("/tmp/wmill/{}", $w_id); + std::fs::create_dir_all(&file_path).unwrap(); + + let mut multipart = $multipart; + while let Some(field) = multipart.next_field().await.unwrap() { + let name = field.name().unwrap().to_string(); + let data = field.bytes().await.unwrap(); + if name == "app" { + let app = serde_json::from_slice(&data).map_err(to_anyhow)?; + let (ntx, npath, nid) = $internal_fn( + $authed.clone(), + $db.clone(), + $user_db.clone(), + $w_id, + $path, + true, + app, + ) + .await?; + saved_app = Some((npath, nid, ntx)); + } else if name == "js" { + if let Some((_npath, id, _tx)) = saved_app.as_ref() { + let file_path = format!("{}/{}.js", file_path, id); + std::fs::write(file_path, data).unwrap(); + uploaded_js = true; + } else { + return Err(Error::BadRequest( + "App payload need to be created first".to_string(), + )); + } + } else if name == "css" { + if let Some((_npath, id, _tx)) = saved_app.as_ref() { + let file_path = format!("{}/{}.css", file_path, id); + std::fs::write(file_path, data).unwrap(); + } else { + return Err(Error::BadRequest( + "App payload need to be created first".to_string(), + )); + } + } else { + return Err(Error::BadRequest(format!("Unsupported field: {}", name))); + } + } + if !uploaded_js { + return Err(Error::BadRequest("js or css file not uploaded".to_string())); + } + if let Some((npath, id, tx)) = saved_app { + tx.commit().await?; + Ok((npath, id)) + } else { + Err(Error::BadRequest("App not created".to_string())) + } + } + }; +} + +async fn create_app_raw<'a>( + authed: ApiAuthed, + Extension(user_db): Extension, + Extension(db): Extension, + Extension(webhook): Extension, + Path(w_id): Path, + multipart: Multipart, +) -> Result<(StatusCode, String)> { + let (path, _id) = process_app_multipart!( + authed, + user_db, + db, + &w_id, + "", + multipart, + |authed, db, user_db, w_id, _path, raw_app, app| create_app_internal( + authed, db, user_db, w_id, raw_app, app + ) + ) + .await?; + + webhook.send_message( + w_id.clone(), + WebhookMessage::CreateApp { workspace: w_id, path: path.clone() }, + ); + Ok((StatusCode::CREATED, path)) +} + +async fn list_paths_from_workspace_runnable( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, runnable_kind, path)): Path<(String, RunnableKind, StripPath)>, +) -> JsonResult> { + let mut tx = user_db.begin(&authed).await?; + let runnables = sqlx::query_scalar!( + r#"SELECT a.path + FROM workspace_runnable_dependencies wru + JOIN app a + ON wru.app_path = a.path AND wru.workspace_id = a.workspace_id + WHERE wru.runnable_path = $1 AND wru.runnable_is_flow = $2 AND wru.workspace_id = $3"#, + path.to_path(), + matches!(runnable_kind, RunnableKind::Flow), + w_id + ) + .fetch_all(&mut *tx) + .await?; + tx.commit().await?; + Ok(Json(runnables)) +} + async fn create_app( authed: ApiAuthed, Extension(user_db): Extension, Extension(db): Extension, Extension(webhook): Extension, Path(w_id): Path, - Json(mut app): Json, + Json(app): Json, ) -> Result<(StatusCode, String)> { - let mut tx = user_db.clone().begin(&authed).await?; + let path = app.path.clone(); + let (new_tx, _path, _id) = create_app_internal(authed, db, user_db, &w_id, false, app).await?; + new_tx.commit().await?; + + webhook.send_message( + w_id.clone(), + WebhookMessage::CreateApp { workspace: w_id, path: path.clone() }, + ); + + Ok((StatusCode::CREATED, path)) +} + +async fn create_app_internal<'a>( + authed: ApiAuthed, + db: sqlx::Pool, + user_db: UserDB, + w_id: &String, + raw_app: bool, + mut app: CreateApp, +) -> Result<(sqlx::Transaction<'a, sqlx::Postgres>, String, i64)> { + let mut tx = user_db.clone().begin(&authed).await?; app.policy.on_behalf_of = Some(username_to_permissioned_as(&authed.username)); app.policy.on_behalf_of_email = Some(authed.email.clone()); - + let path = app.path.clone(); if &app.path == "" { return Err(Error::BadRequest("App path cannot be empty".to_string())); } - let exists = sqlx::query_scalar!( "SELECT EXISTS(SELECT 1 FROM app WHERE path = $1 AND workspace_id = $2)", &app.path, @@ -736,21 +932,19 @@ async fn create_app( .fetch_one(&mut *tx) .await? .unwrap_or(false); - if exists { return Err(Error::BadRequest(format!( "App with path {} already exists", &app.path ))); } - if let Some(custom_path) = &app.custom_path { require_admin(authed.is_admin, &authed.username)?; let exists = sqlx::query_scalar!( "SELECT EXISTS(SELECT 1 FROM app WHERE custom_path = $1 AND ($2::TEXT IS NULL OR workspace_id = $2))", custom_path, - if *CLOUD_HOSTED { Some(&w_id) } else { None } + if *CLOUD_HOSTED { Some(w_id) } else { None } ) .fetch_one(&mut *tx) .await?.unwrap_or(false); @@ -762,7 +956,6 @@ async fn create_app( ))); } } - sqlx::query!( "DELETE FROM draft WHERE path = $1 AND workspace_id = $2 AND typ = 'app'", &app.path, @@ -770,7 +963,6 @@ async fn create_app( ) .execute(&mut *tx) .await?; - let id = sqlx::query_scalar!( "INSERT INTO app (workspace_id, path, summary, policy, versions, draft_only, custom_path) @@ -781,24 +973,24 @@ async fn create_app( json!(app.policy), app.draft_only, app.custom_path + .as_ref() .map(|s| if s.is_empty() { None } else { Some(s) }) .flatten() ) .fetch_one(&mut *tx) .await?; - let v_id = sqlx::query_scalar!( "INSERT INTO app_version - (app_id, value, created_by) - VALUES ($1, $2::text::json, $3) RETURNING id", + (app_id, value, created_by, raw_app) + VALUES ($1, $2::text::json, $3, $4) RETURNING id", id, //to preserve key orders serde_json::to_string(&app.value).unwrap(), authed.username, + raw_app ) .fetch_one(&mut *tx) .await?; - sqlx::query!( "UPDATE app SET versions = array_append(versions, $1::bigint) WHERE id = $2", v_id, @@ -812,22 +1004,20 @@ async fn create_app( &authed, "apps.create", ActionKind::Create, - &w_id, + w_id, Some(&app.path), None, ) .await?; - let mut args: HashMap> = HashMap::new(); - if let Some(dm) = app.deployment_message { + if let Some(dm) = &app.deployment_message { args.insert("deployment_message".to_string(), to_raw_value(&dm)); } - let tx = PushIsolationLevel::Transaction(tx); let (dependency_job_uuid, new_tx) = push( &db, tx, - &w_id, + w_id, JobPayload::AppDependencies { path: app.path.clone(), version: v_id }, PushArgs { args: &args, extra: None }, &authed.username, @@ -851,14 +1041,7 @@ async fn create_app( .await?; tracing::info!("Pushed app dependency job {}", dependency_job_uuid); - new_tx.commit().await?; - - webhook.send_message( - w_id.clone(), - WebhookMessage::CreateApp { workspace: w_id, path: app.path.clone() }, - ); - - Ok((StatusCode::CREATED, app.path)) + Ok((new_tx, path, v_id)) } async fn list_hub_apps(Extension(db): Extension) -> impl IntoResponse { @@ -979,12 +1162,76 @@ async fn update_app( Path((w_id, path)): Path<(String, StripPath)>, Json(ns): Json, ) -> Result { - use sql_builder::prelude::*; - + // create_app_internal(authed, user_db, db, &w_id, &mut app).await?; let path = path.to_path(); + let opath = path.to_string(); + let (new_tx, npath, _v_id) = + update_app_internal(authed, db, user_db, &w_id, path, false, ns).await?; + new_tx.commit().await?; + webhook.send_message( + w_id.clone(), + WebhookMessage::UpdateApp { + workspace: w_id.clone(), + old_path: opath.clone(), + new_path: npath.clone(), + }, + ); + + Ok(format!("app {} updated (npath: {:?})", opath, npath)) +} + +async fn update_app_raw<'a>( + authed: ApiAuthed, + Extension(user_db): Extension, + Extension(db): Extension, + Extension(webhook): Extension, + Path((w_id, path)): Path<(String, StripPath)>, + multipart: Multipart, +) -> Result { + let path = path.to_path(); + let opath = path.to_string(); + let (npath, _id) = process_app_multipart!( + authed, + user_db, + db, + &w_id, + path, + multipart, + update_app_internal + ) + .await?; + + webhook.send_message( + w_id.clone(), + WebhookMessage::UpdateApp { + workspace: w_id.clone(), + old_path: opath.to_owned(), + new_path: npath.clone(), + }, + ); + + Ok(format!("app {} updated (npath: {:?})", opath, npath)) +} +// async fn create_app_internal<'a>( +// authed: ApiAuthed, +// db: sqlx::Pool, +// user_db: UserDB, +// w_id: &String, +// app: &mut CreateApp, +// ) + +async fn update_app_internal<'a>( + authed: ApiAuthed, + db: sqlx::Pool, + user_db: UserDB, + w_id: &str, + path: &str, + raw_app: bool, + ns: EditApp, +) -> Result<(sqlx::Transaction<'a, sqlx::Postgres>, String, i64)> { + use sql_builder::prelude::*; let mut tx = user_db.clone().begin(&authed).await?; - let npath = if ns.policy.is_some() || ns.path.is_some() || ns.summary.is_some() @@ -1031,7 +1278,7 @@ async fn update_app( let exists = sqlx::query_scalar!( "SELECT EXISTS(SELECT 1 FROM app WHERE custom_path = $1 AND ($2::TEXT IS NULL OR workspace_id = $2) AND NOT (path = $3 AND workspace_id = $4))", ncustom_path, - if *CLOUD_HOSTED { Some(&w_id) } else { None }, + if *CLOUD_HOSTED { Some(w_id) } else { None }, path, w_id ) @@ -1078,12 +1325,13 @@ async fn update_app( let v_id = sqlx::query_scalar!( "INSERT INTO app_version - (app_id, value, created_by) - VALUES ($1, $2::text::json, $3) RETURNING id", + (app_id, value, created_by, raw_app) + VALUES ($1, $2::text::json, $3, $4) RETURNING id", app_id, //to preserve key orders serde_json::to_string(&nvalue).unwrap(), authed.username, + raw_app ) .fetch_one(&mut *tx) .await?; @@ -1114,7 +1362,6 @@ async fn update_app( ))); } }; - sqlx::query!( "DELETE FROM draft WHERE path = $1 AND workspace_id = $2 AND typ = 'app'", path, @@ -1122,29 +1369,26 @@ async fn update_app( ) .execute(&mut *tx) .await?; - audit_log( &mut *tx, &authed, "apps.update", ActionKind::Update, - &w_id, + w_id, Some(&npath), None, ) .await?; - let tx = PushIsolationLevel::Transaction(tx); let mut args: HashMap> = HashMap::new(); if let Some(dm) = ns.deployment_message { args.insert("deployment_message".to_string(), to_raw_value(&dm)); } args.insert("parent_path".to_string(), to_raw_value(&path)); - let (dependency_job_uuid, new_tx) = push( &db, tx, - &w_id, + w_id, JobPayload::AppDependencies { path: npath.clone(), version: v_id }, PushArgs { args: &args, extra: None }, &authed.username, @@ -1167,18 +1411,7 @@ async fn update_app( ) .await?; tracing::info!("Pushed app dependency job {}", dependency_job_uuid); - new_tx.commit().await?; - - webhook.send_message( - w_id.clone(), - WebhookMessage::UpdateApp { - workspace: w_id, - old_path: path.to_owned(), - new_path: npath.clone(), - }, - ); - - Ok(format!("app {} updated (npath: {:?})", path, npath)) + Ok((new_tx, npath, v_id)) } #[derive(Debug, Deserialize, Clone)] @@ -1497,6 +1730,13 @@ async fn upload_s3_file_from_app() -> Result<()> { )); } +#[cfg(not(feature = "parquet"))] +async fn delete_s3_file_from_app() -> Result<()> { + return Err(Error::BadRequest( + "This endpoint requires the parquet feature to be enabled".to_string(), + )); +} + #[cfg(feature = "parquet")] #[derive(Debug, Deserialize, Clone)] struct UploadFileToS3Query { @@ -1511,6 +1751,106 @@ struct UploadFileToS3Query { force_viewer_allowed_resources: Option, } +#[cfg(feature = "parquet")] +#[derive(Serialize, Deserialize)] +struct S3DeleteTokenClaims { + file_key: String, + on_behalf_of_email: String, + permissioned_as: String, + username: String, + s3_resource_path: Option, + workspace: String, + pub exp: usize, +} + +#[cfg(feature = "parquet")] +#[derive(Deserialize)] +struct S3TokenRequestBody { + s3_objects: Vec, +} +#[cfg(feature = "parquet")] +async fn sign_s3_objects( + Extension(db): Extension, + Path(w_id): Path, + Json(body): Json, +) -> Result>> { + let workspace_key = get_workspace_key(&w_id, &db).await?; + + let futures = body.s3_objects.into_iter().map(|s3_object| async { + let exp = (chrono::Utc::now() + chrono::Duration::hours(12)).timestamp(); + let mut message = format!("file_key={}&exp={}", s3_object.s3.clone(), exp); + if let Some(ref storage) = s3_object.storage { + message = format!("{}&storage={}", message, storage); + } + + let mut max = HmacSha256::new_from_slice(workspace_key.as_bytes()) + .map_err(|err| Error::internal_err(format!("Failed to create hmac: {}", err)))?; + max.update(message.as_bytes()); + let result = max.finalize(); + let signature = hex::encode(result.into_bytes()); + + let presigned = format!("exp={}&sig={}", exp, signature); + + Ok::<_, Error>(S3Object { presigned: Some(presigned), ..s3_object }) + }); + + let signed_s3_objects = futures::future::try_join_all(futures).await?; + + Ok(Json(signed_s3_objects)) +} + +#[cfg(feature = "parquet")] +async fn validate_s3_signature(file_query: &AppS3FileQuery, w_id: &str, db: &DB) -> Result<()> { + let workspace_key = get_workspace_key(w_id, &db).await?; + + let Some(exp) = file_query + .exp + .as_ref() + .map(|e| e.parse::().unwrap_or_default()) + else { + return Err(Error::BadRequest("Missing exp".to_string())); + }; + + let Some(ref sig) = file_query.sig else { + return Err(Error::BadRequest("Missing signature".to_string())); + }; + + let mut message = format!("file_key={}&exp={}", file_query.s3, exp); + + if let Some(ref storage) = file_query.storage { + message = format!("{}&storage={}", message, storage); + } + + let mut mac = HmacSha256::new_from_slice(workspace_key.as_bytes()) + .map_err(|err| Error::internal_err(format!("Failed to create hmac: {}", err)))?; + + mac.update(message.as_bytes()); + + let sig_bytes = hex::decode(sig)?; + mac.verify_slice(&sig_bytes) + .map_err(|err| Error::BadRequest(format!("Invalid signature: {}", err)))?; + + if exp < chrono::Utc::now().timestamp() { + return Err(Error::BadRequest("Signature expired".to_string())); + } + + Ok(()) +} + +#[cfg(not(feature = "parquet"))] +async fn sign_s3_objects() -> Result<()> { + return Err(Error::BadRequest( + "This endpoint requires the parquet feature to be enabled".to_string(), + )); +} + +#[cfg(feature = "parquet")] +#[derive(Serialize)] +struct AppUploadFileResponse { + file_key: String, + delete_token: String, +} + #[cfg(feature = "parquet")] async fn upload_s3_file_from_app( OptAuthed(opt_authed): OptAuthed, @@ -1518,7 +1858,7 @@ async fn upload_s3_file_from_app( Path((w_id, path)): Path<(String, StripPath)>, Query(query): Query, request: axum::extract::Request, -) -> JsonResult { +) -> JsonResult { let policy = if let Some(file_key_regex) = query.force_viewer_file_key_regex { Some(Policy { execution_mode: ExecutionMode::Viewer, @@ -1537,6 +1877,7 @@ async fn upload_s3_file_from_app( .map(|s| s.split(',').map(|s| s.to_string()).collect()) .unwrap_or_default(), }]), + allowed_s3_keys: None, }) } else { let policy_o = sqlx::query_scalar!( @@ -1554,7 +1895,10 @@ async fn upload_s3_file_from_app( let user_db = UserDB::new(db.clone()); - let (s3_resource_opt, file_key) = if policy.as_ref().is_some_and(|p| p.s3_inputs.is_some()) { + let (s3_resource_opt, file_key, on_behalf_of_email, permissioned_as, username) = if policy + .as_ref() + .is_some_and(|p| p.s3_inputs.is_some()) + { let policy = policy.unwrap(); let s3_inputs = policy.s3_inputs.as_ref().unwrap(); @@ -1562,11 +1906,11 @@ async fn upload_s3_file_from_app( get_on_behalf_details_from_policy_and_authed(&policy, &opt_authed).await?; let on_behalf_authed = fetch_api_authed_from_permissioned_as( - permissioned_as, - email, + permissioned_as.clone(), + email.clone(), &w_id, &db, - Some(username), + Some(username.clone()), ) .await?; @@ -1617,6 +1961,9 @@ async fn upload_s3_file_from_app( .await?, ), file_key, + email, + permissioned_as, + username, ) } else { return Err(Error::BadRequest( @@ -1640,13 +1987,16 @@ async fn upload_s3_file_from_app( .await?, ), file_key, + email, + permissioned_as, + username, ) } } else { let (_, s3_resource_opt) = get_workspace_s3_resource(&on_behalf_authed, &db, None, "", &w_id, None) .await?; - (s3_resource_opt, file_key) + (s3_resource_opt, file_key, email, permissioned_as, username) } } else { return Err(Error::BadRequest( @@ -1672,7 +2022,7 @@ async fn upload_s3_file_from_app( let (_, s3_resource_opt) = get_workspace_s3_resource(&on_behalf_authed, &db, None, "", &w_id, None).await?; - (s3_resource_opt, file_key) + (s3_resource_opt, file_key, email, permissioned_as, username) } } else { // backward compatibility (no policy) @@ -1682,6 +2032,12 @@ async fn upload_s3_file_from_app( .file_key .unwrap_or_else(|| get_random_file_name(query.file_extension)); + let (on_behalf_of_email, permissioned_as, username) = ( + authed.email.clone(), + username_to_permissioned_as(&authed.username), + authed.display_username().to_string(), + ); + if let Some(ref s3_resource_path) = query.s3_resource_path { ( Some( @@ -1698,12 +2054,21 @@ async fn upload_s3_file_from_app( .await?, ), file_key, + on_behalf_of_email, + permissioned_as, + username, ) } else { let (_, s3_resource) = get_workspace_s3_resource(&authed, &db, None, "", &w_id, None).await?; - (s3_resource, file_key) + ( + s3_resource, + file_key, + on_behalf_of_email, + permissioned_as, + username, + ) } } else { return Err(Error::BadRequest("Missing s3 policy".to_string())); @@ -1733,7 +2098,88 @@ async fn upload_s3_file_from_app( upload_file_from_req(s3_client, &file_key, request, options).await?; - return Ok(Json(UploadFileResponse { file_key })); + let delete_token = jwt::encode_with_internal_secret(S3DeleteTokenClaims { + file_key: file_key.clone(), + on_behalf_of_email, + permissioned_as, + username, + s3_resource_path: query.s3_resource_path, + workspace: w_id.clone(), + exp: (chrono::Utc::now() + chrono::Duration::hours(12)).timestamp() as usize, + }) + .await?; + + return Ok(Json(AppUploadFileResponse { file_key, delete_token })); +} + +#[cfg(feature = "parquet")] +#[derive(Deserialize)] +struct DeleteS3FileQuery { + delete_token: String, +} + +#[cfg(feature = "parquet")] +async fn delete_s3_file_from_app( + Extension(db): Extension, + Extension(user_db): Extension, + Path(w_id): Path, + Query(query): Query, +) -> Result<()> { + let S3DeleteTokenClaims { + file_key, + on_behalf_of_email, + permissioned_as, + username, + s3_resource_path, + workspace, + .. + } = jwt::decode_with_internal_secret::(&query.delete_token).await?; + + if workspace != w_id { + return Err(Error::BadRequest("Invalid workspace".to_string())); + } + + let on_behalf_authed = fetch_api_authed_from_permissioned_as( + permissioned_as, + on_behalf_of_email, + &w_id, + &db, + Some(username), + ) + .await?; + + let s3_resource = if let Some(s3_resource_path) = s3_resource_path { + get_s3_resource( + &on_behalf_authed, + &db, + Some(user_db), + "", + &w_id, + s3_resource_path.as_str(), + None, + None, + ) + .await? + } else { + let (_, s3_resource) = + get_workspace_s3_resource(&on_behalf_authed, &db, None, "", &w_id, None).await?; + + s3_resource.ok_or(Error::internal_err( + "No files storage resource defined at the workspace level".to_string(), + ))? + }; + + let s3_client = build_object_store_client(&s3_resource).await?; + + let path = object_store::path::Path::parse(file_key.as_str()) + .map_err(|e| Error::internal_err(format!("Error parsing file key: {}", e)))?; + + s3_client.delete(&path).await.map_err(|err| { + tracing::error!("Error deleting file: {:?}", err); + Error::internal_err(format!("Error deleting file: {}", err.to_string())) + })?; + + Ok(()) } #[cfg(not(feature = "parquet"))] @@ -1749,26 +2195,41 @@ async fn get_on_behalf_authed_from_app( path: &str, w_id: &str, opt_authed: &Option, -) -> Result { - let policy_o = sqlx::query_scalar!( - "SELECT policy from app WHERE path = $1 AND workspace_id = $2", - path, - w_id - ) - .fetch_optional(db) - .await?; - - let policy = policy_o - .map(|p| serde_json::from_value::(p).map_err(to_anyhow)) - .transpose()? - .unwrap_or_else(|| Policy { + force_allowed_s3_keys: Option>, +) -> Result<(ApiAuthed, Policy)> { + let policy = if let Some(force_allowed_s3_keys) = force_allowed_s3_keys { + Policy { execution_mode: ExecutionMode::Viewer, triggerables: None, triggerables_v2: None, on_behalf_of: None, on_behalf_of_email: None, s3_inputs: None, - }); + allowed_s3_keys: Some(force_allowed_s3_keys), + } + } else { + // TODO: improve db query to not return uneeded fields + let policy_o = sqlx::query_scalar!( + "SELECT policy from app WHERE path = $1 AND workspace_id = $2", + path, + w_id + ) + .fetch_optional(db) + .await?; + + policy_o + .map(|p| serde_json::from_value::(p).map_err(to_anyhow)) + .transpose()? + .unwrap_or_else(|| Policy { + execution_mode: ExecutionMode::Viewer, + triggerables: None, + triggerables_v2: None, + on_behalf_of: None, + on_behalf_of_email: None, + s3_inputs: None, + allowed_s3_keys: None, + }) + }; let (username, permissioned_as, email) = get_on_behalf_details_from_policy_and_authed(&policy, &opt_authed).await?; @@ -1777,85 +2238,121 @@ async fn get_on_behalf_authed_from_app( fetch_api_authed_from_permissioned_as(permissioned_as, email, &w_id, &db, Some(username)) .await?; - Ok(on_behalf_authed) + Ok((on_behalf_authed, policy)) } #[cfg(feature = "parquet")] async fn check_if_allowed_to_access_s3_file_from_app( db: &DB, opt_authed: &Option, - file_key: &str, + file_query: &AppS3FileQuery, w_id: &str, path: &str, + policy: &Policy, ) -> Result<()> { // if anonymous, check that the file was the result of an app script ran by an anonymous user in the last 3 hours // otherwise, if logged in, allow any file (TODO: change that when we implement better s3 policy) - let allowed = opt_authed.is_some() - || sqlx::query_scalar!( - r#"SELECT EXISTS ( - SELECT 1 FROM v2_as_completed_job - WHERE workspace_id = $2 - AND (job_kind = 'appscript' OR job_kind = 'preview') - AND created_by = 'anonymous' - AND started_at > now() - interval '3 hours' - AND script_path LIKE $3 || '/%' - AND result @> ('{"s3":"' || $1 || '"}')::jsonb - )"#, - file_key, - w_id, - path, - ) - .fetch_one(db) - .await? - .unwrap_or(false); - - if !allowed { - Err(Error::BadRequest("File restricted".to_string())) - } else { + if file_query.sig.is_some() { + validate_s3_signature(file_query, w_id, &db).await + } else if opt_authed.is_some() { Ok(()) + } else { + let allowed = policy + .allowed_s3_keys + .as_ref() + .unwrap() + .iter() + .any(|key| key.s3_path == file_query.s3 && key.storage == file_query.storage) + || { + sqlx::query_scalar!( + r#"SELECT EXISTS ( + SELECT 1 FROM v2_as_completed_job + WHERE workspace_id = $2 + AND (job_kind = 'appscript' OR job_kind = 'preview') + AND created_by = 'anonymous' + AND started_at > now() - interval '3 hours' + AND script_path LIKE $3 || '/%' + AND result @> ('{"s3":"' || $1 || '"}')::jsonb + )"#, + file_query.s3, + w_id, + path, + ) + .fetch_one(db) + .await? + .unwrap_or(false) + }; + + if !allowed { + Err(Error::BadRequest("File restricted".to_string())) + } else { + Ok(()) + } } } +#[cfg(feature = "parquet")] +#[derive(Deserialize, Debug)] +struct AppS3FileQuery { + s3: String, + storage: Option, + sig: Option, + exp: Option, +} + +#[cfg(feature = "parquet")] +#[derive(Deserialize, Debug)] +struct AppS3FileQueryWithForceViewerAllowedS3Keys { + #[serde(flatten)] + pub file_query: AppS3FileQuery, + pub force_viewer_allowed_s3_keys: Option, +} + #[cfg(feature = "parquet")] async fn download_s3_file_from_app( OptAuthed(opt_authed): OptAuthed, Extension(db): Extension, Path((w_id, path)): Path<(String, StripPath)>, - Query(query): Query, + Query(query): Query, ) -> Result { let path = path.to_path(); - let on_behalf_authed = get_on_behalf_authed_from_app(&db, &path, &w_id, &opt_authed).await?; + let force_viewer_allowed_s3_keys = if let Some(force_viewer_allowed_s3_keys) = + query.force_viewer_allowed_s3_keys.clone() + { + Some(serde_json::from_str::>(&force_viewer_allowed_s3_keys).unwrap_or_default()) + } else { + None + }; - check_if_allowed_to_access_s3_file_from_app(&db, &opt_authed, &query.file_key, &w_id, &path) - .await?; + let (on_behalf_authed, policy) = + get_on_behalf_authed_from_app(&db, &path, &w_id, &opt_authed, force_viewer_allowed_s3_keys) + .await?; - download_s3_file_internal(on_behalf_authed, &db, None, "", &w_id, query).await -} + check_if_allowed_to_access_s3_file_from_app( + &db, + &opt_authed, + &query.file_query, + &w_id, + &path, + &policy, + ) + .await?; -#[cfg(not(feature = "parquet"))] -async fn load_s3_file_image_preview_from_app() -> Result<()> { - return Err(Error::BadRequest( - "This endpoint requires the parquet feature to be enabled".to_string(), - )); -} - -#[cfg(feature = "parquet")] -async fn load_s3_file_image_preview_from_app( - OptAuthed(opt_authed): OptAuthed, - Extension(db): Extension, - Path((w_id, path)): Path<(String, StripPath)>, - Query(query): Query, -) -> Result { - let path = path.to_path(); - - let on_behalf_authed = get_on_behalf_authed_from_app(&db, &path, &w_id, &opt_authed).await?; - - check_if_allowed_to_access_s3_file_from_app(&db, &opt_authed, &query.file_key, &w_id, &path) - .await?; - - load_image_preview_internal(on_behalf_authed, &db, "", &w_id, query).await + download_s3_file_internal( + on_behalf_authed, + &db, + None, + "", + &w_id, + DownloadFileQuery { + file_key: query.file_query.s3, + s3_resource_path: None, + storage: query.file_query.storage, + }, + ) + .await } fn get_on_behalf_of(policy: &Policy) -> Result<(String, String)> { diff --git a/backend/windmill-api/src/args.rs b/backend/windmill-api/src/args.rs index a482745f27..fd2ceefabe 100644 --- a/backend/windmill-api/src/args.rs +++ b/backend/windmill-api/src/args.rs @@ -23,7 +23,7 @@ use crate::db::ApiAuthed; #[cfg(feature = "parquet")] use crate::job_helpers_ee::{get_random_file_name, upload_file_internal}; -#[derive(Default)] +#[derive(Debug, Default)] pub struct WebhookArgs { pub args: PushArgsOwned, pub multipart: Option, @@ -163,6 +163,130 @@ async fn req_to_string( .map_err(|e| Error::BadRequest(format!("invalid utf8: {}", e)).into_response()) } +pub async fn try_from_request_body( + request: Request, + _state: &S, + use_raw: Option, + wrap_body: Option, +) -> Result +where + S: Send + Sync, +{ + let (content_type, mut extra, use_raw, wrap_body) = { + let headers_map = request.headers(); + let content_type_header = headers_map.get(CONTENT_TYPE); + let content_type = content_type_header.and_then(|value| value.to_str().ok()); + let uri = request.uri(); + let query = Query::::try_from_uri(uri).unwrap().0; + let mut extra = build_extra(&headers_map, query.include_header); + let query_decode = DecodeQueries::from_uri(uri); + if let Some(DecodeQueries(queries)) = query_decode { + extra.extend(queries); + } + let raw = query.raw.unwrap_or(use_raw.unwrap_or(false)); + let wrap_body = query.wrap_body.unwrap_or(wrap_body.unwrap_or(false)); + (content_type, extra, raw, wrap_body) + }; + + let no_content_type = content_type.is_none(); + if no_content_type || content_type.unwrap().starts_with("application/json") { + let bytes = Bytes::from_request(request, _state) + .await + .map_err(IntoResponse::into_response)?; + if no_content_type && bytes.is_empty() { + if use_raw { + extra.insert("raw_string".to_string(), to_raw_value(&"".to_string())); + } + let mut args = HashMap::new(); + if wrap_body { + args.insert("body".to_string(), to_raw_value(&serde_json::json!({}))); + } + return Ok(WebhookArgs { + args: PushArgsOwned { extra: Some(extra), args: args }, + ..Default::default() + }); + } + let str = String::from_utf8(bytes.to_vec()) + .map_err(|e| Error::BadRequest(format!("invalid utf8: {}", e)).into_response())?; + + PushArgsOwned::from_json(extra, use_raw, wrap_body, str) + .await + .map(|args| WebhookArgs { args, ..Default::default() }) + } else if content_type + .unwrap() + .starts_with("application/cloudevents+json") + { + let str = req_to_string(request, _state).await?; + + PushArgsOwned::from_ce_json(extra, use_raw, str) + .await + .map(|args| WebhookArgs { args, ..Default::default() }) + } else if content_type + .unwrap() + .starts_with("application/cloudevents-batch+json") + { + Err( + Error::BadRequest(format!("Cloud events batching is not supported yet")) + .into_response(), + ) + } else if content_type.unwrap().starts_with("text/plain") { + let str = req_to_string(request, _state).await?; + extra.insert("raw_string".to_string(), to_raw_value(&str)); + Ok(WebhookArgs { + args: PushArgsOwned { extra: Some(extra), args: HashMap::new() }, + ..Default::default() + }) + } else if content_type + .unwrap() + .starts_with("application/x-www-form-urlencoded") + { + let bytes = Bytes::from_request(request, _state) + .await + .map_err(IntoResponse::into_response)?; + + if use_raw { + let raw_string = String::from_utf8(bytes.to_vec()) + .map_err(|e| Error::BadRequest(format!("invalid utf8: {}", e)).into_response())?; + extra.insert("raw_string".to_string(), to_raw_value(&raw_string)); + } + + let payload: HashMap> = serde_urlencoded::from_bytes(&bytes) + .map_err(|e| { + Error::BadRequest(format!("invalid urlencoded data: {}", e)).into_response() + })?; + let payload = payload + .into_iter() + .map(|(k, v)| (k, to_raw_value(&v))) + .collect::>(); + + return Ok(WebhookArgs { + args: PushArgsOwned { extra: Some(extra), args: payload }, + ..Default::default() + }); + } else if content_type.unwrap().starts_with("application/xml") + || content_type.unwrap().starts_with("text/xml") + { + let str = req_to_string(request, _state).await?; + extra.insert("raw_string".to_string(), to_raw_value(&str)); + Ok(WebhookArgs { + args: PushArgsOwned { extra: Some(extra), args: HashMap::new() }, + ..Default::default() + }) + } else if content_type.unwrap().starts_with("multipart/form-data") { + let multipart = Multipart::from_request(request, _state) + .await + .map_err(IntoResponse::into_response)?; + + Ok(WebhookArgs { + args: PushArgsOwned { extra: Some(extra), args: HashMap::new() }, + multipart: Some(multipart), + wrap_body: Some(wrap_body), + }) + } else { + Err(StatusCode::UNSUPPORTED_MEDIA_TYPE.into_response()) + } +} + #[axum::async_trait] impl FromRequest for WebhookArgs where @@ -170,124 +294,10 @@ where { type Rejection = Response; - async fn from_request( - req: Request, - _state: &S, - ) -> Result { - let (content_type, mut extra, use_raw, wrap_body) = { - let headers_map = req.headers(); - let content_type_header = headers_map.get(CONTENT_TYPE); - let content_type = content_type_header.and_then(|value| value.to_str().ok()); - let uri = req.uri(); - let query = Query::::try_from_uri(uri).unwrap().0; - let mut extra = build_extra(&headers_map, query.include_header); - let query_decode = DecodeQueries::from_uri(uri); - if let Some(DecodeQueries(queries)) = query_decode { - extra.extend(queries); - } - let raw = query.raw.as_ref().is_some_and(|x| *x); - let wrap_body = query.wrap_body.as_ref().is_some_and(|x| *x); - (content_type, extra, raw, wrap_body) - }; + async fn from_request(request: Request, _state: &S) -> Result { + let args = try_from_request_body(request, _state, None, None).await?; - let no_content_type = content_type.is_none(); - if no_content_type || content_type.unwrap().starts_with("application/json") { - let bytes = Bytes::from_request(req, _state) - .await - .map_err(IntoResponse::into_response)?; - if no_content_type && bytes.is_empty() { - if use_raw { - extra.insert("raw_string".to_string(), to_raw_value(&"".to_string())); - } - let mut args = HashMap::new(); - if wrap_body { - args.insert("body".to_string(), to_raw_value(&serde_json::json!({}))); - } - return Ok(Self { - args: PushArgsOwned { extra: Some(extra), args: args }, - ..Default::default() - }); - } - let str = String::from_utf8(bytes.to_vec()) - .map_err(|e| Error::BadRequest(format!("invalid utf8: {}", e)).into_response())?; - - PushArgsOwned::from_json(extra, use_raw, wrap_body, str) - .await - .map(|args| Self { args, ..Default::default() }) - } else if content_type - .unwrap() - .starts_with("application/cloudevents+json") - { - let str = req_to_string(req, _state).await?; - - PushArgsOwned::from_ce_json(extra, use_raw, str) - .await - .map(|args| Self { args, ..Default::default() }) - } else if content_type - .unwrap() - .starts_with("application/cloudevents-batch+json") - { - Err( - Error::BadRequest(format!("Cloud events batching is not supported yet")) - .into_response(), - ) - } else if content_type.unwrap().starts_with("text/plain") { - let str = req_to_string(req, _state).await?; - extra.insert("raw_string".to_string(), to_raw_value(&str)); - Ok(Self { - args: PushArgsOwned { extra: Some(extra), args: HashMap::new() }, - ..Default::default() - }) - } else if content_type - .unwrap() - .starts_with("application/x-www-form-urlencoded") - { - let bytes = Bytes::from_request(req, _state) - .await - .map_err(IntoResponse::into_response)?; - - if use_raw { - let raw_string = String::from_utf8(bytes.to_vec()).map_err(|e| { - Error::BadRequest(format!("invalid utf8: {}", e)).into_response() - })?; - extra.insert("raw_string".to_string(), to_raw_value(&raw_string)); - } - - let payload: HashMap> = serde_urlencoded::from_bytes(&bytes) - .map_err(|e| { - Error::BadRequest(format!("invalid urlencoded data: {}", e)).into_response() - })?; - let payload = payload - .into_iter() - .map(|(k, v)| (k, to_raw_value(&v))) - .collect::>(); - - return Ok(Self { - args: PushArgsOwned { extra: Some(extra), args: payload }, - ..Default::default() - }); - } else if content_type.unwrap().starts_with("application/xml") - || content_type.unwrap().starts_with("text/xml") - { - let str = req_to_string(req, _state).await?; - extra.insert("raw_string".to_string(), to_raw_value(&str)); - Ok(Self { - args: PushArgsOwned { extra: Some(extra), args: HashMap::new() }, - ..Default::default() - }) - } else if content_type.unwrap().starts_with("multipart/form-data") { - let multipart = Multipart::from_request(req, _state) - .await - .map_err(IntoResponse::into_response)?; - - Ok(Self { - args: PushArgsOwned { extra: Some(extra), args: HashMap::new() }, - multipart: Some(multipart), - wrap_body: Some(wrap_body), - }) - } else { - Err(StatusCode::UNSUPPORTED_MEDIA_TYPE.into_response()) - } + Ok(args) } } diff --git a/backend/windmill-api/src/auth.rs b/backend/windmill-api/src/auth.rs index 71c6270477..2936b74957 100644 --- a/backend/windmill-api/src/auth.rs +++ b/backend/windmill-api/src/auth.rs @@ -21,7 +21,8 @@ use std::sync::{ use tokio::sync::RwLock; use windmill_common::{ - auth::{get_folders_for_user, get_groups_for_user, JWTAuthClaims, JWT_SECRET}, + auth::{get_folders_for_user, get_groups_for_user, JWTAuthClaims}, + jwt, users::{COOKIE_NAME, SUPERADMIN_SECRET_EMAIL}, }; @@ -99,55 +100,44 @@ impl AuthCache { } } _ if token.starts_with("jwt_") => { - let jwt_secret = JWT_SECRET.read().await; - if !jwt_secret.is_empty() { - let jwt_token = token.trim_start_matches("jwt_"); + let jwt_token = token.trim_start_matches("jwt_"); - let jwt_result = jsonwebtoken::decode::( - jwt_token, - &jsonwebtoken::DecodingKey::from_secret(jwt_secret.as_bytes()), - &jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256), - ); + let jwt_result = jwt::decode_with_internal_secret::(jwt_token).await; - match jwt_result { - Ok(payload) => { - if w_id.is_some_and(|w_id| w_id != payload.claims.workspace_id) { - tracing::error!("JWT auth error: workspace_id mismatch"); - return None; - } - - let username_override = - username_override_from_label(payload.claims.label); - let authed = crate::db::ApiAuthed { - email: payload.claims.email, - username: payload.claims.username, - is_admin: payload.claims.is_admin, - is_operator: payload.claims.is_operator, - groups: payload.claims.groups, - folders: payload.claims.folders, - scopes: None, - username_override, - }; - - self.cache.insert( - key, - ExpiringAuthCache { - authed: authed.clone(), - expiry: chrono::Utc - .timestamp_nanos(payload.claims.exp as i64 * 1_000_000_000), - }, - ); - - Some(authed) - } - Err(err) => { - tracing::error!("JWT auth error: {:?}", err); - None + match jwt_result { + Ok(claims) => { + if w_id.is_some_and(|w_id| w_id != claims.workspace_id) { + tracing::error!("JWT auth error: workspace_id mismatch"); + return None; } + + let username_override = username_override_from_label(claims.label); + let authed = crate::db::ApiAuthed { + email: claims.email, + username: claims.username, + is_admin: claims.is_admin, + is_operator: claims.is_operator, + groups: claims.groups, + folders: claims.folders, + scopes: None, + username_override, + }; + + self.cache.insert( + key, + ExpiringAuthCache { + authed: authed.clone(), + expiry: chrono::Utc + .timestamp_nanos(claims.exp as i64 * 1_000_000_000), + }, + ); + + Some(authed) + } + Err(err) => { + tracing::error!("JWT auth error: {:?}", err); + None } - } else { - tracing::error!("JWT auth error: no jwt secret set"); - None } } _ => { @@ -519,9 +509,15 @@ where .map(|x| x.0) .unwrap_or_default(); let path_vec: Vec<&str> = original_uri.path().split("/").collect(); - let workspace_id = if path_vec.len() >= 4 && path_vec[0] == "" && path_vec[2] == "w" { Some(path_vec[3].to_owned()) + } else if path_vec.len() >= 5 + && path_vec[0] == "" + && path_vec[1] == "api" + && path_vec[2] == "mcp" + && path_vec[3] == "w" + { + Some(path_vec[4].to_string()) } else { if path_vec.len() >= 5 && path_vec[0] == "" diff --git a/backend/windmill-api/src/capture.rs b/backend/windmill-api/src/capture.rs index 4b933b01b4..547b56954b 100644 --- a/backend/windmill-api/src/capture.rs +++ b/backend/windmill-api/src/capture.rs @@ -6,43 +6,81 @@ * LICENSE-AGPL for a copy of the license. */ +#[cfg(feature = "http_trigger")] +use { + crate::{ + args::try_from_request_body, + http_triggers::{build_http_trigger_extra, HttpMethod}, + }, + axum::response::{IntoResponse, Response}, + std::collections::HashMap, +}; + +#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))] +use crate::gcp_triggers_ee::{ + manage_google_subscription, process_google_push_request, validate_jwt_token, + CreateUpdateConfig, SubscriptionMode, +}; + +#[cfg(all(feature = "enterprise", feature = "sqs_trigger"))] +use windmill_common::auth::aws::AwsAuthResourceType; + +#[cfg(any( + feature = "http_trigger", + all(feature = "enterprise", feature = "gcp_trigger") +))] +use { + axum::extract::Request, + http::HeaderMap, + serde::de::DeserializeOwned, + windmill_common::{error::Error, utils::empty_string_as_none}, +}; + +#[cfg(all(feature = "enterprise", feature = "kafka"))] +use crate::kafka_triggers_ee::KafkaTriggerConfigConnection; + +#[cfg(feature = "mqtt_trigger")] +use crate::mqtt_triggers::{MqttClientVersion, MqttV3Config, MqttV5Config, SubscribeTopic}; + +#[cfg(all(feature = "enterprise", feature = "nats"))] +use crate::nats_triggers_ee::NatsTriggerConfigConnection; + +#[cfg(feature = "postgres_trigger")] +use { + crate::postgres_triggers::{ + create_logical_replication_slot_query, create_publication_query, drop_publication_query, + generate_random_string, get_database_connection, PublicationData, + }, + itertools::Itertools, + pg_escape::quote_literal, +}; + +use crate::{ + args::WebhookArgs, + db::{ApiAuthed, DB}, + users::fetch_api_authed, + utils::RunnableKind, +}; + use axum::{ extract::{Extension, Path, Query}, routing::{delete, get, head, post}, Json, Router, }; -#[cfg(feature = "http_trigger")] -use http::HeaderMap; + use hyper::StatusCode; -#[cfg(feature = "http_trigger")] -use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serde_json::value::RawValue; use sqlx::types::Json as SqlxJson; -#[cfg(feature = "http_trigger")] -use std::collections::HashMap; -use std::fmt; -#[cfg(feature = "http_trigger")] -use windmill_common::error::Error; + use windmill_common::{ db::UserDB, error::{JsonResult, Result}, utils::{not_found_if_none, paginate, Pagination, StripPath}, worker::{to_raw_value, CLOUD_HOSTED}, }; -use windmill_queue::{PushArgs, PushArgsOwned}; -#[cfg(feature = "http_trigger")] -use crate::http_triggers::{build_http_trigger_extra, HttpMethod}; -#[cfg(all(feature = "enterprise", feature = "kafka"))] -use crate::kafka_triggers_ee::KafkaTriggerConfigConnection; -#[cfg(all(feature = "enterprise", feature = "nats"))] -use crate::nats_triggers_ee::NatsTriggerConfigConnection; -use crate::{ - args::WebhookArgs, - db::{ApiAuthed, DB}, - users::fetch_api_authed, -}; +use windmill_queue::{PushArgs, PushArgsOwned, TriggerKind}; const KEEP_LAST: i64 = 20; @@ -55,6 +93,10 @@ pub fn workspaced_service() -> Router { ) .route("/get_configs/:runnable_kind/*path", get(get_configs)) .route("/list/:runnable_kind/*path", get(list_captures)) + .route( + "/move/:runnable_kind/*path", + post(move_captures_and_configs), + ) .route("/:id", delete(delete_capture)) .route("/:id", get(get_capture)) } @@ -65,42 +107,28 @@ pub fn workspaced_unauthed_service() -> Router { head(|| async {}).post(webhook_payload), ); - #[cfg(feature = "http_trigger")] + #[cfg(any( + feature = "http_trigger", + all(feature = "enterprise", feature = "gcp_trigger") + ))] { - router.route("/http/:runnable_kind/:path/*route_path", { + #[cfg(feature = "http_trigger")] + let router = router.route("/http/:runnable_kind/:path/*route_path", { head(|| async {}).fallback(http_payload) - }) - } + }); + + #[cfg(all(feature = "enterprise", feature = "gcp_trigger"))] + let router = router.route("/gcp/:runnable_kind/*path", post(gcp_payload)); - #[cfg(not(feature = "http_trigger"))] - { router } -} -#[derive(sqlx::Type, Serialize, Deserialize)] -#[sqlx(type_name = "TRIGGER_KIND", rename_all = "lowercase")] -#[serde(rename_all = "lowercase")] -pub enum TriggerKind { - Webhook, - Http, - Websocket, - Kafka, - Email, - Nats, -} - -impl fmt::Display for TriggerKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s = match self { - TriggerKind::Webhook => "webhook", - TriggerKind::Http => "http", - TriggerKind::Websocket => "websocket", - TriggerKind::Kafka => "kafka", - TriggerKind::Email => "email", - TriggerKind::Nats => "nats", - }; - write!(f, "{}", s) + #[cfg(not(any( + feature = "http_trigger", + all(feature = "enterprise", feature = "gcp_trigger") + )))] + { + router } } @@ -109,6 +137,8 @@ impl fmt::Display for TriggerKind { struct HttpTriggerConfig { route_path: String, http_method: HttpMethod, + raw_string: Option, + wrap_body: Option, } #[cfg(all(feature = "enterprise", feature = "kafka"))] @@ -120,6 +150,29 @@ pub struct KafkaTriggerConfig { pub group_id: String, } +#[cfg(all(feature = "enterprise", feature = "sqs_trigger"))] +#[derive(Debug, Serialize, Deserialize)] +pub struct SqsTriggerConfig { + pub queue_url: String, + pub aws_resource_path: String, + pub message_attributes: Option>, + pub aws_auth_resource_type: AwsAuthResourceType, +} + +#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))] +#[derive(Debug, Serialize, Deserialize)] +pub struct GcpTriggerConfig { + pub gcp_resource_path: String, + pub subscription_mode: SubscriptionMode, + #[serde(default, deserialize_with = "empty_string_as_none")] + pub subscription_id: Option, + #[serde(default, deserialize_with = "empty_string_as_none")] + pub base_endpoint: Option, + #[serde(flatten)] + pub create_update: Option, + pub topic_id: String, +} + #[cfg(all(feature = "enterprise", feature = "nats"))] #[derive(Serialize, Deserialize)] pub struct NatsTriggerConfig { @@ -133,6 +186,26 @@ pub struct NatsTriggerConfig { pub use_jetstream: bool, } +#[cfg(feature = "mqtt_trigger")] +#[derive(Debug, Serialize, Deserialize)] +pub struct MqttTriggerConfig { + pub mqtt_resource_path: String, + pub subscribe_topics: Vec, + pub v3_config: Option, + pub v5_config: Option, + pub client_version: Option, + pub client_id: Option, +} +#[cfg(feature = "postgres_trigger")] +#[derive(Serialize, Deserialize, Debug)] +pub struct PostgresTriggerConfig { + pub postgres_resource_path: String, + pub publication_name: Option, + pub replication_slot_name: Option, + pub publication: PublicationData, +} + +#[cfg(feature = "websocket")] #[derive(Serialize, Deserialize, Debug)] pub struct WebsocketTriggerConfig { pub url: String, @@ -145,11 +218,20 @@ pub struct WebsocketTriggerConfig { enum TriggerConfig { #[cfg(feature = "http_trigger")] Http(HttpTriggerConfig), + #[cfg(feature = "postgres_trigger")] + Postgres(PostgresTriggerConfig), + #[cfg(feature = "websocket")] Websocket(WebsocketTriggerConfig), + #[cfg(all(feature = "enterprise", feature = "sqs_trigger"))] + Sqs(SqsTriggerConfig), #[cfg(all(feature = "enterprise", feature = "kafka"))] Kafka(KafkaTriggerConfig), #[cfg(all(feature = "enterprise", feature = "nats"))] Nats(NatsTriggerConfig), + #[cfg(feature = "mqtt_trigger")] + Mqtt(MqttTriggerConfig), + #[cfg(all(feature = "enterprise", feature = "gcp_trigger"))] + Gcp(GcpTriggerConfig), } #[derive(Serialize, Deserialize)] @@ -177,9 +259,19 @@ async fn get_configs( let configs = sqlx::query_as!( CaptureConfig, - r#"SELECT trigger_config as "trigger_config: _", trigger_kind as "trigger_kind: _", error, last_server_ping - FROM capture_config - WHERE workspace_id = $1 AND path = $2 AND is_flow = $3"#, + r#" + SELECT + trigger_config AS "trigger_config: _", + trigger_kind AS "trigger_kind: _", + error, + last_server_ping + FROM + capture_config + WHERE + workspace_id = $1 + AND path = $2 + AND is_flow = $3 + "#, &w_id, &path.to_path(), matches!(runnable_kind, RunnableKind::Flow), @@ -192,25 +284,166 @@ async fn get_configs( Ok(Json(configs)) } +#[cfg(feature = "postgres_trigger")] +async fn set_postgres_trigger_config( + w_id: &str, + authed: ApiAuthed, + db: &DB, + user_db: UserDB, + mut capture_config: NewCaptureConfig, +) -> Result { + let Some(TriggerConfig::Postgres(mut postgres_config)) = capture_config.trigger_config else { + return Err(windmill_common::error::Error::BadRequest( + "Invalid postgres config".to_string(), + )); + }; + + let mut connection = get_database_connection( + authed, + Some(user_db), + &db, + &postgres_config.postgres_resource_path, + &w_id, + ) + .await?; + + let publication_name = postgres_config + .publication_name + .get_or_insert(format!("windmill_capture_{}", generate_random_string())); + let replication_slot_name = postgres_config + .replication_slot_name + .get_or_insert(publication_name.clone()); + + let query = drop_publication_query(&publication_name); + + sqlx::query(&query).execute(&mut connection).await?; + + let query = create_publication_query( + &publication_name, + postgres_config.publication.table_to_track.as_deref(), + &postgres_config + .publication + .transaction_to_track + .iter() + .map(AsRef::as_ref) + .collect_vec(), + ); + + sqlx::query(&query).execute(&mut connection).await?; + + let query = format!( + "SELECT 1 from pg_replication_slots WHERE slot_name = {}", + quote_literal(replication_slot_name) + ); + + let row = sqlx::query(&query).fetch_optional(&mut connection).await?; + + if row.is_none() { + let query = create_logical_replication_slot_query(&replication_slot_name); + sqlx::query(&query).execute(&mut connection).await?; + } + capture_config.trigger_config = Some(TriggerConfig::Postgres(postgres_config)); + Ok(capture_config) +} + +#[inline] +#[cfg(not(feature = "postgres_trigger"))] +async fn set_postgres_trigger_config( + _w_id: &str, + _authed: ApiAuthed, + _db: &DB, + _user_db: UserDB, + capture_config: NewCaptureConfig, +) -> Result { + Ok(capture_config) +} + +#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))] +async fn set_gcp_trigger_config( + w_id: &str, + authed: ApiAuthed, + db: &DB, + mut capture_config: NewCaptureConfig, +) -> Result { + let Some(TriggerConfig::Gcp(mut gcp_config)) = capture_config.trigger_config else { + return Err(windmill_common::error::Error::BadRequest( + "Invalid GCP Pub/Sub config".to_string(), + )); + }; + + let config = manage_google_subscription( + authed, + db, + w_id, + &gcp_config.gcp_resource_path, + &capture_config.path, + &gcp_config.topic_id, + &mut gcp_config.subscription_id, + &mut gcp_config.base_endpoint, + gcp_config.subscription_mode, + gcp_config.create_update, + false, + ) + .await?; + gcp_config.create_update = Some(config); + gcp_config.subscription_mode = SubscriptionMode::CreateUpdate; + capture_config.trigger_config = Some(TriggerConfig::Gcp(gcp_config)); + + Ok(capture_config) +} + +#[inline] +#[cfg(not(all(feature = "enterprise", feature = "gcp_trigger")))] +async fn set_gcp_trigger_config( + _w_id: &str, + _authed: ApiAuthed, + _db: &DB, + capture_config: NewCaptureConfig, +) -> Result { + Ok(capture_config) +} + async fn set_config( authed: ApiAuthed, Extension(user_db): Extension, + Extension(db): Extension, Path(w_id): Path, Json(nc): Json, -) -> Result<()> { +) -> JsonResult> { + let nc = match nc.trigger_kind { + TriggerKind::Postgres => { + set_postgres_trigger_config(&w_id, authed.clone(), &db, user_db.clone(), nc).await? + } + TriggerKind::Gcp => set_gcp_trigger_config(&w_id, authed.clone(), &db, nc).await?, + _ => nc, + }; + let mut tx = user_db.begin(&authed).await?; sqlx::query!( - "INSERT INTO capture_config - (workspace_id, path, is_flow, trigger_kind, trigger_config, owner, email) - VALUES ($1, $2, $3, $4, $5, $6, $7) + r#" + INSERT INTO capture_config ( + workspace_id, path, is_flow, trigger_kind, trigger_config, owner, email + ) + VALUES ( + $1, $2, $3, $4, $5, $6, $7 + ) ON CONFLICT (workspace_id, path, is_flow, trigger_kind) - DO UPDATE SET trigger_config = $5, owner = $6, email = $7, server_id = NULL, error = NULL", + DO UPDATE + SET + trigger_config = $5, + owner = $6, + email = $7, + server_id = NULL, + error = NULL + "#, &w_id, &nc.path, nc.is_flow, nc.trigger_kind as TriggerKind, - nc.trigger_config.map(|x| SqlxJson(to_raw_value(&x))) as Option>>, + nc.trigger_config + .as_ref() + .map(|x| SqlxJson(to_raw_value(&x))) as Option>>, &authed.username, &authed.email, ) @@ -219,7 +452,7 @@ async fn set_config( tx.commit().await?; - Ok(()) + Ok(Json(nc.trigger_config)) } async fn ping_config( @@ -233,8 +466,19 @@ async fn ping_config( )>, ) -> Result<()> { let mut tx = user_db.begin(&authed).await?; + sqlx::query!( - "UPDATE capture_config SET last_client_ping = now() WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4", + r#" + UPDATE + capture_config + SET + last_client_ping = NOW() + WHERE + workspace_id = $1 + AND path = $2 + AND is_flow = $3 + AND trigger_kind = $4 + "#, &w_id, &path.to_path(), matches!(runnable_kind, RunnableKind::Flow), @@ -242,6 +486,7 @@ async fn ping_config( ) .execute(&mut *tx) .await?; + tx.commit().await?; Ok(()) } @@ -255,13 +500,6 @@ struct Capture { trigger_extra: Option>>, } -#[derive(Deserialize)] -#[serde(rename_all = "lowercase")] -enum RunnableKind { - Script, - Flow, -} - #[derive(Deserialize)] struct ListCapturesQuery { trigger_kind: Option, @@ -281,14 +519,28 @@ async fn list_captures( let captures = sqlx::query_as!( Capture, - r#"SELECT id, created_at, trigger_kind as "trigger_kind: _", CASE WHEN pg_column_size(payload) < 40000 THEN payload ELSE '"WINDMILL_TOO_BIG"'::jsonb END as "payload!: _", trigger_extra as "trigger_extra: _" - FROM capture - WHERE workspace_id = $1 - AND path = $2 AND is_flow = $3 + r#" + SELECT + id, + created_at, + trigger_kind AS "trigger_kind: _", + CASE + WHEN pg_column_size(payload) < 40000 THEN payload + ELSE '"WINDMILL_TOO_BIG"'::jsonb + END AS "payload!: _", + trigger_extra AS "trigger_extra: _" + FROM + capture + WHERE + workspace_id = $1 + AND path = $2 + AND is_flow = $3 AND ($4::trigger_kind IS NULL OR trigger_kind = $4) - ORDER BY created_at DESC + ORDER BY + created_at DESC OFFSET $5 - LIMIT $6"#, + LIMIT $6 + "#, &w_id, &path.to_path(), matches!(runnable_kind, RunnableKind::Flow), @@ -310,14 +562,28 @@ async fn get_capture( Path((w_id, id)): Path<(String, i64)>, ) -> JsonResult { let mut tx = user_db.begin(&authed).await?; + let capture = sqlx::query_as!( Capture, - r#"SELECT id, created_at, trigger_kind as "trigger_kind: _", payload as "payload!: _", trigger_extra as "trigger_extra: _" FROM capture WHERE id = $1 AND workspace_id = $2"#, + r#" + SELECT + id, + created_at, + trigger_kind AS "trigger_kind: _", + payload AS "payload!: _", + trigger_extra AS "trigger_extra: _" + FROM + capture + WHERE + id = $1 + AND workspace_id = $2 + "#, id, &w_id, ) .fetch_one(&mut *tx) - .await?; + .await?; + tx.commit().await?; Ok(Json(capture)) } @@ -328,9 +594,73 @@ async fn delete_capture( Path((_, id)): Path<(String, i64)>, ) -> Result<()> { let mut tx = user_db.begin(&authed).await?; - sqlx::query!("DELETE FROM capture WHERE id = $1", id) - .execute(&mut *tx) - .await?; + sqlx::query!( + r#" + DELETE FROM + capture + WHERE + id = $1 + "#, + id + ) + .execute(&mut *tx) + .await?; + tx.commit().await?; + Ok(()) +} + +#[derive(Deserialize)] +struct MoveCapturesAndConfigsBody { + new_path: String, +} + +async fn move_captures_and_configs( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, runnable_kind, old_path)): Path<(String, RunnableKind, StripPath)>, + Json(body): Json, +) -> Result<()> { + let mut tx = user_db.begin(&authed).await?; + let old_path = old_path.to_path(); + + sqlx::query!( + r#" + UPDATE + capture_config + SET + path = $1 + WHERE + path = $2 + AND workspace_id = $3 + AND is_flow = $4 + "#, + body.new_path, + old_path, + &w_id, + matches!(runnable_kind, RunnableKind::Flow), + ) + .execute(&mut *tx) + .await?; + + sqlx::query!( + r#" + UPDATE + capture + SET + path = $1 + WHERE + path = $2 + AND workspace_id = $3 + AND is_flow = $4 + "#, + body.new_path, + old_path, + &w_id, + matches!(runnable_kind, RunnableKind::Flow), + ) + .execute(&mut *tx) + .await?; + tx.commit().await?; Ok(()) } @@ -350,9 +680,19 @@ pub async fn get_active_capture_owner_and_email( ) -> Result<(String, String)> { let capture_config = sqlx::query_as!( ActiveCaptureOwner, - "SELECT owner, email - FROM capture_config - WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4 AND last_client_ping > NOW() - INTERVAL '10 seconds'", + r#" + SELECT + owner, + email + FROM + capture_config + WHERE + workspace_id = $1 + AND path = $2 + AND is_flow = $3 + AND trigger_kind = $4 + AND last_client_ping > NOW() - INTERVAL '10 seconds' + "#, &w_id, &path, is_flow, @@ -370,7 +710,10 @@ pub async fn get_active_capture_owner_and_email( Ok((capture_config.owner, capture_config.email)) } -#[cfg(feature = "http_trigger")] +#[cfg(any( + feature = "http_trigger", + all(feature = "enterprise", feature = "gcp_trigger") +))] async fn get_capture_trigger_config_and_owner( db: &DB, w_id: &str, @@ -384,16 +727,34 @@ async fn get_capture_trigger_config_and_owner( owner: String, email: String, } - let capture_config = sqlx::query_as!( CaptureTriggerConfigAndOwner, - r#"SELECT trigger_config as "trigger_config: _", owner, email - FROM capture_config - WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4 AND last_client_ping > NOW() - INTERVAL '10 seconds'"#, + r#" + SELECT + trigger_config AS "trigger_config: _", + owner, + email + FROM + capture_config + WHERE + workspace_id = $1 + AND path = $2 + AND is_flow = $3 + AND trigger_kind = $4 + AND last_client_ping > NOW() - INTERVAL '10 seconds' + AND ( + $5::bool IS FALSE + OR ( + trigger_config IS NOT NULL + AND trigger_config ->> 'delivery_type' = 'push' + ) + ) + "#, &w_id, &path, is_flow, kind as &TriggerKind, + matches!(kind, TriggerKind::Gcp) ) .fetch_optional(db) .await?; @@ -426,17 +787,24 @@ async fn clear_captures_history(db: &DB, w_id: &str) -> Result<()> { if *CLOUD_HOSTED { /* Retain only KEEP_LAST most recent captures in this workspace. */ sqlx::query!( - "DELETE FROM capture - WHERE workspace_id = $1 - AND created_at <= - ( - SELECT created_at - FROM capture - WHERE workspace_id = $1 - ORDER BY created_at DESC - OFFSET $2 - LIMIT 1 - )", + r#" + DELETE FROM + capture + WHERE + workspace_id = $1 + AND created_at <= ( + SELECT + created_at + FROM + capture + WHERE + workspace_id = $1 + ORDER BY + created_at DESC + OFFSET $2 + LIMIT 1 + ) + "#, &w_id, KEEP_LAST, ) @@ -457,8 +825,15 @@ pub async fn insert_capture_payload( owner: &str, ) -> Result<()> { sqlx::query!( - "INSERT INTO capture (workspace_id, path, is_flow, trigger_kind, payload, trigger_extra, created_by) - VALUES ($1, $2, $3, $4, $5, $6, $7)", + r#" + INSERT INTO + capture ( + workspace_id, path, is_flow, trigger_kind, payload, trigger_extra, created_by + ) + VALUES ( + $1, $2, $3, $4, $5, $6, $7 + ) + "#, &w_id, path, is_flow, @@ -514,36 +889,94 @@ async fn webhook_payload( Ok(StatusCode::NO_CONTENT) } +#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))] +async fn gcp_payload( + Extension(db): Extension, + Extension(user_db): Extension, + Path((w_id, runnable_kind, path)): Path<(String, RunnableKind, String)>, + headers: HeaderMap, + request: Request, +) -> Result { + let is_flow = matches!(runnable_kind, RunnableKind::Flow); + let (gcp_trigger_config, owner, email): (GcpTriggerConfig, _, _) = + get_capture_trigger_config_and_owner(&db, &w_id, &path, is_flow, &TriggerKind::Gcp).await?; + + let authed = fetch_api_authed(owner.clone(), email, &w_id, &db, None).await?; + + let Some(config) = &gcp_trigger_config.create_update else { + return Err(Error::BadConfig("Bad config".to_string())); + }; + + validate_jwt_token( + &db, + user_db.clone(), + authed.clone(), + &headers, + &gcp_trigger_config.gcp_resource_path, + &w_id, + config.delivery_config.as_ref().unwrap(), + ) + .await?; + + let (args, extra) = process_google_push_request(headers, request).await?; + + let payload = PushArgsOwned { args, extra: None }; + + let _ = insert_capture_payload( + &db, + &w_id, + &path, + is_flow, + &TriggerKind::Gcp, + payload, + Some(to_raw_value(&extra)), + &owner, + ) + .await?; + + Ok(StatusCode::NO_CONTENT) +} + #[cfg(feature = "http_trigger")] async fn http_payload( Extension(db): Extension, - Path((w_id, kind, path, route_path)): Path<(String, RunnableKind, String, StripPath)>, + Path((w_id, runnable_kind, path, route_path)): Path<(String, RunnableKind, String, StripPath)>, Query(query): Query>, method: http::Method, headers: HeaderMap, - args: WebhookArgs, -) -> Result { - let route_path = route_path.to_path(); + request: Request, +) -> std::result::Result { let path = path.replace(".", "/"); - + let is_flow = matches!(runnable_kind, RunnableKind::Flow); + let route_path = route_path.to_path(); let (http_trigger_config, owner, email): (HttpTriggerConfig, _, _) = - get_capture_trigger_config_and_owner( - &db, - &w_id, - &path, - matches!(kind, RunnableKind::Flow), - &TriggerKind::Http, - ) - .await?; + get_capture_trigger_config_and_owner(&db, &w_id, &path, is_flow, &TriggerKind::Http) + .await + .map_err(|e| e.into_response())?; - let authed = fetch_api_authed(owner.clone(), email, &w_id, &db, None).await?; - let args = args.to_push_args_owned(&authed, &db, &w_id).await?; + let args = try_from_request_body( + request, + &(), + http_trigger_config.raw_string, + http_trigger_config.wrap_body, + ) + .await + .map_err(|e| e.into_response())?; + + let authed = fetch_api_authed(owner.clone(), email, &w_id, &db, None) + .await + .map_err(|e| e.into_response())?; + let mut args = args + .to_push_args_owned(&authed, &db, &w_id) + .await + .map_err(|e| e.into_response())?; let mut router = matchit::Router::new(); router.insert(&http_trigger_config.route_path, ()).ok(); let match_ = router.at(route_path).ok(); - let match_ = not_found_if_none(match_, "capture http trigger", &route_path)?; + let match_ = not_found_if_none(match_, "capture http trigger", &route_path) + .map_err(|e| e.into_response())?; let matchit::Match { params, .. } = match_; @@ -552,7 +985,9 @@ async fn http_payload( .map(|(k, v)| (k.to_string(), v.to_string())) .collect(); - let extra: HashMap> = HashMap::from_iter(vec![( + let extra = args.extra.get_or_insert_with(HashMap::new); + + extra.insert( "wm_trigger".to_string(), build_http_trigger_extra( &http_trigger_config.route_path, @@ -563,19 +998,22 @@ async fn http_payload( &headers, ) .await, - )]); + ); + let extra = Some(to_raw_value(&extra)); + args.extra = None; insert_capture_payload( &db, &w_id, &path, - matches!(kind, RunnableKind::Flow), + is_flow, &TriggerKind::Http, args, - Some(to_raw_value(&extra)), + extra, &owner, ) - .await?; + .await + .map_err(|e| e.into_response())?; Ok(StatusCode::NO_CONTENT) } diff --git a/backend/windmill-api/src/concurrency_groups.rs b/backend/windmill-api/src/concurrency_groups.rs index 3f7e41617f..4135c3729c 100644 --- a/backend/windmill-api/src/concurrency_groups.rs +++ b/backend/windmill-api/src/concurrency_groups.rs @@ -199,6 +199,7 @@ async fn get_concurrent_intervals( result: None, tag: None, has_null_parent: None, + worker: None, label: None, scheduled_for_before_now: _, is_not_schedule: _, @@ -214,6 +215,7 @@ async fn get_concurrent_intervals( is_flow_step: _, all_workspaces: _, concurrency_key: Some(_), + allow_wildcards: None, } => true, _ => false, }; diff --git a/backend/windmill-api/src/db.rs b/backend/windmill-api/src/db.rs index a2a641b802..1ecef03fc5 100644 --- a/backend/windmill-api/src/db.rs +++ b/backend/windmill-api/src/db.rs @@ -6,20 +6,22 @@ * LICENSE-AGPL for a copy of the license. */ -use futures::FutureExt; -use sqlx::Executor; +use std::time::Duration; +use futures::FutureExt; use sqlx::{ migrate::{Migrate, MigrateError}, pool::PoolConnection, - PgConnection, Pool, Postgres, + Executor, PgConnection, Pool, Postgres, }; + +use tokio::task::JoinHandle; use windmill_audit::audit_ee::{AuditAuthor, AuditAuthorable}; -use windmill_common::utils::generate_lock_id; use windmill_common::{ db::{Authable, Authed}, error::Error, }; +use windmill_common::{utils::generate_lock_id, worker::MIN_VERSION_IS_AT_LEAST_1_461}; pub type DB = Pool; @@ -30,6 +32,30 @@ async fn current_database(conn: &mut PgConnection) -> Result = vec![(20221207103910, include_str!( + "../../custom_migrations/create_workspace_without_md5.sql" + ).to_string()), + (20240216100535, include_str!( + "../../migrations/20240216100535_improve_policies.up.sql" + ).replace("public.", "")), + (20240403083110, include_str!( + "../../migrations/20240403083110_remove_team_id_constraint.up.sql" + ).replace("public.", "")), + (20240613150524, include_str!( + "../../migrations/20240613150524_add_job_perms.up.sql" + ).replace("public.", "")), + (20250102145420, include_str!( + "../../migrations/20250102145420_more_captures.up.sql" + ).replace("public.", "")), + (20241006144414, include_str!( + "../../custom_migrations/grant_all_current_schema.sql" + ).to_string()), + (20221105003256, "DELETE FROM workspace_invite WHERE workspace_id = 'demo' AND email = 'ruben@windmill.dev';".to_string()), + (20221123151919, "".to_string()), + ].into_iter().collect(); +} + struct CustomMigrator { inner: PoolConnection, } @@ -130,12 +156,13 @@ impl Migrate for CustomMigrator { migration.version, migration.description ); - if migration.version == 20221207103910 { - tracing::info!("Skipping migration 20221207103910 to avoid using md5"); + + + if let Some(migration_sql) = OVERRIDDEN_MIGRATIONS.get(&migration.version) { + tracing::info!("Using custom migration for version {}", migration.version); + self.inner - .execute(include_str!( - "../../custom_migrations/create_workspace_without_md5.sql" - )) + .execute(&**migration_sql) .await?; let _ = sqlx::query( r#" @@ -169,7 +196,7 @@ impl Migrate for CustomMigrator { } } -pub async fn migrate(db: &DB) -> Result<(), Error> { +pub async fn migrate(db: &DB) -> Result>, Error> { let migrator = db.acquire().await?; let mut custom_migrator = CustomMigrator { inner: migrator }; @@ -224,7 +251,31 @@ pub async fn migrate(db: &DB) -> Result<(), Error> { } }); - Ok(()) + let mut jh = None; + if !has_done_migration(db, "v2_finalize_job_completed").await { + let db2 = db.clone(); + let v2jh = tokio::task::spawn(async move { + loop { + if !*MIN_VERSION_IS_AT_LEAST_1_461.read().await { + tracing::info!("Waiting for all workers to be at least version 1.461 before applying v2 finalize migration, sleeping for 5s..."); + tokio::time::sleep(Duration::from_secs(5)).await; + continue; + } + if let Err(err) = v2_finalize(&db2).await { + tracing::error!( + "{err:#}: Could not apply v2 finalize migration, retry in 30s.." + ); + tokio::time::sleep(Duration::from_secs(30)).await; + continue; + } + tracing::info!("v2 finalization step successfully applied."); + break; + } + }); + jh = Some(v2jh) + } + + Ok(jh) } async fn fix_flow_versioning_migration( @@ -274,48 +325,51 @@ async fn fix_flow_versioning_migration( Ok(()) } +async fn has_done_migration(db: &DB, migration_job_name: &str) -> bool { + sqlx::query_scalar!( + "SELECT EXISTS(SELECT name FROM windmill_migrations WHERE name = $1)", + migration_job_name + ) + .fetch_one(db) + .await + .ok() + .flatten() + .unwrap_or(false) +} + macro_rules! run_windmill_migration { - ($migration_job_name:expr, $db:expr, $code:block) => { + ($migration_job_name:expr, $db:expr, |$tx:ident| $code:block) => { { let migration_job_name = $migration_job_name; let db: &Pool = $db; - let has_done_migration = sqlx::query_scalar!( - "SELECT EXISTS(SELECT name FROM windmill_migrations WHERE name = $1)", - migration_job_name - ) - .fetch_one(db) - .await? - .unwrap_or(false); - if !has_done_migration { + let has_done = has_done_migration(db, migration_job_name).await; + if !has_done { tracing::info!("Applying {migration_job_name} migration"); - let mut tx = db.begin().await?; + let mut $tx = db.begin().await?; let mut r = false; while !r { r = sqlx::query_scalar!("SELECT pg_try_advisory_lock(4242)") - .fetch_one(&mut *tx) + .fetch_one(&mut *$tx) .await .map_err(|e| { tracing::error!("Error acquiring {migration_job_name} lock: {e:#}"); sqlx::migrate::MigrateError::Execute(e) })? .unwrap_or(false); + if !r { tracing::info!("PG {migration_job_name} lock already acquired by another server or worker, retrying in 5s. (look for the advisory lock in pg_lock with granted = true)"); + drop($tx); tokio::time::sleep(std::time::Duration::from_secs(5)).await; + $tx = db.begin().await?; } } tracing::info!("acquired lock for {migration_job_name}"); - let has_done_migration = sqlx::query_scalar!( - "SELECT EXISTS(SELECT name FROM windmill_migrations WHERE name = $1)", - migration_job_name - ) - .fetch_one(db) - .await? - .unwrap_or(false); + let has_done = has_done_migration(db, migration_job_name).await; - if !has_done_migration { + if !has_done { $code @@ -323,7 +377,7 @@ macro_rules! run_windmill_migration { "INSERT INTO windmill_migrations (name) VALUES ($1) ON CONFLICT DO NOTHING", migration_job_name ) - .execute(&mut *tx) + .execute(&mut *$tx) .await?; tracing::info!("Finished applying {migration_job_name} migration"); } else { @@ -331,9 +385,9 @@ macro_rules! run_windmill_migration { } let _ = sqlx::query("SELECT pg_advisory_unlock(4242)") - .execute(&mut *tx) + .execute(&mut *$tx) .await?; - tx.commit().await?; + $tx.commit().await?; tracing::info!("released lock for {migration_job_name}"); } else { tracing::debug!("migration {migration_job_name} already done"); @@ -343,6 +397,166 @@ macro_rules! run_windmill_migration { }; } +async fn v2_finalize(db: &DB) -> Result<(), Error> { + run_windmill_migration!("v2_finalize_disable_sync_III", db, |tx| { + tx.execute( + r#" + LOCK TABLE v2_job_queue IN ACCESS EXCLUSIVE MODE; + ALTER TABLE v2_job_queue DISABLE ROW LEVEL SECURITY; + "#, + ) + .await?; + }); + + run_windmill_migration!("v2_finalize_disable_sync_III_2", db, |tx| { + tx.execute( + r#" + LOCK TABLE v2_job_completed IN ACCESS EXCLUSIVE MODE; + ALTER TABLE v2_job_completed DISABLE ROW LEVEL SECURITY; + "#, + ) + .await?; + }); + + run_windmill_migration!("v2_finalize_disable_sync_III_3", db, |tx| { + tx.execute( + r#" + LOCK TABLE v2_job IN ACCESS EXCLUSIVE MODE; + DROP FUNCTION IF EXISTS v2_job_after_update CASCADE; + "#, + ) + .await?; + }); + + run_windmill_migration!("v2_finalize_disable_sync_III_4", db, |tx| { + tx.execute( + r#" + LOCK TABLE v2_job_completed IN ACCESS EXCLUSIVE MODE; + DROP FUNCTION IF EXISTS v2_job_completed_before_insert CASCADE; + DROP FUNCTION IF EXISTS v2_job_completed_before_update CASCADE; + "#, + ) + .await?; + }); + + run_windmill_migration!("v2_finalize_disable_sync_III_5", db, |tx| { + tx.execute( + r#" + LOCK TABLE v2_job_queue IN ACCESS EXCLUSIVE MODE; + 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; + "#, + ) + .await?; + }); + + run_windmill_migration!("v2_finalize_disable_sync_III_6", db, |tx| { + tx.execute( + r#" + LOCK TABLE v2_job_runtime IN ACCESS EXCLUSIVE MODE; + DROP FUNCTION IF EXISTS v2_job_runtime_before_insert CASCADE; + DROP FUNCTION IF EXISTS v2_job_runtime_before_update CASCADE; + "#, + ) + .await?; + }); + + run_windmill_migration!("v2_finalize_disable_sync_III_7", db, |tx| { + tx.execute( + r#" + LOCK TABLE v2_job_status IN ACCESS EXCLUSIVE MODE; + DROP FUNCTION IF EXISTS v2_job_status_before_insert CASCADE; + DROP FUNCTION IF EXISTS v2_job_status_before_update CASCADE; + "#, + ) + .await?; + }); + + run_windmill_migration!("v2_finalize_disable_sync_III_8", db, |tx| { + tx.execute( + r#" + DROP VIEW IF EXISTS completed_job, completed_job_view, job, queue, queue_view CASCADE; + "#, + ) + .await?; + }); + + run_windmill_migration!("v2_finalize_job_queue", db, |tx| { + tx.execute( + r#" + LOCK TABLE v2_job_queue IN ACCESS EXCLUSIVE MODE; + 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; + "#, + ) + .await?; + }); + run_windmill_migration!("v2_finalize_job_completed", db, |tx| { + tx.execute( + r#" + LOCK TABLE v2_job_completed 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; + "#, + ) + .await?; + }); + + Ok(()) +} + async fn fix_job_completed_index(db: &DB) -> Result<(), Error> { // let has_done_migration = sqlx::query_scalar!( // "SELECT EXISTS(SELECT name FROM windmill_migrations WHERE name = 'fix_job_completed_index')" @@ -385,7 +599,7 @@ async fn fix_job_completed_index(db: &DB) -> Result<(), Error> { // tx.commit().await?; // } - run_windmill_migration!("fix_job_completed_index_2", &db, { + run_windmill_migration!("fix_job_completed_index_2", &db, |tx| { // sqlx::query( // "CREATE INDEX CONCURRENTLY IF NOT EXISTS ix_completed_job_workspace_id_created_at_new_2 ON completed_job (workspace_id, job_kind, success, is_skipped, is_flow_step, created_at DESC)" // ).execute(db).await?; @@ -405,7 +619,7 @@ async fn fix_job_completed_index(db: &DB) -> Result<(), Error> { .await?; }); - run_windmill_migration!("fix_job_completed_index_3", &db, { + run_windmill_migration!("fix_job_completed_index_3", &db, |tx| { sqlx::query("DROP INDEX CONCURRENTLY IF EXISTS index_completed_job_on_schedule_path") .execute(db) .await?; @@ -423,8 +637,8 @@ async fn fix_job_completed_index(db: &DB) -> Result<(), Error> { .await?; }); - run_windmill_migration!("fix_job_index_1", &db, { - let migration_job_name = "fix_job_completed_index_4"; + run_windmill_migration!("fix_job_index_1_II", &db, |tx| { + let migration_job_name = "fix_job_index_1_II"; let mut i = 1; tracing::info!("step {i} of {migration_job_name} migration"); sqlx::query!("create index concurrently if not exists ix_job_workspace_id_created_at_new_3 ON v2_job (workspace_id, created_at DESC)") @@ -451,31 +665,26 @@ async fn fix_job_completed_index(db: &DB) -> Result<(), Error> { i += 1; tracing::info!("step {i} of {migration_job_name} migration"); - sqlx::query!("create index concurrently if not exists ix_job_workspace_id_created_at_new_6 ON v2_job (workspace_id, created_at DESC) where kind in ('script', 'flow') AND parent_job IS NULL") - .execute(db) - .await?; - i += 1; - tracing::info!("step {i} of {migration_job_name} migration"); - - sqlx::query!("create index concurrently if not exists ix_job_workspace_id_created_at_new_7 ON v2_job (workspace_id, created_at DESC) where kind in ('script', 'flow') AND parent_job IS NULL") - .execute(db) - .await?; - i += 1; - tracing::info!("step {i} of {migration_job_name} migration"); - sqlx::query!("create index concurrently if not exists ix_completed_job_workspace_id_started_at_new_2 ON v2_job_completed (workspace_id, started_at DESC)") .execute(db) .await?; i += 1; tracing::info!("step {i} of {migration_job_name} migration"); - sqlx::query!("create index concurrently if not exists root_job_index_by_path_2 ON v2_job (workspace_id, runnable_path, created_at desc) WHERE parent_job IS NULL") + sqlx::query!("create index concurrently if not exists ix_job_root_job_index_by_path_2 ON v2_job (workspace_id, runnable_path, created_at desc) WHERE parent_job IS NULL") .execute(db) .await?; i += 1; tracing::info!("step {i} of {migration_job_name} migration"); + sqlx::query!("DROP INDEX CONCURRENTLY IF EXISTS root_job_index_by_path_2") + .execute(db) + .await?; + + i += 1; + tracing::info!("step {i} of {migration_job_name} migration"); + sqlx::query!("create index concurrently if not exists ix_job_created_at ON v2_job (created_at DESC)") .execute(db) .await?; @@ -504,7 +713,7 @@ async fn fix_job_completed_index(db: &DB) -> Result<(), Error> { .await?; }); - run_windmill_migration!("fix_labeled_jobs_index", &db, { + run_windmill_migration!("fix_labeled_jobs_index", &db, |tx| { tracing::info!("Special migration to add index concurrently on job labels 2"); sqlx::query!("DROP INDEX CONCURRENTLY IF EXISTS labeled_jobs_on_jobs") .execute(db) @@ -514,7 +723,7 @@ async fn fix_job_completed_index(db: &DB) -> Result<(), Error> { ).execute(db).await?; }); - run_windmill_migration!("v2_labeled_jobs_index", &db, { + run_windmill_migration!("v2_labeled_jobs_index", &db, |tx| { tracing::info!("Special migration to add index concurrently on job labels"); sqlx::query!( "CREATE INDEX CONCURRENTLY ix_v2_job_labels ON v2_job @@ -525,6 +734,51 @@ async fn fix_job_completed_index(db: &DB) -> Result<(), Error> { .await?; }); + run_windmill_migration!("v2_jobs_rls", &db, |tx| { + sqlx::query!("ALTER TABLE v2_job ENABLE ROW LEVEL SECURITY") + .execute(db) + .await?; + }); + + run_windmill_migration!("v2_improve_v2_job_indices_ii", &db, |tx| { + sqlx::query!("create index concurrently if not exists ix_v2_job_workspace_id_created_at ON v2_job (workspace_id, created_at DESC) where kind in ('script', 'flow', 'singlescriptflow') AND parent_job IS NULL") + .execute(db) + .await?; + + sqlx::query!("DROP INDEX CONCURRENTLY IF EXISTS ix_job_workspace_id_created_at_new_6") + .execute(db) + .await?; + + sqlx::query!("DROP INDEX CONCURRENTLY IF EXISTS ix_job_workspace_id_created_at_new_7") + .execute(db) + .await?; + }); + + run_windmill_migration!("v2_improve_v2_queued_jobs_indices", &db, |tx| { + sqlx::query!("CREATE INDEX CONCURRENTLY IF NOT EXISTS queue_sort_v2 ON v2_job_queue (priority DESC NULLS LAST, scheduled_for, tag) WHERE running = false") + .execute(db) + .await?; + + // sqlx::query!("CREATE INDEX CONCURRENTLY queue_sort_2_v2 ON v2_job_queue (tag, priority DESC NULLS LAST, scheduled_for) WHERE running = false") + // .execute(db) + // .await?; + + sqlx::query!("DROP INDEX CONCURRENTLY IF EXISTS queue_sort") + .execute(db) + .await?; + + sqlx::query!("DROP INDEX CONCURRENTLY IF EXISTS queue_sort_2") + .execute(db) + .await?; + }); + + run_windmill_migration!("audit_timestamps", db, |tx| { + sqlx::query!( + "CREATE INDEX CONCURRENTLY IF NOT EXISTS ix_audit_timestamps ON audit (timestamp DESC)" + ) + .execute(db) + .await?; + }); Ok(()) } diff --git a/backend/windmill-api/src/drafts.rs b/backend/windmill-api/src/drafts.rs index 6ba1f4452f..02d3b3c4fb 100644 --- a/backend/windmill-api/src/drafts.rs +++ b/backend/windmill-api/src/drafts.rs @@ -1,6 +1,6 @@ /* * Author: Ruben Fiszel - * Copyright: Windmill Labs, Inc 2042 + * Copyright: Windmill Labs, Inc 2024 * 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. diff --git a/backend/windmill-api/src/embeddings.rs b/backend/windmill-api/src/embeddings.rs index 30415156ce..aba907ec16 100644 --- a/backend/windmill-api/src/embeddings.rs +++ b/backend/windmill-api/src/embeddings.rs @@ -253,7 +253,7 @@ impl ModelInstance { let token_ids = Tensor::new(&tokens[..], &Device::Cpu)?.unsqueeze(0)?; let token_type_ids = token_ids.zeros_like()?; - let embedding = self.model.forward(&token_ids, &token_type_ids)?; + let embedding = self.model.forward(&token_ids, &token_type_ids, None)?; let embedding = (embedding.sum(1)? / embedding.dim(1)? as f64)?; let embedding = normalize_l2(&embedding)?; diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index 5e779119a8..e34b2cacaf 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -12,7 +12,7 @@ use crate::db::ApiAuthed; use crate::triggers::{ get_triggers_count_internal, list_tokens_internal, TriggersCount, TruncatedTokenWithEmail, }; -use crate::utils::WithStarredInfoQuery; +use crate::utils::{RunnableKind, WithStarredInfoQuery}; use crate::{ db::DB, schedule::clear_schedule, @@ -59,11 +59,16 @@ pub fn workspaced_service() -> Router { .route("/get_triggers_count/*path", get(get_triggers_count)) .route("/list_tokens/*path", get(list_tokens)) .route("/get/*path", get(get_flow_by_path)) + .route("/deployment_status/p/*path", get(get_deployment_status)) .route("/get/draft/*path", get(get_flow_by_path_w_draft)) .route("/exists/*path", get(exists_flow_by_path)) .route("/list_paths", get(list_paths)) .route("/history/p/*path", get(get_flow_history)) .route("/get_latest_version/*path", get(get_latest_version)) + .route( + "/list_paths_from_workspace_runnable/:runnable_kind/*path", + get(list_paths_from_workspace_runnable), + ) .route( "/history_update/v/:version/p/*path", post(update_flow_history), @@ -323,6 +328,28 @@ async fn check_path_conflict<'c>( return Ok(()); } +async fn list_paths_from_workspace_runnable( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, runnable_kind, path)): Path<(String, RunnableKind, StripPath)>, +) -> JsonResult> { + let mut tx = user_db.begin(&authed).await?; + let runnables = sqlx::query_scalar!( + r#"SELECT f.path + FROM workspace_runnable_dependencies wru + JOIN flow f + ON wru.flow_path = f.path AND wru.workspace_id = f.workspace_id + WHERE wru.runnable_path = $1 AND wru.runnable_is_flow = $2 AND wru.workspace_id = $3"#, + path.to_path(), + matches!(runnable_kind, RunnableKind::Flow), + w_id + ) + .fetch_all(&mut *tx) + .await?; + tx.commit().await?; + Ok(Json(runnables)) +} + async fn create_flow( authed: ApiAuthed, Extension(db): Extension, @@ -356,8 +383,8 @@ async fn create_flow( sqlx::query!( "INSERT INTO flow (workspace_id, path, summary, description, \ - dependency_job, draft_only, tag, dedicated_worker, visible_to_runner_only, on_behalf_of_email, value, schema, edited_by, edited_at) - VALUES ($1, $2, $3, $4, NULL, $5, $6, $7, $8, $9, $10, $11::text::json, $12, now())", + dependency_job, lock_error_logs, draft_only, tag, dedicated_worker, visible_to_runner_only, on_behalf_of_email, value, schema, edited_by, edited_at) + VALUES ($1, $2, $3, $4, NULL, '', $5, $6, $7, $8, $9, $10, $11::text::json, $12, now())", w_id, nf.path, nf.summary, @@ -683,7 +710,7 @@ async fn update_flow( sqlx::query!( "UPDATE flow SET path = $1, summary = $2, description = $3,\ - dependency_job = NULL, draft_only = NULL, tag = $4, dedicated_worker = $5, visible_to_runner_only = $6, on_behalf_of_email = $7, \ + dependency_job = NULL, lock_error_logs = '', draft_only = NULL, tag = $4, dedicated_worker = $5, visible_to_runner_only = $6, on_behalf_of_email = $7, \ value = $8, schema = $9::text::json, edited_by = $10, edited_at = now() WHERE path = $11 AND workspace_id = $12", if is_new_path { flow_path } else { &nf.path }, // if new path, do not rename directly (to avoid flow_version foreign key constraint) @@ -950,6 +977,31 @@ async fn list_tokens( list_tokens_internal(&db, &w_id, &path, true).await } +#[derive(FromRow, Serialize)] +struct DeploymentStatus { + lock_error_logs: Option, +} +async fn get_deployment_status( + Extension(db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, +) -> JsonResult { + let path = path.to_path(); + let mut tx = db.begin().await?; + let status_o: Option = sqlx::query_as!( + DeploymentStatus, + "SELECT lock_error_logs FROM flow WHERE path = $1 AND workspace_id = $2", + path, + w_id, + ) + .fetch_optional(&mut *tx) + .await?; + + let status = not_found_if_none(status_o, "DeploymentStatus", path)?; + + tx.commit().await?; + Ok(Json(status)) +} + async fn get_flow_by_path( authed: ApiAuthed, Extension(user_db): Extension, @@ -961,7 +1013,7 @@ async fn get_flow_by_path( let flow_o = if query.with_starred_info.unwrap_or(false) { sqlx::query_as::<_, FlowWithStarred>( - "SELECT flow.workspace_id, flow.path, flow.summary, flow.description, flow.archived, flow.extra_perms, flow.draft_only, flow.dedicated_worker, flow.tag, flow.ws_error_handler_muted, flow.timeout, flow.visible_to_runner_only, flow.on_behalf_of_email, flow_version.schema, flow_version.value, flow_version.created_at as edited_at, flow_version.created_by as edited_by, favorite.path IS NOT NULL as starred + "SELECT flow.workspace_id, flow.path, flow.lock_error_logs, flow.summary, flow.description, flow.archived, flow.extra_perms, flow.draft_only, flow.dedicated_worker, flow.tag, flow.ws_error_handler_muted, flow.timeout, flow.visible_to_runner_only, flow.on_behalf_of_email, flow_version.schema, flow_version.value, flow_version.created_at as edited_at, flow_version.created_by as edited_by, favorite.path IS NOT NULL as starred FROM flow LEFT JOIN favorite ON favorite.favorite_kind = 'flow' @@ -978,7 +1030,7 @@ async fn get_flow_by_path( .await? } else { sqlx::query_as::<_, FlowWithStarred>( - "SELECT flow.workspace_id, flow.path, flow.summary, flow.description, flow.archived, flow.extra_perms, flow.draft_only, flow.dedicated_worker, flow.tag, flow.ws_error_handler_muted, flow.timeout, flow.visible_to_runner_only, flow.on_behalf_of_email, flow_version.schema, flow_version.value, flow_version.created_at as edited_at, flow_version.created_by as edited_by, NULL as starred + "SELECT flow.workspace_id, flow.path, flow.lock_error_logs, flow.summary, flow.description, flow.archived, flow.extra_perms, flow.draft_only, flow.dedicated_worker, flow.tag, flow.ws_error_handler_muted, flow.timeout, flow.visible_to_runner_only, flow.on_behalf_of_email, flow_version.schema, flow_version.value, flow_version.created_at as edited_at, flow_version.created_by as edited_by, NULL as starred FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.path = $1 AND flow.workspace_id = $2" @@ -1131,12 +1183,18 @@ async fn archive_flow_by_path( Ok(format!("Flow {path} archived")) } +#[derive(Deserialize)] +struct DeleteFlowQuery { + keep_captures: Option, +} + async fn delete_flow_by_path( authed: ApiAuthed, Extension(db): Extension, Extension(user_db): Extension, Extension(webhook): Extension, Path((w_id, path)): Path<(String, StripPath)>, + Query(query): Query, ) -> Result { let path = path.to_path(); let mut tx = user_db.begin(&authed).await?; @@ -1157,21 +1215,23 @@ async fn delete_flow_by_path( .execute(&mut *tx) .await?; - sqlx::query!( - "DELETE FROM capture_config WHERE path = $1 AND workspace_id = $2 AND is_flow IS TRUE", - path, - &w_id - ) - .execute(&mut *tx) - .await?; + if !query.keep_captures.unwrap_or(false) { + sqlx::query!( + "DELETE FROM capture_config WHERE path = $1 AND workspace_id = $2 AND is_flow IS TRUE", + path, + &w_id + ) + .execute(&mut *tx) + .await?; - sqlx::query!( - "DELETE FROM capture WHERE path = $1 AND workspace_id = $2 AND is_flow IS TRUE", - path, - &w_id - ) - .execute(&mut *tx) - .await?; + sqlx::query!( + "DELETE FROM capture WHERE path = $1 AND workspace_id = $2 AND is_flow IS TRUE", + path, + &w_id + ) + .execute(&mut *tx) + .await?; + } audit_log( &mut *tx, @@ -1285,7 +1345,7 @@ mod tests { }), stop_after_if: Some(StopAfterIf { expr: "foo = 'bar'".to_string(), - skip_if_stopped: false, + ..Default::default() }), stop_after_all_iters_if: None, summary: None, @@ -1314,7 +1374,7 @@ mod tests { }), stop_after_if: Some(StopAfterIf { expr: "previous.isEmpty()".to_string(), - skip_if_stopped: false, + ..Default::default() }), stop_after_all_iters_if: None, summary: None, @@ -1342,7 +1402,7 @@ mod tests { .into(), stop_after_if: Some(StopAfterIf { expr: "previous.isEmpty()".to_string(), - skip_if_stopped: false, + ..Default::default() }), stop_after_all_iters_if: None, summary: None, @@ -1392,7 +1452,8 @@ mod tests { }, "stop_after_if": { "expr": "foo = 'bar'", - "skip_if_stopped": false + "skip_if_stopped": false, + "error_message": null } }, { @@ -1414,6 +1475,7 @@ mod tests { "stop_after_if": { "expr": "previous.isEmpty()", "skip_if_stopped": false, + "error_message": null } } ], @@ -1426,7 +1488,8 @@ mod tests { }, "stop_after_if": { "expr": "previous.isEmpty()", - "skip_if_stopped": false + "skip_if_stopped": false, + "error_message": null } }, }); diff --git a/backend/windmill-api/src/folders.rs b/backend/windmill-api/src/folders.rs index cab039c64f..5047a96cad 100644 --- a/backend/windmill-api/src/folders.rs +++ b/backend/windmill-api/src/folders.rs @@ -42,6 +42,7 @@ pub fn workspaced_service() -> Router { .route("/listnames", get(list_foldernames)) .route("/create", post(create_folder)) .route("/get/:name", get(get_folder)) + .route("/exists/:name", get(exists_folder)) .route("/update/:name", post(update_folder)) .route("/getusage/:name", get(get_folder_usage)) .route("/delete/:name", delete(delete_folder)) @@ -426,6 +427,22 @@ async fn get_folder( Ok(Json(folder)) } +async fn exists_folder( + Extension(db): Extension, + Path((w_id, name)): Path<(String, String)>, +) -> JsonResult { + let exists = sqlx::query_scalar!( + "SELECT EXISTS(SELECT 1 FROM folder WHERE name = $1 AND workspace_id = $2)", + name, + w_id + ) + .fetch_one(&db) + .await? + .unwrap_or(false); + + Ok(Json(exists)) +} + #[derive(Serialize)] struct FolderUsage { pub scripts: i64, diff --git a/backend/windmill-api/src/granular_acls.rs b/backend/windmill-api/src/granular_acls.rs index 1f0f5bebf0..0cdac8c3f0 100644 --- a/backend/windmill-api/src/granular_acls.rs +++ b/backend/windmill-api/src/granular_acls.rs @@ -23,7 +23,7 @@ use windmill_common::{ utils::{not_found_if_none, StripPath}, }; -const KINDS: [&str; 13] = [ +const KINDS: [&str; 14] = [ "script", "group_", "resource", @@ -37,6 +37,7 @@ const KINDS: [&str; 13] = [ "websocket_trigger", "kafka_trigger", "nats_trigger", + "mqtt_trigger" ]; pub fn workspaced_service() -> Router { diff --git a/backend/windmill-api/src/groups.rs b/backend/windmill-api/src/groups.rs index b63d0f9de8..e25d3ee7da 100644 --- a/backend/windmill-api/src/groups.rs +++ b/backend/windmill-api/src/groups.rs @@ -72,6 +72,7 @@ pub struct NewGroup { pub struct GroupInfo { pub workspace_id: String, pub name: String, + #[serde(skip_serializing_if = "Option::is_none")] pub summary: Option, pub members: Vec, pub extra_perms: serde_json::Value, diff --git a/backend/windmill-api/src/http_trigger_auth.rs b/backend/windmill-api/src/http_trigger_auth.rs new file mode 100644 index 0000000000..b4fac32925 --- /dev/null +++ b/backend/windmill-api/src/http_trigger_auth.rs @@ -0,0 +1,751 @@ +use axum::response::{IntoResponse, Response}; +use base64::{ + prelude::{BASE64_STANDARD, BASE64_URL_SAFE}, + Engine, +}; +use hmac::{Hmac, Mac}; +use http::{header, HeaderMap, HeaderValue, StatusCode}; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use serde_json::json; +use sha1::Sha1; +use sha2::{Sha256, Sha512}; +use std::{borrow::Cow, collections::HashMap}; + +pub type HmacSha256 = Hmac; +pub type HmacSha512 = Hmac; +pub type HmacSha1 = Hmac; + +mod github { + use super::*; + pub struct Github; + + impl WebhookHandler for Github { + fn handle_challenge_request<'header>( + &self, + _: &'header HeaderMap, + _: &SignatureConfigData, + _: &str, + ) -> Result, AuthenticationError> { + Ok(None) + } + + fn get_hmac_authentication_data<'payload, 'header, 'prefix>( + &self, + headers: &'header HeaderMap, + raw_payload: &'payload str, + ) -> Result, AuthenticationError> + { + let github_secret_header = headers.try_get_webhook_header("X-Hub-Signature-256")?; + + let authentication_data = SignatureAuthenticationData::new( + Cow::Borrowed(raw_payload), + github_secret_header, + Some("sha256="), + SignatureAuthenticationDetails::new(HmacAlgorithm::Sha256, Encoding::Hex), + ); + + Ok(authentication_data) + } + } +} + +mod slack { + use super::*; + pub struct Slack; + + impl WebhookHandler for Slack { + fn handle_challenge_request<'header>( + &self, + _: &'header HeaderMap, + _: &SignatureConfigData, + _: &str, + ) -> Result, AuthenticationError> { + Ok(None) + } + + fn get_hmac_authentication_data<'payload, 'header, 'prefix>( + &self, + headers: &'header HeaderMap, + raw_payload: &'payload str, + ) -> Result, AuthenticationError> + { + let slack_secret_signature = headers.try_get_webhook_header("X-Slack-Signature")?; + let slack_timestamp_header = + headers.try_get_webhook_header("X-Slack-Request-Timestamp")?; + let signed_payload = format!("v0:{}:{}", slack_timestamp_header, raw_payload); + + Ok(SignatureAuthenticationData::new( + Cow::Owned(signed_payload), + slack_secret_signature, + Some("v0="), + SignatureAuthenticationDetails::new(HmacAlgorithm::Sha256, Encoding::Hex), + )) + } + } +} + +mod stripe { + use super::*; + + pub struct Stripe; + + impl WebhookHandler for Stripe { + fn handle_challenge_request<'header>( + &self, + _: &'header HeaderMap, + _: &SignatureConfigData, + _: &str, + ) -> Result, AuthenticationError> { + Ok(None) + } + + fn get_hmac_authentication_data<'payload, 'header, 'prefix>( + &self, + headers: &'header HeaderMap, + raw_payload: &'payload str, + ) -> Result, AuthenticationError> + { + let stripe_signature_header = headers.try_get_webhook_header("STRIPE-SIGNATURE")?; + + let stripe_signature = parse_signature(stripe_signature_header, (",", "=")); + + let timestamp = *stripe_signature + .get("t") + .ok_or(AuthenticationError::InvalidTimestamp)?; + let v1 = *stripe_signature + .get("v1") + .ok_or(AuthenticationError::InvalidSignature)?; + + let signed_payload = format!("{}.{}", timestamp, raw_payload); + + Ok(SignatureAuthenticationData::new( + Cow::Owned(signed_payload), + v1, + None, + SignatureAuthenticationDetails::new(HmacAlgorithm::Sha256, Encoding::Hex), + )) + } + } +} + +mod tiktok { + use super::*; + + pub struct TikTok; + + impl WebhookHandler for TikTok { + fn handle_challenge_request<'header>( + &self, + _: &'header HeaderMap, + _: &SignatureConfigData, + _: &str, + ) -> Result, AuthenticationError> { + Ok(None) + } + + fn get_hmac_authentication_data<'payload, 'header, 'prefix>( + &self, + headers: &'header HeaderMap, + raw_payload: &'payload str, + ) -> Result, AuthenticationError> + { + let tiktok_secret_signature = headers.try_get_webhook_header("TikTok-Signature")?; + + let stripe_signature = parse_signature(tiktok_secret_signature, (",", "=")); + + let timestamp = *stripe_signature + .get("t") + .ok_or(AuthenticationError::InvalidTimestamp)?; + let s = *stripe_signature + .get("s") + .ok_or(AuthenticationError::InvalidSignature)?; + + let signed_payload = format!("{}.{}", timestamp, raw_payload); + + Ok(SignatureAuthenticationData::new( + Cow::Owned(signed_payload), + s, + None, + SignatureAuthenticationDetails::new(HmacAlgorithm::Sha256, Encoding::Hex), + )) + } + } +} + +mod twitch { + use super::*; + use http::header; + use serde_json::value::RawValue; + #[derive(Debug, Deserialize)] + struct TwitchCrcBody { + challenge: String, + #[allow(unused)] + subscription: Box, + } + + pub struct Twitch; + + impl WebhookHandler for Twitch { + fn get_hmac_authentication_data<'payload, 'header, 'prefix>( + &self, + headers: &'header HeaderMap, + raw_payload: &'payload str, + ) -> Result, AuthenticationError> + { + let twitch_secret_signature = + headers.try_get_webhook_header("Twitch-Eventsub-Message-Signature")?; + let twitch_message_id_header = + headers.try_get_webhook_header("Twitch-Eventsub-Message-Id")?; + let twitch_timestamp_header = + headers.try_get_webhook_header("Twitch-Eventsub-Message-Timestamp")?; + + let message = format!( + "{}{}{}", + twitch_message_id_header, twitch_timestamp_header, raw_payload + ); + + Ok(SignatureAuthenticationData::new( + Cow::Owned(message), + twitch_secret_signature, + Some("sha256="), + SignatureAuthenticationDetails::new(HmacAlgorithm::Sha256, Encoding::Hex), + )) + } + + fn handle_challenge_request<'header>( + &self, + headers: &'header HeaderMap, + signature_config_data: &SignatureConfigData, + raw_payload: &str, + ) -> Result, AuthenticationError> { + let authentication_data = self.get_hmac_authentication_data(headers, raw_payload)?; + verify_hmac_signature(authentication_data, &signature_config_data.secret_key)?; + + let twitch_eventsub_message_type = + headers.try_get_webhook_header("Twitch-Eventsub-Message-Type")?; + + if twitch_eventsub_message_type != "webhook_callback_verification" { + return Ok(None); + } + let twitch_crc_body = + serde_json::from_str::(raw_payload).map_err(|e| { + AuthenticationError::InvalidChallengeResponse(format!( + "Twitch :{}", + e.to_string() + )) + })?; + + let response = ( + StatusCode::OK, + [(header::CONTENT_TYPE, "text/plain")], + twitch_crc_body.challenge.to_string(), + ); + + Ok(Some(response.into_response())) + } + } +} + +mod zoom { + use axum::Json; + + use super::*; + + #[derive(Debug, Deserialize)] + struct ZoomPayload { + #[serde(rename = "plainToken")] + plain_token: String, + } + + #[derive(Debug, Deserialize)] + #[allow(unused)] + struct ZoomChallengeResponse { + payload: ZoomPayload, + event_ts: u64, + event: String, + } + + pub struct Zoom; + + impl WebhookHandler for Zoom { + fn handle_challenge_request<'header>( + &self, + _: &'header HeaderMap, + signature_config_data: &SignatureConfigData, + raw_payload: &str, + ) -> Result, AuthenticationError> { + let Ok(zoom_request_body) = serde_json::from_str::(raw_payload) + else { + return Ok(None); + }; + + if zoom_request_body.event != "endpoint.url_validation" { + return Ok(None); + } + + let hmac_signature = calculate_hmac_signature( + HmacAlgorithm::Sha256, + &signature_config_data.secret_key, + &zoom_request_body.payload.plain_token, + ); + + let encoded_hmac_signature = encode_hmac_signature(Encoding::Hex, &hmac_signature); + + let response = ( + StatusCode::OK, + Json(json!({ + "plainToken": zoom_request_body.payload.plain_token, + "encryptedToken": encoded_hmac_signature + })), + ); + + Ok(Some(response.into_response())) + } + + fn get_hmac_authentication_data<'payload, 'header, 'prefix>( + &self, + headers: &'header HeaderMap, + raw_payload: &'payload str, + ) -> Result, AuthenticationError> + { + let zoom_signature_header = headers.try_get_webhook_header("x-zm-signature")?; + let zoom_timestamp_header = headers.try_get_webhook_header("x-zm-request-timestamp")?; + + let message = format!("v0:{}:{}", zoom_timestamp_header, raw_payload); + + Ok(SignatureAuthenticationData::new( + Cow::Owned(message), + zoom_signature_header, + Some("v0="), + SignatureAuthenticationDetails::new(HmacAlgorithm::Sha256, Encoding::Hex), + )) + } + } +} + +use constant_time_eq::constant_time_eq; +use github::Github; +use slack::Slack; +use stripe::Stripe; +use tiktok::TikTok; +use twitch::Twitch; +use zoom::Zoom; + +#[derive(Debug)] +pub struct SignatureAuthenticationDetails { + pub algorithm_to_use: HmacAlgorithm, + pub header_key_encoding: Encoding, +} + +impl SignatureAuthenticationDetails { + #[inline] + fn new(algorithm_to_use: HmacAlgorithm, header_key_encoding: Encoding) -> Self { + Self { algorithm_to_use, header_key_encoding } + } +} + +fn parse_signature<'header>( + signature: &'header str, + splitters: (&str, &str), +) -> HashMap<&'header str, &'header str> { + let headers: HashMap<&str, &str> = signature + .split(splitters.0) + .map(|header| { + let mut key_and_value = header.split(splitters.1); + let key = key_and_value.next(); + let value = key_and_value.next(); + (key, value) + }) + .filter_map(|(key, value)| match (key, value) { + (Some(key), Some(value)) => Some((key, value)), + _ => None, + }) + .collect(); + headers +} + +#[derive(Debug)] +pub struct SignatureAuthenticationData<'payload, 'header, 'prefix> { + pub signed_payload: Cow<'payload, str>, + pub header_key_value: &'header str, + pub signature_prefix: Option<&'prefix str>, + pub config: SignatureAuthenticationDetails, +} + +impl<'payload, 'header, 'prefix> SignatureAuthenticationData<'payload, 'header, 'prefix> { + pub fn new( + signed_payload: Cow<'payload, str>, + header_key_value: &'header str, + signature_prefix: Option<&'prefix str>, + config: SignatureAuthenticationDetails, + ) -> Self { + Self { signed_payload, header_key_value, signature_prefix, config } + } +} + +pub trait WebhookHandler { + fn handle_challenge_request<'header>( + &self, + headers: &'header HeaderMap, + signature_config_data: &SignatureConfigData, + raw_payload: &str, + ) -> Result, AuthenticationError>; + + fn get_hmac_authentication_data<'payload, 'header, 'prefix>( + &self, + headers: &'header HeaderMap, + raw_payload: &'payload str, + ) -> Result, AuthenticationError>; +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum HmacAlgorithm { + Sha1, + Sha256, + Sha512, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Encoding { + Base64, + Base64Uri, + Hex, +} +#[derive(Debug, Serialize, Deserialize)] +pub struct SignatureAuthenticationMethod { + algorithm: HmacAlgorithm, + encoding: Encoding, + signature_header_name: String, + signature_prefix: Option, +} + +pub struct SignatureConfigData<'config> { + secret_key: &'config str, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SignatureAuthentication { + signature_provider: WebhookType, + secret_key: String, + authentication_config: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct BasicAuthAuthentication { + pub username: String, + pub password: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ApiKeyAuthentication { + api_key_header: String, + api_key_secret: String, +} + +#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy, Serialize, Deserialize)] +#[non_exhaustive] +pub enum WebhookType { + Github, + Slack, + Stripe, + TikTok, + Twitch, + Zoom, + Custom, +} + +impl WebhookType { + pub fn get_webhook_handler(&self) -> Option<&'static dyn WebhookHandler> { + let handler: &'static dyn WebhookHandler = match *self { + WebhookType::Github => &Github, + WebhookType::Slack => &Slack, + WebhookType::Stripe => &Stripe, + WebhookType::TikTok => &TikTok, + WebhookType::Twitch => &Twitch, + WebhookType::Zoom => &Zoom, + WebhookType::Custom => return None, + }; + Some(handler) + } +} + +trait TryGetWebhookHeader { + fn try_get_webhook_header<'header>( + &'header self, + header_name: &str, + ) -> Result<&'header str, AuthenticationError>; +} + +impl TryGetWebhookHeader for HeaderMap { + fn try_get_webhook_header<'header>( + &'header self, + header_name: &str, + ) -> Result<&'header str, AuthenticationError> { + let Some(signature_header) = self.get(header_name) else { + return Err(AuthenticationError::MissingHeader(header_name.to_string())); + }; + let Some(signature_header) = signature_header.to_str().ok() else { + return Err(AuthenticationError::InvalidHeader(header_name.to_string())); + }; + + Ok(signature_header) + } +} + +pub fn calculate_hmac_signature(algorithm: HmacAlgorithm, secret: &str, payload: &str) -> Vec { + match algorithm { + HmacAlgorithm::Sha1 => { + let mut mac = + HmacSha1::new_from_slice(secret.as_bytes()).expect("HMAC can take key of any size"); + mac.update(payload.as_bytes()); + mac.finalize().into_bytes().to_vec() + } + HmacAlgorithm::Sha256 => { + let mut mac = HmacSha256::new_from_slice(secret.as_bytes()) + .expect("HMAC can take key of any size"); + mac.update(payload.as_bytes()); + mac.finalize().into_bytes().to_vec() + } + HmacAlgorithm::Sha512 => { + let mut mac = HmacSha512::new_from_slice(secret.as_bytes()) + .expect("HMAC can take key of any size"); + mac.update(payload.as_bytes()); + mac.finalize().into_bytes().to_vec() + } + } +} + +pub fn encode_hmac_signature(encoding: Encoding, hmac_signature: &[u8]) -> String { + match encoding { + Encoding::Hex => hex::encode(hmac_signature), + Encoding::Base64 => BASE64_STANDARD.encode(hmac_signature), + Encoding::Base64Uri => BASE64_URL_SAFE.encode(hmac_signature), + } +} + +pub fn verify_hmac_signature( + authentication_data: SignatureAuthenticationData, + webhook_signing_secret: &str, +) -> Result<(), AuthenticationError> { + let hmac_signature = calculate_hmac_signature( + authentication_data.config.algorithm_to_use, + &webhook_signing_secret, + &authentication_data.signed_payload, + ); + + let encoded_signature = encode_hmac_signature( + authentication_data.config.header_key_encoding, + &hmac_signature, + ); + + let final_expected_signature = + if let Some(signature_prefix) = authentication_data.signature_prefix { + format!("{}{}", signature_prefix, encoded_signature) + } else { + encoded_signature + }; + + if !constant_time_eq( + final_expected_signature.as_bytes(), + authentication_data.header_key_value.as_bytes(), + ) { + return Err(AuthenticationError::InvalidSignature); + } + + Ok(()) +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum AuthenticationMethod { + Signature(SignatureAuthentication), + BasicAuth(BasicAuthAuthentication), + ApiKey(ApiKeyAuthentication), +} + +impl AuthenticationMethod { + pub fn authenticate_http_request( + &self, + headers: &HeaderMap, + raw_payload: Option<&String>, + ) -> Result, AuthenticationError> { + match self { + AuthenticationMethod::Signature(SignatureAuthentication { + secret_key, + authentication_config, + signature_provider, + }) => { + let raw_payload = raw_payload.ok_or(AuthenticationError::InvalidPayload)?; + let config_data = SignatureConfigData { secret_key: &secret_key }; + let handler = signature_provider.get_webhook_handler(); + let challenge_response = handler + .map(|handler| { + handler.handle_challenge_request(headers, &config_data, raw_payload) + }) + .transpose()? + .flatten(); + + if let Some(challenge_response) = challenge_response { + return Ok(Some(challenge_response)); + } + + let authentication_data = match handler { + Some(handler) => handler.get_hmac_authentication_data(headers, raw_payload)?, + None => { + let authentication_config = authentication_config + .as_ref() + .ok_or(AuthenticationError::InvalidCustomConfig)?; + let signature_header_value = headers + .try_get_webhook_header(&authentication_config.signature_header_name)?; + SignatureAuthenticationData::new( + Cow::Borrowed(raw_payload), + signature_header_value, + authentication_config.signature_prefix.as_deref(), + SignatureAuthenticationDetails::new( + authentication_config.algorithm, + authentication_config.encoding, + ), + ) + } + }; + + verify_hmac_signature(authentication_data, &secret_key)?; + } + AuthenticationMethod::ApiKey(ApiKeyAuthentication { + api_key_header, + api_key_secret, + }) => { + let api_key_to_cmp = headers + .try_get_webhook_header(&api_key_header) + .map_err(|_| AuthenticationError::InvalidApiKey)?; + if api_key_to_cmp != api_key_secret { + return Err(AuthenticationError::InvalidApiKey); + } + } + AuthenticationMethod::BasicAuth(BasicAuthAuthentication { username, password }) => { + let mut credentials_store = headers + .try_get_webhook_header("Authorization") + .map_err(|_| AuthenticationError::UnauthorizedBasicHttpAuth)? + .split(' '); + + let _ = credentials_store + .next() + .filter(|r#type| *r#type == "Basic") + .ok_or(AuthenticationError::UnauthorizedBasicHttpAuth)?; + + let credentials_as_base64 = credentials_store + .next() + .ok_or(AuthenticationError::UnauthorizedBasicHttpAuth)?; + + let credentials_from_base64_as_bytes = BASE64_STANDARD + .decode(credentials_as_base64.as_bytes()) + .map_err(|_| AuthenticationError::UnauthorizedBasicHttpAuth)?; + + let credentials_separated_with_colon = + String::from_utf8(credentials_from_base64_as_bytes) + .map_err(|_| AuthenticationError::UnauthorizedBasicHttpAuth)?; + + let credentials = credentials_separated_with_colon.split(':').collect_vec(); + + if credentials.len() != 2 { + return Err(AuthenticationError::UnauthorizedBasicHttpAuth); + } + + if credentials.get(0).unwrap() != username + || credentials.get(1).unwrap() != password + { + return Err(AuthenticationError::UnauthorizedBasicHttpAuth); + } + } + } + + Ok(None) + } +} + +#[derive(thiserror::Error, Debug)] +#[allow(unused)] +pub enum AuthenticationError { + #[error("failed to parse timestamp")] + InvalidTimestamp, + + #[error("invalid secret")] + InvalidSecret(#[from] base64::DecodeError), + + #[error("invalid header `{0}`")] + InvalidHeader(String), + + #[error("signature timestamp too old")] + TimestampTooOldError, + + #[error("signature timestamp too far in future")] + FutureTimestampError, + + #[error("missing header {0}")] + MissingHeader(String), + + #[error("signature invalid")] + InvalidSignature, + + #[error("payload invalid")] + InvalidPayload, + + #[error("invalid custom config")] + InvalidCustomConfig, + + #[error("invalid auth header: {0}")] + InvalidAuthHeader(String), + + #[error("invalid api key")] + InvalidApiKey, + + #[error("invalid challenge response: {0}")] + InvalidChallengeResponse(String), + + #[error("")] + UnauthorizedBasicHttpAuth, +} + +impl IntoResponse for AuthenticationError { + fn into_response(self) -> Response { + let (status, error_message) = match &self { + AuthenticationError::InvalidTimestamp + | AuthenticationError::InvalidPayload + | AuthenticationError::InvalidHeader(_) + | AuthenticationError::MissingHeader(_) + | AuthenticationError::TimestampTooOldError + | AuthenticationError::FutureTimestampError + | AuthenticationError::InvalidCustomConfig + | AuthenticationError::InvalidChallengeResponse(_) => { + (StatusCode::BAD_REQUEST, self.to_string()) + } + + AuthenticationError::InvalidSecret(_) + | AuthenticationError::InvalidSignature + | AuthenticationError::InvalidAuthHeader(_) => { + (StatusCode::UNAUTHORIZED, self.to_string()) + } + AuthenticationError::UnauthorizedBasicHttpAuth => { + return ( + StatusCode::UNAUTHORIZED, + [(header::WWW_AUTHENTICATE, r#"Basic realm="Restricted Area""#)], + "Unauthorized", + ) + .into_response() + } + AuthenticationError::InvalidApiKey => { + return (StatusCode::UNAUTHORIZED, "Unauthorized").into_response() + } + }; + + let body = json!({ "error": error_message }); + + let mut headers = HeaderMap::new(); + headers.insert("Content-Type", HeaderValue::from_static("application/json")); + + (status, headers, body.to_string()).into_response() + } +} diff --git a/backend/windmill-api/src/http_triggers.rs b/backend/windmill-api/src/http_triggers.rs index 68e21d17ed..fa26406a13 100644 --- a/backend/windmill-api/src/http_triggers.rs +++ b/backend/windmill-api/src/http_triggers.rs @@ -1,7 +1,10 @@ +use crate::http_trigger_auth::{self}; #[cfg(feature = "parquet")] use crate::job_helpers_ee::get_workspace_s3_resource; +use crate::resources::try_get_resource_from_db_as; +use crate::utils::non_empty_str; use crate::{ - args::WebhookArgs, + args::try_from_request_body, auth::{AuthCache, OptTokened}, db::{ApiAuthed, DB}, jobs::{ @@ -10,8 +13,9 @@ use crate::{ }, users::fetch_api_authed, }; +use axum::response::Response; use axum::{ - extract::{Path, Query}, + extract::{Path, Query, Request}, response::IntoResponse, routing::{delete, get, post}, Extension, Json, Router, @@ -22,9 +26,11 @@ use http::{HeaderMap, StatusCode}; use serde::{Deserialize, Serialize}; use sql_builder::{bind::Bind, SqlBuilder}; use sqlx::prelude::FromRow; +use std::borrow::Cow; use std::{collections::HashMap, sync::Arc}; use tower_http::cors::CorsLayer; use windmill_audit::{audit_ee::audit_log, ActionKind}; +use windmill_common::error::Error; #[cfg(feature = "parquet")] use windmill_common::s3_helpers::build_object_store_client; use windmill_common::{ @@ -75,7 +81,7 @@ pub fn workspaced_service() -> Router { .route("/route_exists", post(exists_route)) } -#[derive(Serialize, Deserialize, sqlx::Type)] +#[derive(Serialize, Deserialize, sqlx::Type, Debug)] #[sqlx(type_name = "HTTP_METHOD", rename_all = "lowercase")] #[serde(rename_all = "lowercase")] pub enum HttpMethod { @@ -100,36 +106,57 @@ impl TryFrom<&http::Method> for HttpMethod { } } -#[derive(Deserialize)] +#[derive(sqlx::Type, Serialize, Deserialize, Debug, PartialEq, Clone, Copy)] +#[sqlx(type_name = "AUTHENTICATION_METHOD", rename_all = "snake_case")] +#[serde(rename_all(serialize = "snake_case", deserialize = "snake_case"))] +pub enum AuthenticationMethod { + None, + Windmill, + ApiKey, + BasicHttp, + CustomScript, + Signature, +} + +#[derive(Debug, Deserialize)] struct NewTrigger { path: String, route_path: String, script_path: String, is_flow: bool, is_async: bool, - requires_auth: bool, - http_method: HttpMethod, + authentication_resource_path: Option, + authentication_method: AuthenticationMethod, static_asset_config: Option>, + http_method: HttpMethod, + workspaced_route: Option, is_static_website: bool, + wrap_body: Option, + raw_string: Option, } #[derive(FromRow, Serialize)] -struct HttpTrigger { - workspace_id: String, - path: String, - route_path: String, - route_path_key: String, - script_path: String, - is_flow: bool, - edited_by: String, - email: String, - edited_at: chrono::DateTime, - extra_perms: serde_json::Value, - is_async: bool, - requires_auth: bool, - http_method: HttpMethod, - static_asset_config: Option>, - is_static_website: bool, +pub struct HttpTrigger { + pub workspace_id: String, + pub path: String, + pub route_path: String, + pub route_path_key: String, + pub script_path: String, + pub is_flow: bool, + pub edited_by: String, + pub email: String, + pub edited_at: chrono::DateTime, + pub extra_perms: serde_json::Value, + pub is_async: bool, + pub authentication_method: AuthenticationMethod, + pub http_method: HttpMethod, + #[serde(skip_serializing_if = "Option::is_none")] + pub static_asset_config: Option>, + pub is_static_website: bool, + pub authentication_resource_path: Option, + pub workspaced_route: bool, + pub wrap_body: bool, + pub raw_string: bool, } #[derive(Deserialize)] @@ -139,10 +166,15 @@ struct EditTrigger { script_path: String, is_flow: bool, is_async: bool, - requires_auth: bool, + authentication_method: AuthenticationMethod, + #[serde(deserialize_with = "non_empty_str")] + authentication_resource_path: Option, http_method: HttpMethod, static_asset_config: Option>, + workspaced_route: Option, is_static_website: bool, + wrap_body: Option, + raw_string: Option, } #[derive(Deserialize)] @@ -163,7 +195,27 @@ async fn list_triggers( let mut tx = user_db.begin(&authed).await?; let (per_page, offset) = paginate(Pagination { per_page: lst.per_page, page: lst.page }); let mut sqlb = SqlBuilder::select_from("http_trigger") - .field("*") + .fields(&[ + "workspace_id", + "path", + "route_path", + "route_path_key", + "workspaced_route", + "wrap_body", + "raw_string", + "script_path", + "is_flow", + "http_method", + "edited_by", + "email", + "edited_at", + "extra_perms", + "is_async", + "authentication_method", + "static_asset_config", + "is_static_website", + "authentication_resource_path", + ]) .order_by("edited_at", true) .and_where("workspace_id = ?".bind(&w_id)) .offset(offset) @@ -198,9 +250,33 @@ async fn get_trigger( let path = path.to_path(); let trigger = sqlx::query_as!( HttpTrigger, - r#"SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, http_method as "http_method: _", edited_by, email, edited_at, extra_perms, is_async, requires_auth, static_asset_config as "static_asset_config: _", is_static_website - FROM http_trigger - WHERE workspace_id = $1 AND path = $2"#, + r#" + SELECT + workspace_id, + path, + route_path, + route_path_key, + workspaced_route, + script_path, + is_flow, + http_method as "http_method: _", + edited_by, + email, + edited_at, + extra_perms, + is_async, + authentication_method as "authentication_method: _", + static_asset_config as "static_asset_config: _", + is_static_website, + authentication_resource_path, + wrap_body, + raw_string + FROM + http_trigger + WHERE + workspace_id = $1 AND + path = $2 + "#, w_id, path, ) @@ -213,6 +289,23 @@ async fn get_trigger( Ok(Json(trigger)) } +fn validate_authentication_method( + authentication_method: AuthenticationMethod, + raw_string: Option, +) -> error::Result<()> { + match (authentication_method, raw_string) { + (AuthenticationMethod::CustomScript, raw) if !raw.unwrap_or(false) == true => { + return Err(Error::BadRequest( + "To use custom script authentication, please enable the raw body option." + .to_string(), + )); + } + _ => {} + } + + Ok(()) +} + async fn create_trigger( authed: ApiAuthed, Extension(db): Extension, @@ -226,12 +319,21 @@ async fn create_trigger( return Err(error::Error::BadRequest("Invalid route path".to_string())); } + validate_authentication_method(ct.authentication_method, ct.raw_string)?; + // route path key is extracted from the route path to check for uniqueness // it replaces /?:{key} with :key // it will also remove the leading / if present, not an issue as we only allow : after slashes - let route_path_key = ROUTE_PATH_KEY_RE.replace_all(ct.route_path.as_str(), ":key"); - - let exists = route_path_key_exists(&route_path_key, &ct.http_method, &w_id, None, &db).await?; + let route_path_key = ROUTE_PATH_KEY_RE.replace_all(&ct.route_path, ":key"); + let exists = route_path_key_exists( + &route_path_key, + &ct.http_method, + &w_id, + None, + ct.workspaced_route, + &db, + ) + .await?; if exists { return Err(error::Error::BadRequest( "A route already exists with this path".to_string(), @@ -246,22 +348,51 @@ async fn create_trigger( let mut tx = user_db.begin(&authed).await?; sqlx::query!( - "INSERT INTO http_trigger (workspace_id, path, route_path, route_path_key, script_path, is_flow, is_async, requires_auth, http_method, static_asset_config, edited_by, email, edited_at, is_static_website) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, now(), $13)", + r#" + INSERT INTO http_trigger ( + workspace_id, + path, + route_path, + route_path_key, + workspaced_route, + authentication_resource_path, + wrap_body, + raw_string, + script_path, + is_flow, + is_async, + authentication_method, + http_method, + static_asset_config, + edited_by, + email, + edited_at, + is_static_website + ) + VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, now(), $17 + ) + "#, w_id, ct.path, ct.route_path, &route_path_key, + ct.workspaced_route, + ct.authentication_resource_path, + ct.wrap_body.unwrap_or(false), + ct.raw_string.unwrap_or(false), ct.script_path, ct.is_flow, ct.is_async, - ct.requires_auth, + ct.authentication_method as _, ct.http_method as _, ct.static_asset_config as _, &authed.username, &authed.email, - ct.is_static_website, + ct.is_static_website ) - .execute(&mut *tx).await?; + .execute(&mut *tx) + .await?; audit_log( &mut *tx, @@ -287,13 +418,14 @@ async fn update_trigger( Json(ct): Json, ) -> error::Result { let path = path.to_path(); - if *CLOUD_HOSTED && (ct.is_static_website || ct.static_asset_config.is_some()) { return Err(error::Error::BadRequest( "Static website and static asset are not supported on cloud".to_string(), )); } + validate_authentication_method(ct.authentication_method, ct.raw_string)?; + let mut tx; if authed.is_admin { let Some(route_path) = ct.route_path else { @@ -308,9 +440,15 @@ async fn update_trigger( let route_path_key = ROUTE_PATH_KEY_RE.replace_all(&route_path, ":key"); - let exists = - route_path_key_exists(&route_path_key, &ct.http_method, &w_id, Some(&path), &db) - .await?; + let exists = route_path_key_exists( + &route_path_key, + &ct.http_method, + &w_id, + Some(&path), + ct.workspaced_route, + &db, + ) + .await?; if exists { return Err(error::Error::BadRequest( "A route already exists with this path".to_string(), @@ -318,13 +456,38 @@ async fn update_trigger( } tx = user_db.begin(&authed).await?; - sqlx::query!( - "UPDATE http_trigger - SET route_path = $1, route_path_key = $2, script_path = $3, path = $4, is_flow = $5, http_method = $6, static_asset_config = $7, edited_by = $8, email = $9, is_async = $10, requires_auth = $11, edited_at = now(), is_static_website = $12 - WHERE workspace_id = $13 AND path = $14", + r#" + UPDATE + http_trigger + SET + route_path = $1, + route_path_key = $2, + workspaced_route = $3, + wrap_body = $4, + raw_string = $5, + authentication_resource_path = $6, + script_path = $7, + path = $8, + is_flow = $9, + http_method = $10, + static_asset_config = $11, + edited_by = $12, + email = $13, + is_async = $14, + authentication_method = $15, + edited_at = now(), + is_static_website = $16 + WHERE + workspace_id = $17 AND + path = $18 + "#, route_path, &route_path_key, + ct.workspaced_route, + ct.wrap_body, + ct.raw_string, + ct.authentication_resource_path, ct.script_path, ct.path, ct.is_flow, @@ -333,17 +496,43 @@ async fn update_trigger( &authed.username, &authed.email, ct.is_async, - ct.requires_auth, + ct.authentication_method as _, ct.is_static_website, w_id, path, ) - .execute(&mut *tx).await?; + .execute(&mut *tx) + .await?; } else { tx = user_db.begin(&authed).await?; sqlx::query!( - "UPDATE http_trigger SET script_path = $1, path = $2, is_flow = $3, http_method = $4, static_asset_config = $5, edited_by = $6, email = $7, is_async = $8, requires_auth = $9, edited_at = now(), is_static_website = $10 - WHERE workspace_id = $11 AND path = $12", + r#" + UPDATE + http_trigger + SET + workspaced_route = $1, + wrap_body = $2, + raw_string = $3, + authentication_resource_path = $4, + script_path = $5, + path = $6, + is_flow = $7, + http_method = $8, + static_asset_config = $9, + edited_by = $10, + email = $11, + is_async = $12, + authentication_method = $13, + edited_at = now(), + is_static_website = $14 + WHERE + workspace_id = $15 AND + path = $16 + "#, + ct.workspaced_route, + ct.wrap_body, + ct.raw_string, + ct.authentication_resource_path, ct.script_path, ct.path, ct.is_flow, @@ -352,12 +541,13 @@ async fn update_trigger( &authed.username, &authed.email, ct.is_async, - ct.requires_auth, + ct.authentication_method as _, ct.is_static_website, w_id, path, ) - .execute(&mut *tx).await?; + .execute(&mut *tx) + .await?; } audit_log( @@ -385,9 +575,11 @@ async fn delete_trigger( let path = path.to_path(); let mut tx = user_db.begin(&authed).await?; sqlx::query!( - "DELETE FROM http_trigger WHERE workspace_id = $1 AND path = $2", + "DELETE FROM http_trigger + WHERE workspace_id = $1 + AND path = $2", w_id, - path, + path ) .execute(&mut *tx) .await?; @@ -414,13 +606,17 @@ async fn exists_trigger( ) -> JsonResult { let path = path.to_path(); let exists = sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE path = $1 AND workspace_id = $2)", + "SELECT EXISTS( + SELECT 1 FROM http_trigger + WHERE path = $1 AND workspace_id = $2 + )", path, - w_id, + w_id ) .fetch_one(&db) .await? .unwrap_or(false); + Ok(Json(exists)) } @@ -429,6 +625,7 @@ struct RouteExists { route_path: String, http_method: HttpMethod, trigger_path: Option, + workspaced_route: Option, } async fn route_path_key_exists( @@ -436,22 +633,47 @@ async fn route_path_key_exists( http_method: &HttpMethod, w_id: &str, trigger_path: Option<&str>, + workspaced_route: Option, db: &DB, ) -> error::Result { let exists = if *CLOUD_HOSTED { sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE route_path_key = $1 AND workspace_id = $2 AND http_method = $3 AND ($4::TEXT IS NULL OR path != $4))", - &route_path_key, - w_id, - http_method as &HttpMethod, - trigger_path - ) - .fetch_one(db) - .await? - .unwrap_or(false) + r#" + SELECT EXISTS( + SELECT 1 + FROM http_trigger + WHERE + route_path_key = $1 + AND workspace_id = $2 + AND http_method = $3 + AND ($4::TEXT IS NULL OR path != $4) + ) + "#, + &route_path_key, + w_id, + http_method as &HttpMethod, + trigger_path + ) + .fetch_one(db) + .await? + .unwrap_or(false) } else { + let route_path_key = match workspaced_route { + Some(true) => Cow::Owned(format!("{}/{}", w_id, route_path_key.trim_matches('/'))), + _ => Cow::Borrowed(route_path_key), + }; sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE route_path_key = $1 AND http_method = $2 AND ($3::TEXT IS NULL OR path != $3))", + r#" + SELECT EXISTS( + SELECT 1 + FROM http_trigger + WHERE + ((workspaced_route IS TRUE AND workspace_id || '/' || route_path_key = $1) + OR (workspaced_route IS FALSE AND route_path_key = $1)) + AND http_method = $2 + AND ($3::TEXT IS NULL OR path != $3) + ) + "#, &route_path_key, http_method as &HttpMethod, trigger_path @@ -460,13 +682,16 @@ async fn route_path_key_exists( .await? .unwrap_or(false) }; + Ok(exists) } async fn exists_route( Extension(db): Extension, Path(w_id): Path, - Json(RouteExists { route_path, http_method, trigger_path }): Json, + Json(RouteExists { route_path, http_method, trigger_path, workspaced_route }): Json< + RouteExists, + >, ) -> JsonResult { let route_path_key = ROUTE_PATH_KEY_RE.replace_all(route_path.as_str(), ":key"); @@ -475,6 +700,7 @@ async fn exists_route( &http_method, &w_id, trigger_path.as_deref(), + workspaced_route, &db, ) .await?; @@ -482,6 +708,7 @@ async fn exists_route( Ok(Json(exists)) } +#[derive(Debug, Deserialize)] struct TriggerRoute { path: String, script_path: String, @@ -489,11 +716,15 @@ struct TriggerRoute { route_path: String, workspace_id: String, is_async: bool, - requires_auth: bool, + authentication_method: AuthenticationMethod, edited_by: String, email: String, static_asset_config: Option>, is_static_website: bool, + authentication_resource_path: Option, + workspaced_route: bool, + wrap_body: bool, + raw_string: bool, } async fn get_http_route_trigger( @@ -513,7 +744,29 @@ async fn get_http_route_trigger( let route_path = StripPath(splitted.collect::>().join("/")); let triggers = sqlx::query_as!( TriggerRoute, - r#"SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, static_asset_config as "static_asset_config: _", is_static_website FROM http_trigger WHERE workspace_id = $1 AND http_method = $2"#, + r#" + SELECT + path, + script_path, + is_flow, + route_path, + workspace_id, + is_async, + authentication_method AS "authentication_method: _", + edited_by, + email, + static_asset_config AS "static_asset_config: _", + wrap_body, + raw_string, + workspaced_route, + is_static_website, + authentication_resource_path + FROM + http_trigger + WHERE + workspace_id = $1 AND + http_method = $2 + "#, w_id, http_method as HttpMethod ) @@ -523,7 +776,28 @@ async fn get_http_route_trigger( } else { let triggers = sqlx::query_as!( TriggerRoute, - r#"SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, static_asset_config as "static_asset_config: _", is_static_website FROM http_trigger WHERE http_method = $1"#, + r#" + SELECT + path, + script_path, + is_flow, + route_path, + authentication_resource_path, + workspace_id, + is_async, + authentication_method AS "authentication_method: _", + edited_by, + email, + static_asset_config AS "static_asset_config: _", + wrap_body, + raw_string, + workspaced_route, + is_static_website + FROM + http_trigger + WHERE + http_method = $1 + "#, http_method as HttpMethod ) .fetch_all(db) @@ -534,10 +808,13 @@ async fn get_http_route_trigger( let mut router = matchit::Router::new(); for (idx, trigger) in triggers.iter().enumerate() { - let route_path = trigger.route_path.clone(); + let route_path = match trigger.workspaced_route { + true => format!("{}/{}", &trigger.workspace_id, &trigger.route_path), + _ => trigger.route_path.clone(), + }; if trigger.is_static_website { router - .insert(format!("{}/*wm_subpath", route_path), idx) + .insert(format!("/{}/*wm_subpath", route_path), idx) .unwrap_or_else(|e| { tracing::warn!( "Failed to consider http trigger route {}: {:?}", @@ -546,19 +823,22 @@ async fn get_http_route_trigger( ); }); } - router.insert(route_path.as_str(), idx).unwrap_or_else(|e| { - tracing::warn!( - "Failed to consider http trigger route {}: {:?}", - route_path, - e, - ); - }); + router + .insert(format!("/{}", route_path), idx) + .unwrap_or_else(|e| { + tracing::warn!( + "Failed to consider http trigger route {}: {:?}", + route_path, + e, + ); + }); } - let trigger_idx = router.at(route_path.0.as_str()).ok(); + let requested_path = format!("/{}", route_path.0); + let trigger_idx = router.at(requested_path.as_str()).ok(); let matchit::Match { value: trigger_idx, params } = - not_found_if_none(trigger_idx, "Trigger", route_path.0.as_str())?; + not_found_if_none(trigger_idx, "Trigger", requested_path.as_str())?; let trigger = triggers.remove(trigger_idx.to_owned()); @@ -567,7 +847,7 @@ async fn get_http_route_trigger( .map(|(k, v)| (k.to_string(), v.to_string())) .collect(); - let username_override = if trigger.requires_auth { + let username_override = if let AuthenticationMethod::Windmill = trigger.authentication_method { let opt_authed = if let Some(token) = token { auth_cache .get_authed(Some(trigger.workspace_id.clone()), token) @@ -579,7 +859,16 @@ async fn get_http_route_trigger( // check that the user has access to the trigger let mut tx = user_db.begin(&authed).await?; let exists = sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1 AND path = $2)", + r#" + SELECT EXISTS( + SELECT 1 + FROM + http_trigger + WHERE + workspace_id = $1 AND + path = $2 + ) + "#, trigger.workspace_id, trigger.path ) @@ -648,10 +937,10 @@ async fn route_job( Query(query): Query>, method: http::Method, headers: HeaderMap, - args: WebhookArgs, -) -> impl IntoResponse { + request: Request, +) -> Result { let route_path = route_path.to_path().trim_end_matches("/"); - let (trigger, called_path, params, authed) = match get_http_route_trigger( + let (trigger, called_path, params, authed) = get_http_route_trigger( route_path, &auth_cache, token.as_ref(), @@ -660,25 +949,82 @@ async fn route_job( &method, ) .await - { - Ok(trigger) => trigger, - Err(e) => return e.into_response(), - }; + .map_err(|e| e.into_response())?; - let mut args = match args + let args = try_from_request_body( + request, + &(), + Some(match trigger.authentication_method { + AuthenticationMethod::CustomScript | AuthenticationMethod::Signature => true, + _ => trigger.raw_string, + }), + Some(trigger.wrap_body), + ) + .await + .map_err(|e| e.into_response())?; + + let mut args = args .to_push_args_owned(&authed, &db, &trigger.workspace_id) .await - { - Ok(args) => args, - Err(e) => return e.into_response(), - }; + .map_err(|e| e.into_response())?; + + match trigger.authentication_method { + AuthenticationMethod::None + | AuthenticationMethod::Windmill + | AuthenticationMethod::CustomScript => {} + _ => { + let resource_path = match trigger.authentication_resource_path { + Some(resource_path) => resource_path, + None => { + return Err(Error::BadRequest( + "Missing authentication resource path".to_string(), + ) + .into_response()) + } + }; + + let authentication_method = + try_get_resource_from_db_as::( + authed.clone(), + Some(user_db.clone()), + &db, + &resource_path, + &trigger.workspace_id, + ) + .await + .map_err(|e| e.into_response())?; + + let raw_payload = args + .extra + .as_ref() + .and_then(|extra| { + extra + .get("raw_string") + .and_then(|value| Some(value.to_string())) + .and_then(|raw_payload| Some(serde_json::from_str::(&raw_payload))) + }) + .transpose() + .map_err(|e| { + windmill_common::error::Error::SerdeJson { location: e.to_string(), error: e } + .into_response() + })?; + + let response = authentication_method + .authenticate_http_request(&headers, raw_payload.as_ref()) + .map_err(|e| e.into_response())?; + + if let Some(response) = response { + return Ok(response); + } + } + } #[cfg(not(feature = "parquet"))] if trigger.static_asset_config.is_some() { - return error::Error::internal_err( + return Err(error::Error::internal_err( "Static asset configuration is not supported in this build".to_string(), ) - .into_response(); + .into_response()); } #[cfg(feature = "parquet")] @@ -778,13 +1124,14 @@ async fn route_job( }; match build_static_response_f.await { Ok((status, headers, body_stream)) => { - return (status, headers, body_stream).into_response() + return Ok((status, headers, body_stream).into_response()) } - Err(e) => return e.into_response(), + Err(e) => return Err(e.into_response()), } } let extra = args.extra.get_or_insert_with(HashMap::new); + extra.insert( "wm_trigger".to_string(), build_http_trigger_extra( @@ -800,7 +1147,7 @@ async fn route_job( let run_query = RunJobQuery::default(); - if trigger.is_flow { + let response = if trigger.is_flow { if trigger.is_async { run_flow_by_path_inner( authed, @@ -856,5 +1203,7 @@ async fn route_job( .await .into_response() } - } + }; + + Ok(response) } diff --git a/backend/windmill-api/src/inputs.rs b/backend/windmill-api/src/inputs.rs index 1ce3fa9226..9894275737 100644 --- a/backend/windmill-api/src/inputs.rs +++ b/backend/windmill-api/src/inputs.rs @@ -82,9 +82,9 @@ impl RunnableType { fn column_name(&self) -> &'static str { match self { - RunnableType::ScriptHash => "script_hash", - RunnableType::ScriptPath => "script_path", - RunnableType::FlowPath => "script_path", + RunnableType::ScriptHash => "runnable_id", + RunnableType::ScriptPath => "runnable_path", + RunnableType::FlowPath => "runnable_path", } } } @@ -118,6 +118,8 @@ pub struct CompletedJobMini { #[derive(Deserialize)] struct GetInputHistory { include_preview: Option, + args: Option, + include_non_root: Option, } async fn get_input_history( @@ -132,13 +134,27 @@ async fn get_input_history( let mut tx = user_db.begin(&authed).await?; + let args_query = if let Some(args) = &g.args { + sql_builder::bind::Bind::bind(&"and v2_job.args @> ?", &args.replace("'", "''")) + } else { + "".to_string() + }; + + let include_non_root = if g.include_non_root.unwrap_or(false) { + "" + } else { + "AND parent_job IS NULL" + }; + let sql = &format!( - "select id, created_at, created_by, 'null'::jsonb as args, success from v2_as_completed_job \ - where {} = $1 and job_kind = any($2) and workspace_id = $3 \ - order by created_at desc limit $4 offset $5", - r.runnable_type.column_name() + "select id, v2_job.created_at, created_by, 'null'::jsonb as args, status = 'success' as success from v2_job JOIN v2_job_completed USING (id) \ + where v2_job.workspace_id = $3 and {} = $1 and kind = any($2) {args_query} AND v2_job_completed.status != 'skipped' {include_non_root} \ + order by v2_job.created_at desc limit $4 offset $5", + r.runnable_type.column_name(), + ); + // tracing::info!("sql: {}", sql); let query = sqlx::query_as::<_, CompletedJobMini>(sql); let query = match r.runnable_type { @@ -177,9 +193,9 @@ async fn get_input_history( row.created_by ), created_at: row.created_at, - args: row.args.unwrap_or(sqlx::types::Json( + args: sqlx::types::Json( serde_json::value::RawValue::from_string("null".to_string()).unwrap(), - )), + ), created_by: row.created_by, is_public: true, success: row.success, diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 774656d521..8884b7fb12 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -8,7 +8,9 @@ use axum::body::Body; use axum::http::HeaderValue; -use futures::TryFutureExt; +#[cfg(feature = "deno_core")] +use deno_core::{op2, serde_v8, v8, JsRuntime, OpState}; +use futures::{StreamExt, TryFutureExt}; use http::{HeaderMap, HeaderName}; use itertools::Itertools; use quick_cache::sync::Cache; @@ -26,9 +28,8 @@ use tower::ServiceBuilder; use windmill_common::error::JsonResult; use windmill_common::flow_status::{JobResult, RestartedFrom}; use windmill_common::jobs::{format_completed_job_result, format_result, ENTRYPOINT_OVERRIDE}; -use windmill_common::worker::{CLOUD_HOSTED, TMP_DIR}; +use windmill_common::worker::{Connection, CLOUD_HOSTED, TMP_DIR}; -#[cfg(all(feature = "enterprise", feature = "parquet"))] use windmill_common::scripts::PREVIEW_IS_CODEBASE_HASH; use windmill_common::variables::get_workspace_key; @@ -143,6 +144,13 @@ pub fn workspaced_service() -> Router { .layer(cors.clone()) .layer(ce_headers.clone()), ) + .route( + "/run/batch_rerun_jobs", + post(batch_rerun_jobs) + .head(|| async { "" }) + .layer(cors.clone()) + .layer(ce_headers.clone()), + ) .route( "/run/workflow_as_code/:job_id/:entrypoint", post(run_workflow_as_code) @@ -206,6 +214,13 @@ pub fn workspaced_service() -> Router { "/list", get(list_jobs).layer(Extension(api_list_jobs_query_duration)), ) + .route( + "/list_selected_job_groups", + // We use post because sending a huge array as a query param can produce + // URLs that may be too long + post(list_selected_job_groups), + ) + .route("/list_filtered_uuids", get(list_filtered_job_uuids)) .route("/queue/list", get(list_queue_jobs)) .route("/queue/count", get(count_queue_jobs)) .route("/queue/list_filtered_uuids", get(list_filtered_uuids)) @@ -375,7 +390,6 @@ async fn cancel_job_api( email: "anonymous".to_string(), }, }; - let (mut tx, job_option) = tokio::time::timeout( std::time::Duration::from_secs(120), windmill_queue::cancel_job( @@ -590,7 +604,9 @@ async fn get_flow_job_debug_info( Extension(db): Extension, Path((w_id, id)): Path<(String, Uuid)>, ) -> error::Result { - let job = GetQuery::new().fetch_queued(&db, id, &w_id).await?; + let job = GetQuery::new() + .fetch_queued((&db).into(), id, &w_id) + .await?; if let Some(job) = job { let is_flow = job.is_flow(); if job.is_flow_step || !is_flow { @@ -647,6 +663,48 @@ async fn get_flow_job_debug_info( } } +async fn list_selected_job_groups( + authed: ApiAuthed, + Extension(user_db): Extension, + Path(w_id): Path, + Json(uuids): Json>, +) -> error::Result { + let mut tx = user_db.begin(&authed).await?; + + let results = sqlx::query_scalar!( + r#"SELECT jsonb_build_object( + 'kind', jb.kind, + 'script_path', jb.runnable_path, + 'latest_schema', COALESCE( + (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.workspace_id = $1 AND s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC), + (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.workspace_id = $1 AND flow.path = jb.runnable_path AND jb.kind = 'flow') + ), + 'schemas', ARRAY( + SELECT jsonb_build_object( + 'script_hash', LPAD(TO_HEX(COALESCE(s.hash, f.id)), 16, '0'), + 'job_ids', ARRAY_AGG(DISTINCT j.id), + 'schema', (ARRAY_AGG(COALESCE(s.schema, f.schema)))[1] + ) FROM v2_job j + LEFT JOIN script s ON s.hash = j.runnable_id AND j.kind = 'script' + LEFT JOIN flow_version f ON f.id = j.runnable_id AND j.kind = 'flow' + WHERE j.id = ANY(ARRAY_AGG(jb.id)) + GROUP BY COALESCE(s.hash, f.id) + ) + ) FROM v2_job jb + WHERE (jb.kind = 'flow' OR jb.kind = 'script') + AND jb.workspace_id = $1 AND jb.id = ANY($2) + GROUP BY jb.kind, jb.runnable_path"#, + &w_id, + &uuids + ) + .fetch_all(&mut *tx) + .await?; + + tx.commit().await?; + + Ok(Json(results).into_response()) +} + #[derive(Deserialize)] struct GetJobQuery { pub no_logs: Option, @@ -679,59 +737,167 @@ async fn get_job( } macro_rules! get_job_query { - ("v2_as_completed_job", $($opts:tt)*) => { + ("v2_job_completed", $($opts:tt)*) => { get_job_query!( - @impl "v2_as_completed_job", ($($opts)*), - "duration_ms, success, result, result_columns, deleted, is_skipped, result->'wm_labels' as labels, \ + @impl "v2_job_completed", ($($opts)*), + "v2_job_completed.duration_ms, CASE WHEN status = 'success' OR status = 'skipped' THEN true ELSE false END as success, result_columns, deleted, status = 'skipped' as is_skipped, result->'wm_labels' as labels, \ CASE WHEN result is null or pg_column_size(result) < 90000 THEN result ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as result", + "", ) }; - ("v2_as_queue", $($opts:tt)*) => { + ("v2_job_queue", $($opts:tt)*) => { get_job_query!( - @impl "v2_as_queue", ($($opts)*), - "scheduled_for, running, last_ping, suspend, suspend_until, same_worker, pre_run_error, visible_to_owner, \ - root_job, leaf_jobs, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id, cache_ttl,\ + @impl "v2_job_queue", ($($opts)*), + "scheduled_for, running, ping as last_ping, suspend, suspend_until, same_worker, pre_run_error, visible_to_owner, \ + flow_innermost_root_job AS root_job, flow_leaf_jobs AS leaf_jobs, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id, cache_ttl,\ script_entrypoint_override", + "LEFT JOIN v2_job_runtime ON v2_job_runtime.id = v2_job_queue.id LEFT JOIN v2_job_status ON v2_job_status.id = v2_job_queue.id", ) }; - (@impl $table:literal, (with_logs: $with_logs:expr, $($rest:tt)*), $additional_fields:literal, $($args:tt)*) => { + (@impl $table:literal, (with_logs: $with_logs:expr, $($rest:tt)*), $additional_fields:literal, $additional_joins:literal, $($args:tt)*) => { if $with_logs { - get_job_query!(@impl $table, ($($rest)*), $additional_fields, logs = "right(job_logs.logs, 20000)", $($args)*) + get_job_query!(@impl $table, ($($rest)*), $additional_fields, $additional_joins, logs = "right(job_logs.logs, 20000)", $($args)*) } else { - get_job_query!(@impl $table, ($($rest)*), $additional_fields, logs = "null", $($args)*) + get_job_query!(@impl $table, ($($rest)*), $additional_fields, $additional_joins, logs = "null", $($args)*) } }; - (@impl $table:literal, (with_code: $with_code:expr, $($rest:tt)*), $additional_fields:literal, $($args:tt)*) => { + (@impl $table:literal, (with_code: $with_code:expr, $($rest:tt)*), $additional_fields:literal, $additional_joins:literal, $($args:tt)*) => { if $with_code { - get_job_query!(@impl $table, ($($rest)*), $additional_fields, lock = "raw_lock", code = "raw_code", $($args)*) + get_job_query!(@impl $table, ($($rest)*), $additional_fields, $additional_joins, lock = "raw_lock", code = "raw_code", $($args)*) } else { - get_job_query!(@impl $table, ($($rest)*), $additional_fields, lock = "null", code = "null", $($args)*) + get_job_query!(@impl $table, ($($rest)*), $additional_fields, $additional_joins, lock = "null", code = "null", $($args)*) } }; - (@impl $table:literal, (with_flow: $with_flow:expr, $($rest:tt)*), $additional_fields:literal, $($args:tt)*) => { + (@impl $table:literal, (with_flow: $with_flow:expr, $($rest:tt)*), $additional_fields:literal, $additional_joins:literal, $($args:tt)*) => { if $with_flow { - get_job_query!(@impl $table, ($($rest)*), $additional_fields, flow = "raw_flow", $($args)*) + get_job_query!(@impl $table, ($($rest)*), $additional_fields, $additional_joins, flow = "raw_flow", $($args)*) } else { - get_job_query!(@impl $table, ($($rest)*), $additional_fields, flow = "null", $($args)*) + get_job_query!(@impl $table, ($($rest)*), $additional_fields, $additional_joins, flow = "null", $($args)*) } }; - (@impl $table:literal, (), $additional_fields:literal, $($args:tt)*) => { + (@impl $table:literal, (), $additional_fields:literal, $additional_joins:literal, $($args:tt)*) => { const_format::formatcp!( "SELECT \ - id, {table}.workspace_id, parent_job, created_by, {table}.created_at, started_at, script_hash, script_path, \ - CASE WHEN args is null or pg_column_size(args) < 90000 THEN args ELSE '{{\"reason\": \"WINDMILL_TOO_BIG\"}}'::jsonb END as args, \ - {logs} as logs, {code} as raw_code, canceled, canceled_by, canceled_reason, job_kind, \ - schedule_path, permissioned_as, flow_status, {flow} as raw_flow, is_flow_step, language, \ - {lock} as raw_lock, email, visible_to_owner, mem_peak, tag, priority, preprocessed, {additional_fields} \ - FROM {table} LEFT JOIN job_logs ON id = job_id \ - WHERE id = $1 AND {table}.workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3)) LIMIT 1", + {table}.id, {table}.workspace_id, parent_job, v2_job.created_by, v2_job.created_at, started_at, v2_job.runnable_id as script_hash, v2_job.runnable_path as script_path, \ + CASE WHEN args is null THEN NULL + WHEN pg_column_size(args) < 90000 THEN + CASE WHEN jsonb_typeof(args) = 'object' THEN args + ELSE jsonb_build_object('value', args) + END + ELSE '{{\"reason\": \"WINDMILL_TOO_BIG\"}}'::jsonb END as args, COALESCE(flow_status, workflow_as_code_status) AS flow_status, \ + {logs} as logs, {code} as raw_code, canceled_by is not null as canceled, canceled_by, canceled_reason, kind as job_kind, \ + CASE WHEN trigger_kind = 'schedule'::job_trigger_kind THEN trigger END AS schedule_path, permissioned_as, \ + {flow} as raw_flow, flow_step_id IS NOT NULL AS is_flow_step, script_lang as language, \ + {lock} as raw_lock, permissioned_as_email as email, visible_to_owner, memory_peak as mem_peak, v2_job.tag, v2_job.priority, preprocessed, worker,\ + {additional_fields} \ + FROM {table} + INNER JOIN v2_job ON v2_job.id = {table}.id \ + {additional_joins} \ + LEFT JOIN job_logs ON {table}.id = job_id \ + WHERE {table}.id = $1 AND {table}.workspace_id = $2 AND ($3::text[] IS NULL OR v2_job.tag = ANY($3))", table = $table, additional_fields = $additional_fields, + additional_joins = $additional_joins, $($args)* ) } } +// CREATE OR REPLACE VIEW v2_as_queue AS +// SELECT +// j.id, +// j.workspace_id, +// j.parent_job, +// j.created_by, +// j.created_at, +// q.started_at, +// q.scheduled_for, +// q.running, +// j.runnable_id AS script_hash, +// j.runnable_path AS script_path, +// j.args, +// j.raw_code, +// q.canceled_by IS NOT NULL AS canceled, +// q.canceled_by, +// q.canceled_reason, +// r.ping AS last_ping, +// j.kind AS job_kind, +// CASE WHEN j.trigger_kind = 'schedule'::job_trigger_kind THEN j.trigger END +// AS schedule_path, +// j.permissioned_as, +// COALESCE(s.flow_status, s.workflow_as_code_status) AS flow_status, +// j.raw_flow, +// j.flow_step_id IS NOT NULL AS is_flow_step, +// j.script_lang AS language, +// q.suspend, +// q.suspend_until, +// j.same_worker, +// j.raw_lock, +// j.pre_run_error, +// j.permissioned_as_email AS email, +// j.visible_to_owner, +// r.memory_peak AS mem_peak, +// j.flow_innermost_root_job AS root_job, +// s.flow_leaf_jobs AS leaf_jobs, +// j.tag, +// j.concurrent_limit, +// j.concurrency_time_window_s, +// j.timeout, +// j.flow_step_id, +// j.cache_ttl, +// j.priority, +// NULL::TEXT AS logs, +// j.script_entrypoint_override, +// j.preprocessed +// FROM v2_job_queue q +// JOIN v2_job j USING (id) +// LEFT JOIN v2_job_runtime r USING (id) +// LEFT JOIN v2_job_status s USING (id) +// ; + +// -- Add up migration script here +// CREATE OR REPLACE VIEW v2_as_completed_job AS +// SELECT +// j.id, +// j.workspace_id, +// j.parent_job, +// j.created_by, +// j.created_at, +// c.duration_ms, +// c.status = 'success' OR c.status = 'skipped' AS success, +// j.runnable_id AS script_hash, +// j.runnable_path AS script_path, +// j.args, +// c.result, +// FALSE AS deleted, +// j.raw_code, +// c.status = 'canceled' AS canceled, +// c.canceled_by, +// c.canceled_reason, +// j.kind AS job_kind, +// CASE WHEN j.trigger_kind = 'schedule'::job_trigger_kind THEN j.trigger END +// AS schedule_path, +// j.permissioned_as, +// COALESCE(c.flow_status, c.workflow_as_code_status) AS flow_status, +// j.raw_flow, +// j.flow_step_id IS NOT NULL AS is_flow_step, +// j.script_lang AS language, +// c.started_at, +// c.status = 'skipped' AS is_skipped, +// j.raw_lock, +// j.permissioned_as_email AS email, +// j.visible_to_owner, +// c.memory_peak AS mem_peak, +// j.tag, +// j.priority, +// NULL::TEXT AS logs, +// c.result_columns, +// j.script_entrypoint_override, +// j.preprocessed +// FROM v2_job_completed c +// JOIN v2_job j USING (id) +// ; + #[derive(Copy, Clone)] struct GetQuery<'a> { with_logs: bool, @@ -821,8 +987,9 @@ impl<'a> GetQuery<'a> { // Try to fetch the code from the cache, fallback to the preview code. // NOTE: This could check for the job kinds instead of the `or_else` but it's not // necessary as `fetch_script` return early if the job kind is not a preview one. - cache::job::fetch_script(db, kind, hash) - .or_else(|_| cache::job::fetch_preview_script(db, &id, raw_lock, raw_code)) + let conn = Connection::from(db.clone()); + cache::job::fetch_script(db.clone(), kind, hash) + .or_else(|_| cache::job::fetch_preview_script(&conn, &id, raw_lock, raw_code)) .await .ok() .inspect(|data| { @@ -837,7 +1004,7 @@ impl<'a> GetQuery<'a> { job_id: Uuid, workspace_id: &str, ) -> error::Result>> { - let query = get_job_query!("v2_as_queue", + let query = get_job_query!("v2_job_queue", with_logs: self.with_logs, with_code: self.with_code, with_flow: self.with_flow, @@ -851,7 +1018,7 @@ impl<'a> GetQuery<'a> { self.check_auth(job.as_ref().map(|job| job.created_by.as_str()))?; if let Some(job) = job.as_mut() { - self.resolve_raw_values(db, job.id, job.job_kind, job.script_hash, job) + self.resolve_raw_values(&db, job.id, job.job_kind, job.script_hash, job) .await; } if self.with_flow { @@ -869,11 +1036,13 @@ impl<'a> GetQuery<'a> { job_id: Uuid, workspace_id: &str, ) -> error::Result>> { - let query = get_job_query!("v2_as_completed_job", + let query = get_job_query!("v2_job_completed", with_logs: self.with_logs, with_code: self.with_code, with_flow: self.with_flow, ); + + // tracing::info!("query: {}", query); let query = sqlx::query_as::<_, JobExtended>(query) .bind(job_id) .bind(workspace_id) @@ -886,12 +1055,14 @@ impl<'a> GetQuery<'a> { self.resolve_raw_values(db, job.id, job.job_kind, job.script_hash, job) .await; } + if self.with_flow { cjob = resolve_maybe_value(db, workspace_id, self.with_code, cjob, |job| { job.raw_flow.as_mut() }) .await?; } + if let Some(mut cjob) = cjob { cjob.inner = format_completed_job_result(cjob.inner); return Ok(Some(cjob)); @@ -901,7 +1072,7 @@ impl<'a> GetQuery<'a> { async fn fetch(self, db: &DB, job_id: Uuid, workspace_id: &str) -> error::Result { let cjob = self - .fetch_completed(db, job_id, workspace_id) + .fetch_completed(db.into(), job_id, workspace_id) .await? .map(Job::CompletedJob); @@ -909,10 +1080,19 @@ impl<'a> GetQuery<'a> { Some(cjob) => Ok(cjob), None => { let job_maybe = self - .fetch_queued(db, job_id, workspace_id) + .fetch_queued(db.into(), job_id, workspace_id) .await? .map(Job::QueuedJob); - not_found_if_none(job_maybe, "Job", job_id.to_string()) + // potential race condition here, if the job was in queue and completed right after the fetch completed, so we need to check one last time + if let Some(job) = job_maybe { + return Ok(job); + } else { + let cjob2 = self + .fetch_completed(db.into(), job_id, workspace_id) + .await? + .map(Job::CompletedJob); + not_found_if_none(cjob2, "Job", job_id.to_string()) + } } } } @@ -1158,7 +1338,7 @@ pub struct ListableCompletedJob { pub parent_job: Option, pub created_by: String, pub created_at: chrono::DateTime, - pub started_at: chrono::DateTime, + pub started_at: Option>, pub duration_ms: i64, pub success: bool, #[serde(skip_serializing_if = "Option::is_none")] @@ -1248,6 +1428,7 @@ pub struct ListQueueQuery { pub order_desc: Option, pub job_kinds: Option, pub suspended: Option, + pub worker: Option, // filter by matching a subset of the args using base64 encoded json subset pub args: Option, pub tag: Option, @@ -1257,6 +1438,7 @@ pub struct ListQueueQuery { pub has_null_parent: Option, pub is_not_schedule: Option, pub concurrency_key: Option, + pub allow_wildcards: Option, } impl From for ListQueueQuery { @@ -1272,6 +1454,7 @@ impl From for ListQueueQuery { created_after: lcq.created_after, created_or_started_before: lcq.created_or_started_before, created_or_started_after: lcq.created_or_started_after, + worker: lcq.worker, running: lcq.running, parent_job: lcq.parent_job, order_desc: lcq.order_desc, @@ -1286,6 +1469,7 @@ impl From for ListQueueQuery { has_null_parent: lcq.has_null_parent, is_not_schedule: lcq.is_not_schedule, concurrency_key: lcq.concurrency_key, + allow_wildcards: lcq.allow_wildcards, } } } @@ -1296,34 +1480,50 @@ pub fn filter_list_queue_query( w_id: &str, join_outstanding_wait_times: bool, ) -> SqlBuilder { + sqlb.join("v2_job").on_eq("v2_job_queue.id", "v2_job.id"); + if join_outstanding_wait_times { sqlb.left() .join("outstanding_wait_time") - .on_eq("id", "outstanding_wait_time.job_id"); + .on_eq("v2_job.id", "outstanding_wait_time.job_id"); } if w_id != "admins" || !lq.all_workspaces.is_some_and(|x| x) { - sqlb.and_where_eq("workspace_id", "?".bind(&w_id)); + sqlb.and_where_eq("v2_job.workspace_id", "?".bind(&w_id)); + } + + if let Some(w) = &lq.worker { + if lq.allow_wildcards.unwrap_or(false) { + sqlb.and_where_like_left("v2_job_queue.worker", w.replace("*", "%")); + } else { + sqlb.and_where_eq("v2_job_queue.worker", "?".bind(w)); + } } if let Some(ps) = &lq.script_path_start { - sqlb.and_where_like_left("script_path", ps); + sqlb.and_where_like_left("runnable_path", ps); } if let Some(p) = &lq.script_path_exact { - sqlb.and_where_eq("script_path", "?".bind(p)); + sqlb.and_where_eq("runnable_path", "?".bind(p)); } if let Some(p) = &lq.schedule_path { - sqlb.and_where_eq("schedule_path", "?".bind(p)); + sqlb.and_where_eq("trigger", "?".bind(p)); + sqlb.and_where_eq("trigger_kind", "'schedule'"); } if let Some(h) = &lq.script_hash { - sqlb.and_where_eq("script_hash", "?".bind(h)); + sqlb.and_where_eq("runnable_id", "?".bind(h)); } if let Some(cb) = &lq.created_by { sqlb.and_where_eq("created_by", "?".bind(cb)); } if let Some(t) = &lq.tag { - sqlb.and_where_eq("tag", "?".bind(t)); + if lq.allow_wildcards.unwrap_or(false) { + sqlb.and_where_like_left("v2_job.tag", t.replace("*", "%")); + } else { + sqlb.and_where_eq("v2_job.tag", "?".bind(t)); + } } + if let Some(r) = &lq.running { sqlb.and_where_eq("running", &r); } @@ -1337,7 +1537,11 @@ pub fn filter_list_queue_query( sqlb.and_where_ge("started_at", "?".bind(&dt.to_rfc3339())); } if let Some(fs) = &lq.is_flow_step { - sqlb.and_where_eq("is_flow_step", fs); + if *fs { + sqlb.and_where_is_not_null("flow_step_id"); + } else { + sqlb.and_where_is_null("flow_step_id"); + } } if let Some(fs) = &lq.has_null_parent { if *fs { @@ -1346,20 +1550,20 @@ pub fn filter_list_queue_query( } if let Some(dt) = &lq.created_before { - sqlb.and_where_le("created_at", "?".bind(&dt.to_rfc3339())); + sqlb.and_where_le("v2_job.created_at", "?".bind(&dt.to_rfc3339())); } if let Some(dt) = &lq.created_after { - sqlb.and_where_ge("created_at", "?".bind(&dt.to_rfc3339())); + sqlb.and_where_ge("v2_job.created_at", "?".bind(&dt.to_rfc3339())); } if let Some(dt) = &lq.created_or_started_after { let ts = dt.timestamp_millis(); - sqlb.and_where(format!("(started_at IS NOT NULL AND started_at >= to_timestamp({} / 1000.0)) OR (started_at IS NULL AND created_at >= to_timestamp({} / 1000.0))", ts, ts)); + sqlb.and_where(format!("(started_at IS NOT NULL AND started_at >= to_timestamp({} / 1000.0)) OR (started_at IS NULL AND v2_job.created_at >= to_timestamp({} / 1000.0))", ts, ts)); } if let Some(dt) = &lq.created_or_started_before { let ts = dt.timestamp_millis(); - sqlb.and_where(format!("(started_at IS NOT NULL AND started_at < to_timestamp({} / 1000.0)) OR (started_at IS NULL AND created_at < to_timestamp({} / 1000.0))", ts, ts)); + sqlb.and_where(format!("(started_at IS NOT NULL AND started_at < to_timestamp({} / 1000.0)) OR (started_at IS NULL AND v2_job.created_at < to_timestamp({} / 1000.0))", ts, ts)); } if let Some(s) = &lq.suspended { @@ -1372,7 +1576,7 @@ pub fn filter_list_queue_query( if let Some(jk) = &lq.job_kinds { sqlb.and_where_in( - "job_kind", + "kind", &jk.split(',').into_iter().map(quote).collect::>(), ); } @@ -1386,7 +1590,8 @@ pub fn filter_list_queue_query( } if lq.is_not_schedule.unwrap_or(false) { - sqlb.and_where("schedule_path IS null"); + sqlb.and_where("trigger_kind != 'schedule'") + .or_where("trigger_kind IS NULL"); } sqlb @@ -1401,15 +1606,18 @@ pub fn list_queue_jobs_query( tags: Option>, ) -> SqlBuilder { let (limit, offset) = paginate_without_limits(pagination); - let mut sqlb = SqlBuilder::select_from("v2_as_queue") + let mut sqlb = SqlBuilder::select_from("v2_job_queue") .fields(fields) - .order_by("created_at", lq.order_desc.unwrap_or(true)) + .order_by("v2_job.created_at", lq.order_desc.unwrap_or(true)) .limit(limit) .offset(offset) .clone(); if let Some(tags) = tags { - sqlb.and_where_in("tag", &tags.iter().map(|x| quote(x)).collect::>()); + sqlb.and_where_in( + "v2_job.tag", + &tags.iter().map(|x| quote(x)).collect::>(), + ); } filter_list_queue_query(sqlb, lq, w_id, join_outstanding_wait_times) @@ -1448,26 +1656,25 @@ async fn list_queue_jobs( &w_id, &lq, &[ - "id", - "running", - "created_by", - "created_at", - "started_at", - "scheduled_for", - "script_hash", - "script_path", + "v2_job.id", + "v2_job_queue.running", + "v2_job.created_by", + "v2_job.created_at", + "v2_job_queue.started_at", + "v2_job_queue.scheduled_for", + "v2_job.runnable_id as script_hash", + "v2_job.runnable_path as script_path", "null as args", - "job_kind", - "schedule_path", - "permissioned_as", - "is_flow_step", - "language", - "same_worker", - "email", - "suspend", - "tag", - "priority", - "workspace_id", + "v2_job.kind as job_kind", + "CASE WHEN v2_job.trigger_kind = 'schedule' THEN v2_job.trigger END as schedule_path", + "v2_job.permissioned_as", + "v2_job.flow_step_id IS NOT NULL as is_flow_step", + "v2_job.script_lang as language", + "v2_job.permissioned_as_email as email", + "v2_job_queue.suspend", + "v2_job.tag", + "v2_job.priority", + "v2_job.workspace_id", ], pagination, false, @@ -1598,6 +1805,37 @@ async fn cancel_selection( cancel_jobs(jobs_to_cancel, &db, authed.username.as_str(), w_id.as_str()).await } +async fn list_filtered_job_uuids( + authed: ApiAuthed, + Extension(db): Extension, + Path(w_id): Path, + Query(lq): Query, +) -> error::JsonResult> { + require_admin(authed.is_admin, &authed.username)?; + check_scopes(&authed, || format!("jobs:listjobs"))?; + + let mut sqlb = list_completed_jobs_query( + w_id.as_str(), + None, + 0, + &lq, + &["v2_job.id"], + false, + get_scope_tags(&authed), + ); + let sqlb2 = list_queue_jobs_query( + w_id.as_str(), + &lq.into(), + &["v2_job.id"], + Pagination { page: None, per_page: None }, + false, + get_scope_tags(&authed), + ); + let query = sqlb.union_all(sqlb2.subquery()?).subquery()?; + let ids = sqlx::query_scalar(query.as_str()).fetch_all(&db).await?; + Ok(Json(ids)) +} + async fn list_filtered_uuids( authed: ApiAuthed, Extension(db): Extension, @@ -1607,16 +1845,20 @@ async fn list_filtered_uuids( ) -> error::JsonResult> { require_admin(authed.is_admin, &authed.username)?; - let mut sqlb = SqlBuilder::select_from("v2_as_queue") - .fields(&["id"]) + let mut sqlb = SqlBuilder::select_from("v2_job_queue") + .fields(&["v2_job_queue.id"]) .clone(); sqlb = join_concurrency_key(lq.concurrency_key.as_ref(), sqlb); - sqlb.and_where_is_null("schedule_path"); + sqlb.and_where_ne("v2_job.trigger_kind", "'schedule'") + .or_where_is_null("v2_job.trigger_kind"); if let Some(tags) = get_scope_tags(&authed) { - sqlb.and_where_in("tag", &tags.iter().map(|x| quote(x)).collect::>()); + sqlb.and_where_in( + "v2_job.tag", + &tags.iter().map(|x| quote(x)).collect::>(), + ); } sqlb = filter_list_queue_query(sqlb, &lq, w_id.as_str(), false); @@ -1627,7 +1869,7 @@ async fn list_filtered_uuids( Ok(Json(jobs)) } -#[derive(Serialize, Debug, FromRow)] +#[derive(Serialize)] struct QueueStats { database_length: i64, suspended: Option, @@ -1636,6 +1878,7 @@ struct QueueStats { #[derive(Deserialize)] pub struct CountQueueJobsQuery { all_workspaces: Option, + tags: Option, } async fn count_queue_jobs( @@ -1643,12 +1886,16 @@ async fn count_queue_jobs( Path(w_id): Path, Query(cq): Query, ) -> error::JsonResult { + let tags = cq + .tags + .map(|t| t.split(',').map(|s| s.to_string()).collect::>()); Ok(Json( sqlx::query_as!( QueueStats, - "SELECT coalesce(COUNT(*) FILTER(WHERE suspend = 0 AND running = false), 0) as \"database_length!\", coalesce(COUNT(*) FILTER(WHERE suspend > 0), 0) as \"suspended!\" FROM v2_as_queue WHERE (workspace_id = $1 OR $2) AND scheduled_for <= now()", + "SELECT coalesce(COUNT(*) FILTER(WHERE suspend = 0 AND running = false), 0) as \"database_length!\", coalesce(COUNT(*) FILTER(WHERE suspend > 0), 0) as \"suspended!\" FROM v2_as_queue WHERE (workspace_id = $1 OR $2) AND scheduled_for <= now() AND ($3::text[] IS NULL OR tag = ANY($3))", w_id, w_id == "admins" && cq.all_workspaces.unwrap_or(false), + tags.as_ref().map(|v| v.as_slice()) ) .fetch_one(&db) .await?, @@ -1668,28 +1915,33 @@ async fn count_completed_jobs_detail( Path(w_id): Path, Query(query): Query, ) -> error::JsonResult { - let mut sqlb = SqlBuilder::select_from("v2_as_completed_job"); + let mut sqlb = SqlBuilder::select_from("v2_job_completed"); + //FOR RLS + sqlb.join("v2_job USING (id)"); sqlb.field("COUNT(*) as count"); if !query.all_workspaces.unwrap_or(false) { - sqlb.and_where_eq("workspace_id", "?".bind(&w_id)); + sqlb.and_where_eq("v2_job.workspace_id", "?".bind(&w_id)); } if let Some(after_s_ago) = query.completed_after_s_ago { let after = Utc::now() - chrono::Duration::seconds(after_s_ago); - sqlb.and_where_gt( - "started_at + duration_ms / 1000 * interval '1 second'", - "?".bind(&after.to_rfc3339()), - ); + sqlb.and_where_gt("ended_at", "?".bind(&after.to_rfc3339())); } if let Some(success) = query.success { - sqlb.and_where_eq("success", "?".bind(&success)); + if success { + sqlb.and_where_eq("status", "'success'") + .or_where_eq("status", "'skipped'"); + } else { + sqlb.and_where_ne("status", "'success'") + .and_where_ne("status", "'skipped'"); + } } if let Some(tags) = query.tags { sqlb.and_where_in( - "tag", + "v2_job.tag", &tags .split(",") .map(|t| format!("'{}'", t)) @@ -1740,7 +1992,7 @@ async fn list_jobs( let sqlc = if lq.running.is_none() { Some(list_completed_jobs_query( &w_id, - per_page + offset, + Some(per_page + offset), 0, &ListCompletedQuery { order_desc: Some(true), ..lqc }, UnifiedJob::completed_job_fields(), @@ -1779,18 +2031,20 @@ async fn list_jobs( } else { if sqlc.is_none() { return Err(error::Error::BadRequest( - "cannot specify success, label, created_or_started_before, or started_before with running".to_string(), + "cannot specify success, label, created_or_started_before, or starte + d_before with running" + .to_string(), )); } sqlc.unwrap().limit(per_page).offset(offset).query()? }; - let mut tx = user_db.begin(&authed).await?; + let mut tx: Transaction<'_, Postgres> = user_db.begin(&authed).await?; #[cfg(feature = "prometheus")] let start = Instant::now(); #[cfg(feature = "prometheus")] - if _api_list_jobs_query_duration.is_some() { + if _api_list_jobs_query_duration.is_some() || true { tracing::info!("list_jobs query: {}", sql); } @@ -2410,6 +2664,9 @@ pub struct JobExtended { #[serde(skip_serializing_if = "Option::is_none")] pub raw_flow: Option>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub worker: Option, + #[sqlx(skip)] #[serde(skip_serializing_if = "Option::is_none")] pub self_wait_time_ms: Option, @@ -2429,6 +2686,7 @@ impl JobExtended { raw_code: None, raw_lock: None, raw_flow: None, + worker: None, self_wait_time_ms, aggregate_wait_time_ms, } @@ -2644,81 +2902,85 @@ pub struct UnifiedJob { pub self_wait_time_ms: Option, pub aggregate_wait_time_ms: Option, pub preprocessed: Option, + pub worker: Option, } const CJ_FIELDS: &[&str] = &[ "'CompletedJob' as typ", - "id", - "workspace_id", - "parent_job", - "created_by", - "created_at", - "started_at", + "v2_job.id", + "v2_job.workspace_id", + "v2_job.parent_job", + "v2_job.created_by", + "v2_job.created_at", + "v2_job_completed.started_at", "null as scheduled_for", "null as running", - "script_hash", - "script_path", + "v2_job.runnable_id as script_hash", + "v2_job.runnable_path as script_path", "null as args", - "duration_ms", - "success", - "deleted", - "canceled", - "canceled_by", - "job_kind", - "schedule_path", - "permissioned_as", - "is_flow_step", - "language", - "is_skipped", - "email", - "visible_to_owner", + "v2_job_completed.duration_ms", + "v2_job_completed.status = 'success' OR v2_job_completed.status = 'skipped' as success", + "false as deleted", + "v2_job_completed.status = 'canceled' as canceled", + "v2_job_completed.canceled_by", + "v2_job.kind as job_kind", + "CASE WHEN v2_job.trigger_kind = 'schedule' THEN v2_job.trigger END as schedule_path", + "v2_job.permissioned_as", + "v2_job.flow_step_id IS NOT NULL as is_flow_step", + "v2_job.script_lang as language", + "v2_job_completed.status = 'skipped' as is_skipped", + "v2_job.permissioned_as_email as email", + "v2_job.visible_to_owner", "null as suspend", - "mem_peak", - "tag", + "v2_job_completed.memory_peak as mem_peak", + "v2_job.tag", "null as concurrent_limit", "null as concurrency_time_window_s", - "priority", - "result->'wm_labels' as labels", + "v2_job.priority", + "v2_job_completed.result->'wm_labels' as labels", "self_wait_time_ms", "aggregate_wait_time_ms", - "preprocessed", + "v2_job.preprocessed", + "v2_job_completed.worker", ]; + const QJ_FIELDS: &[&str] = &[ "'QueuedJob' as typ", - "id", - "workspace_id", - "parent_job", - "created_by", - "created_at", - "started_at", - "scheduled_for", - "running", - "script_hash", - "script_path", + "v2_job.id", + "v2_job.workspace_id", + "v2_job.parent_job", + "v2_job.created_by", + "v2_job.created_at", + "v2_job_queue.started_at", + "v2_job_queue.scheduled_for", + "v2_job_queue.running", + "v2_job.runnable_id as script_hash", + "v2_job.runnable_path as script_path", "null as args", "null as duration_ms", "null as success", "false as deleted", - "canceled", - "canceled_by", - "job_kind", - "schedule_path", - "permissioned_as", - "is_flow_step", - "language", + "v2_job_queue.canceled_by IS NOT NULL as canceled", + "v2_job_queue.canceled_by", + "v2_job.kind as job_kind", + "CASE WHEN v2_job.trigger_kind = 'schedule' THEN v2_job.trigger END as schedule_path", + "v2_job.permissioned_as", + "v2_job.flow_step_id IS NOT NULL as is_flow_step", + "v2_job.script_lang as language", "false as is_skipped", - "email", - "visible_to_owner", - "suspend", - "mem_peak", - "tag", - "concurrent_limit", - "concurrency_time_window_s", - "priority", + "v2_job.permissioned_as_email as email", + "v2_job.visible_to_owner", + "v2_job_queue.suspend", + "null as mem_peak", + "v2_job.tag", + "v2_job.concurrent_limit", + "v2_job.concurrency_time_window_s", + "v2_job.priority", "null as labels", "self_wait_time_ms", "aggregate_wait_time_ms", - "preprocessed", + "v2_job.preprocessed", + "v2_job_queue.worker", ]; impl UnifiedJob { @@ -2830,16 +3092,17 @@ struct CancelJob { enum PreviewKind { Code, Identity, - Http, Noop, Bundle, Tarbundle, + ScriptHash, } #[derive(Deserialize)] struct Preview { content: Option, kind: Option, + script_hash: Option, path: Option, args: Option>>, language: Option, @@ -2978,6 +3241,272 @@ pub async fn check_license_key_valid() -> error::Result<()> { Ok(()) } +use windmill_common::flows::InputTransform; + +#[derive(Deserialize)] +struct BatchReRunJobsBodyArgs { + job_ids: Vec, + script_options_by_path: HashMap, + flow_options_by_path: HashMap, +} + +#[derive(Deserialize)] +struct BatchReRunOptions { + input_transforms: Option>, + use_latest_version: Option, +} + +#[derive(sqlx::FromRow, Serialize, Clone)] +struct BatchReRunQueryReturnType { + id: Uuid, + kind: JobKind, + script_path: String, + script_hash: ScriptHash, + input: serde_json::Value, + scheduled_for: chrono::DateTime, + #[serde(skip_serializing_if = "Option::is_none")] + schema: Option, +} + +#[cfg(feature = "deno_core")] +#[op2] +#[string] +fn get_deno_core_job_value(state: &mut OpState) -> Option { + let obj = state.borrow::(); + let str = serde_json::to_string(&obj).ok()?; + Some(str) +} + +#[cfg(feature = "deno_core")] +async fn batch_rerun_compute_js_expression( + expr: String, + job: BatchReRunQueryReturnType, +) -> error::Result> { + let ext = deno_core::Extension { + name: "batch_rerun_arg_transform_ext", + ops: vec![get_deno_core_job_value()].into(), + ..Default::default() + }; + let mut isolate = + JsRuntime::new(deno_core::RuntimeOptions { extensions: vec![ext], ..Default::default() }); + + { + let op_state = isolate.op_state(); + let mut op_state = op_state.borrow_mut(); + op_state.put(BatchReRunQueryReturnType { schema: None, ..job }); + } + isolate + .execute_script( + "", + "let job = JSON.parse(Deno.core.ops.get_deno_core_job_value());", + ) + .map_err(|e| Error::ExecutionErr(e.to_string()))?; + + // Run user expr + let result = isolate + .execute_script("", expr) + .map_err(|e| Error::ExecutionErr(e.to_string()))?; + let mut scope = isolate.handle_scope(); + let result = v8::Local::new(&mut scope, result); + let result: serde_json::Value = + serde_v8::from_v8(&mut scope, result).map_err(|e| Error::ExecutionErr(e.to_string()))?; + let result = JsonRawValue::from_string(result.to_string())?; + Ok(result) +} + +async fn batch_rerun_jobs( + authed: ApiAuthed, + Extension(db): Extension, + Extension(user_db): Extension, + Path(w_id): Path, + Json(body): Json, +) -> Response { + let stream = batch_rerun_jobs_inner(authed, db, user_db, w_id, body); + + let body = axum::body::Body::from_stream(stream.map(Result::<_, std::convert::Infallible>::Ok)); + + Response::builder() + .status(201) + .header("Content-Type", "text/event-stream") + .header("Cache-Control", "no-cache") + .body(body) + .unwrap() +} + +fn batch_rerun_jobs_inner( + authed: ApiAuthed, + db: DB, + user_db: UserDB, + w_id: String, + body: BatchReRunJobsBodyArgs, +) -> impl futures::Stream { + let (tx, rx) = tokio::sync::mpsc::channel(10); + tokio::spawn(async move { + let mut job_stream = sqlx::query_as!( + BatchReRunQueryReturnType, + r#"SELECT + j.id, + j.kind AS "kind: _", + COALESCE(s.path, f.path) AS "script_path!", + COALESCE(s.hash, f.id) AS "script_hash!: _", + COALESCE(jc.started_at, jq.scheduled_for, make_date(1970, 1, 1)) AS "scheduled_for!: _", + args AS input, + COALESCE(s.schema, f.schema) AS "schema: _" + FROM v2_job j + LEFT JOIN script s ON j.runnable_id = s.hash AND j.kind = 'script' + LEFT JOIN flow_version f ON j.runnable_id = f.id AND j.runnable_path = f.path AND j.kind = 'flow' + LEFT JOIN v2_job_completed jc ON jc.id = j.id + LEFT JOIN v2_job_queue jq ON jq.id = j.id + WHERE j.id = ANY($1) + AND j.workspace_id = $2 + AND COALESCE(s.hash, f.id) IS NOT NULL + AND COALESCE(s.path, f.path) IS NOT NULL"#, + &body.job_ids, + w_id + ).fetch(&db); + while let Some(Ok(job)) = job_stream.next().await { + let job_result = + batch_rerun_handle_job(&job, &authed, &db, &user_db, &w_id, &body).await; + let send_to_stream_result = tx + .send(match job_result { + Ok(uuid) => format!("{}\n", uuid), + Err(err) => format!("Error: {}\n", err.to_string()), + }) + .await; + match send_to_stream_result { + Ok(_) => {} + Err(e) => tracing::error!("Couldn't re-run job {}: {}", job.id, e.to_string()), + } + } + }); + tokio_stream::wrappers::ReceiverStream::new(rx) +} + +async fn batch_rerun_handle_job( + job: &BatchReRunQueryReturnType, + authed: &ApiAuthed, + db: &DB, + user_db: &UserDB, + w_id: &String, + body: &BatchReRunJobsBodyArgs, +) -> error::Result { + let options = if matches!(job.kind, JobKind::Script) { + &body.script_options_by_path + } else { + &body.flow_options_by_path + } + .get(&job.script_path); + + let mut args: HashMap> = serde_json::from_value(job.input.clone())?; + let use_latest_version = options.and_then(|o| o.use_latest_version).unwrap_or(false); + let input_transforms = options + .and_then(|o| o.input_transforms.as_ref()) + .map(|t| t.iter()) + .into_iter() + .flatten(); + + let latest_schema; + let schema = if use_latest_version { + latest_schema = sqlx::query_scalar!( + r#"SELECT COALESCE( + (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC), + (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.path = jb.runnable_path AND jb.kind = 'flow') + ) FROM v2_job jb + WHERE jb.id = $1 AND jb.workspace_id = $2 + GROUP BY jb.kind, jb.runnable_path"#, + &job.id, + &w_id + ).fetch_optional(db).await?.flatten(); + latest_schema.as_ref() + } else { + job.schema.as_ref() + }; + let schema = schema + .and_then(serde_json::Value::as_object) + .and_then(|s| s.get("properties")) + .and_then(serde_json::Value::as_object); + for (property_name, transform) in input_transforms { + let schema_has_key = schema + .map(|s| s.contains_key(property_name)) + .unwrap_or(false); + if !schema_has_key { + continue; + } + match transform { + InputTransform::Static { value } => { + args.insert(property_name.clone(), value.clone()); + } + InputTransform::Javascript { expr } => { + #[cfg(not(feature = "deno_core"))] + Err(error::Error::ExecutionErr( + format!("deno_core feature is not activated, cannot evaluate: {expr}") + .to_string(), + ))?; + + #[cfg(feature = "deno_core")] + args.insert( + property_name.clone(), + batch_rerun_compute_js_expression(expr.clone(), job.clone()).await?, + ); + } + } + } + + // Call appropriate function to push job to queue + match job.kind { + JobKind::Flow => { + let result = run_flow_by_path_inner( + authed.clone(), + db.clone(), + user_db.clone(), + w_id.clone(), + StripPath(job.script_path.clone()), + RunJobQuery { ..Default::default() }, + PushArgsOwned { extra: None, args }, + None, + ) + .await; + if let Ok((_, uuid)) = result { + return Ok(uuid); + } + } + JobKind::Script => { + let result = if use_latest_version { + run_script_by_path_inner( + authed.clone(), + db.clone(), + user_db.clone(), + w_id.clone(), + StripPath(job.script_path.clone()), + RunJobQuery { ..Default::default() }, + PushArgsOwned { extra: None, args }, + None, + ) + .await + } else { + run_job_by_hash_inner( + authed.clone(), + db.clone(), + user_db.clone(), + w_id.clone(), + job.script_hash, + RunJobQuery { ..Default::default() }, + PushArgsOwned { extra: None, args }, + None, + ) + .await + }; + if let Ok((_, uuid)) = result { + return Ok(uuid); + } + } + _ => {} + } + Err(error::Error::ExecutionErr( + format!("Couldn't re-run job {}", job.id).to_string(), + )) +} + pub async fn run_flow_by_path( authed: ApiAuthed, Extension(db): Extension, @@ -3323,7 +3852,7 @@ pub async fn run_workflow_as_code( path: job.script_path, language: job.language.unwrap_or_else(|| ScriptLang::Deno), lock: raw_lock, - custom_concurrency_key: windmill_queue::custom_concurrency_key(&db, job.id) + custom_concurrency_key: windmill_queue::custom_concurrency_key(&db, &job.id) .await .map_err(to_anyhow)?, concurrent_limit: job.concurrent_limit, @@ -3424,8 +3953,12 @@ pub async fn run_workflow_as_code( sqlx::query!( "INSERT INTO v2_job_status (id, workflow_as_code_status) VALUES ($1, JSONB_SET('{}'::JSONB, array[$2], $3)) - ON CONFLICT (id) DO UPDATE SET workflow_as_code_status = - COALESCE(EXCLUDED.workflow_as_code_status, '{}'::JSONB) || $3", + ON CONFLICT (id) DO UPDATE SET + workflow_as_code_status = JSONB_SET( + COALESCE(v2_job_status.workflow_as_code_status, '{}'::JSONB), + array[$2], + $3 + )", job_id, uuid.to_string(), serde_json::json!({ "scheduled_for": Utc::now(), "name": entrypoint }), @@ -4086,7 +4619,6 @@ pub async fn run_wait_result_script_by_hash( check_license_key_valid().await?; let args = args.to_push_args_owned(&authed, &db, &w_id).await?; - check_queue_too_long(&db, run_query.queue_limit).await?; let hash = script_hash.0; @@ -4320,7 +4852,10 @@ async fn run_preview_script( Some(PreviewKind::Identity) => JobPayload::Identity, Some(PreviewKind::Noop) => JobPayload::Noop, _ => JobPayload::Code(RawCode { - hash: None, + hash: preview + .script_hash + .as_ref() + .and_then(|s| windmill_common::scripts::to_i64(s).ok()), content: preview.content.unwrap_or_default(), path: preview.path, language: preview.language.unwrap_or(ScriptLang::Deno), @@ -4357,7 +4892,6 @@ async fn run_preview_script( Ok((StatusCode::CREATED, uuid.to_string())) } -#[cfg(all(feature = "enterprise", feature = "parquet"))] async fn run_bundle_preview_script( authed: ApiAuthed, Extension(db): Extension, @@ -4368,8 +4902,6 @@ async fn run_bundle_preview_script( ) -> error::Result<(StatusCode, String)> { use windmill_common::scripts::PREVIEW_IS_TAR_CODEBASE_HASH; - check_license_key_valid().await?; - check_scopes(&authed, || format!("jobs:runscript"))?; if authed.is_operator { return Err(error::Error::NotAuthorized( @@ -4417,8 +4949,8 @@ async fn run_bundle_preview_script( path: preview.path, language: preview.language.unwrap_or(ScriptLang::Deno), lock: preview.lock, - concurrent_limit: None, // TODO(gbouv): once I find out how to store limits in the content of a script, should be easy to plug limits here - concurrency_time_window_s: None, // TODO(gbouv): same as above + concurrent_limit: None, + concurrency_time_window_s: None, cache_ttl: None, dedicated_worker: preview.dedicated_worker, custom_concurrency_key: None, @@ -4464,21 +4996,48 @@ async fn run_bundle_preview_script( uploaded = true; - if let Some(os) = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS + #[cfg(all(feature = "enterprise", feature = "parquet"))] + let object_store = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS .read() .await - .clone() + .clone(); + + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + let object_store: Option<()> = None; + + if &windmill_common::utils::MODE_AND_ADDONS.mode + == &windmill_common::utils::Mode::Standalone + && object_store.is_none() { - let path = windmill_common::s3_helpers::bundle(&w_id, &id); - if let Err(e) = os - .put(&object_store::path::Path::from(path.clone()), data.into()) - .await - { - tracing::info!("Failed to put snapshot to s3 at {path}: {:?}", e); - return Err(Error::ExecutionErr(format!("Failed to put {path} to s3"))); - } + std::fs::create_dir_all( + windmill_common::worker::ROOT_STANDALONE_BUNDLE_DIR.clone(), + )?; + windmill_common::worker::write_file_bytes( + &windmill_common::worker::ROOT_STANDALONE_BUNDLE_DIR, + &id, + &data, + )?; } else { - return Err(Error::BadConfig("Object store is required for snapshot script and is not configured for servers".to_string())); + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + { + return Err(Error::ExecutionErr("codebase is an EE feature".to_string())); + } + + #[cfg(all(feature = "enterprise", feature = "parquet"))] + if let Some(os) = object_store { + check_license_key_valid().await?; + + let path = windmill_common::s3_helpers::bundle(&w_id, &id); + if let Err(e) = os + .put(&object_store::path::Path::from(path.clone()), data.into()) + .await + { + tracing::info!("Failed to put snapshot to s3 at {path}: {:?}", e); + return Err(Error::ExecutionErr(format!("Failed to put {path} to s3"))); + } + } else { + return Err(Error::BadConfig("Object store is required for snapshot script and is not configured for servers".to_string())); + } } } // println!("Length of `{}` is {} bytes", name, data.len()); @@ -4497,13 +5056,6 @@ async fn run_bundle_preview_script( Ok((StatusCode::CREATED, job_id.unwrap().to_string())) } -#[cfg(not(all(feature = "enterprise", feature = "parquet")))] -async fn run_bundle_preview_script() -> error::Result<(StatusCode, String)> { - return Err(Error::BadRequest( - "bundle preview is an ee feature".to_string(), - )); -} - #[derive(Deserialize)] pub struct RunDependenciesRequest { pub raw_scripts: Vec, @@ -4669,6 +5221,7 @@ struct BatchInfo { flow_value: Option, path: Option, rawscript: Option, + tag: Option, } #[tracing::instrument(level = "trace", skip_all)] @@ -4837,6 +5390,8 @@ async fn add_batch_jobs( } else { format!("{}", language.as_str()) } + } else if let Some(tag) = batch_info.tag { + tag } else { format!("{}", language.as_str()) }; @@ -4891,12 +5446,27 @@ async fn add_batch_jobs( .await?; sqlx::query!( - "INSERT INTO v2_job_runtime (id) SELECT unnest($1::uuid[])", + "INSERT INTO v2_job_runtime (id, ping) SELECT unnest($1::uuid[]), null", &uuids, ) .execute(&mut *tx) .await?; + sqlx::query!( + "INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) + SELECT unnest($1::uuid[]), $2, $3, $4, $5, $6, $7, $8", + &uuids, + authed.email, + authed.username, + authed.is_admin, + authed.is_operator, + &[], + &[], + w_id, + ) + .execute(&mut *tx) + .await?; + if let Some(flow_status) = flow_status { sqlx::query!( "INSERT INTO v2_job_status (id, flow_status) @@ -4909,6 +5479,13 @@ async fn add_batch_jobs( } if let Some(custom_concurrency_key) = custom_concurrency_key { + sqlx::query!( + "INSERT INTO concurrency_counter(concurrency_id, job_uuids) + VALUES ($1, '{}'::jsonb)", + &custom_concurrency_key + ) + .execute(&mut *tx) + .await?; sqlx::query!( "INSERT INTO concurrency_key (job_id, key) SELECT id, $1 FROM unnest($2::uuid[]) as id", custom_concurrency_key, @@ -5246,44 +5823,76 @@ pub fn filter_list_completed_query( w_id: &str, join_outstanding_wait_times: bool, ) -> SqlBuilder { + sqlb.join("v2_job") + .on_eq("v2_job_completed.id", "v2_job.id"); + if join_outstanding_wait_times { sqlb.left() .join("outstanding_wait_time") - .on_eq("id", "outstanding_wait_time.job_id"); + .on_eq("v2_job.id", "outstanding_wait_time.job_id"); } if let Some(label) = &lq.label { - let mut wh = format!("result->'wm_labels' ? "); - wh.push_str(&format!("'{}'", &label.replace("'", "''"))); - sqlb.and_where("result ? 'wm_labels'"); - sqlb.and_where(&wh); + if lq.allow_wildcards.unwrap_or(false) { + let wh = format!( + "EXISTS (SELECT 1 FROM jsonb_array_elements_text(result->'wm_labels') label WHERE jsonb_typeof(result->'wm_labels') = 'array' AND label LIKE '{}')", + &label.replace("*", "%").replace("'", "''") + ); + sqlb.and_where("result ? 'wm_labels'"); + sqlb.and_where(&wh); + } else { + let mut wh = format!("result->'wm_labels' ? "); + wh.push_str(&format!("'{}'", &label.replace("'", "''"))); + sqlb.and_where("result ? 'wm_labels'"); + sqlb.and_where(&wh); + } + } + + if let Some(worker) = &lq.worker { + if lq.allow_wildcards.unwrap_or(false) { + sqlb.and_where_like_left("v2_job_completed.worker", worker.replace("*", "%")); + } else { + sqlb.and_where_eq("v2_job_completed.worker", "?".bind(worker)); + } } if w_id != "admins" || !lq.all_workspaces.is_some_and(|x| x) { - sqlb.and_where_eq("workspace_id", "?".bind(&w_id)); + sqlb.and_where_eq("v2_job.workspace_id", "?".bind(&w_id)); } if let Some(p) = &lq.schedule_path { - sqlb.and_where_eq("schedule_path", "?".bind(p)); + sqlb.and_where_eq("trigger", "?".bind(p)); + sqlb.and_where_eq("trigger_kind", "'schedule'"); } if let Some(ps) = &lq.script_path_start { - sqlb.and_where_like_left("script_path", ps); + sqlb.and_where_like_left("runnable_path", ps); } if let Some(p) = &lq.script_path_exact { - sqlb.and_where_eq("script_path", "?".bind(p)); + sqlb.and_where_eq("runnable_path", "?".bind(p)); } if let Some(h) = &lq.script_hash { - sqlb.and_where_eq("script_hash", "?".bind(h)); + sqlb.and_where_eq("runnable_id", "?".bind(h)); } if let Some(t) = &lq.tag { - sqlb.and_where_eq("tag", "?".bind(t)); + if lq.allow_wildcards.unwrap_or(false) { + sqlb.and_where_like_left("v2_job.tag", t.replace("*", "%")); + } else { + sqlb.and_where_eq("v2_job.tag", "?".bind(t)); + } } + if let Some(cb) = &lq.created_by { sqlb.and_where_eq("created_by", "?".bind(cb)); } if let Some(r) = &lq.success { - sqlb.and_where_eq("success", r); + if *r { + sqlb.and_where_eq("status", "'success'") + .or_where_eq("status", "'skipped'"); + } else { + sqlb.and_where_eq("status", "'failure'") + .or_where_eq("status", "'canceled'"); + } } if let Some(pj) = &lq.parent_job { sqlb.and_where_eq("parent_job", "?".bind(pj)); @@ -5314,10 +5923,18 @@ pub fn filter_list_completed_query( } if let Some(sk) = &lq.is_skipped { - sqlb.and_where_eq("is_skipped", sk); + if *sk { + sqlb.and_where_eq("status", "'skipped'"); + } else { + sqlb.and_where_ne("status", "'skipped'"); + } } if let Some(fs) = &lq.is_flow_step { - sqlb.and_where_eq("is_flow_step", fs); + if *fs { + sqlb.and_where_is_not_null("flow_step_id"); + } else { + sqlb.and_where_is_null("flow_step_id"); + } } if let Some(fs) = &lq.has_null_parent { if *fs { @@ -5326,7 +5943,7 @@ pub fn filter_list_completed_query( } if let Some(jk) = &lq.job_kinds { sqlb.and_where_in( - "job_kind", + "kind", &jk.split(',').into_iter().map(quote).collect::>(), ); } @@ -5340,7 +5957,8 @@ pub fn filter_list_completed_query( } if lq.is_not_schedule.unwrap_or(false) { - sqlb.and_where("schedule_path IS null"); + sqlb.and_where("trigger_kind != 'schedule'") + .or_where("trigger_kind IS NULL"); } sqlb @@ -5348,22 +5966,27 @@ pub fn filter_list_completed_query( pub fn list_completed_jobs_query( w_id: &str, - per_page: usize, + per_page: Option, offset: usize, lq: &ListCompletedQuery, fields: &[&str], join_outstanding_wait_times: bool, tags: Option>, ) -> SqlBuilder { - let mut sqlb = SqlBuilder::select_from("v2_as_completed_job") + let mut sqlb = SqlBuilder::select_from("v2_job_completed") .fields(fields) - .order_by("created_at", lq.order_desc.unwrap_or(true)) + .order_by("v2_job.created_at", lq.order_desc.unwrap_or(true)) .offset(offset) - .limit(per_page) .clone(); + if let Some(per_page) = per_page { + sqlb.limit(per_page); + } if let Some(tags) = tags { - sqlb.and_where_in("tag", &tags.iter().map(|x| quote(x)).collect::>()); + sqlb.and_where_in( + "v2_job.tag", + &tags.iter().map(|x| quote(x)).collect::>(), + ); } filter_list_completed_query(sqlb, lq, w_id, join_outstanding_wait_times) @@ -5401,6 +6024,8 @@ pub struct ListCompletedQuery { pub label: Option, pub is_not_schedule: Option, pub concurrency_key: Option, + pub worker: Option, + pub allow_wildcards: Option, } async fn list_completed_jobs( @@ -5416,39 +6041,39 @@ async fn list_completed_jobs( let sql = list_completed_jobs_query( &w_id, - per_page, + Some(per_page), offset, &lq, &[ - "id", - "workspace_id", - "parent_job", - "created_by", - "created_at", - "started_at", - "duration_ms", - "success", - "script_hash", - "script_path", - "deleted", - "canceled", - "canceled_by", - "canceled_reason", - "job_kind", - "schedule_path", - "permissioned_as", + "v2_job.id", + "v2_job.workspace_id", + "v2_job.parent_job", + "v2_job.created_by", + "v2_job.created_at", + "v2_job_completed.started_at", + "v2_job_completed.duration_ms", + "v2_job_completed.status = 'success' OR v2_job_completed.status = 'skipped' as success", + "v2_job.runnable_id as script_hash", + "v2_job.runnable_path as script_path", + "false as deleted", + "v2_job_completed.status = 'canceled' as canceled", + "v2_job_completed.canceled_by", + "v2_job_completed.canceled_reason", + "v2_job.kind as job_kind", + "CASE WHEN v2_job.trigger_kind = 'schedule' THEN v2_job.trigger END as schedule_path", + "v2_job.permissioned_as", "null as raw_code", "null as flow_status", "null as raw_flow", - "is_flow_step", - "language", - "is_skipped", - "email", - "visible_to_owner", - "mem_peak", - "tag", - "priority", - "result->'wm_labels' as labels", + "v2_job.flow_step_id IS NOT NULL as is_flow_step", + "v2_job.script_lang as language", + "v2_job_completed.status = 'skipped' as is_skipped", + "v2_job.permissioned_as_email as email", + "v2_job.visible_to_owner", + "v2_job_completed.memory_peak as mem_peak", + "v2_job.tag", + "v2_job.priority", + "v2_job_completed.result->'wm_labels' as labels", "'CompletedJob' as type", ], false, @@ -5721,6 +6346,7 @@ async fn get_completed_job_result_maybe( async fn delete_completed_job<'a>( authed: ApiAuthed, Extension(user_db): Extension, + Extension(db): Extension, Path((w_id, id)): Path<(String, Uuid)>, ) -> error::Result { check_scopes(&authed, || format!("jobs:deletejob"))?; @@ -5729,9 +6355,8 @@ async fn delete_completed_job<'a>( require_admin(authed.is_admin, &authed.username)?; let tags = get_scope_tags(&authed); - let job_o = sqlx::query_as::<_, CompletedJob>( - "WITH mark_as_deleted AS ( - UPDATE v2_job_completed c SET + let job_o = sqlx::query_scalar!( + "UPDATE v2_job_completed c SET result = NULL, deleted = TRUE FROM v2_job j @@ -5740,15 +6365,15 @@ async fn delete_completed_job<'a>( AND c.workspace_id = $2 AND ($3::TEXT[] IS NULL OR tag = ANY($3)) RETURNING c.id - ) SELECT * FROM v2_as_completed_job WHERE id = (SELECT id FROM mark_as_deleted)", + ", + id, + &w_id, + tags.as_ref().map(|v| v.as_slice()) as Option<&[&str]>, ) - .bind(id) - .bind(&w_id) - .bind(tags.as_ref().map(|v| v.as_slice())) .fetch_optional(&mut *tx) .await?; - let cj = not_found_if_none(job_o, "Completed Job", id.to_string())?; + not_found_if_none(job_o, "Completed Job", id.to_string())?; sqlx::query!("UPDATE v2_job SET args = NULL WHERE id = $1", id) .execute(&mut *tx) @@ -5769,9 +6394,5 @@ async fn delete_completed_job<'a>( .await?; tx.commit().await?; - - let cj = format_completed_job_result(cj); - - let response = Json(cj).into_response(); - Ok(response) + return get_completed_job(OptAuthed(Some(authed)), Extension(db), Path((w_id, id))).await; } diff --git a/backend/windmill-api/src/lib.rs b/backend/windmill-api/src/lib.rs index 0afa15bc69..2275868285 100644 --- a/backend/windmill-api/src/lib.rs +++ b/backend/windmill-api/src/lib.rs @@ -18,6 +18,8 @@ use crate::oauth2_ee::SlackVerifier; #[cfg(feature = "smtp")] use crate::smtp_server_ee::SmtpServer; +#[cfg(feature = "mcp")] +use crate::mcp::{setup_mcp_server, Runner as McpRunner}; use crate::tracing_init::MyOnFailure; use crate::{ tracing_init::{MyMakeSpan, MyOnResponse}, @@ -25,6 +27,9 @@ use crate::{ webhook_util::WebhookShared, }; +#[cfg(feature = "agent_worker_server")] +use agent_workers_ee::AgentCache; + use anyhow::Context; use argon2::Argon2; use axum::extract::DefaultBodyLimit; @@ -34,6 +39,7 @@ use http::HeaderValue; use reqwest::Client; #[cfg(feature = "oauth2")] use std::collections::HashMap; +use tokio::task::JoinHandle; use windmill_common::global_settings::load_value_from_global_settings; use windmill_common::global_settings::EMAIL_DOMAIN_SETTING; use windmill_common::worker::HUB_CACHE_DIR; @@ -57,9 +63,11 @@ use windmill_common::error::AppError; use crate::teams_approvals::request_teams_approval; +#[cfg(feature = "agent_worker_server")] +mod agent_workers_ee; mod ai; mod apps; -mod args; +pub mod args; mod audit; mod auth; mod capture; @@ -75,6 +83,8 @@ mod folders; mod granular_acls; mod groups; #[cfg(feature = "http_trigger")] +mod http_trigger_auth; +#[cfg(feature = "http_trigger")] mod http_triggers; mod indexer_ee; mod inputs; @@ -84,12 +94,18 @@ mod postgres_triggers; #[cfg(feature = "enterprise")] mod apps_ee; +#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))] +mod gcp_triggers_ee; +#[cfg(feature = "enterprise")] +mod git_sync_ee; #[cfg(feature = "parquet")] mod job_helpers_ee; pub mod job_metrics; pub mod jobs; #[cfg(all(feature = "enterprise", feature = "kafka"))] mod kafka_triggers_ee; +#[cfg(feature = "mqtt_trigger")] +mod mqtt_triggers; #[cfg(all(feature = "enterprise", feature = "nats"))] mod nats_triggers_ee; #[cfg(feature = "oauth2")] @@ -108,7 +124,11 @@ mod slack_approvals; mod teams_approvals; #[cfg(feature = "smtp")] mod smtp_server_ee; +#[cfg(all(feature = "enterprise", feature = "sqs_trigger"))] +mod sqs_triggers_ee; + mod static_assets; +#[cfg(all(feature = "stripe", feature = "enterprise"))] mod stripe_ee; mod teams_ee; mod tracing_init; @@ -117,7 +137,7 @@ mod users; mod users_ee; mod utils; mod variables; -mod webhook_util; +pub mod webhook_util; #[cfg(feature = "websocket")] mod websocket_triggers; mod workers; @@ -126,6 +146,9 @@ mod workspaces_ee; mod workspaces_export; mod workspaces_extra; +#[cfg(feature = "mcp")] +mod mcp; + pub const DEFAULT_BODY_LIMIT: usize = 2097152 * 100; // 200MB lazy_static::lazy_static! { @@ -203,10 +226,11 @@ pub async fn run_server( job_index_reader: Option, log_index_reader: Option, addr: SocketAddr, - mut rx: tokio::sync::broadcast::Receiver<()>, + mut killpill_rx: tokio::sync::broadcast::Receiver<()>, port_tx: tokio::sync::oneshot::Sender, server_mode: bool, - #[cfg(feature = "smtp")] base_internal_url: String, + mcp_mode: bool, + _base_internal_url: String, ) -> anyhow::Result<()> { let user_db = UserDB::new(db.clone()); @@ -240,7 +264,10 @@ pub async fn run_server( .layer(Extension(log_index_reader)) // .layer(Extension(index_writer)) .layer(CookieManagerLayer::new()) - .layer(Extension(WebhookShared::new(rx.resubscribe(), db.clone()))) + .layer(Extension(WebhookShared::new( + killpill_rx.resubscribe(), + db.clone(), + ))) .layer(DefaultBodyLimit::max( REQUEST_SIZE_LIMIT.read().await.clone(), )); @@ -273,7 +300,7 @@ pub async fn run_server( db: db.clone(), user_db: user_db, auth_cache: auth_cache.clone(), - base_internal_url: base_internal_url.clone(), + base_internal_url: _base_internal_url.clone(), }); if let Err(err) = smtp_server.start_listener_thread(addr).await { tracing::error!("Error starting SMTP server: {err:#}"); @@ -322,31 +349,155 @@ pub async fn run_server( } }; - if !*CLOUD_HOSTED { + let mqtt_triggers_service = { + #[cfg(all(feature = "mqtt_trigger"))] + { + mqtt_triggers::workspaced_service() + } + + #[cfg(not(feature = "mqtt_trigger"))] + { + Router::new() + } + }; + + let gcp_triggers_service = { + #[cfg(all(feature = "enterprise", feature = "gcp_trigger"))] + { + gcp_triggers_ee::workspaced_service() + } + + #[cfg(not(all(feature = "enterprise", feature = "gcp_trigger")))] + { + Router::new() + } + }; + + let sqs_triggers_service = { + #[cfg(all(feature = "enterprise", feature = "sqs_trigger"))] + { + sqs_triggers_ee::workspaced_service() + } + + #[cfg(not(all(feature = "enterprise", feature = "sqs_trigger")))] + { + Router::new() + } + }; + + let websocket_triggers_service = { #[cfg(feature = "websocket")] { - let ws_killpill_rx = rx.resubscribe(); + websocket_triggers::workspaced_service() + } + + #[cfg(not(feature = "websocket"))] + Router::new() + }; + + let http_triggers_service = { + #[cfg(feature = "http_trigger")] + { + http_triggers::workspaced_service() + } + + #[cfg(not(feature = "http_trigger"))] + Router::new() + }; + + let postgres_triggers_service = { + #[cfg(feature = "postgres_trigger")] + { + postgres_triggers::workspaced_service() + } + + #[cfg(not(feature = "postgres_trigger"))] + Router::new() + }; + + if !*CLOUD_HOSTED && server_mode && !mcp_mode { + #[cfg(feature = "websocket")] + { + let ws_killpill_rx = killpill_rx.resubscribe(); websocket_triggers::start_websockets(db.clone(), ws_killpill_rx); } #[cfg(all(feature = "enterprise", feature = "kafka"))] { - let kafka_killpill_rx = rx.resubscribe(); + let kafka_killpill_rx = killpill_rx.resubscribe(); kafka_triggers_ee::start_kafka_consumers(db.clone(), kafka_killpill_rx); } #[cfg(all(feature = "enterprise", feature = "nats"))] { - let nats_killpill_rx = rx.resubscribe(); + let nats_killpill_rx = killpill_rx.resubscribe(); nats_triggers_ee::start_nats_consumers(db.clone(), nats_killpill_rx); } + #[cfg(feature = "postgres_trigger")] { - let db_killpill_rx = rx.resubscribe(); + let db_killpill_rx = killpill_rx.resubscribe(); postgres_triggers::start_database(db.clone(), db_killpill_rx); } + + #[cfg(feature = "mqtt_trigger")] + { + let mqtt_killpill_rx = killpill_rx.resubscribe(); + mqtt_triggers::start_mqtt_consumer(db.clone(), mqtt_killpill_rx); + } + + #[cfg(all(feature = "enterprise", feature = "sqs_trigger"))] + { + let sqs_killpill_rx = killpill_rx.resubscribe(); + sqs_triggers_ee::start_sqs(db.clone(), sqs_killpill_rx); + } + + #[cfg(all(feature = "enterprise", feature = "gcp_trigger"))] + { + let gcp_killpill_rx = killpill_rx.resubscribe(); + gcp_triggers_ee::start_consuming_gcp_pubsub_event(db.clone(), gcp_killpill_rx); + } } + let listener = tokio::net::TcpListener::bind(addr) + .await + .context("binding main windmill server")?; + let port = listener.local_addr().map(|x| x.port()).unwrap_or(8000); + let ip = listener + .local_addr() + .map(|x| x.ip().to_string()) + .unwrap_or("localhost".to_string()); + + // Setup MCP server + #[allow(unused_variables)] + let (mcp_router, mcp_main_ct, mcp_service_ct) = { + #[cfg(feature = "mcp")] + if server_mode || mcp_mode { + let (mcp_sse_server, mcp_router) = setup_mcp_server(addr, "/api/mcp/w/:workspace_id")?; + #[cfg(feature = "mcp")] + let mcp_main_ct = mcp_sse_server.config.ct.clone(); // Token to signal shutdown *to* MCP + #[cfg(feature = "mcp")] + let mcp_service_ct = mcp_sse_server.with_service(McpRunner::new); // Token to wait for MCP *service* shutdown + (mcp_router, Some(mcp_main_ct), Some(mcp_service_ct)) + } else { + (Router::new(), None, None) + } + + #[cfg(not(feature = "mcp"))] + (Router::new(), None::<()>, None::<()>) + }; + + #[cfg(feature = "agent_worker_server")] + let (agent_workers_router, agent_workers_bg_processor, agent_workers_killpill_tx) = + if server_mode { + agent_workers_ee::workspaced_service(db.clone(), _base_internal_url.clone()) + } else { + (Router::new(), vec![], None) + }; + + #[cfg(feature = "agent_worker_server")] + let agent_cache = Arc::new(AgentCache::new()); + // build our application with a route let app = Router::new() .nest( @@ -395,35 +546,14 @@ pub async fn run_server( .nest("/variables", variables::workspaced_service()) .nest("/workspaces", workspaces::workspaced_service()) .nest("/oidc", oidc_ee::workspaced_service()) - .nest("/http_triggers", { - #[cfg(feature = "http_trigger")] - { - http_triggers::workspaced_service() - } - - #[cfg(not(feature = "http_trigger"))] - Router::new() - }) - .nest("/websocket_triggers", { - #[cfg(feature = "websocket")] - { - websocket_triggers::workspaced_service() - } - - #[cfg(not(feature = "websocket"))] - Router::new() - }) + .nest("/http_triggers", http_triggers_service) + .nest("/websocket_triggers", websocket_triggers_service) .nest("/kafka_triggers", kafka_triggers_service) .nest("/nats_triggers", nats_triggers_service) - .nest("/postgres_triggers", { - #[cfg(feature = "postgres_trigger")] - { - postgres_triggers::workspaced_service() - } - - #[cfg(not(feature = "postgres_trigger"))] - Router::new() - }), + .nest("/mqtt_triggers", mqtt_triggers_service) + .nest("/sqs_triggers", sqs_triggers_service) + .nest("/gcp_triggers", gcp_triggers_service) + .nest("/postgres_triggers", postgres_triggers_service), ) .nest("/workspaces", workspaces::global_service()) .nest( @@ -441,6 +571,7 @@ pub async fn run_server( .nest("/apps", apps::global_service().layer(cors.clone())) .nest("/schedules", schedule::global_service()) .nest("/embeddings", embeddings::global_service()) + .nest("/ai", ai::global_service()) .route_layer(from_extractor::()) .route_layer(from_extractor::()) .nest("/jobs", jobs::global_root_service()) @@ -478,6 +609,28 @@ pub async fn run_server( .layer(from_extractor::()) .layer(cors.clone()), ) + .nest("/mcp/w/:workspace_id", mcp_router) + .layer(from_extractor::()) + .nest("/agent_workers", { + #[cfg(feature = "agent_worker_server")] + { + agent_workers_ee::global_service().layer(Extension(agent_cache.clone())) + } + #[cfg(not(feature = "agent_worker_server"))] + { + Router::new() + } + }) + .nest("/w/:workspace_id/agent_workers", { + #[cfg(feature = "agent_worker_server")] + { + agent_workers_router.layer(Extension(agent_cache.clone())) + } + #[cfg(not(feature = "agent_worker_server"))] + { + Router::new() + } + }) .nest( "/w/:workspace_id/jobs_u", jobs::workspace_unauthed_service().layer(cors.clone()), @@ -502,6 +655,24 @@ pub async fn run_server( "/w/:workspace_id/jobs/teams_approval/:job_id", get(request_teams_approval), ) + .nest("/w/:workspace_id/github_app", { + #[cfg(feature = "enterprise")] + { + git_sync_ee::workspaced_service() + } + + #[cfg(not(feature = "enterprise"))] + Router::new() + }) + .nest("/github_app", { + #[cfg(feature = "enterprise")] + { + git_sync_ee::global_service() + } + + #[cfg(not(feature = "enterprise"))] + Router::new() + }) .nest( "/w/:workspace_id/resources_u", resources::public_service().layer(cors.clone()), @@ -538,6 +709,20 @@ pub async fn run_server( } .layer(from_extractor::()), ) + .nest( + "/gcp/w/:workspace_id", + { + #[cfg(all(feature = "enterprise", feature = "gcp_trigger"))] + { + gcp_triggers_ee::gcp_push_route_handler() + } + #[cfg(not(all(feature = "enterprise", feature = "gcp_trigger")))] + { + Router::new() + } + } + .layer(from_extractor::()), + ) .route("/version", get(git_v)) .route("/uptodate", get(is_up_to_date)) .route("/ee_license", get(ee_license)) @@ -559,13 +744,6 @@ pub async fn run_server( ) }; - let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); - let port = listener.local_addr().map(|x| x.port()).unwrap_or(8000); - let ip = listener - .local_addr() - .map(|x| x.ip().to_string()) - .unwrap_or("localhost".to_string()); - let server = axum::serve(listener, app.into_make_service()); tracing::info!( @@ -580,11 +758,36 @@ pub async fn run_server( .expect("Failed to send port"); let server = server.with_graceful_shutdown(async move { - rx.recv().await.ok(); + killpill_rx.recv().await.ok(); + #[cfg(feature = "agent_worker_server")] + if let Some(agent_workers_killpill_tx) = agent_workers_killpill_tx { + if let Err(e) = agent_workers_killpill_tx.kill().await { + tracing::error!("Error killing agent workers: {e:#}"); + } + } tracing::info!("Graceful shutdown of server"); + + #[cfg(feature = "mcp")] + { + if let Some(mcp_main_ct) = mcp_main_ct { + tracing::info!("Received shutdown signal, cancelling MCP server..."); + mcp_main_ct.cancel(); + } + if let Some(mcp_service_ct) = mcp_service_ct { + tracing::info!("Waiting for MCP service cancellation..."); + mcp_service_ct.cancelled().await; + tracing::info!("MCP service cancelled."); + } + } }); server.await?; + #[cfg(feature = "agent_worker_server")] + for (i, bg_processor) in agent_workers_bg_processor.into_iter().enumerate() { + tracing::info!("server off. shutting down agent worker bg processor {i}"); + bg_processor.await?; + tracing::info!("agent worker bg processor {i} shut down"); + } Ok(()) } @@ -649,7 +852,8 @@ async fn openapi_json() -> &'static str { include_str!("../openapi-deref.json") } -pub async fn migrate_db(db: &DB) -> anyhow::Result<()> { - db::migrate(db).await?; - Ok(()) +pub async fn migrate_db(db: &DB) -> anyhow::Result>> { + db::migrate(db) + .await + .map_err(|e| anyhow::anyhow!("Error migrating db: {e:#}")) } diff --git a/backend/windmill-api/src/mcp.rs b/backend/windmill-api/src/mcp.rs new file mode 100644 index 0000000000..c8c86e2e1f --- /dev/null +++ b/backend/windmill-api/src/mcp.rs @@ -0,0 +1,1143 @@ +use std::borrow::Cow; +use std::collections::HashMap; +use std::net::SocketAddr; +use std::sync::Arc; + +use axum::body::to_bytes; +use axum::Router; +use rmcp::transport::sse_server::{SseServer, SseServerConfig}; +use rmcp::{ + handler::server::ServerHandler, + model::*, + service::{RequestContext, RoleServer}, + Error, +}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use sql_builder::prelude::*; +use sqlx::FromRow; +use tokio::try_join; +use tokio_util::sync::CancellationToken; +use windmill_common::db::UserDB; +use windmill_common::worker::to_raw_value; +use windmill_common::{DB, HUB_BASE_URL}; + +use windmill_common::scripts::{get_full_hub_script_by_path, Schema}; + +use crate::db::ApiAuthed; +use crate::jobs::{ + run_wait_result_flow_by_path_internal, run_wait_result_script_by_path_internal, RunJobQuery, +}; +use crate::HTTP_CLIENT; +use windmill_common::utils::{query_elems_from_hub, StripPath}; + +/// Transforms the path for workspace scripts/flows. +/// +/// This function takes a path and a type string. +/// It then formats the transformed path with the type prefix. +/// This is used when listing, because we can't have names with slashes. +/// Because we replace slashes with underscores, we also need to escape underscores. +/// +/// # Parameters +/// - `path`: The path to transform. +/// - `type_str`: The type of the item (script or flow). +/// +/// # Returns +/// - `String`: The transformed path. +fn transform_path(path: &str, type_str: &str) -> String { + // Only apply special underscore escaping for paths starting with "f/" + let transformed = if path.starts_with("f/") { + let escaped_path = path.replace('_', "__"); + escaped_path.replace('/', "_") + } else { + path.replace('/', "_") + }; + + // first letter of type_str is used as prefix, only one letter to avoid reaching 60 char name limit + format!("{}-{}", &type_str[..1], transformed) +} + +fn convert_schema_to_schema_type(schema: Option) -> SchemaType { + let schema_obj = if let Some(ref s) = schema { + match serde_json::from_str::(s.0.get()) { + Ok(val) => val, + Err(_) => SchemaType::default(), + } + } else { + SchemaType::default() + }; + schema_obj +} + +trait ToolableItem { + fn get_path_or_id(&self) -> String; + fn get_summary(&self) -> &str; + fn get_description(&self) -> &str; + fn get_schema(&self) -> SchemaType; + fn is_hub(&self) -> bool; + fn item_type(&self) -> &'static str; + fn get_integration_type(&self) -> Option; +} + +impl ToolableItem for ScriptInfo { + fn get_path_or_id(&self) -> String { + transform_path(&self.path, "script") + } + fn get_summary(&self) -> &str { + self.summary.as_deref().unwrap_or("No summary") + } + fn get_description(&self) -> &str { + self.description.as_deref().unwrap_or("No description") + } + fn get_schema(&self) -> SchemaType { + convert_schema_to_schema_type(self.schema.clone()) + } + fn is_hub(&self) -> bool { + false + } + fn item_type(&self) -> &'static str { + "script" + } + fn get_integration_type(&self) -> Option { + None + } +} + +impl ToolableItem for FlowInfo { + fn get_path_or_id(&self) -> String { + transform_path(&self.path, "flow") + } + fn get_summary(&self) -> &str { + self.summary.as_deref().unwrap_or("No summary") + } + fn get_description(&self) -> &str { + self.description.as_deref().unwrap_or("No description") + } + fn get_schema(&self) -> SchemaType { + convert_schema_to_schema_type(self.schema.clone()) + } + fn is_hub(&self) -> bool { + false + } + fn item_type(&self) -> &'static str { + "flow" + } + fn get_integration_type(&self) -> Option { + None + } +} + +impl ToolableItem for HubScriptInfo { + fn get_path_or_id(&self) -> String { + let id = self.version_id; + let summary = self.summary.as_deref().unwrap_or("No summary"); + format!("hs-{}-{}", id, summary.replace(" ", "_")) + } + fn get_summary(&self) -> &str { + self.summary.as_deref().unwrap_or("No summary") + } + fn get_description(&self) -> &str { + self.description.as_deref().unwrap_or("No description") + } + fn get_schema(&self) -> SchemaType { + match serde_json::from_value::(self.schema.clone().unwrap_or_default()) { + Ok(schema_type) => schema_type, + Err(_) => SchemaType::default(), + } + } + fn is_hub(&self) -> bool { + true + } + fn item_type(&self) -> &'static str { + "script" + } + fn get_integration_type(&self) -> Option { + self.app.clone() + } +} + +#[derive(Clone)] +pub struct Runner {} + +#[derive(Serialize, Deserialize, Debug)] +struct HubResponse { + asks: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +struct HubScriptInfo { + version_id: u64, + summary: Option, + description: Option, + schema: Option, + app: Option, +} + +#[derive(Serialize, FromRow, Deserialize, Debug, Clone)] +struct SchemaType { + r#type: String, + properties: std::collections::HashMap, + required: Vec, +} + +impl Default for SchemaType { + fn default() -> Self { + Self { + r#type: "object".to_string(), + properties: std::collections::HashMap::new(), + required: vec![], + } + } +} + +#[derive(Serialize, FromRow, Debug)] +struct ScriptInfo { + path: String, + summary: Option, + description: Option, + schema: Option, +} + +#[derive(Serialize, FromRow)] +struct ItemSchema { + schema: Option, +} + +#[derive(Serialize, FromRow, Debug)] +struct FlowInfo { + path: String, + summary: Option, + description: Option, + schema: Option, +} + +#[derive(Serialize, FromRow, Debug)] +struct ResourceInfo { + path: String, + description: Option, + resource_type: String, +} + +#[derive(Serialize, FromRow, Debug, Clone)] +struct ResourceType { + name: String, + description: Option, +} + +impl Runner { + pub fn new() -> Self { + Self {} + } + + async fn get_item_schema( + path: &str, + user_db: &UserDB, + authed: &ApiAuthed, + workspace_id: &str, + item_type: &str, + ) -> Result, Error> { + let mut sqlb = SqlBuilder::select_from(&format!("{} as o", item_type)); + sqlb.fields(&["o.schema"]); + sqlb.and_where("o.path = ?".bind(&path)); + sqlb.and_where("o.workspace_id = ?".bind(&workspace_id)); + sqlb.and_where("o.archived = false"); + sqlb.and_where("o.draft_only IS NOT TRUE"); + let sql = sqlb.sql().map_err(|_e| { + tracing::error!("failed to build sql: {}", _e); + Error::internal_error("failed to build sql", None) + })?; + let mut tx = user_db + .clone() + .begin(authed) + .await + .map_err(|_e| Error::internal_error("failed to begin transaction", None))?; + let item = sqlx::query_as::<_, ItemSchema>(&sql) + .fetch_one(&mut *tx) + .await + .map_err(|_e| { + tracing::error!("failed to fetch item schema: {}", _e); + Error::internal_error("failed to fetch item schema", None) + })?; + tx.commit() + .await + .map_err(|_e| Error::internal_error("failed to commit transaction", None))?; + Ok(item.schema) + } + + /// Reverses the transformation of a path. + /// + /// This function takes a transformed path and reverses the transformation applied by `transform_path`. + /// It checks if the path starts with "h" (indicating a Hub script) and removes the prefix if present. + /// It then determines the type of the item (script or flow) based on the prefix. + /// This is used in call_tool to get the original path, and the type of the item. + /// + /// # Parameters + /// - `transformed_path`: The transformed path to reverse. + /// + /// # Returns + /// - `Result<(&str, String, bool), String>`: A tuple containing the original path, the type of the item, and a boolean indicating if it's a Hub script. + /// - `Err(String)`: If the path is invalid. + fn reverse_transform(transformed_path: &str) -> Result<(&str, String, bool), String> { + let is_hub = transformed_path.starts_with("h"); + let transformed_path = if is_hub { + transformed_path[1..].to_string() + } else { + transformed_path.to_string() + }; + let type_str = if transformed_path.starts_with("s-") { + "script" + } else if transformed_path.starts_with("f-") { + "flow" + } else { + return Err(format!( + "Invalid prefix in transformed path: {}", + transformed_path + )); + }; + + let mangled_path = &transformed_path[2..]; + + // Check if this path was previously transformed with special underscore handling + let is_special_path = mangled_path.starts_with("f_"); + + let original_path = if is_hub { + let parts = mangled_path.split("-").collect::>(); + parts[0].to_string() + } else if is_special_path { + const TEMP_PLACEHOLDER: &str = "@@UNDERSCORE@@"; + let path_with_placeholder = mangled_path.replace("__", TEMP_PLACEHOLDER); + let path_with_slashes = path_with_placeholder.replace('_', "/"); + path_with_slashes.replace(TEMP_PLACEHOLDER, "_") + } else { + mangled_path.replacen('_', "/", 2) + }; + + Ok((type_str, original_path, is_hub)) + } + + async fn inner_get_resources_types( + user_db: &UserDB, + authed: &ApiAuthed, + workspace_id: &str, + ) -> Result, Error> { + let mut sqlb = SqlBuilder::select_from("resource_type as o"); + sqlb.fields(&["o.name", "o.description"]); + sqlb.and_where("o.workspace_id = ?".bind(&workspace_id)); + let sql = sqlb.sql().map_err(|_e| { + tracing::error!("failed to build sql: {}", _e); + Error::internal_error("failed to build sql", None) + })?; + let mut tx = user_db + .clone() + .begin(authed) + .await + .map_err(|_e| Error::internal_error("failed to begin transaction", None))?; + let rows = sqlx::query_as::<_, ResourceType>(&sql) + .fetch_all(&mut *tx) + .await + .map_err(|_e| { + tracing::error!("Failed to fetch resource types: {}", _e); + Error::internal_error("failed to fetch resource types", None) + })?; + tx.commit() + .await + .map_err(|_e| Error::internal_error("failed to commit transaction", None))?; + Ok(rows) + } + + async fn inner_get_resources( + user_db: &UserDB, + authed: &ApiAuthed, + workspace_id: &str, + resource_type: &str, + ) -> Result, Error> { + let mut sqlb = SqlBuilder::select_from("resource as o"); + sqlb.fields(&["o.path", "o.description", "o.resource_type"]); + sqlb.and_where("o.workspace_id = ?".bind(&workspace_id)); + sqlb.and_where("o.resource_type = ?".bind(&resource_type)); + let sql = sqlb.sql().map_err(|_e| { + tracing::error!("failed to build sql: {}", _e); + Error::internal_error("failed to build sql", None) + })?; + let mut tx = user_db + .clone() + .begin(authed) + .await + .map_err(|_e| Error::internal_error("failed to begin transaction", None))?; + let rows = sqlx::query_as::<_, ResourceInfo>(&sql) + .fetch_all(&mut *tx) + .await + .map_err(|_e| { + tracing::error!("Failed to fetch resources: {}", _e); + Error::internal_error("failed to fetch resources", None) + })?; + tx.commit() + .await + .map_err(|_e| Error::internal_error("failed to commit transaction", None))?; + + Ok(rows) + } + + async fn inner_get_items sqlx::FromRow<'a, sqlx::postgres::PgRow> + Send + Unpin>( + user_db: &UserDB, + authed: &ApiAuthed, + workspace_id: &str, + scope_type: &str, + item_type: &str, + ) -> Result, Error> { + let mut sqlb = SqlBuilder::select_from(&format!("{} as o", item_type)); + sqlb.fields(&["o.path", "o.summary", "o.description", "o.schema"]); + if scope_type == "favorites" { + sqlb.join("favorite") + .on("favorite.favorite_kind = ? AND favorite.workspace_id = o.workspace_id AND favorite.path = o.path AND favorite.usr = ?".bind(&item_type) + .bind(&authed.username)); + } + sqlb.and_where("o.workspace_id = ?".bind(&workspace_id)) + .and_where("o.archived = false") + .and_where("o.draft_only IS NOT TRUE") + .order_by( + if item_type == "flow" { + "o.edited_at" + } else { + "o.created_at" + }, + false, + ) + .limit(100); + let sql = sqlb.sql().map_err(|_e| { + tracing::error!("failed to build sql: {}", _e); + Error::internal_error("failed to build sql", None) + })?; + let mut tx = user_db + .clone() + .begin(authed) + .await + .map_err(|_e| Error::internal_error("failed to begin transaction", None))?; + let rows = sqlx::query_as::<_, T>(&sql) + .fetch_all(&mut *tx) + .await + .map_err(|_e| { + tracing::error!("Failed to fetch {}: {}", item_type, _e); + Error::internal_error(format!("failed to fetch {}", item_type), None) + })?; + tx.commit() + .await + .map_err(|_e| Error::internal_error("failed to commit transaction", None))?; + Ok(rows) + } + + async fn inner_get_scripts_from_hub( + db: &DB, + scope_integrations: Option<&str>, + ) -> Result, Error> { + let query_params = Some(vec![ + ("limit", "100".to_string()), + ("with_schema", "true".to_string()), + ("apps", scope_integrations.unwrap_or("").to_string()), + ]); + let url = format!("{}/scripts/top", *HUB_BASE_URL.read().await); + let (_status_code, _headers, response) = + query_elems_from_hub(&HTTP_CLIENT, &url, query_params, &db) + .await + .map_err(|e| { + tracing::error!("Failed to get items from hub: {}", e); + Error::internal_error(format!("Failed to get items from hub: {}", e), None) + })?; + let body_bytes = to_bytes(response, usize::MAX).await.map_err(|e| { + tracing::error!("Failed to read response body: {}", e); + Error::internal_error(format!("Failed to read response body: {}", e), None) + })?; + let body_str = String::from_utf8(body_bytes.to_vec()).map_err(|e| { + tracing::error!("Failed to decode response body: {}", e); + Error::internal_error(format!("Failed to decode response body: {}", e), None) + })?; + let hub_response: HubResponse = serde_json::from_str(&body_str).map_err(|e| { + tracing::error!("Failed to parse hub response: {}", e); + Error::internal_error(format!("Failed to parse hub response: {}", e), None) + })?; + + Ok(hub_response.asks) + } + + /// Transforms a value if it's an object. + /// + /// This function takes a key and a value, and a schema object. + /// If the value is a string that starts with "$res:", it returns the value as is. + /// Otherwise, it checks if the key is defined in the schema and if it's an object type. + /// If it is, it transforms the value to a string. This is because some clients do not support object types. + /// # Parameters + /// - `key`: The key of the value to transform. + /// - `value`: The value to transform. + /// - `schema_obj`: The schema object. + /// + /// # Returns + /// - `Value`: The transformed value. + fn transform_value_if_object( + key: &str, + value: &Value, + schema_obj: &Option, + ) -> Value { + if value.is_string() && value.as_str().unwrap().starts_with("$res:") { + return value.clone(); + } + + let schema_obj = match schema_obj { + Some(s) => s, + None => return value.clone(), + }; + + // Check if property is defined in schema and is an object type + let is_obj_type = match schema_obj.properties.get(key) { + Some(property) => { + let prop_type = property.get("type").and_then(|t| t.as_str()); + prop_type == Some("object") + } + None => false, + }; + + // If it's an object type and we received a string, try to parse it + if is_obj_type && value.is_string() { + if let Some(str_val) = value.as_str() { + if let Ok(obj_val) = serde_json::from_str::(str_val) { + return obj_val; + } + } + } + + value.clone() + } + + /// Reverses the transformation of a key. + /// + /// This function takes a transformed key and a schema object. + /// It then reverses the transformation applied by `apply_key_transformation`. This can be subject to collisions, but it's unlikely and is ok for our use case. + /// # Parameters + /// - `transformed_key`: The transformed key to reverse. + /// - `schema_obj`: The schema object. + /// + /// # Returns + /// - `String`: The original key. + fn reverse_transform_key(transformed_key: &str, schema_obj: &Option) -> String { + let schema_obj = match schema_obj { + Some(s) => s, + None => { + // No schema available, return the key as is (best guess) + return transformed_key.to_string(); + } + }; + + for original_key_in_schema in schema_obj.properties.keys() { + // Apply the SAME forward transformation to the schema key + let potential_transformed_key = + Runner::apply_key_transformation(original_key_in_schema); + + // If it matches the key we received, we found the likely original + if potential_transformed_key == transformed_key { + return original_key_in_schema.clone(); + } + } + + transformed_key.to_string() + } + + /// Applies a key transformation to a key. + /// + /// This function takes a key and replaces spaces with underscores. + /// It also removes any characters that are not alphanumeric or underscores. + /// This is used when listing, because we can't have names with spaces or special characters in the schema properties. + /// # Parameters + /// - `key`: The key to transform. + /// + /// # Returns + /// - `String`: The transformed key. + fn apply_key_transformation(key: &str) -> String { + key.replace(' ', "_") + .chars() + .filter(|c| c.is_alphanumeric() || *c == '_') + .collect::() + } + + /// Transforms the schema for resources. + /// + /// This function takes a schema and a database connection, and attempts to transform the schema for resources. + /// It replaces invalid characters in property keys with underscores and converts object properties to strings. + /// It also fetches resource type information and adds it to the description of resource properties. + /// + /// # Parameters + /// - `schema`: The schema to transform. + /// - `user_db`: The database connection. + /// - `authed`: The authenticated user. + /// - `w_id`: The workspace ID. + /// - `resources_cache`: A mutable reference to the resources cache. + /// - `resources_types`: A reference to the resource types. + /// + /// # Returns + /// - `Result`: The transformed schema. + /// - `Err(Error)`: If the transformation fails. + async fn transform_schema_for_resources( + schema: &SchemaType, + user_db: &UserDB, + authed: &ApiAuthed, + w_id: &str, + resources_cache: &mut HashMap>, + resources_types: &Vec, + ) -> Result { + let mut schema_obj: SchemaType = schema.clone(); + + // replace invalid char in property key with underscore + let replacements: Vec<(String, String, serde_json::Value)> = schema_obj + .properties + .iter() + .filter_map(|(key, value)| { + if key.chars().any(|c| !c.is_alphanumeric() && c != '_') { + let new_key = Runner::apply_key_transformation(key); + Some((key.clone(), new_key, value.clone())) + } else { + None + } + }) + .collect(); + + for (old_key, new_key, value) in replacements { + schema_obj.properties.remove(&old_key); + schema_obj.properties.insert(new_key, value); + } + + for (_key, prop_value) in schema_obj.properties.iter_mut() { + if let serde_json::Value::Object(prop_map) = prop_value { + // transform object properties to string because some client does not support object, might change in the future + if let Some(type_value) = prop_map.get("type") { + if let serde_json::Value::String(type_str) = type_value { + if type_str == "object" { + prop_map.insert( + "type".to_string(), + serde_json::Value::String("string".to_string()), + ); + } + } + } + // if property is a resource, fetch the resource type infos, and add each available resource to the description + if let Some(format_value) = prop_map.get("format") { + if let serde_json::Value::String(format_str) = format_value { + if format_str.starts_with("resource-") { + let resource_type_key = + format_str.split("-").last().unwrap_or_default().to_string(); + let resource_type = resources_types + .iter() + .find(|rt| rt.name == resource_type_key); + let resource_type_obj = resource_type.cloned().unwrap_or_else(|| { + tracing::info!("Resource type not found: {}", resource_type_key); + ResourceType { name: resource_type_key.clone(), description: None } + }); + + if !resources_cache.contains_key(&resource_type_key) { + let available_resources = Runner::inner_get_resources( + user_db, + authed, + &w_id, + &resource_type_key, + ) + .await; + + match available_resources { + Ok(cache_data) => { + resources_cache + .insert(resource_type_key.clone(), cache_data); + } + Err(e) => { + tracing::error!( + "Failed to fetch resource cache data: {}", + e + ); + continue; // Skip this property if fetching failed + } + } + } + + if let Some(resource_cache) = resources_cache.get(&resource_type_key) { + let resources_count = resource_cache.len(); + let description = format!( + "This is a resource named `{}` with the following description: `{}`.\nThe path of the resource should be used to specify the resource.\n{}", + resource_type_obj.name, + resource_type_obj.description.as_deref().unwrap_or("No description"), + if resources_count == 0 { + "This resource does not have any available instances, you should create one from your windmill workspace." + } else if resources_count > 1 { + "This resource has multiple available instances, you should precisely select the one you want to use." + } else { + "There is 1 resource available." + } + ); + prop_map.insert( + "type".to_string(), + serde_json::Value::String("string".to_string()), + ); + prop_map.insert( + "description".to_string(), + serde_json::Value::String(description), + ); + if resources_count > 0 { + let resources_description = resource_cache + .iter() + .map(|resource| { + format!( + "{}: $res:{}", + resource + .description + .as_deref() + .unwrap_or("No title"), + resource.path + ) + }) + .collect::>() + .join("\n"); + + prop_map.insert( + "description".to_string(), + serde_json::Value::String(format!( + "{}\nHere are the available resources, in the format title:path. Title can be empty. Path should be used to specify the resource:\n{}", + prop_map.get("description").unwrap_or(&serde_json::Value::String("No description".to_string())), + resources_description + )), + ); + } + } + } + } + } + } else { + tracing::warn!( + "Schema property value is not a JSON object: {:?}", + prop_value + ); + } + } + + Ok(schema_obj) + } + + /// Fetches the schema for a Hub script. + /// + /// This function takes a script path and a database connection, and attempts to fetch the schema for the script. + /// It strips the path to remove any leading slashes, and then attempts to retrieve the full script using `get_full_hub_script_by_path`. + /// If successful, it converts the schema string to a `Schema` object. + /// If the schema cannot be converted, it logs a warning and returns `None`. + /// + /// # Parameters + /// - `path`: The path of the script to fetch the schema for. + /// - `db`: The database connection. + /// + /// # Returns + /// - `Ok(Option)`: The schema if found, otherwise `None`. + /// - `Err(Error)`: If the request fails. + async fn get_hub_script_schema(path: &str, db: &DB) -> Result, Error> { + let strip_path = StripPath(path.to_string()); + let res = get_full_hub_script_by_path(strip_path, &HTTP_CLIENT, Some(db)) + .await + .map_err(|e| { + tracing::error!("Failed to get hub script: {}", e); + Error::internal_error(format!("Failed to get hub script: {}", e), None) + })?; + match serde_json::from_str::(res.schema.get()) { + Ok(schema) => Ok(Some(schema)), + Err(e) => { + tracing::warn!("Failed to convert schema: {}", e); + Ok(None) + } + } + } + + /// Creates a `Tool` from a `ToolableItem`. + /// + /// This function takes an item that implements the `ToolableItem` trait and converts it into an RMCP `Tool`. + /// It handles both workspace scripts/flows and Hub scripts differently, depending on the item type. + /// + /// # Parameters + /// - `item`: The item to convert to a `Tool`. + /// - `user_db`: The database connection. + /// - `authed`: The authenticated user. + /// - `workspace_id`: The workspace ID. + /// - `resources_cache`: A mutable reference to the resources cache. + /// - `resources_types`: A reference to the resource types. + /// + /// # Returns + /// - `Ok(Tool)`: The created `Tool`. + async fn create_tool_from_item( + item: &T, + user_db: &UserDB, + authed: &ApiAuthed, + workspace_id: &str, + resources_cache: &mut HashMap>, + resources_types: &Vec, + ) -> Result { + let is_hub = item.is_hub(); + let path = item.get_path_or_id(); + let item_type = item.item_type(); + let description = format!( + "This is a {} named `{}` with the following description: `{}`.{}", + item_type, + item.get_summary(), + item.get_description(), + if is_hub { + format!( + " It is a tool used for the following app: {}", + item.get_integration_type() + .unwrap_or("No integration type".to_string()) + ) + } else { + "".to_string() + } + ); + let schema_obj = Runner::transform_schema_for_resources( + &item.get_schema(), + user_db, + authed, + &workspace_id, + resources_cache, + &resources_types, + ) + .await?; + let input_schema_map = match serde_json::to_value(schema_obj) { + Ok(Value::Object(map)) => map, + Ok(_) => { + tracing::warn!("Schema object for tool '{}' did not serialize to a JSON object, using empty schema.", path); + serde_json::Map::new() + } + Err(e) => { + tracing::error!( + "Failed to serialize schema object for tool '{}': {}. Using empty schema.", + path, + e + ); + serde_json::Map::new() + } + }; + Ok(Tool { + name: Cow::Owned(path), + description: Some(Cow::Owned(description)), + input_schema: Arc::new(input_schema_map), + annotations: None, + }) + } +} + +impl ServerHandler for Runner { + /// Handles the `CallTool` request from the MCP client. + /// + /// This involves: + /// 1. Parsing arguments and extracting context (DB, Auth). + /// 2. Reversing the tool name (`request.name`) to get the original path and type using `reverse_transform`. + /// 3. Handling Hub scripts: If identified as a Hub script, searches the Hub for the actual script ID. + /// 4. Fetching the schema for the item (needed for argument transformation). + /// 5. Transforming incoming arguments: + /// - Reversing key transformations (e.g., `user_input` back to `user input`). + /// - Parsing stringified JSON objects back into JSON values based on schema type. + /// 6. Executing the corresponding script or flow using internal Windmill runners. + /// 7. Formatting the execution result into an RMCP `CallToolResult`. + /// + /// # Parameters + /// - `request`: The `CallToolRequestParam` containing the tool name and arguments. + /// - `context`: The `RequestContext` providing access to workspace ID, DB connections, auth info. + /// + /// # Returns + /// - `Ok(CallToolResult)`: On successful execution, containing the output. + /// - `Err(Error)`: If any step fails (parsing, DB access, execution, reversing transform, hub search). + async fn call_tool( + &self, + request: CallToolRequestParam, + context: RequestContext, + ) -> Result { + let parse_args = |args_opt: Option| -> Result { + args_opt.map(Value::Object).ok_or_else(|| { + Error::invalid_params( + "Missing arguments for tool", + Some(request.name.clone().into()), + ) + }) + }; + + let authed = context + .req_extensions + .get::() + .ok_or_else(|| Error::internal_error("ApiAuthed not found", None))?; + let db = context + .req_extensions + .get::() + .ok_or_else(|| Error::internal_error("DB not found", None))?; + let user_db = context + .req_extensions + .get::() + .ok_or_else(|| Error::internal_error("UserDB not found", None))?; + let args = parse_args(request.arguments)?; + + let (tool_type, path, is_hub) = + Runner::reverse_transform(&request.name).unwrap_or_default(); + + let item_schema = if is_hub { + Runner::get_hub_script_schema(&format!("hub/{}", path), db).await? + } else { + Runner::get_item_schema(&path, user_db, authed, &context.workspace_id, &tool_type) + .await? + }; + + let schema_obj = if let Some(ref s) = item_schema { + match serde_json::from_str::(s.0.get()) { + Ok(val) => Some(val), + Err(e) => { + tracing::warn!("Failed to parse schema: {}", e); + None + } + } + } else { + None + }; + + let push_args = if let Value::Object(map) = args.clone() { + let mut args_hash = HashMap::new(); + for (k, v) in map { + // need to transform back the key without invalid characters to the original key + let original_key = Runner::reverse_transform_key(&k, &schema_obj); + + // object properties are transformed to string because some client does not support object, might change in the future + let transformed_v = Runner::transform_value_if_object(&k, &v, &schema_obj); + args_hash.insert(original_key, to_raw_value(&transformed_v)); + } + windmill_queue::PushArgsOwned { extra: None, args: args_hash } + } else { + windmill_queue::PushArgsOwned::default() + }; + + let w_id = context.workspace_id.clone(); + let script_or_flow_path = if is_hub { + StripPath(format!("hub/{}", path)) + } else { + StripPath(path) + }; + let run_query = RunJobQuery::default(); + + let result = if tool_type == "script" { + run_wait_result_script_by_path_internal( + db.clone(), + run_query, + script_or_flow_path, + authed.clone(), + user_db.clone(), + w_id.clone(), + push_args, + None, + ) + .await + } else { + run_wait_result_flow_by_path_internal( + db.clone(), + run_query, + script_or_flow_path, + authed.clone(), + user_db.clone(), + push_args, + w_id.clone(), + None, + ) + .await + }; + + match result { + Ok(response) => { + let body_bytes = to_bytes(response.into_body(), usize::MAX) + .await + .map_err(|e| { + Error::internal_error(format!("Failed to read response body: {}", e), None) + })?; + let body_str = String::from_utf8(body_bytes.to_vec()).map_err(|e| { + Error::internal_error(format!("Failed to decode response body: {}", e), None) + })?; + Ok(CallToolResult::success(vec![Content::text(body_str)])) + } + Err(e) => Err(Error::internal_error( + format!("Failed to run script: {}", e), + None, + )), + } + } + + /// Fetches available tools (scripts, flows, hub scripts) based on the user's scope. + /// + /// - Determines scope (all, favorites, hub-specific) from auth token. + /// - Fetches relevant items (workspace scripts/flows, hub scripts) concurrently. + /// - Fetches resource type information needed for schema enrichment. + /// - Transforms each item into an RMCP `Tool` definition, including schema adjustments + /// (like resource description enrichment and object->string conversion). + /// + /// # Parameters + /// - `_request`: Optional pagination parameters (currently ignored). + /// - `_context`: The `RequestContext` providing workspace ID, DB, auth. + /// + /// # Returns + /// - `Ok(ListToolsResult)`: A list of `Tool` definitions. Pagination is not yet implemented. + /// - `Err(Error)`: If fetching data from DB or Hub fails. + async fn list_tools( + &self, + _request: Option, + mut _context: RequestContext, + ) -> Result { + let workspace_id = _context.workspace_id.clone(); + let db = _context + .req_extensions + .get::() + .ok_or_else(|| Error::internal_error("DB not found", None))?; + let user_db = _context + .req_extensions + .get::() + .ok_or_else(|| Error::internal_error("UserDB not found", None))?; + let authed = _context + .req_extensions + .get::() + .ok_or_else(|| Error::internal_error("ApiAuthed not found", None))?; + let owned_scope = authed.scopes.as_ref().and_then(|scopes| { + scopes + .iter() + .find(|scope| scope.starts_with("mcp:") && !scope.contains("hub")) + }); + let hub_scope = authed + .scopes + .as_ref() + .and_then(|scopes| scopes.iter().find(|scope| scope.starts_with("mcp:hub"))); + let scope_type = owned_scope.map_or("all", |scope| { + let parts = scope.split(":").collect::>(); + parts[1] + }); + let scope_integrations = hub_scope.and_then(|scope| { + let parts = scope.split(":").collect::>(); + if parts.len() == 3 { + Some(parts[2]) + } else { + None + } + }); + + let scripts_fn = Runner::inner_get_items::( + user_db, + authed, + &workspace_id, + scope_type, + "script", + ); + let flows_fn = + Runner::inner_get_items::(user_db, authed, &workspace_id, scope_type, "flow"); + let resources_types_fn = Runner::inner_get_resources_types(user_db, authed, &workspace_id); + let hub_scripts_fn = Runner::inner_get_scripts_from_hub(db, scope_integrations.as_deref()); + let (scripts, flows, resources_types, hub_scripts) = if scope_integrations.is_some() { + let (scripts, flows, resources_types, hub_scripts) = + try_join!(scripts_fn, flows_fn, resources_types_fn, hub_scripts_fn)?; + (scripts, flows, resources_types, hub_scripts) + } else { + let (scripts, flows, resources_types) = + try_join!(scripts_fn, flows_fn, resources_types_fn)?; + (scripts, flows, resources_types, vec![]) + }; + + let mut resources_cache: HashMap> = HashMap::new(); + let mut tools: Vec = Vec::new(); + + for script in scripts { + tools.push( + Runner::create_tool_from_item( + &script, + user_db, + authed, + &workspace_id, + &mut resources_cache, + &resources_types, + ) + .await?, + ); + } + + for flow in flows { + tools.push( + Runner::create_tool_from_item( + &flow, + user_db, + authed, + &workspace_id, + &mut resources_cache, + &resources_types, + ) + .await?, + ); + } + + for hub_script in hub_scripts { + tools.push( + Runner::create_tool_from_item( + &hub_script, + user_db, + authed, + &workspace_id, + &mut resources_cache, + &resources_types, + ) + .await?, + ); + } + + Ok(ListToolsResult { tools, next_cursor: None }) + } + + fn get_info(&self) -> ServerInfo { + ServerInfo { + protocol_version: Default::default(), + capabilities: ServerCapabilities::builder() + .enable_tools() + .enable_tool_list_changed() + .build(), + server_info: Implementation::from_build_env(), + instructions: Some("This server provides a list of scripts and flows the user can run on Windmill. Each flow and script is a tool callable with their respective arguments.".to_string()), + } + } + + async fn initialize( + &self, + _request: InitializeRequestParam, + _context: RequestContext, + ) -> Result { + Ok(self.get_info()) + } + + async fn list_resources( + &self, + _request: Option, + _context: RequestContext, + ) -> Result { + Ok(ListResourcesResult { resources: vec![], next_cursor: None }) + } + + async fn list_prompts( + &self, + _request: Option, + _context: RequestContext, + ) -> Result { + Ok(ListPromptsResult::default()) + } + + async fn list_resource_templates( + &self, + _request: Option, + _context: RequestContext, + ) -> Result { + Ok(ListResourceTemplatesResult::default()) + } +} + +pub fn setup_mcp_server(addr: SocketAddr, path: &str) -> anyhow::Result<(SseServer, Router)> { + let config = SseServerConfig { + bind: addr, + sse_path: "/sse".to_string(), + post_path: "/message".to_string(), + full_message_path: path.to_string(), + ct: CancellationToken::new(), + sse_keep_alive: None, + }; + + Ok(SseServer::new(config)) +} diff --git a/backend/windmill-api/src/mqtt_triggers.rs b/backend/windmill-api/src/mqtt_triggers.rs new file mode 100644 index 0000000000..67c4c12d5b --- /dev/null +++ b/backend/windmill-api/src/mqtt_triggers.rs @@ -0,0 +1,1804 @@ +use crate::{ + capture::{insert_capture_payload, MqttTriggerConfig}, + db::{ApiAuthed, DB}, + jobs::{run_flow_by_path_inner, run_script_by_path_inner, RunJobQuery}, + resources::try_get_resource_from_db_as, + users::fetch_api_authed, +}; +use windmill_queue::TriggerKind; + +use axum::{ + async_trait, + extract::{Path, Query}, + Extension, Json, +}; +use axum::{ + routing::{delete, get, post}, + Router, +}; +use base64::prelude::*; +use bytes::Bytes; +use http::StatusCode; +use itertools::Itertools; +use rumqttc::{ + v5::{ + mqttbytes::{ + v5::{ConnectProperties, Filter, PublishProperties}, + QoS as V5QoS, + }, + AsyncClient as V5AsyncClient, Event as V5Event, EventLoop as V5EventLoop, + Incoming as V5Incoming, MqttOptions as V5MqttOptions, + }, + AsyncClient as V3AsyncClient, Event as V3Event, EventLoop as V3EventLoop, + Incoming as V3Incoming, MqttOptions as V3MqttOptions, QoS as V3QoS, SubscribeFilter, + TlsConfiguration, Transport, +}; +use serde::{Deserialize, Serialize}; +use sql_builder::{bind::Bind, SqlBuilder}; +use sqlx::{FromRow, Type}; +use std::collections::HashMap; +use std::time::Duration; +use windmill_audit::{audit_ee::audit_log, ActionKind}; +use windmill_common::{ + db::UserDB, + error::{self, JsonResult}, + utils::{not_found_if_none, paginate, report_critical_error, Pagination, StripPath}, + worker::{to_raw_value, CLOUD_HOSTED}, + INSTANCE_NAME, +}; + +use rand::seq::SliceRandom; +use serde_json::value::RawValue; +use sqlx::types::Json as SqlxJson; + +use windmill_queue::PushArgsOwned; + +pub fn workspaced_service() -> Router { + Router::new() + .route("/create", post(create_mqtt_trigger)) + .route("/list", get(list_mqtt_triggers)) + .route("/get/*path", get(get_mqtt_trigger)) + .route("/update/*path", post(update_mqtt_trigger)) + .route("/delete/*path", delete(delete_mqtt_trigger)) + .route("/exists/*path", get(exists_mqtt_trigger)) + .route("/setenabled/*path", post(set_enabled)) + .route("/test", post(test_mqtt_connection)) +} + +#[derive(Debug, thiserror::Error)] +enum Error { + #[error("{0}")] + Common(#[from] windmill_common::error::Error), + #[error("{0}")] + V5RumqttClient(#[from] rumqttc::v5::ClientError), + #[error("{0}")] + V5ConnectionError(#[from] rumqttc::v5::ConnectionError), + #[error("{0}")] + V3RumqttClient(#[from] rumqttc::ClientError), + #[error("{0}")] + V3ConnectionError(#[from] rumqttc::ConnectionError), + #[error("{0}")] + Base64Decode(#[from] base64::DecodeError), +} + +async fn run_job( + args: Option>>, + extra: Option>>, + db: &DB, + trigger: &MqttTrigger, +) -> anyhow::Result<()> { + let args = PushArgsOwned { args: args.unwrap_or_default(), extra }; + + let authed = fetch_api_authed( + trigger.edited_by.clone(), + trigger.email.clone(), + &trigger.workspace_id, + db, + Some(format!("mqtt-{}", trigger.path)), + ) + .await?; + + let user_db = UserDB::new(db.clone()); + + let run_query = RunJobQuery::default(); + + if trigger.is_flow { + run_flow_by_path_inner( + authed, + db.clone(), + user_db, + trigger.workspace_id.clone(), + StripPath(trigger.script_path.to_owned()), + run_query, + args, + None, + ) + .await?; + } else { + run_script_by_path_inner( + authed, + db.clone(), + user_db, + trigger.workspace_id.clone(), + StripPath(trigger.script_path.to_owned()), + run_query, + args, + None, + ) + .await?; + } + + Ok(()) +} + +#[derive(Clone, Debug, Deserialize, Serialize, Type)] +#[serde(rename_all = "lowercase")] +pub enum QualityOfService { + Qos0, + Qos1, + Qos2, +} + +impl From for V3QoS { + fn from(value: QualityOfService) -> Self { + match value { + QualityOfService::Qos0 => V3QoS::AtMostOnce, + QualityOfService::Qos1 => V3QoS::AtLeastOnce, + QualityOfService::Qos2 => V3QoS::ExactlyOnce, + } + } +} + +impl From for V5QoS { + fn from(value: QualityOfService) -> Self { + match value { + QualityOfService::Qos0 => V5QoS::AtMostOnce, + QualityOfService::Qos1 => V5QoS::AtLeastOnce, + QualityOfService::Qos2 => V5QoS::ExactlyOnce, + } + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct MqttV3Config { + clean_session: Option, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct MqttV5Config { + clean_start: Option, + session_expiry_interval: Option, + topic_alias_maximum: Option, +} + +#[derive(Debug, Deserialize, Serialize, Type)] +#[sqlx(type_name = "MQTT_CLIENT_VERSION")] +#[sqlx(rename_all = "lowercase")] +#[serde(rename_all = "lowercase")] +pub enum MqttClientVersion { + V3, + V5, +} + +#[derive(Debug, Deserialize)] +pub struct Tls { + enabled: bool, + ca_certificate: String, + //encoded in base64 + pkcs12_client_certificate: Option, + pkcs12_certificate_password: Option, +} + +#[derive(Debug, Deserialize)] +pub struct Credentials { + username: Option, + password: Option, +} + +#[derive(Debug, Deserialize)] +pub struct MqttResource { + broker: String, + port: u16, + credentials: Option, + tls: Option, +} +#[derive(Clone, Debug, FromRow, Serialize, Deserialize)] +pub struct SubscribeTopic { + qos: QualityOfService, + topic: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct NewMqttTrigger { + mqtt_resource_path: String, + subscribe_topics: Vec, + v3_config: Option, + v5_config: Option, + #[serde(skip_serializing_if = "Option::is_none")] + client_version: Option, + client_id: Option, + path: String, + script_path: String, + is_flow: bool, + enabled: bool, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct EditMqttTrigger { + mqtt_resource_path: String, + subscribe_topics: Vec, + v3_config: Option, + v5_config: Option, + client_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + client_version: Option, + path: String, + script_path: String, + is_flow: bool, +} + +#[derive(Debug, Serialize, Deserialize, FromRow)] +pub struct MqttTrigger { + mqtt_resource_path: String, + subscribe_topics: Vec>, + v3_config: Option>, + v5_config: Option>, + client_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + client_version: Option, + path: String, + script_path: String, + is_flow: bool, + workspace_id: String, + edited_by: String, + email: String, + edited_at: chrono::DateTime, + extra_perms: Option, + error: Option, + server_id: Option, + last_server_ping: Option>, + enabled: bool, +} + +#[derive(Deserialize, Serialize)] +pub struct ListMqttTriggerQuery { + page: Option, + per_page: Option, + path: Option, + is_flow: Option, + path_start: Option, +} + +#[derive(Deserialize)] +pub struct SetEnabled { + enabled: bool, +} + +const KEEP_ALIVE: u64 = 60; +const CLIENT_CONNECTION_TIMEOUT: u64 = 60; +const TOPIC_ALIAS_MAXIMUM: u16 = 65535; +struct MqttClientBuilder<'client> { + mqtt_resource: MqttResource, + client_id: &'client str, + subscribe_topics: Vec, + v3_config: Option<&'client MqttV3Config>, + v5_config: Option<&'client MqttV5Config>, + mqtt_client_version: Option<&'client MqttClientVersion>, +} + +impl<'client> MqttClientBuilder<'client> { + fn new( + mqtt_resource: MqttResource, + client_id: Option<&'client str>, + subscribe_topics: Vec, + v3_config: Option<&'client MqttV3Config>, + v5_config: Option<&'client MqttV5Config>, + mqtt_client_version: Option<&'client MqttClientVersion>, + ) -> Self { + Self { + mqtt_resource, + client_id: client_id.unwrap_or(""), + subscribe_topics, + v3_config, + v5_config, + mqtt_client_version, + } + } + + async fn build_client(&self) -> Result { + match self.mqtt_client_version { + Some(MqttClientVersion::V5) | None => self.build_v5_client().await, + Some(MqttClientVersion::V3) => self.build_v3_client().await, + } + } + + fn get_tls_configuration(&self) -> Result, Error> { + let transport = match self.mqtt_resource.tls { + Some(ref tls) if tls.enabled => { + let transport = match tls.ca_certificate.trim().is_empty() { + true => rumqttc::Transport::Tls(TlsConfiguration::Native), + false => rumqttc::Transport::Tls(TlsConfiguration::SimpleNative { + ca: tls.ca_certificate.as_bytes().to_vec(), + client_auth: { + match tls.pkcs12_client_certificate.as_ref() { + Some(client_certificate) + if !client_certificate.trim().is_empty() => + { + let client_certificate = + BASE64_STANDARD.decode(client_certificate)?; + let password = tls + .pkcs12_certificate_password + .clone() + .unwrap_or("".to_string()); + Some((client_certificate, password)) + } + _ => None, + } + }, + }), + }; + + Some(transport) + } + _ => None, + }; + + Ok(transport) + } + + async fn build_v5_client(&self) -> Result { + let mut mqtt_options = V5MqttOptions::new( + self.client_id, + &self.mqtt_resource.broker, + self.mqtt_resource.port, + ); + + if let Some(credentials) = &self.mqtt_resource.credentials { + let username = credentials.username.as_deref().unwrap_or(""); + let password = credentials.password.as_deref().unwrap_or(""); + mqtt_options.set_credentials(username, password); + } + + if let Some(transport) = self.get_tls_configuration()? { + mqtt_options.set_transport(transport); + } + + mqtt_options.set_connection_timeout(CLIENT_CONNECTION_TIMEOUT); + + mqtt_options.set_keep_alive(Duration::from_secs(KEEP_ALIVE)); + + if let Some(v5_config) = self.v5_config { + mqtt_options.set_clean_start(v5_config.clean_start.unwrap_or(true)); + mqtt_options.set_connect_properties(ConnectProperties { + session_expiry_interval: v5_config.session_expiry_interval, + receive_maximum: None, + max_packet_size: None, + topic_alias_max: v5_config.topic_alias_maximum.or(Some(TOPIC_ALIAS_MAXIMUM)), + request_response_info: None, + request_problem_info: None, + user_properties: vec![], + authentication_method: None, + authentication_data: None, + }); + } + + let (async_client, mut event_loop) = + V5AsyncClient::new(mqtt_options, self.subscribe_topics.len()); + event_loop.verify_connection().await?; + + if !self.subscribe_topics.is_empty() { + let subscribe_filters = self + .subscribe_topics + .iter() + .map(|topic| Filter::new(topic.topic.clone(), topic.qos.clone().into())) + .collect_vec(); + + async_client.subscribe_many(subscribe_filters).await?; + } + Ok(MqttClientResult::V5((V5MqttHandler, event_loop))) + } + + async fn build_v3_client(&self) -> Result { + let mut mqtt_options = V3MqttOptions::new( + self.client_id, + &self.mqtt_resource.broker, + self.mqtt_resource.port, + ); + + if let Some(credentials) = &self.mqtt_resource.credentials { + let username = credentials.username.as_deref().unwrap_or(""); + let password = credentials.password.as_deref().unwrap_or(""); + mqtt_options.set_credentials(username, password); + } + + if let Some(transport) = self.get_tls_configuration()? { + mqtt_options.set_transport(transport); + } + mqtt_options.set_keep_alive(Duration::from_secs(KEEP_ALIVE)); + if let Some(v3_config) = self.v3_config { + mqtt_options.set_clean_session(v3_config.clean_session.unwrap_or(true)); + } + + let (async_client, mut event_loop) = + V3AsyncClient::new(mqtt_options, self.subscribe_topics.len()); + event_loop.verify_connection().await?; + + if !self.subscribe_topics.is_empty() { + let subscribe_filters = self + .subscribe_topics + .iter() + .map(|topic| SubscribeFilter::new(topic.topic.clone(), topic.qos.clone().into())) + .collect_vec(); + + async_client.subscribe_many(subscribe_filters).await?; + } + Ok(MqttClientResult::V3((V3MqttHandler, event_loop))) + } +} + +fn convert_disconnect_packet_into_string( + disconnect: rumqttc::v5::mqttbytes::v5::Disconnect, +) -> String { + let err_message = disconnect + .properties + .map(|properties| properties.reason_string) + .flatten(); + let reason_code = disconnect.reason_code as u8; + format!( + "Disconnected by the broker, reason code: {}, {}", + reason_code, + err_message + .map(|err| format!("message: {}", err)) + .unwrap_or("".to_string()) + ) +} + +#[derive(Debug, Deserialize)] +pub struct TestMqttConnection { + mqtt_resource_path: String, + client_version: Option, + v3_config: Option, + v5_config: Option, +} + +pub async fn test_mqtt_connection( + authed: ApiAuthed, + Extension(db): Extension, + Extension(user_db): Extension, + Path(workspace_id): Path, + Json(test_postgres): Json, +) -> error::Result<()> { + let TestMqttConnection { mqtt_resource_path, client_version, v3_config, v5_config } = + test_postgres; + + let mqtt_resource = try_get_resource_from_db_as::( + authed, + Some(user_db), + &db, + &mqtt_resource_path, + &workspace_id, + ) + .await?; + + let connect_f = async { + let client_builder = MqttClientBuilder::new( + mqtt_resource, + Some(""), + vec![], + v3_config.as_ref(), + v5_config.as_ref(), + client_version.as_ref(), + ); + + client_builder.build_client().await.map_err(|err| { + error::Error::BadConfig(format!( + "Error connecting to mqtt broker: {}", + err.to_string() + )) + }) + }; + tokio::time::timeout(tokio::time::Duration::from_secs(30), connect_f) + .await + .map_err(|_| { + error::Error::BadConfig(format!( + "Timeout occurred while trying to connect to mqtt broker after 30 seconds" + )) + })??; + + Ok(()) +} + +pub async fn create_mqtt_trigger( + authed: ApiAuthed, + Extension(user_db): Extension, + Path(w_id): Path, + Json(new_mqtt_trigger): Json, +) -> error::Result<(StatusCode, String)> { + if *CLOUD_HOSTED { + return Err(error::Error::BadRequest( + "Mqtt triggers are not supported on multi-tenant cloud, use dedicated cloud or self-host".to_string(), + )); + } + + let NewMqttTrigger { + mqtt_resource_path, + subscribe_topics, + path, + script_path, + enabled, + is_flow, + v3_config, + v5_config, + client_version, + client_id, + } = new_mqtt_trigger; + + let mut tx = user_db.begin(&authed).await?; + + let subscribe_topics = subscribe_topics.into_iter().map(SqlxJson).collect_vec(); + let v3_config = v3_config.map(SqlxJson); + let v5_config = v5_config.map(SqlxJson); + + sqlx::query!( + r#" + INSERT INTO mqtt_trigger ( + mqtt_resource_path, + subscribe_topics, + client_version, + client_id, + v3_config, + v5_config, + workspace_id, + path, + script_path, + is_flow, + email, + enabled, + edited_by + ) + VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7, + $8, + $9, + $10, + $11, + $12, + $13 + )"#, + mqtt_resource_path, + subscribe_topics.as_slice() as &[SqlxJson], + client_version as Option, + client_id, + v3_config as Option>, + v5_config as Option>, + &w_id, + &path, + script_path, + is_flow, + &authed.email, + enabled, + &authed.username + ) + .execute(&mut *tx) + .await?; + + audit_log( + &mut *tx, + &authed, + "mqtt_triggers.create", + ActionKind::Create, + &w_id, + Some(path.as_str()), + None, + ) + .await?; + + tx.commit().await?; + + Ok((StatusCode::CREATED, path.to_string())) +} + +pub async fn list_mqtt_triggers( + authed: ApiAuthed, + Extension(user_db): Extension, + Path(w_id): Path, + Query(lst): Query, +) -> error::JsonResult> { + let mut tx = user_db.begin(&authed).await?; + let (per_page, offset) = paginate(Pagination { per_page: lst.per_page, page: lst.page }); + let mut sqlb = SqlBuilder::select_from("mqtt_trigger") + .fields(&[ + "mqtt_resource_path", + "subscribe_topics", + "v3_config", + "v5_config", + "client_version", + "client_id", + "workspace_id", + "path", + "script_path", + "is_flow", + "edited_by", + "email", + "edited_at", + "server_id", + "last_server_ping", + "extra_perms", + "error", + "enabled", + ]) + .order_by("edited_at", true) + .and_where("workspace_id = ?".bind(&w_id)) + .offset(offset) + .limit(per_page) + .clone(); + if let Some(path) = lst.path { + sqlb.and_where_eq("script_path", "?".bind(&path)); + } + if let Some(is_flow) = lst.is_flow { + sqlb.and_where_eq("is_flow", "?".bind(&is_flow)); + } + if let Some(path_start) = &lst.path_start { + sqlb.and_where_like_left("path", path_start); + } + let sql = sqlb + .sql() + .map_err(|e| error::Error::InternalErr(e.to_string()))?; + let rows = sqlx::query_as::<_, MqttTrigger>(&sql) + .fetch_all(&mut *tx) + .await + .map_err(|e| { + tracing::debug!("Error fetching mqtt_trigger: {:#?}", e); + windmill_common::error::Error::InternalErr("server error".to_string()) + })?; + tx.commit().await.map_err(|e| { + tracing::debug!("Error committing mqtt_trigger: {:#?}", e); + windmill_common::error::Error::InternalErr("server error".to_string()) + })?; + + Ok(Json(rows)) +} + +pub async fn get_mqtt_trigger( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, +) -> JsonResult { + let mut tx = user_db.begin(&authed).await?; + let path = path.to_path(); + let trigger = sqlx::query_as!( + MqttTrigger, + r#" + SELECT + mqtt_resource_path, + subscribe_topics as "subscribe_topics!: Vec>", + v3_config as "v3_config!: Option>", + v5_config as "v5_config!: Option>", + client_version AS "client_version: _", + client_id, + workspace_id, + path, + script_path, + is_flow, + edited_by, + email, + edited_at, + server_id, + last_server_ping, + extra_perms, + error, + enabled + FROM + mqtt_trigger + WHERE + workspace_id = $1 AND + path = $2 + "#, + w_id, + &path + ) + .fetch_optional(&mut *tx) + .await?; + tx.commit().await?; + + let trigger = not_found_if_none(trigger, "Mqtt Trigger", path)?; + + Ok(Json(trigger)) +} + +pub async fn update_mqtt_trigger( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, + Json(mqtt_trigger): Json, +) -> error::Result { + let workspace_path = path.to_path(); + let EditMqttTrigger { + mqtt_resource_path, + subscribe_topics, + script_path, + path, + is_flow, + v3_config, + v5_config, + client_version, + client_id, + } = mqtt_trigger; + + let mut tx = user_db.begin(&authed).await?; + + let subscribe_topics = subscribe_topics.into_iter().map(SqlxJson).collect_vec(); + + let v3_config = v3_config.map(SqlxJson); + let v5_config = v5_config.map(SqlxJson); + + sqlx::query!( + r#" + UPDATE + mqtt_trigger + SET + mqtt_resource_path = $1, + subscribe_topics = $2, + client_version = $3, + client_id = $4, + v3_config = $5, + v5_config = $6, + is_flow = $7, + edited_by = $8, + email = $9, + script_path = $10, + path = $11, + edited_at = now(), + error = NULL, + server_id = NULL + WHERE + workspace_id = $12 AND + path = $13 + "#, + mqtt_resource_path, + subscribe_topics.as_slice() as &[SqlxJson], + client_version as Option, + client_id, + v3_config as Option>, + v5_config as Option>, + is_flow, + &authed.username, + &authed.email, + script_path, + path, + w_id, + workspace_path, + ) + .execute(&mut *tx) + .await?; + + audit_log( + &mut *tx, + &authed, + "mqtt_triggers.update", + ActionKind::Create, + &w_id, + Some(&path), + None, + ) + .await?; + + tx.commit().await?; + + Ok(workspace_path.to_string()) +} + +pub async fn delete_mqtt_trigger( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, +) -> error::Result { + let path = path.to_path(); + let mut tx = user_db.begin(&authed).await?; + sqlx::query!( + r#" + DELETE + FROM + mqtt_trigger + WHERE + workspace_id = $1 AND + path = $2 + "#, + w_id, + path, + ) + .execute(&mut *tx) + .await?; + + audit_log( + &mut *tx, + &authed, + "mqtt_triggers.delete", + ActionKind::Delete, + &w_id, + Some(path), + None, + ) + .await?; + + tx.commit().await?; + + Ok(format!("Mqtt trigger {path} deleted")) +} + +pub async fn exists_mqtt_trigger( + Extension(db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, +) -> JsonResult { + let path = path.to_path(); + let exists = sqlx::query_scalar!( + r#" + SELECT EXISTS( + SELECT + 1 + FROM + mqtt_trigger + WHERE + path = $1 AND + workspace_id = $2 + )"#, + path, + w_id, + ) + .fetch_one(&db) + .await? + .unwrap_or(false); + Ok(Json(exists)) +} + +pub async fn set_enabled( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, + Json(payload): Json, +) -> error::Result { + let mut tx = user_db.begin(&authed).await?; + let path = path.to_path(); + + // important to set server_id, last_server_ping and error to NULL to stop current mqtt listener + let one_o = sqlx::query_scalar!( + r#" + UPDATE + mqtt_trigger + SET + enabled = $1, + email = $2, + edited_by = $3, + edited_at = now(), + server_id = NULL, + error = NULL + WHERE + path = $4 AND + workspace_id = $5 + RETURNING 1 + "#, + payload.enabled, + &authed.email, + &authed.username, + path, + w_id, + ) + .fetch_optional(&mut *tx) + .await? + .flatten(); + + not_found_if_none(one_o, "Mqtt trigger", path)?; + + audit_log( + &mut *tx, + &authed, + "mqtt_triggers.setenabled", + ActionKind::Update, + &w_id, + Some(path), + Some([("enabled", payload.enabled.to_string().as_ref())].into()), + ) + .await?; + + tx.commit().await?; + + Ok(format!( + "successfully updated mqtt trigger at path {} to status {}", + path, payload.enabled + )) +} + +async fn loop_ping(db: &DB, mqtt: &MqttConfig, error: Option<&str>) { + loop { + if mqtt.update_ping(db, error).await.is_none() { + return; + } + + tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; + } +} + +enum MqttClientResult { + V3((V3MqttHandler, V3EventLoop)), + V5((V5MqttHandler, V5EventLoop)), +} + +trait MqttEvent { + type IncomingPacket; + type PublishPacket; + type Event; + + fn handle_publish_packet(publish_packet: Self::PublishPacket) -> PublishData; + fn handle_event(&self, event: Self::Event) -> Result, String>; +} + +struct V5MqttHandler; + +impl MqttEvent for V5MqttHandler { + type IncomingPacket = V5Incoming; + type PublishPacket = rumqttc::v5::mqttbytes::v5::Publish; + type Event = V5Event; + + fn handle_publish_packet(publish_packet: Self::PublishPacket) -> PublishData { + PublishData::new( + String::from_utf8(publish_packet.topic.as_ref().to_vec()).unwrap_or("".to_string()), + publish_packet.retain, + publish_packet.pkid, + publish_packet.properties, + publish_packet.qos as u8, + ) + } + + fn handle_event(&self, event: Self::Event) -> Result, String> { + tracing::debug!("Inside V5 event"); + match event { + Self::Event::Incoming(packet) => match packet { + Self::IncomingPacket::Publish(publish_packet) => { + return Ok(Some(( + publish_packet.payload.clone(), + Self::handle_publish_packet(publish_packet), + ))) + } + Self::IncomingPacket::Disconnect(disconnect) => { + return Err(convert_disconnect_packet_into_string(disconnect)); + } + packet => { + tracing::debug!("Received = {:#?}", packet); + } + }, + Self::Event::Outgoing(packet) => { + tracing::debug!("Outgoing Received = {:#?}", packet); + } + } + + Ok(None) + } +} + +struct V3MqttHandler; + +impl MqttEvent for V3MqttHandler { + type IncomingPacket = V3Incoming; + type PublishPacket = rumqttc::mqttbytes::v4::Publish; + type Event = V3Event; + + fn handle_publish_packet(publish_packet: Self::PublishPacket) -> PublishData { + PublishData::new( + publish_packet.topic, + publish_packet.retain, + publish_packet.pkid, + None, + publish_packet.qos as u8, + ) + } + + fn handle_event(&self, event: Self::Event) -> Result, String> { + tracing::debug!("Inside V3 event"); + match event { + Self::Event::Incoming(packet) => match packet { + Self::IncomingPacket::Publish(publish_packet) => { + return Ok(Some(( + publish_packet.payload.clone(), + Self::handle_publish_packet(publish_packet), + ))) + } + packet => { + tracing::debug!("Received = {:?}", packet); + } + }, + Self::Event::Outgoing(packet) => { + tracing::debug!("Outgoing Received = {:?}", packet); + } + } + + Ok(None) + } +} + +const TIMEOUT_DURATION: u64 = 10; +const CONNECTION_TIMEOUT: Duration = Duration::from_secs(TIMEOUT_DURATION); + +#[async_trait] +trait EventLoop { + type Event; + type Error; + + async fn poll(&mut self) -> Result; + async fn verify_connection(&mut self) -> Result<(), Error>; +} + +#[async_trait] +impl EventLoop for V5EventLoop { + type Event = V5Event; + type Error = rumqttc::v5::ConnectionError; + async fn poll(&mut self) -> Result { + self.poll().await + } + + async fn verify_connection(&mut self) -> Result<(), Error> { + let start = std::time::Instant::now(); + + while start.elapsed() < CONNECTION_TIMEOUT { + match self.poll().await? { + Self::Event::Incoming(V5Incoming::ConnAck(_)) => return Ok(()), + Self::Event::Incoming(V5Incoming::Disconnect(disconnect)) => { + return Err(Error::Common(error::Error::BadConfig( + convert_disconnect_packet_into_string(disconnect), + ))); + } + _ => continue, + } + } + + Err(Error::Common(error::Error::BadConfig(format!( + "Timeout occurred while trying to connect to mqtt broker after {} seconds", + TIMEOUT_DURATION + )))) + } +} + +#[async_trait] +impl EventLoop for V3EventLoop { + type Event = V3Event; + type Error = rumqttc::ConnectionError; + + async fn poll(&mut self) -> Result { + self.poll().await + } + + async fn verify_connection(&mut self) -> Result<(), Error> { + let start = std::time::Instant::now(); + + while start.elapsed() < CONNECTION_TIMEOUT { + match self.poll().await? { + Self::Event::Incoming(rumqttc::Packet::ConnAck(_)) => return Ok(()), + _ => continue, + } + } + + Err(Error::Common(error::Error::BadConfig(format!( + "Timeout occurred while trying to connect to mqtt broker after {} seconds", + TIMEOUT_DURATION + )))) + } +} + +async fn handle_publish_packet(db: &DB, mqtt: &MqttConfig, payload: Bytes, publish: PublishData) { + let args = HashMap::from([("payload".to_string(), to_raw_value(&payload.as_ref()))]); + let extra = Some(HashMap::from([( + "wm_trigger".to_string(), + to_raw_value(&serde_json::json!({ + "kind": "mqtt", + "mqtt": { + "topic": publish.topic, + "retain": publish.retain, + "pkid": publish.pkid, + "qos": publish.qos, + "v5": publish.v5.map(|properties| { + serde_json::json!({ + "payload_format_indicator": properties.payload_format_indicator, + "topic_alias": properties.topic_alias, + "response_topic": properties.response_topic, + "correlation_data": properties.correlation_data.as_deref(), + "user_properties": properties.user_properties, + "subscription_identifiers": properties.subscription_identifiers, + "content_type": properties.content_type, + }) + }) + } + })), + )])); + mqtt.handle(&db, Some(args), extra).await; +} + +async fn handle_event(db: &DB, mqtt: &MqttConfig, handler: H, mut event_loop: E) -> () +where + H: MqttEvent, + E: EventLoop, + E::Error: ToString, +{ + loop { + let event = event_loop.poll().await; + + match event { + Ok(event) => { + let publish_data = handler.handle_event(event); + if let Ok(Some((payload, publish_data))) = publish_data { + handle_publish_packet(db, mqtt, payload, publish_data).await; + } + } + Err(err) => { + let err = err.to_string(); + tracing::debug!("Error: {}", &err); + mqtt.disable_with_error(&db, err).await; + return; + } + } + } +} + +#[derive(Debug)] +enum MqttConfig { + Trigger(MqttTrigger), + Capture(CaptureConfigForMqttTrigger), +} + +impl MqttConfig { + async fn update_ping(&self, db: &DB, error: Option<&str>) -> Option<()> { + match self { + MqttConfig::Trigger(trigger) => trigger.update_ping(db, error).await, + MqttConfig::Capture(capture) => capture.update_ping(db, error).await, + } + } + + async fn disable_with_error(&self, db: &DB, error: String) -> () { + match self { + MqttConfig::Trigger(trigger) => trigger.disable_with_error(&db, error).await, + MqttConfig::Capture(capture) => capture.disable_with_error(db, error).await, + } + } + + async fn start_consuming_messages( + &self, + db: &DB, + ) -> std::result::Result { + let mqtt_resource_path; + let subscribe_topics; + let workspace_id; + let authed; + let client_version; + let client_id; + let v3_config; + let v5_config; + match self { + MqttConfig::Capture(capture) => { + mqtt_resource_path = &capture.trigger_config.0.mqtt_resource_path; + subscribe_topics = capture.trigger_config.0.subscribe_topics.clone(); + workspace_id = &capture.workspace_id; + authed = capture.fetch_authed(&db).await?; + client_version = capture.trigger_config.0.client_version.as_ref(); + client_id = capture.trigger_config.0.client_id.as_deref(); + v3_config = capture.trigger_config.0.v3_config.as_ref(); + v5_config = capture.trigger_config.0.v5_config.as_ref(); + } + MqttConfig::Trigger(trigger) => { + mqtt_resource_path = &trigger.mqtt_resource_path; + subscribe_topics = trigger + .subscribe_topics + .iter() + .map(|topic| topic.0.clone()) + .collect_vec(); + workspace_id = &trigger.workspace_id; + client_version = trigger.client_version.as_ref(); + authed = trigger.fetch_authed(&db).await?; + client_id = trigger.client_id.as_deref(); + v3_config = trigger.v3_config.as_ref().map(|v3_config| &v3_config.0); + v5_config = trigger.v5_config.as_ref().map(|v5_config| &v5_config.0); + } + } + let mqtt_resource = try_get_resource_from_db_as::( + authed, + Some(UserDB::new(db.clone())), + db, + mqtt_resource_path, + workspace_id, + ) + .await?; + let client_builder = MqttClientBuilder::new( + mqtt_resource, + client_id, + subscribe_topics, + v3_config, + v5_config, + client_version, + ); + + client_builder.build_client().await + } + + async fn handle( + &self, + db: &DB, + args: Option>>, + extra: Option>>, + ) -> () { + match self { + MqttConfig::Trigger(trigger) => trigger.handle(&db, args, extra).await, + MqttConfig::Capture(capture) => capture.handle(&db, args, extra).await, + } + } +} + +impl MqttTrigger { + async fn try_to_listen_to_mqtt_messages( + self, + db: DB, + killpill_rx: tokio::sync::broadcast::Receiver<()>, + ) -> () { + let mqtt_trigger = sqlx::query_scalar!( + r#" + UPDATE + mqtt_trigger + SET + server_id = $1, + last_server_ping = now(), + error = 'Connecting...' + WHERE + enabled IS TRUE + AND workspace_id = $2 + AND path = $3 + AND (last_server_ping IS NULL + OR last_server_ping < now() - INTERVAL '15 seconds' + ) + RETURNING true + "#, + *INSTANCE_NAME, + self.workspace_id, + self.path, + ) + .fetch_optional(&db) + .await; + match mqtt_trigger { + Ok(has_lock) => { + if has_lock.flatten().unwrap_or(false) { + tracing::info!("Spawning new task to listen to mqtt notifications"); + tokio::spawn(async move { + listen_to_messages(MqttConfig::Trigger(self), db.clone(), killpill_rx) + .await; + }); + } else { + tracing::info!("Mqtt trigger {} already being listened to", self.path); + } + } + Err(err) => { + tracing::error!( + "Error acquiring lock for mqtt trigger {}: {:?}", + self.path, + err + ); + } + }; + } + + async fn update_ping(&self, db: &DB, error: Option<&str>) -> Option<()> { + let updated = sqlx::query_scalar!( + r#" + UPDATE + mqtt_trigger + SET + last_server_ping = now(), + error = $1 + WHERE + workspace_id = $2 + AND path = $3 + AND server_id = $4 + AND enabled IS TRUE + RETURNING 1 + "#, + error, + &self.workspace_id, + &self.path, + *INSTANCE_NAME + ) + .fetch_optional(db) + .await; + + match updated { + Ok(updated) => { + if updated.flatten().is_none() { + // allow faster restart of mqtt trigger + sqlx::query!( + r#" + UPDATE + mqtt_trigger + SET + last_server_ping = NULL + WHERE + workspace_id = $1 + AND path = $2 + AND server_id IS NULL"#, + &self.workspace_id, + &self.path, + ) + .execute(db) + .await + .ok(); + tracing::info!( + "Mqtt trigger {} changed, disabled, or deleted, stopping...", + self.path + ); + return None; + } + } + Err(err) => { + tracing::warn!( + "Error updating ping of mqtt trigger {}: {:?}", + self.path, + err + ); + } + }; + + Some(()) + } + + async fn disable_with_error(&self, db: &DB, error: String) -> () { + match sqlx::query!( + r#" + UPDATE + mqtt_trigger + SET + enabled = FALSE, + error = $1, + server_id = NULL, + last_server_ping = NULL + WHERE + workspace_id = $2 AND + path = $3 + "#, + error, + self.workspace_id, + self.path, + ) + .execute(db) + .await + { + Ok(_) => { + report_critical_error( + format!( + "Disabling mqtt trigger {} because of error: {}", + self.path, error + ), + db.clone(), + Some(&self.workspace_id), + None, + ) + .await; + } + Err(disable_err) => { + report_critical_error( + format!("Could not disable mqtt trigger {} with err {}, disabling because of error {}", self.path, disable_err, error), + db.clone(), + Some(&self.workspace_id), + None, + ).await; + } + } + } + + async fn fetch_authed(&self, db: &DB) -> error::Result { + fetch_api_authed( + self.edited_by.clone(), + self.email.clone(), + &self.workspace_id, + db, + Some(format!("mqtt-{}", self.path)), + ) + .await + } + + async fn handle( + &self, + db: &DB, + args: Option>>, + extra: Option>>, + ) -> () { + if let Err(err) = run_job(args, extra, db, self).await { + report_critical_error( + format!("Failed to trigger job from mqtt {}: {:?}", self.path, err), + db.clone(), + Some(&self.workspace_id), + None, + ) + .await; + }; + } +} + +struct PublishData { + topic: String, + retain: bool, + pkid: u16, + v5: Option, + qos: u8, +} + +impl PublishData { + fn new( + topic: String, + retain: bool, + pkid: u16, + v5: Option, + qos: u8, + ) -> PublishData { + PublishData { topic, retain, pkid, v5, qos } + } +} + +async fn listen_to_messages( + mqtt: MqttConfig, + db: DB, + mut killpill_rx: tokio::sync::broadcast::Receiver<()>, +) { + tokio::select! { + biased; + + _ = killpill_rx.recv() => { + return; + } + + _ = loop_ping(&db, &mqtt, Some("Connecting...")) => { + return; + } + + result = mqtt.start_consuming_messages(&db) => { + tokio::select! { + biased; + + _ = killpill_rx.recv() => { + return; + } + + _ = loop_ping(&db, &mqtt, None) => { + return; + } + + _ = async { + match result { + Ok(connection) => { + match connection { + MqttClientResult::V3((v3_handler, event_loop)) => handle_event(&db, &mqtt, v3_handler, event_loop).await, + MqttClientResult::V5((v5_handler, event_loop)) => handle_event(&db, &mqtt, v5_handler, event_loop).await, + } + } + Err(err) => { + tracing::error!( + "Mqtt trigger error while trying to start listening to notifications: {}", + &err + ); + mqtt.disable_with_error(&db, err.to_string()).await + } + } + } => {} + } + } + } +} + +#[derive(Debug, Deserialize)] +struct CaptureConfigForMqttTrigger { + trigger_config: SqlxJson, + path: String, + is_flow: bool, + workspace_id: String, + owner: String, + email: String, +} + +impl CaptureConfigForMqttTrigger { + async fn try_to_listen_to_mqtt_messages( + self, + db: DB, + killpill_rx: tokio::sync::broadcast::Receiver<()>, + ) -> () { + match sqlx::query_scalar!( + r#" + UPDATE + capture_config + SET + server_id = $1, + last_server_ping = now(), + error = 'Connecting...' + WHERE + last_client_ping > NOW() - INTERVAL '10 seconds' AND + workspace_id = $2 AND + path = $3 AND + is_flow = $4 AND + trigger_kind = 'mqtt' AND + (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') + RETURNING true + "#, + *INSTANCE_NAME, + self.workspace_id, + self.path, + self.is_flow, + ) + .fetch_optional(&db) + .await + { + Ok(has_lock) => { + if has_lock.flatten().unwrap_or(false) { + tokio::spawn(listen_to_messages( + MqttConfig::Capture(self), + db, + killpill_rx, + )); + } else { + tracing::info!("Mqtt {} already being listened to", self.path); + } + } + Err(err) => { + tracing::error!( + "Error acquiring lock for capture mqtt {}: {:?}", + self.path, + err + ); + } + }; + } + + async fn update_ping(&self, db: &DB, error: Option<&str>) -> Option<()> { + match sqlx::query_scalar!( + r#" + UPDATE + capture_config + SET + last_server_ping = now(), + error = $1 + WHERE + workspace_id = $2 AND + path = $3 AND + is_flow = $4 AND + trigger_kind = 'mqtt' AND + server_id = $5 AND + last_client_ping > NOW() - INTERVAL '10 seconds' + RETURNING 1 + "#, + error, + self.workspace_id, + self.path, + self.is_flow, + *INSTANCE_NAME + ) + .fetch_optional(db) + .await + { + Ok(updated) => { + if updated.flatten().is_none() { + // allow faster restart of mqtt capture + sqlx::query!( + r#"UPDATE + capture_config + SET + last_server_ping = NULL + WHERE + workspace_id = $1 AND + path = $2 AND + is_flow = $3 AND + trigger_kind = 'mqtt' AND + server_id IS NULL + "#, + self.workspace_id, + self.path, + self.is_flow, + ) + .execute(db) + .await + .ok(); + tracing::info!( + "Mqtt capture {} changed, disabled, or deleted, stopping...", + self.path + ); + return None; + } + } + Err(err) => { + tracing::warn!( + "Error updating ping of capture mqtt {}: {:?}", + self.path, + err + ); + } + }; + + Some(()) + } + + async fn fetch_authed(&self, db: &DB) -> error::Result { + fetch_api_authed( + self.owner.clone(), + self.email.clone(), + &self.workspace_id, + db, + Some(format!("mqtt-{}", self.get_trigger_path())), + ) + .await + } + + fn get_trigger_path(&self) -> String { + format!( + "{}-{}", + if self.is_flow { "flow" } else { "script" }, + self.path + ) + } + + async fn disable_with_error(&self, db: &DB, error: String) -> () { + if let Err(err) = sqlx::query!( + r#" + UPDATE + capture_config + SET + error = $1, + server_id = NULL, + last_server_ping = NULL + WHERE + workspace_id = $2 AND + path = $3 AND + is_flow = $4 AND + trigger_kind = 'mqtt' + "#, + error, + self.workspace_id, + self.path, + self.is_flow, + ) + .execute(db) + .await + { + tracing::error!( + "Could not disable mqtt capture {} ({}) with err {}, disabling because of error {}", + self.path, + self.workspace_id, + err, + error + ); + } + } + + async fn handle( + &self, + db: &DB, + args: Option>>, + extra: Option>>, + ) -> () { + let args = PushArgsOwned { args: args.unwrap_or_default(), extra: None }; + let extra = extra.as_ref().map(to_raw_value); + if let Err(err) = insert_capture_payload( + db, + &self.workspace_id, + &self.path, + self.is_flow, + &TriggerKind::Mqtt, + args, + extra, + &self.owner, + ) + .await + { + tracing::error!("Error inserting capture payload: {:?}", err); + } + } +} + +async fn listen_to_unlistened_mqtt_events( + db: &DB, + killpill_rx: &tokio::sync::broadcast::Receiver<()>, +) { + let mqtt_triggers = sqlx::query_as!( + MqttTrigger, + r#" + SELECT + mqtt_resource_path, + subscribe_topics as "subscribe_topics!: Vec>", + v3_config as "v3_config!: Option>", + v5_config as "v5_config!: Option>", + client_version as "client_version: _", + client_id, + workspace_id, + path, + script_path, + is_flow, + edited_by, + email, + edited_at, + server_id, + last_server_ping, + extra_perms, + error, + enabled + FROM + mqtt_trigger + WHERE + enabled IS TRUE + AND (last_server_ping IS NULL OR + last_server_ping < now() - interval '15 seconds' + ) + "# + ) + .fetch_all(db) + .await; + + match mqtt_triggers { + Ok(mut triggers) => { + triggers.shuffle(&mut rand::rng()); + for trigger in triggers { + trigger + .try_to_listen_to_mqtt_messages(db.clone(), killpill_rx.resubscribe()) + .await; + } + } + Err(err) => { + tracing::error!("Error fetching mqtt triggers: {:?}", err); + } + }; + + let mqtt_triggers_capture = sqlx::query_as!( + CaptureConfigForMqttTrigger, + r#" + SELECT + path, + is_flow, + workspace_id, + owner, + email, + trigger_config as "trigger_config!: _" + FROM + capture_config + WHERE + trigger_kind = 'mqtt' AND + last_client_ping > NOW() - INTERVAL '10 seconds' AND + trigger_config IS NOT NULL AND + (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') + "# + ) + .fetch_all(db) + .await; + + match mqtt_triggers_capture { + Ok(mut captures) => { + captures.shuffle(&mut rand::rng()); + for capture in captures { + capture + .try_to_listen_to_mqtt_messages(db.clone(), killpill_rx.resubscribe()) + .await; + } + } + Err(err) => { + tracing::error!("Error fetching captures mqtt triggers: {:?}", err); + } + }; +} + +pub fn start_mqtt_consumer(db: DB, mut killpill_rx: tokio::sync::broadcast::Receiver<()>) { + tokio::spawn(async move { + listen_to_unlistened_mqtt_events(&db, &killpill_rx).await; + loop { + tokio::select! { + biased; + _ = killpill_rx.recv() => { + return; + } + _ = tokio::time::sleep(tokio::time::Duration::from_secs(15)) => { + listen_to_unlistened_mqtt_events(&db, &killpill_rx).await + } + } + } + }); +} diff --git a/backend/windmill-api/src/postgres_triggers/handler.rs b/backend/windmill-api/src/postgres_triggers/handler.rs index 760be062f7..d1cd92e8c3 100644 --- a/backend/windmill-api/src/postgres_triggers/handler.rs +++ b/backend/windmill-api/src/postgres_triggers/handler.rs @@ -1,50 +1,47 @@ -use std::{ - collections::{ - hash_map::Entry::{Occupied, Vacant}, - HashMap, - }, - str::FromStr, +use std::collections::{ + hash_map::Entry::{Occupied, Vacant}, + HashMap, }; use crate::{ db::{ApiAuthed, DB}, postgres_triggers::mapper::{Mapper, MappingInfo}, + resources::try_get_resource_from_db_as, }; use axum::{ extract::{Path, Query}, Extension, Json, }; -use chrono::Utc; use http::StatusCode; use itertools::Itertools; use pg_escape::{quote_identifier, quote_literal}; use quick_cache::sync::Cache; -use rand::Rng; use rust_postgres::types::Type; use serde::{Deserialize, Deserializer, Serialize}; use sql_builder::{bind::Bind, SqlBuilder}; -use sqlx::{ - postgres::{types::Oid, PgConnectOptions, PgSslMode}, - Connection, FromRow, PgConnection, QueryBuilder, -}; +use sqlx::{postgres::types::Oid, FromRow, PgConnection}; use windmill_audit::{audit_ee::audit_log, ActionKind}; use windmill_common::error::Error; use windmill_common::{ db::UserDB, - error::{self, JsonResult}, + error::{self, JsonResult, Result}, utils::{not_found_if_none, paginate, Pagination, StripPath}, worker::CLOUD_HOSTED, }; -use super::get_database_resource; +use super::{ + create_logical_replication_slot_query, create_publication_query, drop_publication_query, + generate_random_string, get_database_connection, get_raw_postgres_connection, + ERROR_PUBLICATION_NAME_NOT_EXISTS, ERROR_REPLICATION_SLOT_NOT_EXISTS, +}; use lazy_static::lazy_static; #[derive(FromRow, Serialize, Deserialize, Debug)] -pub struct Database { +pub struct Postgres { pub user: String, pub password: String, pub host: String, - pub port: u16, + pub port: Option, pub dbname: String, #[serde(default)] pub sslmode: String, @@ -88,7 +85,7 @@ impl Relations { } } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] pub struct EditPostgresTrigger { replication_slot_name: String, publication_name: String, @@ -112,48 +109,38 @@ pub struct NewPostgresTrigger { publication: Option, } -pub async fn get_database_connection( - authed: ApiAuthed, - user_db: Option, - db: &DB, - postgres_resource_path: &str, - w_id: &str, -) -> Result { - let database = get_database_resource(authed, user_db, db, postgres_resource_path, w_id).await?; - - Ok(get_raw_postgres_connection(&database).await?) +#[derive(Serialize, Deserialize)] +pub struct TestPostgres { + pub postgres_resource_path: String, } -pub async fn get_raw_postgres_connection(db: &Database) -> Result { - let options = { - let sslmode = if !db.sslmode.is_empty() { - PgSslMode::from_str(&db.sslmode)? - } else { - PgSslMode::Prefer - }; - let options = PgConnectOptions::new() - .host(&db.host) - .database(&db.dbname) - .port(db.port) - .ssl_mode(sslmode) - .username(&db.user); - - let options = if !db.root_certificate_pem.is_empty() { - options.ssl_root_cert_from_pem(db.root_certificate_pem.as_bytes().to_vec()) - } else { - options - }; - - if !db.password.is_empty() { - options.password(&db.password) - } else { - options - } - }; - - PgConnection::connect_with(&options) +pub async fn test_postgres_connection( + authed: ApiAuthed, + Extension(db): Extension, + Extension(user_db): Extension, + Path(workspace_id): Path, + Json(test_postgres): Json, +) -> Result<()> { + let connect_f = async { + get_database_connection( + authed, + Some(user_db), + &db, + &test_postgres.postgres_resource_path, + &workspace_id, + ) .await - .map_err(|e| e.into()) + .map_err(|err| { + error::Error::BadConfig(format!("Error connecting to postgres: {}", err.to_string())) + }) + }; + tokio::time::timeout(tokio::time::Duration::from_secs(30), connect_f) + .await + .map_err(|_| { + error::Error::BadConfig(format!("Timeout connecting to postgres after 30 seconds")) + })??; + + Ok(()) } #[derive(Deserialize, Debug)] @@ -165,19 +152,20 @@ pub enum Language { #[derive(Debug, Deserialize)] pub struct TemplateScript { postgres_resource_path: String, - #[serde(deserialize_with = "check_if_not_duplication_relation")] + #[serde(deserialize_with = "check_if_valid_relation")] relations: Option>, language: Language, } -fn check_if_not_duplication_relation<'de, D>( +fn check_if_valid_relation<'de, D>( relations: D, ) -> std::result::Result>, D::Error> where D: Deserializer<'de>, { let relations: Option> = Option::deserialize(relations)?; - + let mut track_all_table_in_schema = false; + let mut track_specific_columns_in_table = false; match relations { Some(relations) => { for relation in relations.iter() { @@ -187,12 +175,25 @@ where )); } + if !track_all_table_in_schema && relation.table_to_track.is_empty() { + track_all_table_in_schema = true; + continue; + } + for table_to_track in relation.table_to_track.iter() { if table_to_track.table_name.trim().is_empty() { return Err(serde::de::Error::custom( "Table name must not be empty".to_string(), )); } + + if !track_specific_columns_in_table && !table_to_track.columns_name.is_empty() { + track_specific_columns_in_table = true; + } + } + + if track_all_table_in_schema && track_specific_columns_in_table { + return Err(serde::de::Error::custom("Incompatible tracking options. Schema-level tracking and specific table tracking with column selection cannot be used together. Refer to the documentation for valid configurations.")); } } @@ -221,12 +222,16 @@ pub struct PostgresTrigger { pub edited_by: String, pub email: String, pub edited_at: chrono::DateTime, + #[serde(skip_serializing_if = "Option::is_none")] pub extra_perms: Option, pub postgres_resource_path: String, + #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub server_id: Option, pub replication_slot_name: String, pub publication_name: String, + #[serde(skip_serializing_if = "Option::is_none")] pub last_server_ping: Option>, pub enabled: bool, } @@ -245,13 +250,105 @@ pub struct SetEnabled { pub enabled: bool, } +#[derive(Serialize, Deserialize)] +pub struct PostgresPublicationReplication { + publication_name: String, + replication_slot_name: String, +} + +impl PostgresPublicationReplication { + pub fn new( + publication_name: String, + replication_slot_name: String, + ) -> PostgresPublicationReplication { + PostgresPublicationReplication { publication_name, replication_slot_name } + } +} + +async fn check_if_publication_exist( + connection: &mut PgConnection, + publication_name: &str, +) -> Result<()> { + sqlx::query!( + "SELECT pubname FROM pg_publication WHERE pubname = $1", + publication_name + ) + .fetch_one(connection) + .await + .map_err(|err| match err { + sqlx::Error::RowNotFound => { + Error::BadRequest(ERROR_PUBLICATION_NAME_NOT_EXISTS.to_string()) + } + err => Error::SqlErr { error: err, location: "pg_trigger".to_string() }, + })?; + Ok(()) +} + +async fn check_if_logical_replication_slot_exist( + connection: &mut PgConnection, + replication_slot_name: &str, +) -> Result<()> { + sqlx::query!( + "SELECT slot_name FROM pg_replication_slots where slot_name = $1", + &replication_slot_name + ) + .fetch_one(connection) + .await + .map_err(|err| match err { + _ => Error::BadRequest(ERROR_REPLICATION_SLOT_NOT_EXISTS.to_string()), + })?; + Ok(()) +} + +async fn create_custom_slot_and_publication_inner( + authed: ApiAuthed, + user_db: UserDB, + db: &DB, + postgres_resource_path: &str, + w_id: &str, + publication: &PublicationData, +) -> Result { + let publication_name = format!("windmill_trigger_{}", generate_random_string()); + let replication_slot_name = publication_name.clone(); + + let query = create_publication_query( + &publication_name, + publication.table_to_track.as_deref(), + &publication + .transaction_to_track + .iter() + .map(AsRef::as_ref) + .collect_vec(), + ); + + let mut connection = get_database_connection( + authed.clone(), + Some(user_db.clone()), + &db, + &postgres_resource_path, + &w_id, + ) + .await?; + + sqlx::query(&query).execute(&mut connection).await?; + + let query = create_logical_replication_slot_query(&replication_slot_name); + + sqlx::query(&query).execute(&mut connection).await?; + + Ok(PostgresPublicationReplication::new( + publication_name, + replication_slot_name, + )) +} + pub async fn create_postgres_trigger( authed: ApiAuthed, Extension(user_db): Extension, Extension(db): Extension, Path(w_id): Path, Json(new_postgres_trigger): Json, -) -> error::Result<(StatusCode, String)> { +) -> Result<(StatusCode, String)> { if *CLOUD_HOSTED { return Err(error::Error::BadRequest( "Postgres triggers are not supported on multi-tenant cloud, use dedicated cloud or self-host".to_string(), @@ -274,59 +371,32 @@ pub async fn create_postgres_trigger( "Publication data is missing".to_string(), )); } + let (pub_name, slot_name) = if publication_name.is_none() && replication_slot_name.is_none() { + if publication.is_none() { + return Err(Error::BadRequest("publication must be set".to_string())); + } + let PostgresPublicationReplication { publication_name, replication_slot_name } = + create_custom_slot_and_publication_inner( + authed.clone(), + user_db.clone(), + &db, + &postgres_resource_path, + &w_id, + &publication.unwrap(), + ) + .await?; - let create_slot = replication_slot_name.is_none(); - let create_publication = publication_name.is_none(); - - let name; - let mut pub_name = publication_name.as_deref().unwrap_or_default(); - let mut slot_name = replication_slot_name.as_deref().unwrap_or_default(); - if create_publication || create_slot { - let generate_random_string = move || { - let timestamp = Utc::now().timestamp_millis().to_string(); - let mut rng = rand::rng(); - let charset = "abcdefghijklmnopqrstuvwxyz0123456789"; - - let random_part = (0..10) - .map(|_| { - charset - .chars() - .nth(rng.random_range(0..charset.len())) - .unwrap() - }) - .collect::(); - - format!("{}_{}", timestamp, random_part) - }; - - name = format!("windmill_{}", generate_random_string()); - pub_name = &name; - slot_name = &name; - let publication = publication.unwrap(); - - let mut connection = get_database_connection( - authed.clone(), - Some(user_db.clone()), - &db, - &postgres_resource_path, - &w_id, - ) - .await?; - - new_publication( - &mut connection, - pub_name, - publication.table_to_track.as_deref(), - &publication - .transaction_to_track - .iter() - .map(AsRef::as_ref) - .collect_vec(), - ) - .await?; - - new_slot(&mut connection, slot_name).await?; - } + (publication_name, replication_slot_name) + } else { + if publication_name.is_none() { + return Err(Error::BadRequest("Missing publication name".to_string())); + } else if replication_slot_name.is_none() { + return Err(Error::BadRequest( + "Missing replication slot name".to_string(), + )); + } + (publication_name.unwrap(), replication_slot_name.unwrap()) + }; let mut tx = user_db.begin(&authed).await?; @@ -446,10 +516,10 @@ pub async fn list_postgres_triggers( #[derive(Deserialize, Serialize, Debug)] pub struct PublicationData { - #[serde(default, deserialize_with = "check_if_not_duplication_relation")] - table_to_track: Option>, + #[serde(default, deserialize_with = "check_if_valid_relation")] + pub table_to_track: Option>, #[serde(deserialize_with = "check_if_valid_transaction_type")] - transaction_to_track: Vec, + pub transaction_to_track: Vec, } fn check_if_valid_transaction_type<'de, D>( @@ -505,7 +575,7 @@ pub async fn list_slot_name( Extension(user_db): Extension, Extension(db): Extension, Path((w_id, postgres_resource_path)): Path<(String, String)>, -) -> error::Result>> { +) -> Result>> { let mut connection = get_database_connection( authed.clone(), Some(user_db.clone()), @@ -539,28 +609,13 @@ pub struct Slot { name: String, } -async fn new_slot(connection: &mut PgConnection, name: &str) -> error::Result<()> { - let query = format!( - r#" - SELECT - * - FROM - pg_create_logical_replication_slot({}, 'pgoutput');"#, - quote_literal(&name) - ); - - sqlx::query(&query).execute(connection).await?; - - Ok(()) -} - pub async fn create_slot( authed: ApiAuthed, Extension(user_db): Extension, Extension(db): Extension, Path((w_id, postgres_resource_path)): Path<(String, String)>, Json(Slot { name }): Json, -) -> error::Result { +) -> Result { let mut connection = get_database_connection( authed.clone(), Some(user_db.clone()), @@ -570,9 +625,11 @@ pub async fn create_slot( ) .await?; - new_slot(&mut connection, &name).await?; + let query = create_logical_replication_slot_query(&name); - Ok(format!("Slot {} created!", name)) + sqlx::query(&query).execute(&mut connection).await?; + + Ok(format!("Replication slot {} created!", name)) } pub async fn drop_slot_name( @@ -581,20 +638,44 @@ pub async fn drop_slot_name( Extension(db): Extension, Path((w_id, postgres_resource_path)): Path<(String, String)>, Json(Slot { name }): Json, -) -> error::Result { - let mut connection = get_database_connection( - authed.clone(), - Some(user_db.clone()), +) -> Result { + let database = try_get_resource_from_db_as::( + authed, + Some(user_db), &db, &postgres_resource_path, &w_id, ) .await?; - let query = format!("SELECT pg_drop_replication_slot({});", quote_literal(&name)); - sqlx::query(&query).execute(&mut connection).await?; + let mut connection = get_raw_postgres_connection(&database).await?; - Ok(format!("Slot name {} deleted!", name)) + let active_pid = sqlx::query_scalar!( + r#"SELECT + active_pid + FROM + pg_replication_slots + WHERE + slot_name = $1 + "#, + &name + ) + .fetch_optional(&mut connection) + .await? + .flatten(); + + if let Some(pid) = active_pid { + sqlx::query("SELECT pg_terminate_backend($1)") + .bind(pid) + .execute(&mut connection) + .await?; + } + sqlx::query("SELECT pg_drop_replication_slot($1)") + .bind(&name) + .execute(&mut connection) + .await?; + + Ok(format!("Replication slot {} deleted!", name)) } #[derive(Debug, Serialize)] struct PublicationName { @@ -606,7 +687,7 @@ pub async fn list_database_publication( Extension(user_db): Extension, Extension(db): Extension, Path((w_id, postgres_resource_path)): Path<(String, String)>, -) -> error::Result>> { +) -> Result>> { let mut connection = get_database_connection( authed.clone(), Some(user_db.clone()), @@ -636,7 +717,7 @@ pub async fn get_publication_info( Extension(user_db): Extension, Extension(db): Extension, Path((w_id, publication_name, postgres_resource_path)): Path<(String, String, String)>, -) -> error::Result> { +) -> Result> { let mut connection = get_database_connection( authed.clone(), Some(user_db.clone()), @@ -647,13 +728,13 @@ pub async fn get_publication_info( .await?; let publication_data = - get_publication_scope_and_transaction(&publication_name, &mut connection).await; + get_publication_scope_and_transaction(&mut connection, &publication_name).await; let (all_table, transaction_to_track) = match publication_data { Ok(pub_data) => pub_data, Err(Error::SqlErr { error: sqlx::Error::RowNotFound, .. }) => { return Err(Error::NotFound( - "Publication was not found, please create a new publication".to_string(), + ERROR_PUBLICATION_NAME_NOT_EXISTS.to_string(), )) } Err(e) => return Err(e), @@ -670,82 +751,13 @@ pub async fn get_publication_info( ))) } -async fn new_publication( - connection: &mut PgConnection, - publication_name: &str, - table_to_track: Option<&[Relations]>, - transaction_to_track: &[&str], -) -> Result<(), Error> { - let mut query = QueryBuilder::new("CREATE PUBLICATION "); - - query.push(quote_identifier(publication_name)); - - match table_to_track { - Some(database_component) if !database_component.is_empty() => { - query.push(" FOR"); - for (i, schema) in database_component.iter().enumerate() { - if schema.table_to_track.is_empty() { - query.push(" TABLES IN SCHEMA "); - query.push(quote_identifier(&schema.schema_name)); - } else { - query.push(" TABLE ONLY "); - for (j, table) in schema.table_to_track.iter().enumerate() { - let table_name = quote_identifier(&table.table_name); - let schema_name = quote_identifier(&schema.schema_name); - let full_name = format!("{}.{}", &schema_name, &table_name); - query.push(full_name); - if !table.columns_name.is_empty() { - query.push(" ("); - let columns = table - .columns_name - .iter() - .map(|column| quote_identifier(column)) - .join(", "); - query.push(&columns); - query.push(")"); - } - - if let Some(where_clause) = &table.where_clause { - query.push(" WHERE ("); - query.push(where_clause); - query.push(')'); - } - - if j + 1 != schema.table_to_track.len() { - query.push(", "); - } - } - } - if i < database_component.len() - 1 { - query.push(", "); - } - } - } - _ => { - query.push(" FOR ALL TABLES "); - } - }; - - if !transaction_to_track.is_empty() { - let transactions = || transaction_to_track.iter().join(", "); - query.push(" WITH (publish = '"); - query.push(transactions()); - query.push("');"); - } - - let query = query.build(); - query.execute(&mut *connection).await?; - - Ok(()) -} - pub async fn create_publication( authed: ApiAuthed, Extension(user_db): Extension, Extension(db): Extension, Path((w_id, publication_name, postgres_resource_path)): Path<(String, String, String)>, Json(publication_data): Json, -) -> error::Result { +) -> Result { let PublicationData { table_to_track, transaction_to_track } = publication_data; let mut connection = get_database_connection( @@ -757,13 +769,13 @@ pub async fn create_publication( ) .await?; - new_publication( - &mut connection, + let query = create_publication_query( &publication_name, table_to_track.as_deref(), &transaction_to_track.iter().map(AsRef::as_ref).collect_vec(), - ) - .await?; + ); + + sqlx::query(&query).execute(&mut connection).await?; Ok(format!( "Publication {} successfully created!", @@ -771,24 +783,12 @@ pub async fn create_publication( )) } -async fn drop_publication( - publication_name: &str, - connection: &mut PgConnection, -) -> Result<(), Error> { - let mut query = QueryBuilder::new("DROP PUBLICATION IF EXISTS "); - let quoted_publication_name = quote_identifier(publication_name); - query.push(quoted_publication_name); - query.push(";"); - query.build().execute(&mut *connection).await?; - Ok(()) -} - pub async fn delete_publication( authed: ApiAuthed, Extension(user_db): Extension, Extension(db): Extension, Path((w_id, publication_name, postgres_resource_path)): Path<(String, String, String)>, -) -> error::Result { +) -> Result { let mut connection = get_database_connection( authed.clone(), Some(user_db.clone()), @@ -798,7 +798,9 @@ pub async fn delete_publication( ) .await?; - drop_publication(&publication_name, &mut connection).await?; + let query = drop_publication_query(&publication_name); + + sqlx::query(&query).execute(&mut connection).await?; Ok(format!( "Publication {} successfully deleted!", @@ -806,65 +808,61 @@ pub async fn delete_publication( )) } -async fn update_publication( - connection: &mut PgConnection, +pub fn get_update_publication_query( publication_name: &str, PublicationData { table_to_track, transaction_to_track }: PublicationData, -) -> error::Result { - let (all_table, _) = - get_publication_scope_and_transaction(&publication_name, connection).await?; - - let mut query = QueryBuilder::new(""); + all_table: bool, +) -> Vec { let quoted_publication_name = quote_identifier(&publication_name); let transaction_to_track_as_str = transaction_to_track.iter().join(","); - + let mut queries = Vec::with_capacity(2); match table_to_track { Some(ref relations) if !relations.is_empty() => { if all_table { - drop_publication(&publication_name, connection).await?; - new_publication( - connection, + queries.push(drop_publication_query(&publication_name)); + queries.push(create_publication_query( &publication_name, table_to_track.as_deref(), &transaction_to_track.iter().map(AsRef::as_ref).collect_vec(), - ) - .await?; + )); } else { - query.push("ALTER PUBLICATION "); - query.push("ed_publication_name); - query.push(" SET"); + let mut query = String::from(""); + + query.push_str("ALTER PUBLICATION "); + query.push_str("ed_publication_name); + query.push_str(" SET"); for (i, schema) in relations.iter().enumerate() { if schema.table_to_track.is_empty() { - query.push(" TABLES IN SCHEMA "); + query.push_str(" TABLES IN SCHEMA "); let quoted_schema = quote_identifier(&schema.schema_name); - query.push("ed_schema); + query.push_str("ed_schema); } else { - query.push(" TABLE ONLY "); + query.push_str(" TABLE ONLY "); for (j, table) in schema.table_to_track.iter().enumerate() { let table_name = quote_identifier(&table.table_name); let schema_name = quote_identifier(&schema.schema_name); let full_name = format!("{}.{}", &schema_name, &table_name); - query.push(&full_name); + query.push_str(&full_name); if !table.columns_name.is_empty() { - query.push(" ("); + query.push_str(" ("); let columns = table .columns_name .iter() .map(|column| quote_identifier(column)) .join(", "); - query.push(&columns); - query.push(") "); + query.push_str(&columns); + query.push_str(") "); } if let Some(where_clause) = &table.where_clause { - query.push(" WHERE ("); - query.push(where_clause); + query.push_str(" WHERE ("); + query.push_str(where_clause); query.push(')'); } if j + 1 != schema.table_to_track.len() { - query.push(", "); + query.push_str(", "); } } } @@ -872,36 +870,35 @@ async fn update_publication( query.push(','); } } - query.push(";"); - query.build().execute(&mut *connection).await?; - query.reset(); - query.push("ALTER PUBLICATION "); - query.push("ed_publication_name); - query.push(format!( + query.push(';'); + + queries.push(query); + + let mut query = String::new(); + + query.push_str("ALTER PUBLICATION "); + query.push_str("ed_publication_name); + query.push_str(&format!( " SET (publish = '{}');", transaction_to_track_as_str )); + queries.push(query); } } _ => { - drop_publication(&publication_name, connection).await?; + queries.push(drop_publication_query(&publication_name)); let to_execute = format!( r#" CREATE - PUBLICATION {} FOR ALL TABLES WITH (publish = '{}') + PUBLICATION {} FOR ALL TABLES WITH (publish = '{}'); "#, quoted_publication_name, transaction_to_track_as_str ); - query.push(&to_execute); + queries.push(to_execute); } }; - query.build().execute(&mut *connection).await?; - - Ok(format!( - "Publication {} successfully updated!", - publication_name - )) + queries } pub async fn alter_publication( @@ -910,7 +907,7 @@ pub async fn alter_publication( Extension(db): Extension, Path((w_id, publication_name, postgres_resource_path)): Path<(String, String, String)>, Json(publication_data): Json, -) -> error::Result { +) -> Result { let mut connection = get_database_connection( authed.clone(), Some(user_db.clone()), @@ -919,15 +916,28 @@ pub async fn alter_publication( &w_id, ) .await?; - let message = update_publication(&mut connection, &publication_name, publication_data).await?; - Ok(message) + check_if_publication_exist(&mut connection, &publication_name).await?; + + let (all_table, _) = + get_publication_scope_and_transaction(&mut connection, &publication_name).await?; + + let queries = get_update_publication_query(&publication_name, publication_data, all_table); + + for query in queries { + sqlx::query(&query).execute(&mut connection).await?; + } + + Ok(format!( + "Publication {} updated with success", + publication_name + )) } async fn get_publication_scope_and_transaction( - publication_name: &str, connection: &mut PgConnection, -) -> Result<(bool, Vec), Error> { + publication_name: &str, +) -> std::result::Result<(bool, Vec), Error> { #[derive(Debug, Deserialize, FromRow)] struct PublicationTransaction { all_table: bool, @@ -972,7 +982,7 @@ async fn get_publication_scope_and_transaction( async fn get_tracked_relations( connection: &mut PgConnection, publication_name: &str, -) -> error::Result> { +) -> Result> { #[derive(Debug, Deserialize, FromRow)] struct PublicationData { schema_name: Option, @@ -985,14 +995,18 @@ async fn get_tracked_relations( PublicationData, r#" SELECT - schemaname AS schema_name, - tablename AS table_name, - attnames AS columns, - rowfilter AS where_clause + schemaname AS schema_name, + tablename AS table_name, + CASE + WHEN array_length(attnames, 1) = (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = pg_publication_tables.schemaname AND table_name = pg_publication_tables.tablename) + THEN NULL + ELSE attnames + END AS columns, + rowfilter AS where_clause FROM pg_publication_tables WHERE - pubname = $1 + pubname = $1; "#, publication_name ) @@ -1007,7 +1021,7 @@ async fn get_tracked_relations( let table_to_track = TableToTrack::new( publication.table_name.unwrap(), publication.where_clause, - publication.columns.unwrap(), + publication.columns.unwrap_or_default(), ); match entry { Occupied(mut occuped) => { @@ -1071,8 +1085,9 @@ pub async fn update_postgres_trigger( Extension(db): Extension, Path((w_id, path)): Path<(String, StripPath)>, Json(postgres_trigger): Json, -) -> error::Result { +) -> Result { let workspace_path = path.to_path(); + let EditPostgresTrigger { replication_slot_name, publication_name, @@ -1083,16 +1098,26 @@ pub async fn update_postgres_trigger( publication, } = postgres_trigger; + let mut connection = get_database_connection( + authed.clone(), + Some(user_db.clone()), + &db, + &postgres_resource_path, + &w_id, + ) + .await?; + + check_if_logical_replication_slot_exist(&mut connection, &replication_slot_name).await?; + if let Some(publication) = publication { - let mut connection = get_database_connection( - authed.clone(), - Some(user_db.clone()), - &db, - &postgres_resource_path, - &w_id, - ) - .await?; - update_publication(&mut connection, &publication_name, publication).await?; + check_if_publication_exist(&mut connection, &publication_name).await?; + let (all_table, _) = + get_publication_scope_and_transaction(&mut connection, &publication_name).await?; + + let queries = get_update_publication_query(&publication_name, publication, all_table); + for query in queries { + sqlx::query(&query).execute(&mut connection).await?; + } } let mut tx = user_db.begin(&authed).await?; @@ -1149,7 +1174,7 @@ pub async fn delete_postgres_trigger( authed: ApiAuthed, Extension(user_db): Extension, Path((w_id, path)): Path<(String, StripPath)>, -) -> error::Result { +) -> Result { let path = path.to_path(); let mut tx = user_db.begin(&authed).await?; sqlx::query!( @@ -1209,7 +1234,7 @@ pub async fn set_enabled( Extension(user_db): Extension, Path((w_id, path)): Path<(String, StripPath)>, Json(payload): Json, -) -> error::Result { +) -> Result { let mut tx = user_db.begin(&authed).await?; let path = path.to_path(); @@ -1260,7 +1285,7 @@ pub async fn set_enabled( )) } -pub async fn get_template_script(Path((_, id)): Path<(String, String)>) -> error::Result { +pub async fn get_template_script(Path((_, id)): Path<(String, String)>) -> Result { let template = if let Some((_, template)) = TEMPLATE.remove(&id) { template } else { @@ -1275,7 +1300,7 @@ pub async fn create_template_script( Extension(db): Extension, Path(w_id): Path, Json(template_script): Json, -) -> error::Result { +) -> Result { let TemplateScript { postgres_resource_path, relations, language } = template_script; if relations.is_none() { return Err(Error::BadRequest( diff --git a/backend/windmill-api/src/postgres_triggers/mapper.rs b/backend/windmill-api/src/postgres_triggers/mapper.rs index ec3626c7f7..bbc7b9c011 100644 --- a/backend/windmill-api/src/postgres_triggers/mapper.rs +++ b/backend/windmill-api/src/postgres_triggers/mapper.rs @@ -26,8 +26,8 @@ fn postgres_to_typescript_type(postgres_type: Option) -> String { Type::DATE_ARRAY => "Array", Type::TIME => "string", Type::TIME_ARRAY => "Array", - Type::TIMESTAMPTZ | Type::TIMESTAMP => "Date", - Type::TIMESTAMPTZ_ARRAY | Type::TIMESTAMP_ARRAY => "Array", + Type::TIMESTAMPTZ | Type::TIMESTAMP => "string", + Type::TIMESTAMPTZ_ARRAY | Type::TIMESTAMP_ARRAY => "Array", Type::UUID => "string", Type::UUID_ARRAY => "Array", Type::JSON | Type::JSONB | Type::JSON_ARRAY | Type::JSONB_ARRAY => "unknown", @@ -124,11 +124,13 @@ export async function main( transaction_type: "insert" | "update" | "delete", schema_name: string, table_name: string, - row: {} + row: {}, + old_row?: {} ) {{ }} "#, - struct_definition + &struct_definition, + &struct_definition ) } } diff --git a/backend/windmill-api/src/postgres_triggers/mod.rs b/backend/windmill-api/src/postgres_triggers/mod.rs index 8adb635030..d041f1f508 100644 --- a/backend/windmill-api/src/postgres_triggers/mod.rs +++ b/backend/windmill-api/src/postgres_triggers/mod.rs @@ -1,26 +1,36 @@ use crate::{ db::{ApiAuthed, DB}, jobs::{run_flow_by_path_inner, run_script_by_path_inner, RunJobQuery}, - resources::get_resource_value_interpolated_internal, + resources::try_get_resource_from_db_as, users::fetch_api_authed, }; +use chrono::Utc; +use itertools::Itertools; +use pg_escape::{quote_identifier, quote_literal}; +use rand::Rng; use serde_json::value::RawValue; +use sqlx::{ + postgres::{PgConnectOptions, PgSslMode}, + Connection, PgConnection, +}; use std::collections::HashMap; +use std::str::FromStr; use axum::{ routing::{delete, get, post}, Router, }; +pub use handler::PostgresTrigger; use handler::{ alter_publication, create_postgres_trigger, create_publication, create_slot, create_template_script, delete_postgres_trigger, delete_publication, drop_slot_name, exists_postgres_trigger, get_postgres_trigger, get_publication_info, get_template_script, is_database_in_logical_level, list_database_publication, list_postgres_triggers, - list_slot_name, set_enabled, update_postgres_trigger, Database, PostgresTrigger, + list_slot_name, set_enabled, test_postgres_connection, update_postgres_trigger, Postgres, + Relations, }; use windmill_common::{db::UserDB, error::Error, utils::StripPath}; use windmill_queue::PushArgsOwned; - mod bool; mod converter; mod handler; @@ -30,39 +40,174 @@ mod relation; mod replication_message; mod trigger; +pub use handler::PublicationData; pub use trigger::start_database; -pub async fn get_database_resource( +const ERROR_REPLICATION_SLOT_NOT_EXISTS: &str = r#"The replication slot associated with this trigger no longer exists. Recreate a new replication slot or select an existing one in the advanced tab, or delete and recreate a new trigger"#; + +const ERROR_PUBLICATION_NAME_NOT_EXISTS: &str = r#"The publication associated with this trigger no longer exists. Recreate a new publication or select an existing one in the advanced tab, or delete and recreate a new trigger"#; + +pub async fn get_database_connection( authed: ApiAuthed, user_db: Option, db: &DB, - database_resource_path: &str, + postgres_resource_path: &str, w_id: &str, -) -> Result { - let resource = get_resource_value_interpolated_internal( - &authed, - user_db, - &db, - &w_id, - &database_resource_path, - None, - "", - ) - .await - .map_err(|_| Error::NotFound("Database resource do not exist".to_string()))?; +) -> std::result::Result { + let database = + try_get_resource_from_db_as::(authed, user_db, db, postgres_resource_path, w_id) + .await?; - let resource = match resource { - Some(resource) => serde_json::from_value::(resource)?, - None => { - return { - Err(Error::NotFound( - "Database resource do not exist".to_string(), - )) + Ok(get_raw_postgres_connection(&database).await?) +} + +pub async fn get_raw_postgres_connection( + db: &Postgres, +) -> std::result::Result { + let options = { + let sslmode = if !db.sslmode.is_empty() { + PgSslMode::from_str(&db.sslmode)? + } else { + PgSslMode::Prefer + }; + let options = { + let inner_options = PgConnectOptions::new() + .host(&db.host) + .database(&db.dbname) + .ssl_mode(sslmode) + .username(&db.user); + + if let Some(port) = db.port { + inner_options.port(port) + } else { + inner_options } + }; + + let options = if !db.root_certificate_pem.is_empty() { + options.ssl_root_cert_from_pem(db.root_certificate_pem.as_bytes().to_vec()) + } else { + options + }; + + if !db.password.is_empty() { + options.password(&db.password) + } else { + options } }; - Ok(resource) + Ok(PgConnection::connect_with(&options).await?) +} + +pub fn create_logical_replication_slot_query(name: &str) -> String { + let query = format!( + r#" + SELECT + * + FROM + pg_create_logical_replication_slot({}, 'pgoutput');"#, + quote_literal(&name) + ); + + query +} + +pub fn create_publication_query( + publication_name: &str, + table_to_track: Option<&[Relations]>, + transaction_to_track: &[&str], +) -> String { + let mut query = String::from("CREATE PUBLICATION "); + + query.push_str("e_identifier(publication_name)); + + match table_to_track { + Some(database_component) if !database_component.is_empty() => { + query.push_str(" FOR"); + for (i, schema) in database_component.iter().enumerate() { + if schema.table_to_track.is_empty() { + query.push_str(" TABLES IN SCHEMA "); + query.push_str("e_identifier(&schema.schema_name)); + } else { + query.push_str(" TABLE ONLY "); + for (j, table) in schema.table_to_track.iter().enumerate() { + let table_name = quote_identifier(&table.table_name); + let schema_name = quote_identifier(&schema.schema_name); + let full_name = format!("{}.{}", &schema_name, &table_name); + query.push_str(&full_name); + if !table.columns_name.is_empty() { + query.push_str(" ("); + let columns = table + .columns_name + .iter() + .map(|column| quote_identifier(column)) + .join(", "); + query.push_str(&columns); + query.push_str(")"); + } + + if let Some(where_clause) = &table.where_clause { + query.push_str(" WHERE ("); + query.push_str(where_clause); + query.push(')'); + } + + if j + 1 != schema.table_to_track.len() { + query.push_str(", "); + } + } + } + if i < database_component.len() - 1 { + query.push_str(", "); + } + } + } + _ => { + query.push_str(" FOR ALL TABLES "); + } + }; + + if !transaction_to_track.is_empty() { + let transactions = || transaction_to_track.iter().join(", "); + query.push_str(" WITH (publish = '"); + query.push_str(&transactions()); + query.push_str("');"); + } + + query +} + +pub fn drop_publication_query(publication_name: &str) -> String { + let mut query = String::from("DROP PUBLICATION IF EXISTS "); + let quoted_publication_name = quote_identifier(publication_name); + query.push_str("ed_publication_name); + query.push_str(";"); + query +} + +pub fn drop_logical_replication_slot_query(replication_slot_name: &str) -> String { + format!( + "SELECT pg_drop_replication_slot({});", + quote_literal(&replication_slot_name) + ) +} + +pub fn generate_random_string() -> String { + let timestamp = Utc::now().timestamp_millis().to_string(); + let mut rng = rand::rng(); + let charset = "abcdefghijklmnopqrstuvwxyz0123456789"; + + let random_part = (0..10) + .map(|_| { + charset + .chars() + .nth(rng.random_range(0..charset.len())) + .unwrap() + }) + .collect::(); + + format!("{}_{}", timestamp, random_part) } fn publication_service() -> Router { @@ -86,6 +231,7 @@ fn slot_service() -> Router { pub fn workspaced_service() -> Router { Router::new() + .route("/test", post(test_postgres_connection)) .route("/create", post(create_postgres_trigger)) .route("/list", get(list_postgres_triggers)) .route("/get/*path", get(get_postgres_trigger)) diff --git a/backend/windmill-api/src/postgres_triggers/relation.rs b/backend/windmill-api/src/postgres_triggers/relation.rs index f893f0ed5b..f313efb893 100644 --- a/backend/windmill-api/src/postgres_triggers/relation.rs +++ b/backend/windmill-api/src/postgres_triggers/relation.rs @@ -47,7 +47,7 @@ impl RelationConverter { .ok_or(RelationConversionError::FailToFindMatchingTable) } - pub fn body_to_json( + pub fn row_to_json( &self, to_decode: (Oid, Vec), ) -> Result, RelationConversionError> { diff --git a/backend/windmill-api/src/postgres_triggers/trigger.rs b/backend/windmill-api/src/postgres_triggers/trigger.rs index ea0f9a5dff..746dca99eb 100644 --- a/backend/windmill-api/src/postgres_triggers/trigger.rs +++ b/backend/windmill-api/src/postgres_triggers/trigger.rs @@ -1,9 +1,9 @@ use std::{collections::HashMap, pin::Pin}; use crate::{ - db::DB, + capture::{insert_capture_payload, PostgresTriggerConfig}, + db::{ApiAuthed, DB}, postgres_triggers::{ - get_database_resource, relation::RelationConverter, replication_message::{ LogicalReplicationMessage::{Begin, Commit, Delete, Insert, Relation, Type, Update}, @@ -11,8 +11,11 @@ use crate::{ }, run_job, }, + resources::try_get_resource_from_db_as, users::fetch_api_authed, }; +use windmill_queue::TriggerKind; + use bytes::{BufMut, Bytes, BytesMut}; use chrono::TimeZone; use futures::{pin_mut, SinkExt, StreamExt}; @@ -21,13 +24,20 @@ use pg_escape::{quote_identifier, quote_literal}; use rand::seq::SliceRandom; use rust_postgres::{config::SslMode, Client, Config, CopyBothDuplex, SimpleQueryMessage}; use rust_postgres_native_tls::MakeTlsConnector; +use serde::Deserialize; +use serde_json::value::RawValue; +use sqlx::types::Json as SqlxJson; + use windmill_common::{ - db::UserDB, utils::report_critical_error, worker::to_raw_value, INSTANCE_NAME, + db::UserDB, error, utils::report_critical_error, worker::to_raw_value, INSTANCE_NAME, }; +use windmill_queue::PushArgsOwned; use super::{ - handler::{Database, PostgresTrigger}, + drop_logical_replication_slot_query, drop_publication_query, get_database_connection, + handler::{Postgres, PostgresTrigger}, replication_message::PrimaryKeepAliveBody, + ERROR_PUBLICATION_NAME_NOT_EXISTS, ERROR_REPLICATION_SLOT_NOT_EXISTS, }; pub struct LogicalReplicationSettings { @@ -72,7 +82,7 @@ enum Error { pub struct PostgresSimpleClient(Client); impl PostgresSimpleClient { - async fn new(database: &Database) -> Result { + async fn new(database: &Postgres) -> Result { let ssl_mode = match database.sslmode.as_ref() { "disable" => SslMode::Disable, "" | "prefer" | "allow" => SslMode::Prefer, @@ -90,11 +100,14 @@ impl PostgresSimpleClient { config .dbname(&database.dbname) .host(&database.host) - .port(database.port) .user(&database.user) .ssl_mode(ssl_mode) .replication_mode(rust_postgres::config::ReplicationMode::Logical); + if let Some(port) = database.port { + config.port(port); + }; + if !database.password.is_empty() { config.password(&database.password); } @@ -106,6 +119,7 @@ impl PostgresSimpleClient { let connector = MakeTlsConnector::new(TlsConnector::new()?); let (client, connection) = config.connect(connector).await?; + tokio::spawn(async move { if let Err(e) = connection.await { tracing::debug!("{:#?}", e); @@ -116,6 +130,13 @@ impl PostgresSimpleClient { Ok(PostgresSimpleClient(client)) } + async fn execute_query( + &self, + query: &str, + ) -> Result, rust_postgres::Error> { + self.0.simple_query(query).await + } + async fn get_logical_replication_stream( &self, publication_name: &str, @@ -162,75 +183,9 @@ impl PostgresSimpleClient { } } -async fn update_ping( - db: &DB, - postgres_trigger: &PostgresTrigger, - error: Option<&str>, -) -> Option<()> { - let updated = sqlx::query_scalar!( - r#" - UPDATE - postgres_trigger - SET - last_server_ping = now(), - error = $1 - WHERE - workspace_id = $2 - AND path = $3 - AND server_id = $4 - AND enabled IS TRUE - RETURNING 1 - "#, - error, - &postgres_trigger.workspace_id, - &postgres_trigger.path, - *INSTANCE_NAME - ) - .fetch_optional(db) - .await; - - match updated { - Ok(updated) => { - if updated.flatten().is_none() { - // allow faster restart of database trigger - sqlx::query!( - r#" - UPDATE - postgres_trigger - SET - last_server_ping = NULL - WHERE - workspace_id = $1 - AND path = $2 - AND server_id IS NULL"#, - &postgres_trigger.workspace_id, - &postgres_trigger.path, - ) - .execute(db) - .await - .ok(); - tracing::info!( - "Postgres trigger {} changed, disabled, or deleted, stopping...", - postgres_trigger.path - ); - return None; - } - } - Err(err) => { - tracing::warn!( - "Error updating ping of postgres trigger {}: {:?}", - postgres_trigger.path, - err - ); - } - }; - - Some(()) -} - -async fn loop_ping(db: &DB, postgres_trigger: &PostgresTrigger, error: Option<&str>) { +async fn loop_ping(db: &DB, pg: &PostgresConfig, error: Option<&str>) { loop { - if update_ping(db, postgres_trigger, error).await.is_none() { + if pg.update_ping(db, error).await.is_none() { return; } @@ -238,78 +193,396 @@ async fn loop_ping(db: &DB, postgres_trigger: &PostgresTrigger, error: Option<&s } } -async fn disable_with_error(postgres_trigger: &PostgresTrigger, db: &DB, error: String) -> () { - match sqlx::query!( - "UPDATE postgres_trigger SET enabled = FALSE, error = $1, server_id = NULL, last_server_ping = NULL WHERE workspace_id = $2 AND path = $3", - error, - postgres_trigger.workspace_id, - postgres_trigger.path, - ) - .execute(db).await { - Ok(_) => { - report_critical_error(format!("Disabling postgres trigger {} because of error: {}", postgres_trigger.path, error), db.clone(), Some(&postgres_trigger.workspace_id), None).await; - }, - Err(disable_err) => { - report_critical_error( - format!("Could not disable postgres trigger {} with err {}, disabling because of error {}", postgres_trigger.path, disable_err, error), - db.clone(), - Some(&postgres_trigger.workspace_id), - None, - ).await; +enum PostgresConfig { + Trigger(PostgresTrigger), + Capture(CaptureConfigForPostgresTrigger), +} + +impl PostgresTrigger { + async fn try_to_listen_to_database_transactions( + self, + db: DB, + killpill_rx: tokio::sync::broadcast::Receiver<()>, + ) -> () { + let postgres_trigger = sqlx::query_scalar!( + r#" + UPDATE postgres_trigger + SET + server_id = $1, + last_server_ping = now(), + error = 'Connecting...' + WHERE + enabled IS TRUE + AND workspace_id = $2 + AND path = $3 + AND (last_server_ping IS NULL + OR last_server_ping < now() - INTERVAL '15 seconds' + ) + RETURNING true + "#, + *INSTANCE_NAME, + self.workspace_id, + self.path, + ) + .fetch_optional(&db) + .await; + match postgres_trigger { + Ok(has_lock) => { + if has_lock.flatten().unwrap_or(false) { + tracing::info!("Spawning new task to listen_to_database_transaction"); + tokio::spawn(async move { + listen_to_transactions( + PostgresConfig::Trigger(self), + db.clone(), + killpill_rx, + ) + .await; + }); + } else { + tracing::info!("Postgres trigger {} already being listened to", self.path); + } + } + Err(err) => { + tracing::error!( + "Error acquiring lock for postgres trigger {}: {:?}", + self.path, + err + ); + } + }; + } + + async fn update_ping(&self, db: &DB, error: Option<&str>) -> Option<()> { + let updated = sqlx::query_scalar!( + r#" + UPDATE + postgres_trigger + SET + last_server_ping = now(), + error = $1 + WHERE + workspace_id = $2 + AND path = $3 + AND server_id = $4 + AND enabled IS TRUE + RETURNING 1 + "#, + error, + &self.workspace_id, + &self.path, + *INSTANCE_NAME + ) + .fetch_optional(db) + .await; + + match updated { + Ok(updated) => { + if updated.flatten().is_none() { + // allow faster restart of database trigger + sqlx::query!( + r#" + UPDATE + postgres_trigger + SET + last_server_ping = NULL + WHERE + workspace_id = $1 + AND path = $2 + AND server_id IS NULL"#, + &self.workspace_id, + &self.path, + ) + .execute(db) + .await + .ok(); + tracing::info!( + "Postgres trigger {} changed, disabled, or deleted, stopping...", + self.path + ); + return None; + } + } + Err(err) => { + tracing::warn!( + "Error updating ping of postgres trigger {}: {:?}", + self.path, + err + ); + } + }; + + Some(()) + } + + async fn disable_with_error(&self, db: &DB, error: String) -> () { + match sqlx::query!( + r#" + UPDATE + postgres_trigger + SET + enabled = FALSE, + error = $1, + server_id = NULL, + last_server_ping = NULL + WHERE + workspace_id = $2 AND + path = $3 + "#, + error, + self.workspace_id, + self.path, + ) + .execute(db) + .await + { + Ok(_) => { + report_critical_error( + format!( + "Disabling postgres trigger {} because of error: {}", + self.path, error + ), + db.clone(), + Some(&self.workspace_id), + None, + ) + .await; + } + Err(disable_err) => { + report_critical_error( + format!("Could not disable postgres trigger {} with err {}, disabling because of error {}", self.path, disable_err, error), + db.clone(), + Some(&self.workspace_id), + None, + ).await; + } } } + + async fn fetch_authed(&self, db: &DB) -> error::Result { + fetch_api_authed( + self.edited_by.clone(), + self.email.clone(), + &self.workspace_id, + db, + Some(format!("pg-{}", self.path)), + ) + .await + } + + async fn handle( + &self, + db: &DB, + args: Option>>, + extra: Option>>, + ) -> () { + if let Err(err) = run_job(args, extra, db, self).await { + report_critical_error( + format!( + "Failed to trigger job from postgres {}: {:?}", + self.path, err + ), + db.clone(), + Some(&self.workspace_id), + None, + ) + .await; + }; + } } -async fn listen_to_transactions( - postgres_trigger: &PostgresTrigger, - db: DB, - mut killpill_rx: tokio::sync::broadcast::Receiver<()>, -) { - let start_logical_replication_streaming = async { - let authed = fetch_api_authed( - postgres_trigger.edited_by.clone(), - postgres_trigger.email.clone(), - &postgres_trigger.workspace_id, - &db, - None, - ) - .await?; +struct PgInfo<'a> { + postgres_resource_path: &'a str, + publication_name: &'a str, + replication_slot_name: &'a str, + workspace_id: &'a str, +} - let database = get_database_resource( +impl PostgresConfig { + async fn update_ping(&self, db: &DB, error: Option<&str>) -> Option<()> { + match self { + PostgresConfig::Trigger(trigger) => trigger.update_ping(db, error).await, + PostgresConfig::Capture(capture) => capture.update_ping(db, error).await, + } + } + + async fn disable_with_error(&self, db: &DB, error: String) -> () { + match self { + PostgresConfig::Trigger(trigger) => trigger.disable_with_error(&db, error).await, + PostgresConfig::Capture(capture) => capture.disable_with_error(db, error).await, + } + } + + fn retrieve_info(&self) -> PgInfo { + let postgres_resource_path; + let publication_name; + let replication_slot_name; + let workspace_id; + + match self { + PostgresConfig::Trigger(trigger) => { + postgres_resource_path = &trigger.postgres_resource_path; + publication_name = &trigger.publication_name; + replication_slot_name = &trigger.replication_slot_name; + workspace_id = &trigger.workspace_id; + } + PostgresConfig::Capture(capture) => { + postgres_resource_path = &capture.trigger_config.postgres_resource_path; + workspace_id = &capture.workspace_id; + publication_name = capture.trigger_config.publication_name.as_ref().unwrap(); + replication_slot_name = capture + .trigger_config + .replication_slot_name + .as_ref() + .unwrap(); + } + }; + + PgInfo { postgres_resource_path, replication_slot_name, workspace_id, publication_name } + } + + async fn start_logical_replication_streaming( + &self, + db: &DB, + ) -> std::result::Result<(CopyBothDuplex, LogicalReplicationSettings), Error> { + let PgInfo { + publication_name, + replication_slot_name, + workspace_id, + postgres_resource_path, + } = self.retrieve_info(); + + let authed = match self { + PostgresConfig::Trigger(trigger) => trigger.fetch_authed(db).await?, + PostgresConfig::Capture(capture) => capture.fetch_authed(db).await?, + }; + + let database = try_get_resource_from_db_as::( authed, Some(UserDB::new(db.clone())), &db, - &postgres_trigger.postgres_resource_path, - &postgres_trigger.workspace_id, + postgres_resource_path, + workspace_id, ) .await?; let client = PostgresSimpleClient::new(&database).await?; - let (logical_replication_stream, logical_replication_settings) = client - .get_logical_replication_stream( - &postgres_trigger.publication_name, - &postgres_trigger.replication_slot_name, - ) + + let publication = client + .execute_query(&format!( + "SELECT pubname FROM pg_publication WHERE pubname = {}", + quote_literal(&publication_name) + )) .await?; - Ok::<_, Error>((logical_replication_stream, logical_replication_settings)) - }; + if !publication.row_exist() { + return Err(Error::Common(error::Error::BadConfig( + ERROR_PUBLICATION_NAME_NOT_EXISTS.to_string(), + ))); + } + + let replication_slot = client + .execute_query(&format!( + "SELECT slot_name FROM pg_replication_slots WHERE slot_name = {}", + quote_literal(&replication_slot_name) + )) + .await?; + + if !replication_slot.row_exist() { + return Err(Error::Common(error::Error::BadConfig( + ERROR_REPLICATION_SLOT_NOT_EXISTS.to_string(), + ))); + } + + let (logical_replication_stream, logical_replication_settings) = client + .get_logical_replication_stream(&publication_name, &replication_slot_name) + .await?; + + Ok((logical_replication_stream, logical_replication_settings)) + } + + fn get_path(&self) -> &str { + match self { + PostgresConfig::Trigger(trigger) => &trigger.path, + PostgresConfig::Capture(capture) => &capture.path, + } + } + + async fn handle( + &self, + db: &DB, + args: Option>>, + extra: Option>>, + ) -> () { + match self { + PostgresConfig::Trigger(trigger) => trigger.handle(&db, args, extra).await, + PostgresConfig::Capture(capture) => capture.handle(&db, args, extra).await, + } + } + + async fn cleanup(&self, db: &DB) -> Result<(), Error> { + match self { + PostgresConfig::Trigger(_) => Ok(()), + PostgresConfig::Capture(capture) => { + let publication_name = capture.trigger_config.publication_name.as_ref().unwrap(); + let replication_slot_name = capture + .trigger_config + .replication_slot_name + .as_ref() + .unwrap(); + let postgres_resource_path = &capture.trigger_config.postgres_resource_path; + let workspace_id = &capture.workspace_id; + let authed = capture.fetch_authed(&db).await?; + + let user_db = UserDB::new(db.clone()); + + let mut connection = get_database_connection( + authed.clone(), + Some(user_db.clone()), + &db, + postgres_resource_path, + workspace_id, + ) + .await?; + + let query = drop_logical_replication_slot_query(replication_slot_name); + + let _ = sqlx::query(&query).execute(&mut connection).await; + + let query = drop_publication_query(publication_name); + + let _ = sqlx::query(&query).execute(&mut connection).await; + + Ok(()) + } + } + } +} + +async fn listen_to_transactions( + pg: PostgresConfig, + db: DB, + mut killpill_rx: tokio::sync::broadcast::Receiver<()>, +) { tokio::select! { biased; _ = killpill_rx.recv() => { + let _ = pg.cleanup(&db).await; return; } - _ = loop_ping(&db, postgres_trigger, Some("Connecting...")) => { + _ = loop_ping(&db, &pg, Some("Connecting...")) => { + let _ = pg.cleanup(&db).await; return; } - result = start_logical_replication_streaming => { + result = pg.start_logical_replication_streaming(&db) => { tokio::select! { biased; _ = killpill_rx.recv() => { + let _ = pg.cleanup(&db).await; return; } - _ = loop_ping(&db, postgres_trigger, None) => { + _ = loop_ping(&db, &pg, None) => { + let _ = pg.cleanup(&db).await; return; } _ = { @@ -318,26 +591,27 @@ async fn listen_to_transactions( Ok((logical_replication_stream, logical_replication_settings)) => { pin_mut!(logical_replication_stream); let mut relations = RelationConverter::new(); - tracing::info!("Starting to listen for postgres trigger {}", postgres_trigger.path); + tracing::info!("Starting to listen for postgres trigger {}", pg.get_path()); loop { let message = logical_replication_stream.next().await; let message = match message { Some(message) => message, None => { - tracing::error!("Stream for postgres trigger {} closed", postgres_trigger.path); - if let None = update_ping(&db, postgres_trigger, Some("Stream closed")).await { + tracing::error!("Stream for postgres trigger {} closed", pg.get_path()); + if let None = pg.update_ping(&db, Some("Stream closed")).await { return; } return; } }; + let message = match message { Ok(message) => message, Err(err) => { - let err = format!("Postgres trigger named {} had an error while receiving a message : {}", &postgres_trigger.path, err.to_string()); - disable_with_error(&postgres_trigger, &db, err).await; + let err = format!("Postgres trigger named {} had an error while receiving a message : {}", pg.get_path(), err.to_string()); + pg.disable_with_error(&db, err).await; return; } }; @@ -345,8 +619,8 @@ async fn listen_to_transactions( let logical_message = match ReplicationMessage::parse(message) { Ok(logical_message) => logical_message, Err(err) => { - let err = format!("Postgres trigger named: {} had an error while parsing message: {}", postgres_trigger.path, err.to_string()); - disable_with_error(&postgres_trigger, &db, err).await; + let err = format!("Postgres trigger named: {} had an error while parsing message: {}", pg.get_path(), err.to_string()); + pg.disable_with_error(&db, err).await; return; } }; @@ -362,7 +636,7 @@ async fn listen_to_transactions( let logical_replication_message = match x_log_data.parse(&logical_replication_settings) { Ok(logical_replication_message) => logical_replication_message, Err(err) => { - tracing::error!("Postgres trigger named: {} had an error while trying to parse incomming stream message: {}", &postgres_trigger.path, err.to_string()); + tracing::error!("Postgres trigger named: {} had an error while trying to parse incomming stream message: {}", pg.get_path(), err.to_string()); continue; } }; @@ -376,35 +650,79 @@ async fn listen_to_transactions( None } Insert(insert) => { - Some((insert.o_id, relations.body_to_json((insert.o_id, insert.tuple)), "insert")) + Some((insert.o_id, Ok(None), relations.row_to_json((insert.o_id, insert.tuple)), "insert")) } Update(update) => { - Some((update.o_id, relations.body_to_json((update.o_id, update.new_tuple)), "update")) + let old_row = update.old_tuple.map(|old_tuple| relations.row_to_json((update.o_id, old_tuple))).transpose(); + let row = relations.row_to_json((update.o_id, update.new_tuple)); + Some((update.o_id, old_row, row, "update")) } Delete(delete) => { - let body = delete.old_tuple.unwrap_or_else(|| delete.key_tuple.unwrap()); - Some((delete.o_id, relations.body_to_json((delete.o_id, body)), "delete")) + let row = delete.old_tuple.unwrap_or_else(|| delete.key_tuple.unwrap()); + Some((delete.o_id, Ok(None), relations.row_to_json((delete.o_id, row)), "delete")) } }; - if let Some((o_id, Ok(body), transaction_type)) = json { - let relation = match relations.get_relation(o_id) { - Ok(relation) => relation, - Err(err) => { - tracing::error!("Postgres trigger named: {}, error: {}", &postgres_trigger.path, err.to_string()); - continue; + match json { + Some((o_id, Ok(old_row), Ok(row), transaction_type)) => { + let relation = match relations.get_relation(o_id) { + Ok(relation) => relation, + Err(err) => { + tracing::error!("Postgres trigger named: {}, error: {}", pg.get_path(), err.to_string()); + continue; + } + }; + let database_info = HashMap::from([ + ("schema_name".to_string(), to_raw_value(&relation.namespace)), + ("table_name".to_string(), to_raw_value(&relation.name)), + ("transaction_type".to_string(), to_raw_value(&transaction_type)), + ("old_row".to_string(), to_raw_value(&old_row)), + ("row".to_string(), to_raw_value(&row)), + ]); + let extra = Some(HashMap::from([( + "wm_trigger".to_string(), + to_raw_value(&serde_json::json!({"kind": "postgres", })), + )])); + + + let _ = pg.handle(&db, Some(database_info), extra).await; + } + Some((o_id, old_row, row, transaction_type)) => { + let relation = match relations.get_relation(o_id) { + Ok(relation) => relation, + Err(err) => { + tracing::error!("Postgres trigger named: {}, error: {}", pg.get_path(), err.to_string()); + continue; + } + }; + + if let Err(err) = old_row { + tracing::error!( + transaction_type = ?transaction_type, + schema = %relation.namespace, + table = %relation.name, + error = %err, + "Failed to decode OLD row for {} transaction on {}.{}", + transaction_type, + relation.namespace, + relation.name, + ); } - }; - let database_info = HashMap::from([ - ("schema_name".to_string(), to_raw_value(&relation.namespace)), - ("table_name".to_string(), to_raw_value(&relation.name)), - ("transaction_type".to_string(), to_raw_value(&transaction_type)), - ("row".to_string(), to_raw_value(&body)), - ]); - let extra = Some(HashMap::from([( - "wm_trigger".to_string(), - to_raw_value(&serde_json::json!({"kind": "postgres", })), - )])); - let _ = run_job(Some(database_info), extra, &db, postgres_trigger).await; + + if let Err(err) = row { + tracing::error!( + transaction_type = ?transaction_type, + schema = %relation.namespace, + table = %relation.name, + error = %err, + "Failed to decode NEW row for {} transaction on {}.{}", + transaction_type, + relation.namespace, + relation.name, + ); + } + + } + _ => {} } } @@ -413,11 +731,12 @@ async fn listen_to_transactions( } Err(err) => { tracing::error!("Postgres trigger error while trying to start logical replication streaming: {}", &err); - disable_with_error(&postgres_trigger, &db, err.to_string()).await + pg.disable_with_error(&db, err.to_string()).await } } } } => { + let _ = pg.cleanup(&db).await; return; } } @@ -425,55 +744,204 @@ async fn listen_to_transactions( } } -async fn try_to_listen_to_database_transactions( - pg_trigger: PostgresTrigger, - db: DB, - killpill_rx: tokio::sync::broadcast::Receiver<()>, -) { - let postgres_trigger = sqlx::query_scalar!( - r#" - UPDATE postgres_trigger - SET - server_id = $1, - last_server_ping = now(), - error = 'Connecting...' - WHERE - enabled IS TRUE - AND workspace_id = $2 - AND path = $3 - AND (last_server_ping IS NULL - OR last_server_ping < now() - INTERVAL '15 seconds' - ) - RETURNING true - "#, - *INSTANCE_NAME, - pg_trigger.workspace_id, - pg_trigger.path, - ) - .fetch_optional(&db) - .await; - match postgres_trigger { - Ok(has_lock) => { - if has_lock.flatten().unwrap_or(false) { - tracing::info!("Spawning new task to listen_to_database_transaction"); - tokio::spawn(async move { - listen_to_transactions(&pg_trigger, db.clone(), killpill_rx).await; - }); - } else { - tracing::info!( - "Postgres trigger {} already being listened to", - pg_trigger.path +#[derive(Deserialize)] +struct CaptureConfigForPostgresTrigger { + trigger_config: SqlxJson, + path: String, + is_flow: bool, + workspace_id: String, + owner: String, + email: String, +} + +impl CaptureConfigForPostgresTrigger { + async fn try_to_listen_to_database_transactions( + self, + db: DB, + killpill_rx: tokio::sync::broadcast::Receiver<()>, + ) -> () { + match sqlx::query_scalar!( + r#" + UPDATE + capture_config + SET + server_id = $1, + last_server_ping = now(), + error = 'Connecting...' + WHERE + last_client_ping > NOW() - INTERVAL '10 seconds' AND + workspace_id = $2 AND + path = $3 AND + is_flow = $4 AND + trigger_kind = 'postgres' AND + (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') + RETURNING true + "#, + *INSTANCE_NAME, + self.workspace_id, + self.path, + self.is_flow, + ) + .fetch_optional(&db) + .await + { + Ok(has_lock) => { + if has_lock.flatten().unwrap_or(false) { + tokio::spawn(listen_to_transactions( + PostgresConfig::Capture(self), + db, + killpill_rx, + )); + } else { + tracing::info!("Postgres {} already being listened to", self.path); + } + } + Err(err) => { + tracing::error!( + "Error acquiring lock for capture postgres {}: {:?}", + self.path, + err ); } + }; + } + + async fn update_ping(&self, db: &DB, error: Option<&str>) -> Option<()> { + match sqlx::query_scalar!( + r#" + UPDATE + capture_config + SET + last_server_ping = now(), + error = $1 + WHERE + workspace_id = $2 AND + path = $3 AND + is_flow = $4 AND + trigger_kind = 'postgres' AND + server_id = $5 AND + last_client_ping > NOW() - INTERVAL '10 seconds' + RETURNING 1 + "#, + error, + self.workspace_id, + self.path, + self.is_flow, + *INSTANCE_NAME + ) + .fetch_optional(db) + .await + { + Ok(updated) => { + if updated.flatten().is_none() { + // allow faster restart of postgres capture + sqlx::query!( + r#"UPDATE + capture_config + SET + last_server_ping = NULL + WHERE + workspace_id = $1 AND + path = $2 AND + is_flow = $3 AND + trigger_kind = 'postgres' AND + server_id IS NULL + "#, + self.workspace_id, + self.path, + self.is_flow, + ) + .execute(db) + .await + .ok(); + tracing::info!( + "Postgres capture {} changed, disabled, or deleted, stopping...", + self.path + ); + return None; + } + } + Err(err) => { + tracing::warn!( + "Error updating ping of capture postgres {}: {:?}", + self.path, + err + ); + } + }; + + Some(()) + } + + async fn fetch_authed(&self, db: &DB) -> error::Result { + fetch_api_authed( + self.owner.clone(), + self.email.clone(), + &self.workspace_id, + db, + Some(format!("postgres-{}", self.get_trigger_path())), + ) + .await + } + + fn get_trigger_path(&self) -> String { + format!( + "{}-{}", + if self.is_flow { "flow" } else { "script" }, + self.path + ) + } + + async fn disable_with_error(&self, db: &DB, error: String) -> () { + if let Err(err) = sqlx::query!( + r#" + UPDATE + capture_config + SET + error = $1, + server_id = NULL, + last_server_ping = NULL + WHERE + workspace_id = $2 AND + path = $3 AND + is_flow = $4 AND + trigger_kind = 'postgres' + "#, + error, + self.workspace_id, + self.path, + self.is_flow, + ) + .execute(db) + .await + { + tracing::error!("Could not disable postgres capture {} ({}) with err {}, disabling because of error {}", self.path, self.workspace_id, err, error); } - Err(err) => { - tracing::error!( - "Error acquiring lock for postgres trigger {}: {:?}", - pg_trigger.path, - err - ); + } + + async fn handle( + &self, + db: &DB, + args: Option>>, + extra: Option>>, + ) -> () { + let args = PushArgsOwned { args: args.unwrap_or_default(), extra: None }; + let extra = extra.as_ref().map(to_raw_value); + if let Err(err) = insert_capture_payload( + db, + &self.workspace_id, + &self.path, + self.is_flow, + &TriggerKind::Postgres, + args, + extra, + &self.owner, + ) + .await + { + tracing::error!("Error inserting capture payload: {:?}", err); } - }; + } } async fn listen_to_unlistened_database_events( @@ -515,18 +983,51 @@ async fn listen_to_unlistened_database_events( Ok(mut triggers) => { triggers.shuffle(&mut rand::rng()); for trigger in triggers { - try_to_listen_to_database_transactions( - trigger, - db.clone(), - killpill_rx.resubscribe(), - ) - .await; + trigger + .try_to_listen_to_database_transactions(db.clone(), killpill_rx.resubscribe()) + .await; } } Err(err) => { tracing::error!("Error fetching postgres triggers: {:?}", err); } }; + + let postgres_triggers_capture = sqlx::query_as!( + CaptureConfigForPostgresTrigger, + r#" + SELECT + path, + is_flow, + workspace_id, + owner, + email, + trigger_config as "trigger_config!: _" + FROM + capture_config + WHERE + trigger_kind = 'postgres' AND + last_client_ping > NOW() - INTERVAL '10 seconds' AND + trigger_config IS NOT NULL AND + (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') + "# + ) + .fetch_all(db) + .await; + + match postgres_triggers_capture { + Ok(mut captures) => { + captures.shuffle(&mut rand::rng()); + for capture in captures { + capture + .try_to_listen_to_database_transactions(db.clone(), killpill_rx.resubscribe()) + .await; + } + } + Err(err) => { + tracing::error!("Error fetching captures postgres triggers: {:?}", err); + } + }; } pub fn start_database(db: DB, mut killpill_rx: tokio::sync::broadcast::Receiver<()>) { diff --git a/backend/windmill-api/src/resources.rs b/backend/windmill-api/src/resources.rs index f3f2285cd0..8a2fab0434 100644 --- a/backend/windmill-api/src/resources.rs +++ b/backend/windmill-api/src/resources.rs @@ -515,7 +515,9 @@ pub async fn transform_json_value<'c>( Value::String(y) if y.starts_with("$res:") => { let path = y.strip_prefix("$res:").unwrap(); if path.split("/").count() < 2 { - return Err(Error::internal_err(format!("Invalid resource path: {path}"))); + return Err(Error::internal_err(format!( + "Invalid resource path: {path}" + ))); } let mut tx: Transaction<'_, Postgres> = authed_transaction_or_default(authed, user_db.clone(), db).await?; @@ -568,7 +570,7 @@ pub async fn transform_json_value<'c>( }; let variables = variables::get_reserved_variables( - db, + &db.into(), workspace, token, &job.email, @@ -597,11 +599,10 @@ pub async fn transform_json_value<'c>( } Value::Object(mut m) => { for (a, b) in m.clone().into_iter() { - m.insert( - a.clone(), + let v = transform_json_value(authed, user_db.clone(), db, workspace, b, job_id, token) - .await?, - ); + .await?; + m.insert(a.clone(), v); } Ok(Value::Object(m)) } @@ -1205,3 +1206,49 @@ async fn update_resource_type( Ok(format!("resource_type {} updated", name)) } + +#[cfg(any( + feature = "http_trigger", + feature = "postgres_trigger", + feature = "mqtt_trigger", + all( + feature = "enterprise", + any(feature = "sqs_trigger", feature = "gcp_trigger") + ) +))] +pub async fn try_get_resource_from_db_as( + authed: ApiAuthed, + user_db: Option, + db: &DB, + resource_path: &str, + w_id: &str, +) -> Result +where + T: serde::de::DeserializeOwned, +{ + let resource = get_resource_value_interpolated_internal( + &authed, + user_db, + &db, + &w_id, + &resource_path, + None, + "", + ) + .await?; + + let resource = match resource { + Some(resource) => serde_json::from_value::(resource) + .map_err(|e| Error::SerdeJson { error: e, location: "resources.rs".to_string() })?, + None => { + return { + Err(Error::NotFound(format!( + "resource at path :{} do not exist", + &resource_path + ))) + } + } + }; + + Ok(resource) +} diff --git a/backend/windmill-api/src/schedule.rs b/backend/windmill-api/src/schedule.rs index 109957da04..90d91c846c 100644 --- a/backend/windmill-api/src/schedule.rs +++ b/backend/windmill-api/src/schedule.rs @@ -29,6 +29,7 @@ use windmill_common::{ error::{Error, JsonResult, Result}, schedule::Schedule, utils::{not_found_if_none, paginate, Pagination, ScheduleType, StripPath}, + worker::to_raw_value, }; use windmill_git_sync::{handle_deployment_metadata, DeployedObject}; use windmill_queue::schedule::push_scheduled_job; @@ -57,6 +58,7 @@ pub struct NewSchedule { pub schedule: String, pub timezone: String, pub summary: Option, + pub description: Option, pub no_flow_overlap: Option, pub script_path: String, pub is_flow: bool, @@ -120,6 +122,12 @@ async fn check_path_conflict<'c>( return Ok(()); } +fn to_json_raw_opt( + value: Option<&serde_json::Value>, +) -> Option>> { + value.map(|v| sqlx::types::Json(to_raw_value(&v))) +} + async fn create_schedule( authed: ApiAuthed, Extension(db): Extension, @@ -154,46 +162,95 @@ async fn create_schedule( let mut tx: Transaction<'_, Postgres> = user_db.begin(&authed).await?; // Check schedule for error - ScheduleType::from_str(&ns.schedule, ns.cron_version.as_deref())?; + ScheduleType::from_str(&ns.schedule, ns.cron_version.as_deref(), true)?; check_path_conflict(&mut tx, &w_id, &ns.path).await?; check_flow_conflict(&mut tx, &w_id, &ns.path, ns.is_flow, &ns.script_path).await?; - let schedule = sqlx::query_as::<_, Schedule>( - "INSERT INTO schedule (workspace_id, path, schedule, timezone, edited_by, script_path, \ - is_flow, args, enabled, email, on_failure, on_failure_times, on_failure_exact, \ - on_failure_extra_args, on_recovery, on_recovery_times, on_recovery_extra_args, \ - on_success, on_success_extra_args, \ - ws_error_handler_muted, retry, summary, no_flow_overlap, tag, paused_until, cron_version \ - ) VALUES ( \ - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26 \ - ) RETURNING *") - .bind(&w_id) - .bind(&ns.path) - .bind(&ns.schedule) - .bind(&ns.timezone) - .bind(&authed.username) - .bind(&ns.script_path) - .bind(&ns.is_flow) - .bind(&ns.args) - .bind(&ns.enabled.unwrap_or(false)) - .bind(&authed.email) - .bind(&ns.on_failure) - .bind(&ns.on_failure_times) - .bind(&ns.on_failure_exact) - .bind(&ns.on_failure_extra_args) - .bind(&ns.on_recovery) - .bind(&ns.on_recovery_times) - .bind(&ns.on_recovery_extra_args) - .bind(&ns.on_success) - .bind(&ns.on_success_extra_args) - .bind(&ns.ws_error_handler_muted.unwrap_or(false)) - .bind(&ns.retry) - .bind(&ns.summary) - .bind(&ns.no_flow_overlap.unwrap_or(false)) - .bind(&ns.tag) - .bind(&ns.paused_until) - .bind(&ns.cron_version.unwrap_or("v2".to_string())) + let schedule = sqlx::query_as!( + Schedule, + r#" + INSERT INTO schedule ( + workspace_id, path, schedule, timezone, edited_by, script_path, + is_flow, args, enabled, email, + on_failure, on_failure_times, on_failure_exact, on_failure_extra_args, + on_recovery, on_recovery_times, on_recovery_extra_args, + on_success, on_success_extra_args, + ws_error_handler_muted, retry, summary, no_flow_overlap, + tag, paused_until, cron_version, description + ) VALUES ( + $1, $2, $3, $4, $5, $6, + $7, $8, $9, $10, + $11, $12, $13, $14, + $15, $16, $17, + $18, $19, + $20, $21, $22, $23, + $24, $25, $26, $27 + ) + RETURNING + workspace_id, + path, + edited_by, + edited_at, + schedule, + timezone, + enabled, + script_path, + is_flow, + args AS "args: _", + extra_perms, + email, + error, + on_failure, + on_failure_times, + on_failure_exact, + on_failure_extra_args AS "on_failure_extra_args: _", + on_recovery, + on_recovery_times, + on_recovery_extra_args AS "on_recovery_extra_args: _", + on_success, + on_success_extra_args AS "on_success_extra_args: _", + ws_error_handler_muted, + retry, + no_flow_overlap, + summary, + description, + tag, + paused_until, + cron_version + "#, + w_id, + ns.path, + ns.schedule, + ns.timezone, + authed.username, + ns.script_path, + ns.is_flow, + to_json_raw_opt(ns.args.as_ref()) + as Option>>, + ns.enabled.unwrap_or(false), + authed.email, + ns.on_failure, + ns.on_failure_times, + ns.on_failure_exact, + to_json_raw_opt(ns.on_failure_extra_args.as_ref()) + as Option>>, + ns.on_recovery, + ns.on_recovery_times, + to_json_raw_opt(ns.on_recovery_extra_args.as_ref()) + as Option>>, + ns.on_success, + to_json_raw_opt(ns.on_success_extra_args.as_ref()) + as Option>>, + ns.ws_error_handler_muted.unwrap_or(false), + ns.retry, + ns.summary, + ns.no_flow_overlap.unwrap_or(false), + ns.tag, + ns.paused_until, + ns.cron_version.clone().unwrap_or_else(|| "v2".to_string()), + ns.description + ) .fetch_one(&mut *tx) .await .map_err(|e| Error::internal_err(format!("inserting schedule in {w_id}: {e:#}")))?; @@ -249,37 +306,95 @@ async fn edit_schedule( let mut tx = user_db.begin(&authed).await?; // Check schedule for error - ScheduleType::from_str(&es.schedule, es.cron_version.as_deref())?; + ScheduleType::from_str(&es.schedule, es.cron_version.as_deref(), true)?; clear_schedule(&mut tx, path, &w_id).await?; - let schedule = sqlx::query_as::<_, Schedule>( - "UPDATE schedule SET schedule = $1, timezone = $2, args = $3, on_failure = $4, on_failure_times = $5, \ - on_failure_exact = $6, on_failure_extra_args = $7, on_recovery = $8, on_recovery_times = $9, \ - on_recovery_extra_args = $10, on_success = $11, on_success_extra_args = $12, \ - ws_error_handler_muted = $13, retry = $14, summary = $15, \ - no_flow_overlap = $16, tag = $17, paused_until = $18, cron_version = COALESCE($21, cron_version) \ - WHERE path = $19 AND workspace_id = $20 RETURNING *") - .bind(&es.schedule) - .bind(&es.timezone) - .bind(&es.args) - .bind(&es.on_failure) - .bind(&es.on_failure_times) - .bind(&es.on_failure_exact) - .bind(&es.on_failure_extra_args) - .bind(&es.on_recovery) - .bind(&es.on_recovery_times) - .bind(&es.on_recovery_extra_args) - .bind(&es.on_success) - .bind(&es.on_success_extra_args) - .bind(&es.ws_error_handler_muted.unwrap_or(false)) - .bind(&es.retry) - .bind(&es.summary) - .bind(&es.no_flow_overlap.unwrap_or(false)) - .bind(&es.tag) - .bind(&es.paused_until) - .bind(&path) - .bind(&w_id) - .bind(&es.cron_version) + let schedule = sqlx::query_as!( + Schedule, + r#" + UPDATE schedule SET + schedule = $1, + timezone = $2, + args = $3, + on_failure = $4, + on_failure_times = $5, + on_failure_exact = $6, + on_failure_extra_args = $7, + on_recovery = $8, + on_recovery_times = $9, + on_recovery_extra_args = $10, + on_success = $11, + on_success_extra_args = $12, + ws_error_handler_muted = $13, + retry = $14, + summary = $15, + no_flow_overlap = $16, + tag = $17, + paused_until = $18, + path = $19, + workspace_id = $20, + cron_version = COALESCE($21, cron_version), + description = $22 + WHERE path = $19 AND workspace_id = $20 + RETURNING + workspace_id, + path, + edited_by, + edited_at, + schedule, + timezone, + enabled, + script_path, + is_flow, + args AS "args: _", + extra_perms, + email, + error, + on_failure, + on_failure_times, + on_failure_exact, + on_failure_extra_args AS "on_failure_extra_args: _", + on_recovery, + on_recovery_times, + on_recovery_extra_args AS "on_recovery_extra_args: _", + on_success, + on_success_extra_args AS "on_success_extra_args: _", + ws_error_handler_muted, + retry, + no_flow_overlap, + summary, + description, + tag, + paused_until, + cron_version + "#, + es.schedule, + es.timezone, + to_json_raw_opt(es.args.as_ref()) + as Option>>, + es.on_failure, + es.on_failure_times, + es.on_failure_exact, + to_json_raw_opt(es.on_failure_extra_args.as_ref()) + as Option>>, + es.on_recovery, + es.on_recovery_times, + to_json_raw_opt(es.on_recovery_extra_args.as_ref()) + as Option>>, + es.on_success, + to_json_raw_opt(es.on_success_extra_args.as_ref()) + as Option>>, + es.ws_error_handler_muted.unwrap_or(false), + es.retry, + es.summary, + es.no_flow_overlap.unwrap_or(false), + es.tag, + es.paused_until, + path, + w_id, + es.cron_version, + es.description + ) .fetch_one(&mut *tx) .await .map_err(|e| Error::internal_err(format!("updating schedule in {w_id}: {e:#}")))?; @@ -329,16 +444,42 @@ pub struct ListScheduleQuery { pub path_start: Option, } +#[derive(sqlx::FromRow, Serialize, Deserialize, Debug, Clone)] +pub struct ScheduleLight { + pub workspace_id: String, + pub path: String, + pub edited_by: String, + pub edited_at: DateTime, + pub schedule: String, + pub timezone: String, + pub enabled: bool, + pub script_path: String, + pub is_flow: bool, + pub summary: Option, + pub extra_perms: serde_json::Value, +} async fn list_schedule( authed: ApiAuthed, Extension(user_db): Extension, Path(w_id): Path, Query(lsq): Query, -) -> JsonResult> { +) -> JsonResult> { let mut tx = user_db.begin(&authed).await?; let (per_page, offset) = paginate(Pagination { per_page: lsq.per_page, page: lsq.page }); let mut sqlb = SqlBuilder::select_from("schedule") - .field("*") + .fields(&[ + "workspace_id", + "path", + "edited_by", + "edited_at", + "schedule", + "timezone", + "enabled", + "script_path", + "is_flow", + "summary", + "extra_perms", + ]) .order_by("edited_at", true) .and_where("workspace_id = ?".bind(&w_id)) .offset(offset) @@ -357,7 +498,7 @@ async fn list_schedule( sqlb.and_where_like_left("path", path_start); } let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; - let rows = sqlx::query_as::<_, Schedule>(&sql) + let rows = sqlx::query_as::<_, ScheduleLight>(&sql) .fetch_all(&mut *tx) .await?; tx.commit().await?; @@ -366,36 +507,8 @@ async fn list_schedule( #[derive(Serialize, Deserialize, Debug)] pub struct ScheduleWJobs { - pub workspace_id: String, pub path: String, - pub edited_by: String, - pub edited_at: DateTime, - pub schedule: String, - pub timezone: String, - pub enabled: bool, - pub script_path: String, - pub is_flow: bool, - pub args: Option, - pub extra_perms: serde_json::Value, - pub email: String, - pub error: Option, - pub on_failure: Option, - pub on_failure_times: Option, - pub on_failure_exact: Option, - pub on_failure_extra_args: Option, - pub on_recovery: Option, - pub on_recovery_times: Option, - pub on_recovery_extra_args: Option, - pub on_success: Option, - pub on_success_extra_args: Option, - pub ws_error_handler_muted: bool, - pub retry: Option, pub jobs: Option>, - pub summary: Option, - pub no_flow_overlap: bool, - pub tag: Option, - pub paused_until: Option>, - pub cron_version: Option, } async fn list_schedule_with_jobs( @@ -407,9 +520,27 @@ async fn list_schedule_with_jobs( let mut tx = user_db.begin(&authed).await?; let (per_page, offset) = paginate(pagination); let rows = sqlx::query_as!(ScheduleWJobs, - "SELECT schedule.*, t.jobs FROM schedule, LATERAL ( SELECT ARRAY (SELECT json_build_object('id', id, 'success', success, 'duration_ms', duration_ms) FROM v2_as_completed_job WHERE - v2_as_completed_job.schedule_path = schedule.path AND v2_as_completed_job.workspace_id = $1 AND parent_job IS NULL AND is_skipped = False ORDER BY started_at DESC LIMIT 20) AS jobs ) t - WHERE schedule.workspace_id = $1 ORDER BY schedule.edited_at desc LIMIT $2 OFFSET $3", + // Query plan: + // - use of the `ix_completed_job_workspace_id_started_at_new_2` index first, then; + // - use of the `ix_v2_job_root_by_path` index; hence the `parent_job IS NULL` clause. + // - both `workspace_id = $1` checks are required to hit both indexes. + "SELECT + schedule.path, t.jobs FROM schedule, + LATERAL(SELECT ARRAY( + SELECT json_build_object('id', id, 'success', status = 'success', 'duration_ms', duration_ms) + FROM v2_job_completed c JOIN v2_job j USING (id) + WHERE trigger_kind = 'schedule' + AND trigger = schedule.path + AND c.workspace_id = $1 + AND j.workspace_id = $1 + AND parent_job IS NULL AND runnable_path = schedule.script_path + AND status <> 'skipped' + ORDER BY created_at DESC + LIMIT 20 + ) AS jobs) t + WHERE workspace_id = $1 + ORDER BY edited_at DESC + LIMIT $2 OFFSET $3", w_id, per_page as i64, offset as i64 @@ -464,7 +595,8 @@ pub struct PreviewPayload { pub async fn preview_schedule( Json(payload): Json, ) -> JsonResult>> { - let schedule = ScheduleType::from_str(&payload.schedule, payload.cron_version.as_deref())?; + let schedule = + ScheduleType::from_str(&payload.schedule, payload.cron_version.as_deref(), true)?; let tz = chrono_tz::Tz::from_str(&payload.timezone).map_err(|e| Error::BadRequest(e.to_string()))?; @@ -483,12 +615,50 @@ pub async fn set_enabled( ) -> Result { let mut tx = user_db.begin(&authed).await?; let path = path.to_path(); - let schedule_o = sqlx::query_as::<_, Schedule>( - "UPDATE schedule SET enabled = $1, email = $2 WHERE path = $3 AND workspace_id = $4 RETURNING *") - .bind(&payload.enabled) - .bind(&authed.email) - .bind(&path) - .bind(&w_id) + let schedule_o = sqlx::query_as!( + Schedule, + r#" + UPDATE schedule SET + enabled = $1, + email = $2 + WHERE path = $3 AND workspace_id = $4 + RETURNING + workspace_id, + path, + edited_by, + edited_at, + schedule, + timezone, + enabled, + script_path, + is_flow, + args AS "args: _", + extra_perms, + email, + error, + on_failure, + on_failure_times, + on_failure_exact, + on_failure_extra_args AS "on_failure_extra_args: _", + on_recovery, + on_recovery_times, + on_recovery_extra_args AS "on_recovery_extra_args: _", + on_success, + on_success_extra_args AS "on_success_extra_args: _", + ws_error_handler_muted, + retry, + no_flow_overlap, + summary, + description, + tag, + paused_until, + cron_version + "#, + payload.enabled, + authed.email, + path, + w_id + ) .fetch_optional(&mut *tx) .await?; @@ -814,6 +984,7 @@ pub struct EditSchedule { pub timezone: String, pub args: Option, pub summary: Option, + pub description: Option, pub on_failure: Option, pub on_failure_times: Option, pub on_failure_exact: Option, diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index cbeb13e106..080c07cc91 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -18,7 +18,6 @@ use crate::{ webhook_util::{WebhookMessage, WebhookShared}, HTTP_CLIENT, }; -#[cfg(all(feature = "enterprise", feature = "parquet"))] use axum::extract::Multipart; use axum::{ @@ -41,8 +40,8 @@ use std::{ }; use windmill_audit::audit_ee::audit_log; use windmill_audit::ActionKind; +use windmill_worker::process_relative_imports; -#[cfg(all(feature = "enterprise", feature = "parquet"))] use windmill_common::error::to_anyhow; use windmill_common::{ @@ -50,6 +49,7 @@ use windmill_common::{ error::{Error, JsonResult, Result}, jobs::JobPayload, schedule::Schedule, + schema::should_validate_schema, scripts::{ to_i64, HubScript, ListScriptQuery, ListableScript, NewScript, Schema, Script, ScriptHash, ScriptHistory, ScriptHistoryUpdate, ScriptKind, ScriptLang, ScriptWithStarred, @@ -157,6 +157,10 @@ pub fn workspaced_service() -> Router { ) .route("/history/p/*path", get(get_script_history)) .route("/get_latest_version/*path", get(get_latest_version)) + .route( + "/list_paths_from_workspace_runnable/*path", + get(list_paths_from_workspace_runnable), + ) .route( "/history_update/h/:hash/p/*path", post(update_script_history), @@ -261,9 +265,12 @@ async fn list_scripts( if lq.show_archived.unwrap_or(false) { sqlb.and_where_eq( - "o.created_at", - "(select max(created_at) from script where o.path = path - AND workspace_id = ?)" + "o.ctid", + "(SELECT ctid FROM script + WHERE path = o.path + AND workspace_id = ? + ORDER BY created_at DESC + LIMIT 1)" .bind(&w_id), ); sqlb.and_where_eq("archived", true); @@ -358,12 +365,6 @@ fn hash_script(ns: &NewScript) -> i64 { dh.finish() as i64 } -#[cfg(not(all(feature = "enterprise", feature = "parquet")))] -async fn create_snapshot_script() -> Result<(StatusCode, String)> { - Err(Error::BadRequest("Upgrade to EE to use bundle".to_string())) -} - -#[cfg(all(feature = "enterprise", feature = "parquet"))] async fn create_snapshot_script( authed: ApiAuthed, Extension(user_db): Extension, @@ -404,21 +405,48 @@ async fn create_snapshot_script( })?; uploaded = true; - if let Some(os) = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS + + #[cfg(all(feature = "enterprise", feature = "parquet"))] + let object_store = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS .read() .await - .clone() + .clone(); + + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + let object_store: Option<()> = None; + + if &windmill_common::utils::MODE_AND_ADDONS.mode + == &windmill_common::utils::Mode::Standalone + && object_store.is_none() { - let path = windmill_common::s3_helpers::bundle(&w_id, &hash); - if let Err(e) = os - .put(&object_store::path::Path::from(path.clone()), data.into()) - .await - { - tracing::info!("Failed to put snapshot to s3 at {path}: {:?}", e); - return Err(Error::ExecutionErr(format!("Failed to put {path} to s3"))); - } + std::fs::create_dir_all( + windmill_common::worker::ROOT_STANDALONE_BUNDLE_DIR.clone(), + )?; + windmill_common::worker::write_file_bytes( + &windmill_common::worker::ROOT_STANDALONE_BUNDLE_DIR, + &hash, + &data, + )?; } else { - return Err(Error::BadConfig("Object store is required for snapshot script and is not configured for servers".to_string())); + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + { + return Err(Error::ExecutionErr("codebase is an EE feature".to_string())); + } + + #[cfg(all(feature = "enterprise", feature = "parquet"))] + if let Some(os) = object_store { + let path = windmill_common::s3_helpers::bundle(&w_id, &hash); + + if let Err(e) = os + .put(&object_store::path::Path::from(path.clone()), data.into()) + .await + { + tracing::info!("Failed to put snapshot to s3 at {path}: {:?}", e); + return Err(Error::ExecutionErr(format!("Failed to put {path} to s3"))); + } + } else { + return Err(Error::BadConfig("Object store is required for snapshot script and is not configured for servers".to_string())); + } } } // println!("Length of `{}` is {} bytes", name, data.len()); @@ -436,6 +464,24 @@ async fn create_snapshot_script( return Ok((StatusCode::CREATED, format!("{}", script_hash.unwrap()))); } +async fn list_paths_from_workspace_runnable( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, path)): Path<(String, StripPath)>, +) -> JsonResult> { + let mut tx = user_db.begin(&authed).await?; + let runnables = sqlx::query_scalar!( + r#"SELECT importer_path FROM dependency_map + WHERE workspace_id = $1 AND imported_path = $2"#, + w_id, + path.to_path(), + ) + .fetch_all(&mut *tx) + .await?; + tx.commit().await?; + Ok(Json(runnables)) +} + async fn create_script( authed: ApiAuthed, Extension(user_db): Extension, @@ -591,20 +637,29 @@ async fn create_script_internal<'c>( .unwrap_or(json!({})); let lock = if ns.codebase.is_some() { Some(String::new()) - } else if !(ns.language == ScriptLang::Python3 - || ns.language == ScriptLang::Go - || ns.language == ScriptLang::Bun - || ns.language == ScriptLang::Bunnative - || ns.language == ScriptLang::Deno - || ns.language == ScriptLang::Rust - || ns.language == ScriptLang::Ansible - || ns.language == ScriptLang::CSharp - || ns.language == ScriptLang::Php) - { + } else if !( + ns.language == ScriptLang::Python3 + || ns.language == ScriptLang::Go + || ns.language == ScriptLang::Bun + || ns.language == ScriptLang::Bunnative + || ns.language == ScriptLang::Deno + || ns.language == ScriptLang::Rust + || ns.language == ScriptLang::Ansible + || ns.language == ScriptLang::CSharp + || ns.language == ScriptLang::Nu + || ns.language == ScriptLang::Php + || ns.language == ScriptLang::Java + // for related places search: ADD_NEW_LANG + ) { Some(String::new()) } else { - ns.lock - .and_then(|e| if e.is_empty() { None } else { Some(e) }) + ns.lock.as_ref().and_then(|e| { + if e.is_empty() { + None + } else { + Some(e.to_string()) + } + }) }; let needs_lock_gen = lock.is_none() && codebase.is_none(); @@ -625,13 +680,48 @@ async fn create_script_internal<'c>( } else { ns.language.clone() }; + + let validate_schema = should_validate_schema(&ns.content, &ns.language); + + let (no_main_func, has_preprocessor) = match lang { + ScriptLang::Bun | ScriptLang::Bunnative | ScriptLang::Deno | ScriptLang::Nativets => { + let args = windmill_parser_ts::parse_deno_signature(&ns.content, true, true, None); + match args { + Ok(args) => (args.no_main_func, args.has_preprocessor), + Err(e) => { + tracing::warn!( + "Error parsing deno signature when deploying script {}: {:?}", + ns.path, + e + ); + (None, None) + } + } + } + ScriptLang::Python3 => { + let args = windmill_parser_py::parse_python_signature(&ns.content, None, true); + match args { + Ok(args) => (args.no_main_func, args.has_preprocessor), + Err(e) => { + tracing::warn!( + "Error parsing python signature when deploying script {}: {:?}", + ns.path, + e + ); + (None, None) + } + } + } + _ => (ns.no_main_func, ns.has_preprocessor), + }; + sqlx::query!( "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, \ content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, \ draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, \ dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, \ - delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email) \ - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32)", + delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation) \ + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33)", &w_id, &hash.0, ns.path, @@ -660,14 +750,15 @@ async fn create_script_internal<'c>( ns.timeout, ns.concurrency_key, ns.visible_to_runner_only, - ns.no_main_func, + no_main_func.filter(|x| *x), // should be Some(true) or None codebase, - ns.has_preprocessor, + has_preprocessor.filter(|x| *x), // should be Some(true) or None if ns.on_behalf_of_email.is_some() { Some(&authed.email) } else { None - } + }, + validate_schema, ) .execute(&mut *tx) .await?; @@ -832,6 +923,40 @@ async fn create_script_internal<'c>( .await?; Ok((hash, new_tx)) } else { + let db2 = db.clone(); + let w_id2 = w_id.clone(); + let authed2 = authed.clone(); + let permissioned_as2 = permissioned_as.clone(); + let script_path2 = script_path.clone(); + let parent_path = p_path_opt.clone(); + let lock = ns.lock.clone(); + let deployment_message = ns.deployment_message.clone(); + let content = ns.content.clone(); + let language = ns.language.clone(); + tokio::spawn(async move { + // wait for 10 seconds to make sure the script is deployed and that the CLI sync that pushed it (f one) is complete + tokio::time::sleep(std::time::Duration::from_secs(10)).await; + if let Err(e) = process_relative_imports( + &db2, + None, + None, + &w_id2, + &script_path2, + parent_path, + deployment_message, + &content, + &Some(language), + &authed2.email, + &authed2.username, + &permissioned_as2, + lock, + ) + .await + { + tracing::error!(%e, "error processing relative imports"); + } + }); + handle_deployment_metadata( &authed.email, &authed.username, @@ -887,7 +1012,7 @@ async fn get_script_by_path( AND favorite.usr = $3 WHERE s.path = $1 AND s.workspace_id = $2 - AND s.created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2)", + ORDER BY s.created_at DESC LIMIT 1", ) .bind(path) .bind(w_id) @@ -896,9 +1021,7 @@ async fn get_script_by_path( .await? } else { sqlx::query_as::<_, ScriptWithStarred>( - "SELECT *, NULL as starred FROM script WHERE path = $1 AND workspace_id = $2 \ - AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND \ - workspace_id = $2)", + "SELECT *, NULL as starred FROM script WHERE path = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1", ) .bind(path) .bind(w_id) @@ -938,9 +1061,8 @@ async fn get_script_by_path_w_draft( let script_o = sqlx::query_as::<_, ScriptWDraft>( "SELECT hash, script.path, summary, description, content, language, kind, tag, schema, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, ws_error_handler_muted, draft.value as draft, dedicated_worker, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, has_preprocessor, on_behalf_of_email FROM script LEFT JOIN draft ON script.path = draft.path AND script.workspace_id = draft.workspace_id AND draft.typ = 'script' - WHERE script.path = $1 AND script.workspace_id = $2 \ - AND script.created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND \ - workspace_id = $2)", + WHERE script.path = $1 AND script.workspace_id = $2 + ORDER BY script.created_at DESC LIMIT 1", ) .bind(path) .bind(w_id) @@ -962,7 +1084,7 @@ async fn get_script_history( "SELECT s.hash as hash, dm.deployment_msg as deployment_msg FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash WHERE s.workspace_id = $1 AND s.path = $2 - ORDER by created_at DESC", + ORDER by s.created_at DESC", w_id, path.to_path(), ) @@ -990,7 +1112,7 @@ async fn get_latest_version( "SELECT s.hash as hash, dm.deployment_msg as deployment_msg FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash WHERE s.workspace_id = $1 AND s.path = $2 - ORDER by created_at DESC", + ORDER by s.created_at DESC LIMIT 1", w_id, path.to_path(), ) @@ -1086,7 +1208,15 @@ async fn toggle_workspace_error_handler( match error_handler_maybe { Some(_) => { sqlx::query_scalar!( - "UPDATE script SET ws_error_handler_muted = $3 WHERE workspace_id = $2 AND path = $1 AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2)", + "UPDATE script + SET ws_error_handler_muted = $3 + WHERE ctid = ( + SELECT ctid FROM script + WHERE path = $1 AND workspace_id = $2 + ORDER BY created_at DESC + LIMIT 1 + ) +", path.to_path(), w_id, req.muted, @@ -1107,6 +1237,7 @@ async fn toggle_workspace_error_handler( async fn get_tokened_raw_script_by_path( Extension(user_db): Extension, + Extension(db): Extension, Path((w_id, token, path)): Path<(String, String, StripPath)>, Extension(cache): Extension>, ) -> Result { @@ -1114,7 +1245,13 @@ async fn get_tokened_raw_script_by_path( .get_authed(Some(w_id.clone()), &token) .await .ok_or_else(|| Error::NotAuthorized("Invalid token".to_string()))?; - return raw_script_by_path(authed, Extension(user_db), Path((w_id, path))).await; + return raw_script_by_path( + authed, + Extension(user_db), + Extension(db), + Path((w_id, path)), + ) + .await; } async fn get_empty_ts_script_by_path() -> String { @@ -1124,22 +1261,25 @@ async fn get_empty_ts_script_by_path() -> String { async fn raw_script_by_path( authed: ApiAuthed, Extension(user_db): Extension, + Extension(db): Extension, Path((w_id, path)): Path<(String, StripPath)>, ) -> Result { - raw_script_by_path_internal(path, user_db, authed, w_id, false).await + raw_script_by_path_internal(path, user_db, db, authed, w_id, false).await } async fn raw_script_by_path_unpinned( authed: ApiAuthed, Extension(user_db): Extension, + Extension(db): Extension, Path((w_id, path)): Path<(String, StripPath)>, ) -> Result { - raw_script_by_path_internal(path, user_db, authed, w_id, true).await + raw_script_by_path_internal(path, user_db, db, authed, w_id, true).await } async fn raw_script_by_path_internal( path: StripPath, user_db: UserDB, + db: DB, authed: ApiAuthed, w_id: String, unpin: bool, @@ -1165,10 +1305,7 @@ async fn raw_script_by_path_internal( let mut tx = user_db.begin(&authed).await?; let content_o = sqlx::query_scalar!( - "SELECT content FROM script WHERE path = $1 AND workspace_id = $2 \ - AND - created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND archived = false AND \ - workspace_id = $2)", + "SELECT content FROM script WHERE path = $1 AND workspace_id = $2 AND archived = false ORDER BY created_at DESC LIMIT 1", path, w_id ) @@ -1176,6 +1313,22 @@ async fn raw_script_by_path_internal( .await?; tx.commit().await?; + if content_o.is_none() { + let exists = sqlx::query_scalar!( + "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 AND archived = false ORDER BY created_at DESC LIMIT 1)", + path, + w_id + ) + .fetch_one(&db) + .await?; + if exists.unwrap_or(false) { + return Err(Error::NotFound(format!( + "Script {path} not visible to {} but exists", + authed.username + ))); + } + } + let content = not_found_if_none(content_o, "Script", path)?; if unpin { @@ -1192,8 +1345,7 @@ async fn exists_script_by_path( let path = path.to_path(); let exists = sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 AND - created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2))", + "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1)", path, w_id ) @@ -1310,9 +1462,7 @@ pub async fn require_is_writer(authed: &ApiAuthed, path: &str, w_id: &str, db: D path, w_id, db, - "SELECT extra_perms FROM script WHERE path = $1 AND workspace_id = $2 \ - AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND \ - workspace_id = $2)", + "SELECT extra_perms FROM script WHERE path = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1", "script", ) .await; @@ -1449,12 +1599,18 @@ async fn delete_script_by_hash( Ok(Json(script)) } +#[derive(Deserialize)] +struct DeleteScriptQuery { + keep_captures: Option, +} + async fn delete_script_by_path( authed: ApiAuthed, Extension(user_db): Extension, Extension(webhook): Extension, Extension(db): Extension, Path((w_id, path)): Path<(String, StripPath)>, + Query(query): Query, ) -> JsonResult { let path = path.to_path(); @@ -1508,21 +1664,23 @@ async fn delete_script_by_path( .execute(&db) .await?; - sqlx::query!( - "DELETE FROM capture_config WHERE path = $1 AND workspace_id = $2 AND is_flow IS FALSE", - path, - w_id - ) - .execute(&db) - .await?; + if !query.keep_captures.unwrap_or(false) { + sqlx::query!( + "DELETE FROM capture_config WHERE path = $1 AND workspace_id = $2 AND is_flow IS FALSE", + path, + w_id + ) + .execute(&db) + .await?; - sqlx::query!( - "DELETE FROM capture WHERE path = $1 AND workspace_id = $2 AND is_flow IS FALSE", - path, - w_id - ) - .execute(&db) - .await?; + sqlx::query!( + "DELETE FROM capture WHERE path = $1 AND workspace_id = $2 AND is_flow IS FALSE", + path, + w_id + ) + .execute(&db) + .await?; + } audit_log( &mut *tx, diff --git a/backend/windmill-api/src/settings.rs b/backend/windmill-api/src/settings.rs index 37a33ee81a..a270074cc0 100644 --- a/backend/windmill-api/src/settings.rs +++ b/backend/windmill-api/src/settings.rs @@ -34,8 +34,8 @@ use windmill_common::{ email_ee::send_email, error::{self, JsonResult, Result}, global_settings::{ - AUTOMATE_USERNAME_CREATION_SETTING, EMAIL_DOMAIN_SETTING, ENV_SETTINGS, - HUB_ACCESSIBLE_URL_SETTING, HUB_BASE_URL_SETTING, + AUTOMATE_USERNAME_CREATION_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING, EMAIL_DOMAIN_SETTING, + ENV_SETTINGS, HUB_ACCESSIBLE_URL_SETTING, HUB_BASE_URL_SETTING, }, server::Smtp, }; @@ -239,6 +239,13 @@ pub async fn set_global_setting_internal( })?; } } + CRITICAL_ALERT_MUTE_UI_SETTING => { + if value.clone().as_bool().unwrap_or(false) { + sqlx::query!("UPDATE alerts SET acknowledged = true") + .execute(db) + .await?; + } + } _ => {} } diff --git a/backend/windmill-api/src/slack_approvals.rs b/backend/windmill-api/src/slack_approvals.rs index de65f7b28d..9a53e80adf 100644 --- a/backend/windmill-api/src/slack_approvals.rs +++ b/backend/windmill-api/src/slack_approvals.rs @@ -16,8 +16,11 @@ use crate::db::{ApiAuthed, DB}; use crate::jobs::{cancel_suspended_job, resume_suspended_job, QueryOrBody, ResumeUrls}; use windmill_common::{ - error::Error, - variables::{build_crypt, decrypt}, + cache, + error::{self, Error}, + jobs::JobKind, + scripts::ScriptHash, + variables::get_secret_value_as_admin, }; use crate::approvals::{ @@ -801,26 +804,9 @@ fn process_non_datetime_inputs( } async fn get_slack_token(db: &DB, slack_resource_path: &str, w_id: &str) -> anyhow::Result { - let slack_token = match sqlx::query!( - "SELECT value, is_secret FROM variable WHERE path = $1", - slack_resource_path - ) - .fetch_optional(db) - .await? - { - Some(row) => row, - None => { - return Err(anyhow::anyhow!("No slack token found")); - } - }; - - if slack_token.is_secret { - let mc = build_crypt(&db, w_id).await?; - let bot_token = decrypt(&mc, slack_token.value)?; - Ok(bot_token) - } else { - Ok(slack_token.value) - } + get_secret_value_as_admin(db, w_id, slack_resource_path) + .await + .map_err(|e| anyhow::anyhow!(e.to_string())) } // Sends a Slack message with a button that opens a modal diff --git a/backend/windmill-api/src/tracing_init.rs b/backend/windmill-api/src/tracing_init.rs index 9f34e93592..c5c841b2dc 100644 --- a/backend/windmill-api/src/tracing_init.rs +++ b/backend/windmill-api/src/tracing_init.rs @@ -33,6 +33,8 @@ impl OnResponse for MyOnResponse { let status = response.status().as_u16(); if response.status().is_success() || response.status().is_redirection() { tracing::info!(latency = latency, status = status, "response") + } else if response.status().as_u16() == 404 { + tracing::warn!(latency = latency, status = status, "response") } else { tracing::error!(latency = latency, status = status, "response") } diff --git a/backend/windmill-api/src/users.rs b/backend/windmill-api/src/users.rs index 71bfeb2c2a..507831e78f 100644 --- a/backend/windmill-api/src/users.rs +++ b/backend/windmill-api/src/users.rs @@ -55,7 +55,6 @@ use windmill_common::{ utils::{not_found_if_none, rd_string, require_admin, Pagination, StripPath}, }; use windmill_git_sync::handle_deployment_metadata; -pub const TTL_TOKEN_DB_H: u32 = 72; const COOKIE_PATH: &str = "/"; @@ -606,6 +605,13 @@ async fn list_invites( Ok(Json(rows)) } +lazy_static::lazy_static! { + static ref INVALIDATE_ALL_SESSIONS_ON_LOGOUT: bool = std::env::var("INVALIDATE_ALL_SESSIONS_ON_LOGOUT") + .unwrap_or("false".to_string()) + .parse::() + .unwrap_or(false); +} + #[derive(Deserialize)] struct LogoutQuery { rd: Option, @@ -623,15 +629,36 @@ async fn logout( } cookies.remove(cookie); let mut tx = db.begin().await?; - let email = sqlx::query_scalar!("DELETE FROM token WHERE token = $1 RETURNING email", token) + + let email = if *INVALIDATE_ALL_SESSIONS_ON_LOGOUT { + sqlx::query_scalar!( + "WITH email_lookup AS ( + SELECT email FROM token WHERE token = $1 + ) + DELETE FROM token + WHERE email = (SELECT email FROM email_lookup) AND label = 'session' + RETURNING email", + token + ) .fetch_optional(&mut *tx) - .await?; + .await? + } else { + sqlx::query_scalar!("DELETE FROM token WHERE token = $1 RETURNING email", token) + .fetch_optional(&mut *tx) + .await? + }; + if let Some(email) = email { let email = email.unwrap_or("noemail".to_string()); + let audit_message = if *INVALIDATE_ALL_SESSIONS_ON_LOGOUT { + "users.logout_all" + } else { + "users.logout" + }; audit_log( &mut *tx, &AuditAuthor { email: email.clone(), username: email, username_override: None }, - "users.logout", + audit_message, ActionKind::Delete, "global", Some(&truncate_token(&token)), @@ -1696,6 +1723,11 @@ async fn refresh_token( Ok("token refreshed".to_string()) } +lazy_static::lazy_static! { + static ref MAX_SESSION_VALIDITY_SECONDS: i64 = std::env::var("MAX_SESSION_VALIDITY_SECONDS").ok().unwrap_or_else(|| String::new()).parse::().unwrap_or(3 * 24 * 60 * 60); + static ref INVALIDATE_OLD_SESSIONS: bool = std::env::var("INVALIDATE_OLD_SESSIONS").ok().unwrap_or_else(|| String::new()).parse::().unwrap_or(false); +} + pub async fn create_session_token<'c>( email: &str, super_admin: bool, @@ -1703,18 +1735,45 @@ pub async fn create_session_token<'c>( cookies: Cookies, ) -> Result { let token = rd_string(32); + + if *INVALIDATE_OLD_SESSIONS { + sqlx::query!( + "DELETE FROM token WHERE email = $1 AND label = 'session'", + email + ) + .execute(&mut **tx) + .await?; + + audit_log( + &mut **tx, + &AuditAuthor { + email: email.to_string(), + username: email.to_string(), + username_override: None, + }, + "users.token.invalidate_old_sessions", + ActionKind::Delete, + &"global", + None, + None, + ) + .instrument(tracing::info_span!("token", email)) + .await?; + } + sqlx::query!( "INSERT INTO token (token, email, label, expiration, super_admin) - VALUES ($1, $2, $3, now() + ($4 || ' hours')::interval, $5)", + VALUES ($1, $2, $3, now() + ($4 || ' seconds')::interval, $5)", token, email, "session", - TTL_TOKEN_DB_H.to_string(), + &MAX_SESSION_VALIDITY_SECONDS.to_string(), super_admin ) .execute(&mut **tx) .await?; + let mut cookie = Cookie::new(COOKIE_NAME, token.clone()); cookie.set_secure(IS_SECURE.read().await.clone()); cookie.set_same_site(Some(tower_cookies::cookie::SameSite::Lax)); @@ -1725,7 +1784,7 @@ pub async fn create_session_token<'c>( } let mut expire: OffsetDateTime = time::OffsetDateTime::now_utc(); - expire += time::Duration::days(3); + expire += time::Duration::seconds(*MAX_SESSION_VALIDITY_SECONDS); cookie.set_expires(expire); cookies.add(cookie); Ok(token) @@ -1845,7 +1904,7 @@ async fn list_tokens( sqlx::query_as!( TruncatedToken, "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, \ - last_used_at, scopes FROM token WHERE email = $1 AND label != 'ephemeral-script' + last_used_at, scopes FROM token WHERE email = $1 AND (label != 'ephemeral-script' OR label IS NULL) ORDER BY created_at DESC LIMIT $2 OFFSET $3", email, per_page as i64, @@ -2044,7 +2103,7 @@ pub struct LoginUserInfo { pub email: Option, pub name: Option, pub company: Option, - + pub preferred_username: Option, pub displayName: Option, } @@ -2478,6 +2537,30 @@ async fn update_username_in_workpsace<'c>( .execute(&mut **tx) .await?; + sqlx::query!( + r#"UPDATE workspace_runnable_dependencies SET flow_path = REGEXP_REPLACE(flow_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE flow_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#, + new_username, + old_username, + w_id + ).execute(&mut **tx) + .await?; + + sqlx::query!( + r#"UPDATE workspace_runnable_dependencies SET app_path = REGEXP_REPLACE(app_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE app_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#, + new_username, + old_username, + w_id + ).execute(&mut **tx) + .await?; + + sqlx::query!( + r#"UPDATE workspace_runnable_dependencies SET runnable_path = REGEXP_REPLACE(runnable_path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE runnable_path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#, + new_username, + old_username, + w_id + ).execute(&mut **tx) + .await?; + sqlx::query!( r#"UPDATE flow_node SET path = REGEXP_REPLACE(path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#, new_username, diff --git a/backend/windmill-api/src/utils.rs b/backend/windmill-api/src/utils.rs index 439944e2b2..8823dca33e 100644 --- a/backend/windmill-api/src/utils.rs +++ b/backend/windmill-api/src/utils.rs @@ -8,7 +8,7 @@ use axum::{body::Body, response::Response}; use regex::Regex; -use serde::Deserialize; +use serde::{Deserialize, Deserializer}; use sqlx::{Postgres, Transaction}; #[cfg(feature = "enterprise")] use windmill_common::worker::CLOUD_HOSTED; @@ -29,6 +29,13 @@ pub struct WithStarredInfoQuery { pub with_starred_info: Option, } +#[derive(Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum RunnableKind { + Script, + Flow, +} + pub async fn require_super_admin(db: &DB, email: &str) -> error::Result<()> { let is_admin = is_super_admin_email(db, email).await?; @@ -183,6 +190,15 @@ pub fn content_plain(body: Body) -> Response { .unwrap() } +#[allow(unused)] +pub fn non_empty_str<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let o: Option = Option::deserialize(deserializer)?; + Ok(o.filter(|s| !s.trim().is_empty())) +} + use serde::Serialize; #[derive(Serialize)] diff --git a/backend/windmill-api/src/variables.rs b/backend/windmill-api/src/variables.rs index 37960e8583..e810f95750 100644 --- a/backend/windmill-api/src/variables.rs +++ b/backend/windmill-api/src/variables.rs @@ -19,6 +19,7 @@ use axum::{ }; use hyper::StatusCode; use serde_json::Value; + use windmill_audit::audit_ee::{audit_log, AuditAuthorable}; use windmill_audit::ActionKind; use windmill_common::{ @@ -31,9 +32,9 @@ use windmill_common::{ }; use lazy_static::lazy_static; -use windmill_common::variables::{decrypt, encrypt}; use serde::Deserialize; use sqlx::{Postgres, Transaction}; +use windmill_common::variables::{decrypt, encrypt}; use windmill_git_sync::{handle_deployment_metadata, DeployedObject}; lazy_static! { @@ -60,7 +61,7 @@ async fn list_contextual_variables( ) -> JsonResult> { Ok(Json( get_reserved_variables( - &db, + &db.into(), &w_id, "q1A0qcPuO00yxioll7iph76N9CJDqn", &email, @@ -690,14 +691,21 @@ pub async fn get_variable_or_self(path: String, db: &DB, w_id: &str) -> Result = std::env::var("INSTANCE_EVENTS_WEBHOOK").ok(); + pub static ref WEBHOOK_CACHE: Cache> = Cache::new(100); + } pub enum WebhookPayload { @@ -76,7 +78,6 @@ impl WebhookShared { .timeout(Duration::from_secs(5)) .build() .unwrap(); - let cache = Cache::new(100); loop { select! { @@ -84,12 +85,12 @@ impl WebhookShared { _ = shutdown_rx.recv() => break, r = rx.recv() => match r { Some(WebhookPayload::WorkspaceEvent(workspace_id, message)) => { - let webhook_opt = match cache.get(&workspace_id) { + let webhook_opt = match WEBHOOK_CACHE.get(&workspace_id) { Some(guard) => { guard }, None => { - let Ok(webook_opt) = + let Ok(mut webhook_opt) = sqlx::query_scalar!( "SELECT webhook FROM workspace_settings WHERE workspace_id = $1", workspace_id @@ -101,13 +102,17 @@ impl WebhookShared { tracing::error!("Webhook Message to send - but cannot get workspace settings! Workspace: {workspace_id}"); continue; }; - cache.insert(workspace_id, webook_opt.clone()); - webook_opt + if webhook_opt.as_ref().is_some_and(|x| x.is_empty()) { + webhook_opt = None; + } + WEBHOOK_CACHE.insert(workspace_id, webhook_opt.clone()); + webhook_opt } }; if let Some(url) = webhook_opt { #[cfg(feature = "prometheus")] let timer = if METRICS_ENABLED.load(std::sync::atomic::Ordering::Relaxed) { Some(WEBHOOK_REQUEST_COUNT.start_timer()) } else { None }; + tracing::info!("Sending webhook message to {}", url); let _ = client.post(url).json(&message).send().await; #[cfg(feature = "prometheus")] timer.map(|x| x.stop_and_record()); diff --git a/backend/windmill-api/src/websocket_triggers.rs b/backend/windmill-api/src/websocket_triggers.rs index a28ed01425..9be3829d83 100644 --- a/backend/windmill-api/src/websocket_triggers.rs +++ b/backend/windmill-api/src/websocket_triggers.rs @@ -30,8 +30,10 @@ use windmill_common::{ }; use windmill_queue::PushArgsOwned; +use windmill_queue::TriggerKind; + use crate::{ - capture::{insert_capture_payload, TriggerKind, WebsocketTriggerConfig}, + capture::{insert_capture_payload, WebsocketTriggerConfig}, db::{ApiAuthed, DB}, jobs::{ run_flow_by_path_inner, run_script_by_path_inner, run_wait_result_internal, RunJobQuery, @@ -88,23 +90,27 @@ enum InitialMessage { #[derive(FromRow, Serialize, Clone)] pub struct WebsocketTrigger { - workspace_id: String, - path: String, - url: String, - script_path: String, - is_flow: bool, - edited_by: String, - email: String, - edited_at: chrono::DateTime, - server_id: Option, - last_server_ping: Option>, - extra_perms: serde_json::Value, - error: Option, - enabled: bool, - filters: Vec>>, - initial_messages: Option>>>, - url_runnable_args: Option>>, - can_return_message: bool, + pub workspace_id: String, + pub path: String, + pub url: String, + pub script_path: String, + pub is_flow: bool, + pub edited_by: String, + pub email: String, + pub edited_at: chrono::DateTime, + #[serde(skip_serializing_if = "Option::is_none")] + pub server_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub last_server_ping: Option>, + pub extra_perms: serde_json::Value, + pub error: Option, + pub enabled: bool, + pub filters: Vec>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub initial_messages: Option>>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub url_runnable_args: Option>>, + pub can_return_message: bool, } #[derive(Deserialize)] diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index 94089ced3c..6b34e7fa85 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -8,7 +8,7 @@ use std::collections::HashMap; -use crate::ai::{AIProvider, AIResource, AI_KEY_CACHE}; +use crate::ai::{AIConfig, AI_REQUEST_CACHE}; use crate::db::ApiAuthed; use crate::users_ee::send_email_if_possible; use crate::utils::get_instance_username_or_create_pending; @@ -143,7 +143,7 @@ pub fn workspaced_service() -> Router { .route("/critical_alerts/mute", post(mute_critical_alerts)) .route("/operator_settings", post(update_operator_settings)); - #[cfg(feature = "stripe")] + #[cfg(all(feature = "stripe", feature = "enterprise"))] { crate::stripe_ee::add_stripe_routes(router) } @@ -184,36 +184,58 @@ struct Workspace { #[derive(FromRow, Serialize, Debug)] pub struct WorkspaceSettings { pub workspace_id: String, + #[serde(skip_serializing_if = "Option::is_none")] pub slack_team_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub teams_team_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub teams_team_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub slack_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub slack_command_script: Option, pub teams_command_script: Option, pub slack_email: String, - pub auto_invite_domain: Option, - pub auto_invite_operator: Option, - pub auto_add: Option, - pub customer_id: Option, - pub plan: Option, - pub webhook: Option, - pub deploy_to: Option, - pub ai_resource: Option, - pub ai_models: Vec, #[serde(skip_serializing_if = "Option::is_none")] - pub code_completion_model: Option, + pub auto_invite_domain: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub auto_invite_operator: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub auto_add: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub customer_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub plan: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub webhook: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub deploy_to: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub ai_config: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub error_handler: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub error_handler_extra_args: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub error_handler_muted_on_cancel: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub large_file_storage: Option, // effectively: DatasetsStorage - pub git_sync: Option, // effectively: WorkspaceGitSyncSettings - pub deploy_ui: Option, // effectively: WorkspaceDeploymentUISettings + #[serde(skip_serializing_if = "Option::is_none")] + pub git_sync: Option, // effectively: WorkspaceGitSyncSettings + #[serde(skip_serializing_if = "Option::is_none")] + pub deploy_ui: Option, // effectively: WorkspaceDeploymentUISettings + #[serde(skip_serializing_if = "Option::is_none")] pub default_app: Option, - pub automatic_billing: bool, + #[serde(skip_serializing_if = "Option::is_none")] pub default_scripts: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub mute_critical_alerts: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub color: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub operator_settings: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub git_app_installations: Option, } #[derive(FromRow, Serialize, Debug)] @@ -267,13 +289,6 @@ struct EditWebhook { webhook: Option, } -#[derive(Deserialize)] -struct EditCopilotConfig { - ai_resource: Option, - code_completion_model: Option, - ai_models: Vec, -} - #[derive(Deserialize, Serialize, Debug)] struct LargeFileStorageWithSecondary { #[serde(flatten)] @@ -369,19 +384,15 @@ async fn list_pending_invites( async fn is_premium( authed: ApiAuthed, - Extension(db): Extension, - Path(w_id): Path, + Extension(_db): Extension, + Path(_w_id): Path, ) -> JsonResult { require_admin(authed.is_admin, &authed.username)?; - let mut tx = db.begin().await?; - let row = sqlx::query_scalar!( - "SELECT premium FROM workspace WHERE workspace.id = $1", - &w_id - ) - .fetch_one(&mut *tx) - .await?; - tx.commit().await?; - Ok(Json(row)) + #[cfg(feature = "cloud")] + let premium = windmill_common::workspaces::is_premium_workspace(&_db, &_w_id).await; + #[cfg(not(feature = "cloud"))] + let premium = false; + Ok(Json(premium)) } async fn exists_workspace( @@ -429,13 +440,12 @@ async fn get_settings( let mut tx = user_db.begin(&authed).await?; let settings = sqlx::query_as!( WorkspaceSettings, - "SELECT * FROM workspace_settings WHERE workspace_id = $1", + "SELECT workspace_id, slack_team_id, teams_team_id, teams_team_name, slack_name, slack_command_script, teams_command_script, slack_email, auto_invite_domain, auto_invite_operator, auto_add, customer_id, plan, webhook, deploy_to, ai_config, error_handler, error_handler_extra_args, error_handler_muted_on_cancel, large_file_storage, git_sync, deploy_ui, default_app, default_scripts, mute_critical_alerts, color, operator_settings, git_app_installations FROM workspace_settings WHERE workspace_id = $1", &w_id ) .fetch_one(&mut *tx) .await .map_err(|e| Error::internal_err(format!("getting settings: {e:#}")))?; - tx.commit().await?; Ok(Json(settings)) } @@ -476,11 +486,11 @@ async fn edit_slack_command( if es.slack_command_script.is_some() { let exists_slack_command_with_team_id = sqlx::query_scalar!( r#" - SELECT EXISTS (SELECT 1 - FROM workspace_settings - WHERE workspace_id <> $1 + SELECT EXISTS (SELECT 1 + FROM workspace_settings + WHERE workspace_id <> $1 AND slack_command_script IS NOT NULL - AND slack_team_id IS NOT NULL + AND slack_team_id IS NOT NULL AND slack_team_id = (SELECT slack_team_id FROM workspace_settings WHERE workspace_id = $1)) "#, &w_id @@ -678,50 +688,26 @@ async fn edit_copilot_config( Extension(db): Extension, Path(w_id): Path, ApiAuthed { is_admin, username, .. }: ApiAuthed, - Json(eo): Json, + Json(ai_config): Json, ) -> Result { require_admin(is_admin, &username)?; let mut tx = db.begin().await?; - if let Some(ai_resource) = &eo.ai_resource { - let parsed_ai_resource = serde_json::from_value::(ai_resource.clone()) - .map_err(|e| Error::BadRequest(e.to_string()))?; + sqlx::query!( + "UPDATE workspace_settings SET ai_config = $1 WHERE workspace_id = $2", + sqlx::types::Json(&ai_config) as sqlx::types::Json<&AIConfig>, + &w_id + ) + .execute(&mut *tx) + .await?; - #[cfg(not(feature = "enterprise"))] - { - if matches!(parsed_ai_resource.provider, AIProvider::CustomAI) { - return Err(Error::BadRequest( - "Custom AI is only available on EE".to_string(), - )); - } + if let Some(ref providers) = ai_config.providers { + for provider in providers.keys() { + AI_REQUEST_CACHE.remove(&(w_id.clone(), provider.clone())); } - - sqlx::query!( - "UPDATE workspace_settings SET ai_resource = $1, code_completion_model = $2, ai_models = $3 WHERE workspace_id = $4", - ai_resource, - eo.code_completion_model, - eo.ai_models.as_slice(), - &w_id - ) - .execute(&mut *tx) - .await?; - - if let Some(cached) = AI_KEY_CACHE.get(&w_id) { - if cached.path != parsed_ai_resource.path { - AI_KEY_CACHE.remove(&w_id); - } - } - } else { - sqlx::query!( - "UPDATE workspace_settings SET ai_resource = NULL, code_completion_model = $1, ai_models = '{}' WHERE workspace_id = $2", - eo.code_completion_model, - &w_id, - ) - .execute(&mut *tx) - .await?; - AI_KEY_CACHE.remove(&w_id); } + audit_log( &mut *tx, &authed, @@ -729,16 +715,7 @@ async fn edit_copilot_config( ActionKind::Update, &w_id, Some(&authed.email), - Some( - [ - ("ai_resource", &format!("{:?}", eo.ai_resource)[..]), - ( - "code_completion_model", - &format!("{:?}", eo.code_completion_model)[..], - ), - ] - .into(), - ), + Some([("ai_config", &format!("{:?}", ai_config)[..])].into()), ) .await?; tx.commit().await?; @@ -746,42 +723,33 @@ async fn edit_copilot_config( Ok(format!("Edit copilot config for workspace {}", &w_id)) } -#[derive(Serialize)] -struct CopilotInfo { - #[serde(skip_serializing_if = "Option::is_none")] - pub ai_provider: Option, - pub exists_ai_resource: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub code_completion_model: Option, - pub ai_models: Vec, -} async fn get_copilot_info( Extension(db): Extension, Path(w_id): Path, -) -> JsonResult { +) -> JsonResult { let mut tx = db.begin().await?; - let record = sqlx::query!( - "SELECT ai_resource, code_completion_model, ai_models FROM workspace_settings WHERE workspace_id = $1", + let copilot_info = sqlx::query_scalar!( + "SELECT ai_config as \"ai_config: sqlx::types::Json\" FROM workspace_settings WHERE workspace_id = $1", &w_id ) .fetch_one(&mut *tx) .await - .map_err(|e| Error::internal_err(format!("getting ai_resource and code_completion_model: {e:#}")))?; + .map_err(|e| { + Error::internal_err(format!( + "getting ai config: {e:#}" + )) + })?; tx.commit().await?; - let (ai_provider, exists_ai_resource) = if let Some(ai_resource) = record.ai_resource { - let ai_resource = serde_json::from_value::(ai_resource)?; - (Some(ai_resource.provider), true) + if let Some(sqlx::types::Json(copilot_info)) = copilot_info { + Ok(Json(copilot_info)) } else { - (None, false) - }; - - Ok(Json(CopilotInfo { - ai_provider, - exists_ai_resource, - code_completion_model: record.code_completion_model, - ai_models: record.ai_models, - })) + Ok(Json(AIConfig { + providers: None, + default_model: None, + code_completion_model: None, + })) + } } async fn edit_large_file_storage_config( @@ -901,7 +869,7 @@ async fn edit_git_sync_config( Ok(format!("Edit git sync config for workspace {}", &w_id)) } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] struct EditDeployUIConfig { #[cfg(feature = "enterprise")] deploy_ui_settings: Option, @@ -929,7 +897,6 @@ async fn edit_deploy_ui_config( require_admin(is_admin, &username)?; let mut tx = db.begin().await?; - let args_for_audit = format!("{:?}", new_config.deploy_ui_settings); audit_log( &mut *tx, @@ -1338,6 +1305,9 @@ struct UsedTriggers { pub kafka_used: bool, pub nats_used: bool, pub postgres_used: bool, + pub mqtt_used: bool, + pub sqs_used: bool, + pub gcp_used: bool, } async fn get_used_triggers( @@ -1349,14 +1319,15 @@ async fn get_used_triggers( let websocket_used = sqlx::query_as!( UsedTriggers, r#" - SELECT - - EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) AS "websocket_used!", - + SELECT + EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) AS "websocket_used!", EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) AS "http_routes_used!", EXISTS(SELECT 1 FROM kafka_trigger WHERE workspace_id = $1) as "kafka_used!", EXISTS(SELECT 1 FROM nats_trigger WHERE workspace_id = $1) as "nats_used!", - EXISTS(SELECT 1 FROM postgres_trigger WHERE workspace_id = $1) AS "postgres_used!" + EXISTS(SELECT 1 FROM postgres_trigger WHERE workspace_id = $1) AS "postgres_used!", + EXISTS(SELECT 1 FROM mqtt_trigger WHERE workspace_id = $1) AS "mqtt_used!", + EXISTS(SELECT 1 FROM sqs_trigger WHERE workspace_id = $1) AS "sqs_used!", + EXISTS(SELECT 1 FROM gcp_trigger WHERE workspace_id = $1) AS "gcp_used!" "#, w_id ) @@ -1386,7 +1357,7 @@ async fn list_workspaces_as_super_admin( workspace.owner AS \"owner!\", workspace.deleted AS \"deleted!\", workspace.premium AS \"premium!\", - workspace_settings.color AS \"color!\" + workspace_settings.color AS \"color\" FROM workspace LEFT JOIN workspace_settings ON workspace.id = workspace_settings.workspace_id LIMIT $1 OFFSET $2", @@ -1420,7 +1391,12 @@ async fn user_workspaces( Ok(Json(WorkspaceList { email, workspaces })) } -async fn check_name_conflict<'c>(tx: &mut Transaction<'c, Postgres>, w_id: &str) -> Result<()> { +pub async fn check_w_id_conflict<'c>(tx: &mut Transaction<'c, Postgres>, w_id: &str) -> Result<()> { + if w_id == "global" { + return Err(windmill_common::error::Error::BadRequest( + "'global' is not allowed as a workspace ID".to_string(), + )); + } let exists = sqlx::query_scalar!("SELECT EXISTS(SELECT 1 FROM workspace WHERE id = $1)", w_id) .fetch_one(&mut **tx) .await? @@ -1478,7 +1454,7 @@ async fn create_workspace( let mut tx: Transaction<'_, Postgres> = db.begin().await?; - check_name_conflict(&mut tx, &nw.id).await?; + check_w_id_conflict(&mut tx, &nw.id).await?; sqlx::query!( "INSERT INTO workspace (id, name, owner) @@ -2073,8 +2049,8 @@ async fn change_workspace_color( async fn get_usage(Extension(db): Extension, Path(w_id): Path) -> Result { let usage = sqlx::query_scalar!( " - SELECT usage.usage FROM usage - WHERE is_workspace = true + SELECT usage.usage FROM usage + WHERE is_workspace = true AND month_ = EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date) AND id = $1", w_id diff --git a/backend/windmill-api/src/workspaces_export.rs b/backend/windmill-api/src/workspaces_export.rs index 1f55d0172a..fea688c78a 100644 --- a/backend/windmill-api/src/workspaces_export.rs +++ b/backend/windmill-api/src/workspaces_export.rs @@ -147,7 +147,9 @@ pub(crate) struct ArchiveQueryParams { skip_secrets: Option, skip_variables: Option, skip_resources: Option, + skip_resource_types: Option, include_schedules: Option, + include_triggers: Option, include_users: Option, include_groups: Option, include_settings: Option, @@ -185,6 +187,8 @@ where "has_draft", "draft_only", "error", + "last_server_ping", + "server_id", ], ignore_keys.unwrap_or(vec![]), ] @@ -221,6 +225,7 @@ struct SimplifiedUser { #[derive(Serialize)] struct SimplifiedGroup { name: String, + #[serde(skip_serializing_if = "Option::is_none")] summary: Option, members: Vec, admins: Vec, @@ -235,20 +240,32 @@ struct SimplifiedSettings { auto_invite_enabled: bool, auto_invite_as: String, auto_invite_mode: String, + #[serde(skip_serializing_if = "Option::is_none")] webhook: Option, + #[serde(skip_serializing_if = "Option::is_none")] deploy_to: Option, + #[serde(skip_serializing_if = "Option::is_none")] error_handler: Option, + #[serde(skip_serializing_if = "Option::is_none")] error_handler_extra_args: Option, error_handler_muted_on_cancel: bool, - ai_resource: Option, - ai_models: Vec, #[serde(skip_serializing_if = "Option::is_none")] - code_completion_model: Option, + ai_config: Option, + #[serde(skip_serializing_if = "Option::is_none")] large_file_storage: Option, + #[serde(skip_serializing_if = "Option::is_none")] git_sync: Option, + #[serde(skip_serializing_if = "Option::is_none")] default_app: Option, + #[serde(skip_serializing_if = "Option::is_none")] default_scripts: Option, name: String, + #[serde(skip_serializing_if = "Option::is_none")] + mute_critical_alerts: Option, + #[serde(skip_serializing_if = "Option::is_none")] + color: Option, + #[serde(skip_serializing_if = "Option::is_none")] + operator_settings: Option, } pub(crate) async fn tarball_workspace( @@ -261,9 +278,11 @@ pub(crate) async fn tarball_workspace( plain_secret, plain_secrets, skip_resources, + skip_resource_types, skip_secrets, skip_variables, include_schedules, + include_triggers, include_users, include_groups, include_settings, @@ -316,8 +335,8 @@ pub(crate) async fn tarball_workspace( { let scripts = sqlx::query_as::<_, Script>( "SELECT * FROM script as o WHERE workspace_id = $1 AND archived = false - AND created_at = (select max(created_at) from script where path = o.path AND \ - workspace_id = $1)", + AND created_at = (select max(created_at) from script where path = o.path AND \ + workspace_id = $1)", ) .bind(&w_id) .fetch_all(&mut *tx) @@ -354,7 +373,10 @@ pub(crate) async fn tarball_workspace( ScriptLang::Rust => "rs", ScriptLang::Ansible => "playbook.yml", ScriptLang::CSharp => "cs", + ScriptLang::Nu => "nu", ScriptLang::OracleDB => "odb.sql", + ScriptLang::Java => "java", + // for related places search: ADD_NEW_LANG }; archive .write_to_archive(&script.content, &format!("{}.{}", script.path, ext)) @@ -393,12 +415,12 @@ pub(crate) async fn tarball_workspace( if !skip_resources.unwrap_or(false) { let resources = sqlx::query_as!( - Resource, - "SELECT * FROM resource WHERE workspace_id = $1 AND resource_type != 'state' AND resource_type != 'cache'", - &w_id - ) - .fetch_all(&mut *tx) - .await?; + Resource, + "SELECT * FROM resource WHERE workspace_id = $1 AND resource_type != 'state' AND resource_type != 'cache'", + &w_id + ) + .fetch_all(&mut *tx) + .await?; for resource in resources { let resource_str = &to_string_without_metadata(&resource, false, None).unwrap(); @@ -408,7 +430,7 @@ pub(crate) async fn tarball_workspace( } } - if !skip_resources.unwrap_or(false) { + if !skip_resource_types.unwrap_or(false) { let resource_types = sqlx::query_as!( ResourceType, "SELECT * FROM resource_type WHERE workspace_id = $1", @@ -430,14 +452,14 @@ pub(crate) async fn tarball_workspace( { let flows = sqlx::query_as::<_, Flow>( - "SELECT flow.workspace_id, flow.path, flow.summary, flow.description, flow.archived, flow.extra_perms, flow.draft_only, flow.dedicated_worker, flow.tag, flow.ws_error_handler_muted, flow.timeout, flow.visible_to_runner_only, flow.on_behalf_of_email, flow_version.schema, flow_version.value, flow_version.created_at as edited_at, flow_version.created_by as edited_by - FROM flow - LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] - WHERE flow.workspace_id = $1 AND flow.archived = false", - ) - .bind(&w_id) - .fetch_all(&mut *tx) - .await?; + "SELECT flow.workspace_id, flow.path, flow.summary, flow.description, flow.archived, flow.extra_perms, flow.draft_only, flow.dedicated_worker, flow.tag, flow.ws_error_handler_muted, flow.timeout, flow.visible_to_runner_only, flow.on_behalf_of_email, flow_version.schema, flow_version.value, flow_version.created_at as edited_at, flow_version.created_by as edited_by + FROM flow + LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] + WHERE flow.workspace_id = $1 AND flow.archived = false", + ) + .bind(&w_id) + .fetch_all(&mut *tx) + .await?; for flow in flows { let flow_str = &to_string_without_metadata(&flow, false, None).unwrap(); @@ -449,14 +471,14 @@ pub(crate) async fn tarball_workspace( if !skip_variables.unwrap_or(false) { let variables = - sqlx::query_as::<_, ExportableListableVariable>(if !skip_secrets.unwrap_or(false) { - "SELECT * FROM variable WHERE workspace_id = $1 AND expires_at IS NULL" - } else { - "SELECT * FROM variable WHERE workspace_id = $1 AND is_secret = false AND expires_at IS NULL" - }) - .bind(&w_id) - .fetch_all(&mut *tx) - .await?; + sqlx::query_as::<_, ExportableListableVariable>(if !skip_secrets.unwrap_or(false) { + "SELECT * FROM variable WHERE workspace_id = $1 AND expires_at IS NULL" + } else { + "SELECT * FROM variable WHERE workspace_id = $1 AND is_secret = false AND expires_at IS NULL" + }) + .bind(&w_id) + .fetch_all(&mut *tx) + .await?; let mc = build_crypt(&db, &w_id).await?; @@ -476,14 +498,14 @@ pub(crate) async fn tarball_workspace( { let apps = sqlx::query_as::<_, AppWithLastVersion>( - "SELECT app.id, app.path, app.summary, app.versions, app.policy, app.custom_path, - app.extra_perms, app_version.value, - app_version.created_at, app_version.created_by from app, app_version - WHERE app.workspace_id = $1 AND app_version.id = app.versions[array_upper(app.versions, 1)]", - ) - .bind(&w_id) - .fetch_all(&mut *tx) - .await?; + "SELECT app.id, app.path, app.summary, app.versions, app.policy, app.custom_path, + app.extra_perms, app_version.value, + app_version.created_at, app_version.created_by from app, app_version + WHERE app.workspace_id = $1 AND app_version.id = app.versions[array_upper(app.versions, 1)] AND app_version.raw_app IS false", + ) + .bind(&w_id) + .fetch_all(&mut *tx) + .await?; for app in apps { let app_str = &to_string_without_metadata(&app, false, None).unwrap(); @@ -496,7 +518,7 @@ pub(crate) async fn tarball_workspace( if include_schedules.unwrap_or(false) { let schedules = sqlx::query_as::<_, Schedule>( "SELECT * FROM schedule - WHERE workspace_id = $1", + WHERE workspace_id = $1", ) .bind(&w_id) .fetch_all(&mut *tx) @@ -510,10 +532,243 @@ pub(crate) async fn tarball_workspace( } } + if include_triggers.unwrap_or(false) { + #[cfg(feature = "http_trigger")] + { + let http_triggers = sqlx::query_as!( + crate::http_triggers::HttpTrigger, + r#" + SELECT + workspace_id, + workspaced_route, + path, + route_path, + route_path_key, + authentication_resource_path, + script_path, + is_flow, + edited_by, + edited_at, + email, + extra_perms, + is_async, + authentication_method AS "authentication_method: _", + http_method AS "http_method: _", + static_asset_config AS "static_asset_config: _", + is_static_website, + wrap_body, + raw_string + FROM http_trigger + WHERE workspace_id = $1 + "#, + &w_id + ) + .fetch_all(&mut *tx) + .await?; + + for trigger in http_triggers { + let trigger_str = &to_string_without_metadata(&trigger, false, None).unwrap(); + archive + .write_to_archive(&trigger_str, &format!("{}.http_trigger.json", trigger.path)) + .await?; + } + } + + #[cfg(feature = "websocket")] + { + let websocket_triggers = sqlx::query_as!( + crate::websocket_triggers::WebsocketTrigger, + r#" + SELECT + workspace_id, + path, + url, + script_path, + is_flow, + edited_by, + email, + edited_at, + server_id, + last_server_ping, + extra_perms, + error, + enabled, + filters AS "filters: _", + initial_messages AS "initial_messages: _", + url_runnable_args AS "url_runnable_args: _", + can_return_message + FROM + websocket_trigger + WHERE + workspace_id = $1 + "#, + &w_id + ) + .fetch_all(&mut *tx) + .await?; + + for trigger in websocket_triggers { + let trigger_str = &to_string_without_metadata(&trigger, false, None).unwrap(); + archive + .write_to_archive( + &trigger_str, + &format!("{}.websocket_trigger.json", trigger.path), + ) + .await?; + } + } + + #[cfg(all(feature = "enterprise", feature = "kafka"))] + { + let kafka_triggers = sqlx::query_as!( + crate::kafka_triggers_ee::KafkaTrigger, + "SELECT * FROM kafka_trigger + WHERE workspace_id = $1", + &w_id + ) + .fetch_all(&mut *tx) + .await?; + + for trigger in kafka_triggers { + let trigger_str = &to_string_without_metadata(&trigger, false, None).unwrap(); + archive + .write_to_archive( + &trigger_str, + &format!("{}.kafka_trigger.json", trigger.path), + ) + .await?; + } + } + + #[cfg(all(feature = "enterprise", feature = "sqs_trigger"))] + { + let sqs_triggers = sqlx::query_as!( + crate::sqs_triggers_ee::SqsTrigger, + r#" + SELECT + aws_auth_resource_type AS "aws_auth_resource_type: _", + aws_resource_path, + message_attributes, + queue_url, + workspace_id, + path, + script_path, + is_flow, + edited_by, + email, + edited_at, + server_id, + last_server_ping, + extra_perms, + error, + enabled + FROM + sqs_trigger + WHERE + workspace_id = $1 + "#, + &w_id + ) + .fetch_all(&mut *tx) + .await?; + + for trigger in sqs_triggers { + let trigger_str = &to_string_without_metadata(&trigger, false, None).unwrap(); + archive + .write_to_archive(&trigger_str, &format!("{}.sqs_trigger.json", trigger.path)) + .await?; + } + } + + #[cfg(all(feature = "enterprise", feature = "gcp_trigger"))] + { + let gcp_triggers = sqlx::query_as!( + crate::gcp_triggers_ee::GcpTrigger, + r#" + SELECT + gcp_resource_path, + subscription_id, + topic_id, + workspace_id, + delivery_type AS "delivery_type: _", + delivery_config AS "delivery_config: _", + subscription_mode AS "subscription_mode: _", + path, + script_path, + is_flow, + edited_by, + email, + edited_at, + server_id, + last_server_ping, + extra_perms, + error, + enabled + FROM + gcp_trigger + WHERE + workspace_id = $1 + "#, + &w_id + ) + .fetch_all(&mut *tx) + .await?; + + for trigger in gcp_triggers { + let trigger_str = &to_string_without_metadata(&trigger, false, None).unwrap(); + archive + .write_to_archive(&trigger_str, &format!("{}.gcp_trigger.json", trigger.path)) + .await?; + } + } + + #[cfg(all(feature = "enterprise", feature = "nats"))] + { + let nats_triggers = sqlx::query_as!( + crate::nats_triggers_ee::NatsTrigger, + "SELECT * FROM nats_trigger + WHERE workspace_id = $1", + &w_id + ) + .fetch_all(&mut *tx) + .await?; + + for trigger in nats_triggers { + let trigger_str: &String = + &to_string_without_metadata(&trigger, false, None).unwrap(); + archive + .write_to_archive(&trigger_str, &format!("{}.nats_trigger.json", trigger.path)) + .await?; + } + } + + #[cfg(feature = "postgres_trigger")] + { + let postgres_triggers = sqlx::query_as!( + crate::postgres_triggers::PostgresTrigger, + "SELECT * FROM postgres_trigger + WHERE workspace_id = $1", + &w_id + ) + .fetch_all(&mut *tx) + .await?; + + for trigger in postgres_triggers { + let trigger_str = &to_string_without_metadata(&trigger, false, None).unwrap(); + archive + .write_to_archive( + &trigger_str, + &format!("{}.postgres_trigger.json", trigger.path), + ) + .await?; + } + } + } + if include_users.unwrap_or(false) { let users = sqlx::query!( "SELECT * FROM usr - WHERE workspace_id = $1", + WHERE workspace_id = $1", &w_id ) .fetch_all(&mut *tx) @@ -532,12 +787,7 @@ pub(crate) async fn tarball_workspace( disabled: user.disabled, email: user.email, }; - let user_str = &to_string_without_metadata( - &user, - false, - Some(vec!["is_admin", "operator", "email"]), - ) - .unwrap(); + let user_str = &to_string_without_metadata(&user, false, Some(vec!["email"])).unwrap(); archive .write_to_archive(&user_str, &format!("users/{}.user.json", user.email)) .await?; @@ -546,16 +796,16 @@ pub(crate) async fn tarball_workspace( if include_groups.unwrap_or(false) { let groups = sqlx::query!( - r#"SELECT g_.workspace_id, name, summary, extra_perms, array_agg(u2g.usr) filter (where u2g.usr is not null) as members - FROM usr u - JOIN usr_to_group u2g ON u2g.usr = u.username AND u2g.workspace_id = u.workspace_id - RIGHT JOIN group_ g_ ON g_.workspace_id = u.workspace_id AND g_.name = u2g.group_ - WHERE g_.workspace_id = $1 AND g_.name != 'all' - GROUP BY g_.workspace_id, name, summary, extra_perms"#, - &w_id - ) - .fetch_all(&mut *tx) - .await?; + r#"SELECT g_.workspace_id, name, summary, extra_perms, array_agg(u2g.usr) filter (where u2g.usr is not null) as members + FROM usr u + JOIN usr_to_group u2g ON u2g.usr = u.username AND u2g.workspace_id = u.workspace_id + RIGHT JOIN group_ g_ ON g_.workspace_id = u.workspace_id AND g_.name = u2g.group_ + WHERE g_.workspace_id = $1 AND g_.name != 'all' + GROUP BY g_.workspace_id, name, summary, extra_perms"#, + &w_id + ) + .fetch_all(&mut *tx) + .await?; for group in groups { let extra_perms: HashMap = serde_json::from_value(group.extra_perms) @@ -606,33 +856,34 @@ pub(crate) async fn tarball_workspace( if include_settings.unwrap_or(false) { let settings = sqlx::query_as!( - SimplifiedSettings, - r#"SELECT - -- slack_team_id, - -- slack_name, - -- slack_command_script, - -- CASE WHEN slack_email = 'missing@email.xyz' THEN NULL ELSE slack_email END AS slack_email, - auto_invite_domain IS NOT NULL AS "auto_invite_enabled!", - CASE WHEN auto_invite_operator IS TRUE THEN 'operator' ELSE 'developer' END AS "auto_invite_as!", - CASE WHEN auto_add IS TRUE THEN 'add' ELSE 'invite' END AS "auto_invite_mode!", - webhook, - deploy_to, - error_handler, - ai_resource, - ai_models, - code_completion_model, - error_handler_extra_args, - error_handler_muted_on_cancel, - large_file_storage, - git_sync, - default_app, - default_scripts, - workspace.name - FROM workspace_settings - LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id - WHERE workspace_id = $1"#, - &w_id - ).fetch_one(&mut *tx).await?; + SimplifiedSettings, + r#"SELECT + -- slack_team_id, + -- slack_name, + -- slack_command_script, + -- CASE WHEN slack_email = 'missing@email.xyz' THEN NULL ELSE slack_email END AS slack_email, + auto_invite_domain IS NOT NULL AS "auto_invite_enabled!", + CASE WHEN auto_invite_operator IS TRUE THEN 'operator' ELSE 'developer' END AS "auto_invite_as!", + CASE WHEN auto_add IS TRUE THEN 'add' ELSE 'invite' END AS "auto_invite_mode!", + webhook, + deploy_to, + error_handler, + ai_config, + error_handler_extra_args, + error_handler_muted_on_cancel, + large_file_storage, + git_sync, + default_app, + default_scripts, + workspace.name, + mute_critical_alerts, + color, + operator_settings + FROM workspace_settings + LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id + WHERE workspace_id = $1"#, + &w_id + ).fetch_one(&mut *tx).await?; let settings_str = serde_json::to_value(settings) .map(|v| serde_json::to_string_pretty(&v).ok()) diff --git a/backend/windmill-api/src/workspaces_extra.rs b/backend/windmill-api/src/workspaces_extra.rs index 2378e85c02..07570df5c3 100644 --- a/backend/windmill-api/src/workspaces_extra.rs +++ b/backend/windmill-api/src/workspaces_extra.rs @@ -1,6 +1,6 @@ use crate::db::ApiAuthed; -use crate::workspaces::CREATE_WORKSPACE_REQUIRE_SUPERADMIN; +use crate::workspaces::{check_w_id_conflict, CREATE_WORKSPACE_REQUIRE_SUPERADMIN}; use crate::{db::DB, utils::require_super_admin}; use axum::{ @@ -46,20 +46,7 @@ pub(crate) async fn change_workspace_id( let mut tx = db.begin().await?; - let workspace_conflict = sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM workspace WHERE id = $1)", - &rw.new_id, - ) - .fetch_one(&mut *tx) - .await? - .unwrap_or(false); - - if workspace_conflict { - return Err(Error::BadRequest(format!( - "workspace id {} already used", - &rw.new_id - ))); - } + check_w_id_conflict(&mut tx, &rw.new_id).await?; // duplicate workspace with new id name sqlx::query!( @@ -202,6 +189,14 @@ pub(crate) async fn change_workspace_id( .execute(&mut *tx) .await?; + sqlx::query!( + "UPDATE workspace_runnable_dependencies SET workspace_id = $1 WHERE workspace_id = $2", + &rw.new_id, + &old_id + ) + .execute(&mut *tx) + .await?; + sqlx::query!( "UPDATE flow_node SET workspace_id = $1 WHERE workspace_id = $2", &rw.new_id, diff --git a/backend/windmill-audit/src/lib.rs b/backend/windmill-audit/src/lib.rs index b6522186ca..10894798fb 100644 --- a/backend/windmill-audit/src/lib.rs +++ b/backend/windmill-audit/src/lib.rs @@ -34,4 +34,5 @@ pub struct ListAuditLogQuery { pub resource: Option, pub before: Option>, pub after: Option>, + pub all_workspaces: Option, } diff --git a/backend/windmill-common/Cargo.toml b/backend/windmill-common/Cargo.toml index 8f1bc299ea..76a6f16962 100644 --- a/backend/windmill-common/Cargo.toml +++ b/backend/windmill-common/Cargo.toml @@ -8,20 +8,24 @@ edition.workspace = true default = [] enterprise = [] jemalloc = ["dep:tikv-jemalloc-ctl"] +tantivy = [] prometheus = ["dep:prometheus"] loki = ["dep:tracing-loki"] benchmark = [] parquet = ["dep:object_store", "dep:aws-config", "dep:aws-sdk-sts"] +aws_auth = ["dep:aws-sdk-sts", "dep:aws-config"] otel = ["dep:opentelemetry-semantic-conventions", "dep:opentelemetry-otlp", "dep:opentelemetry_sdk", "dep:opentelemetry", "dep:tracing-opentelemetry", "dep:opentelemetry-appender-tracing", "dep:tonic"] smtp = ["dep:mail-send"] scoped_cache = [] +cloud = [] [lib] name = "windmill_common" path = "src/lib.rs" [dependencies] +tar.workspace = true hmac.workspace = true sha2.workspace = true thiserror.workspace = true @@ -31,6 +35,8 @@ serde_json.workspace = true chrono.workspace = true chrono-tz.workspace = true hex.workspace = true +reqwest-middleware = { workspace = true } +reqwest-retry = { workspace = true } rand.workspace = true sqlx = { workspace = true, features = ["postgres"] } uuid.workspace = true @@ -61,6 +67,8 @@ async-stream.workspace = true const_format.workspace = true crc.workspace = true windmill-macros.workspace = true +jsonwebtoken.workspace = true +backon.workspace = true semver.workspace = true croner = "2.0.6" @@ -68,6 +76,8 @@ quick_cache.workspace = true pin-project-lite.workspace = true futures.workspace = true tempfile.workspace = true +systemstat.workspace = true +size.workspace = true opentelemetry-semantic-conventions = { workspace = true, optional = true } opentelemetry-otlp = { workspace = true, optional = true } diff --git a/backend/windmill-common/src/agent_workers.rs b/backend/windmill-common/src/agent_workers.rs new file mode 100644 index 0000000000..7c91ec2165 --- /dev/null +++ b/backend/windmill-common/src/agent_workers.rs @@ -0,0 +1,90 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize)] +pub struct QueueInitJob { + pub content: String, +} + +use lazy_static::lazy_static; +use std::time::Duration; + +use reqwest_middleware::ClientBuilder; +use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware}; + +use crate::{jwt::decode_without_verify, worker::HttpClient}; + +lazy_static! { + pub static ref BASE_INTERNAL_URL: String = + std::env::var("BASE_INTERNAL_URL").unwrap_or("http://localhost:8080".to_string()); + pub static ref AGENT_TOKEN: String = std::env::var("AGENT_TOKEN").unwrap_or_default(); + pub static ref DECODED_AGENT_TOKEN: Option = { + if AGENT_TOKEN.is_empty() { + None + } else { + decode_without_verify::(AGENT_TOKEN.trim_start_matches(AGENT_JWT_PREFIX)) + .ok() + } + }; +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct AgentAuth { + pub worker_group: String, + pub suffix: Option, + pub tags: Vec, + pub exp: Option, +} + +pub const AGENT_JWT_PREFIX: &str = "jwt_agent_"; +pub fn build_agent_http_client(worker_suffix: &str) -> HttpClient { + let client = ClientBuilder::new( + reqwest::Client::builder() + .pool_max_idle_per_host(10) + .pool_idle_timeout(Duration::from_secs(60)) + .connect_timeout(Duration::from_secs(10)) + .timeout(Duration::from_secs(30)) + .default_headers({ + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert( + "User-Agent", // Replace with your desired header name + "Windmill-Agent/1.0".parse().unwrap(), // Replace with your desired header value + ); + let token = format!( + "{}{}_{}", + AGENT_JWT_PREFIX, + worker_suffix, + AGENT_TOKEN.trim_start_matches(AGENT_JWT_PREFIX), + ); + headers.insert( + "Authorization", + format!("Bearer {}", token).parse().unwrap(), + ); + headers + }) + .build() + .expect("Failed to create HTTP client"), + ) + .with(RetryTransientMiddleware::new_with_policy( + ExponentialBackoff::builder().build_with_max_retries(5), + )) + .build(); + HttpClient(client) +} + +#[derive(Deserialize, Serialize)] +pub struct PingJobStatus { + pub mem_peak: Option, + pub current_mem: Option, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct PingJobStatusResponse { + pub canceled_by: Option, + pub canceled_reason: Option, + pub already_completed: bool, +} + +// #[derive(Serialize, Deserialize)] +// pub struct PullJobRequest { +// pub worker_name: String, +// } diff --git a/backend/windmill-common/src/apps.rs b/backend/windmill-common/src/apps.rs index bc8429c279..c5b22d434c 100644 --- a/backend/windmill-common/src/apps.rs +++ b/backend/windmill-common/src/apps.rs @@ -6,6 +6,8 @@ * LICENSE-AGPL for a copy of the license. */ +use std::collections::HashMap; + use serde::{Deserialize, Serialize}; /// Id in the `app_script` table. @@ -21,3 +23,8 @@ pub struct ListAppQuery { pub include_draft_only: Option, pub with_deployment_msg: Option, } + +#[derive(Deserialize)] +pub struct RawAppValue { + pub files: HashMap, +} diff --git a/backend/windmill-common/src/auth.rs b/backend/windmill-common/src/auth.rs index 52a966b105..9d1ee74675 100644 --- a/backend/windmill-common/src/auth.rs +++ b/backend/windmill-common/src/auth.rs @@ -1,16 +1,56 @@ +use anyhow::Context; +use chrono::{DateTime, Duration, Utc}; use serde::{Deserialize, Serialize}; -use std::sync::Arc; -use tokio::sync::RwLock; +use uuid::Uuid; use crate::{ db::Authed, error::{Error, Result}, + jwt, users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL, SUPERADMIN_SYNC_EMAIL}, DB, }; -lazy_static::lazy_static! { - pub static ref JWT_SECRET : Arc> = Arc::new(RwLock::new("".to_string())); +#[derive(Debug)] +pub struct IdToken { + token: String, + expiration: DateTime, +} + +pub fn has_expired(expiration_time: DateTime, take: Option) -> bool { + let now = Utc::now(); + + let expiration = match take { + Some(duration) => expiration_time - duration, + None => expiration_time, + }; + + now > expiration +} + +impl From for String { + fn from(value: IdToken) -> Self { + value.token + } +} + +impl ToString for IdToken { + fn to_string(&self) -> String { + self.token.clone() + } +} + +impl IdToken { + pub fn new(token: String, expiration: DateTime) -> Self { + Self { token, expiration } + } + + pub fn token(&self) -> &str { + &self.token + } + pub fn expiration(&self) -> &DateTime { + &self.expiration + } } #[derive(Deserialize, Serialize)] @@ -28,17 +68,14 @@ pub struct JWTAuthClaims { pub scopes: Option>, } -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] pub struct JobPerms { - pub workspace_id: String, - pub job_id: String, pub email: String, pub username: String, pub is_admin: bool, pub is_operator: bool, pub groups: Vec, pub folders: Vec, - pub created_at: chrono::NaiveDateTime, } impl From for Authed { @@ -214,3 +251,169 @@ pub async fn get_groups_for_user( .collect(); Ok(groups) } + +#[tracing::instrument(level = "trace", skip_all)] +pub async fn create_token_for_owner( + db: &DB, + w_id: &str, + owner: &str, + label: &str, + expires_in: u64, + email: &str, + job_id: &Uuid, + perms: Option, +) -> crate::error::Result { + let job_perms = if perms.is_some() { + Ok(perms) + } else { + sqlx::query_as!( + JobPerms, + "SELECT email, username, is_admin, is_operator, groups, folders FROM job_perms WHERE job_id = $1 AND workspace_id = $2", + job_id, + w_id + ) + .fetch_optional(db) + .await + }; + let job_authed = match job_perms { + Ok(Some(jp)) => jp.into(), + _ => { + tracing::warn!("Could not get permissions for job {job_id} from job_perms table, getting permissions directly..."); + fetch_authed_from_permissioned_as(owner.to_string(), email.to_string(), w_id, db) + .await + .map_err(|e| { + Error::internal_err(format!( + "Could not get permissions directly for job {job_id}: {e:#}" + )) + })? + } + }; + + let payload = JWTAuthClaims { + email: job_authed.email, + username: job_authed.username, + is_admin: job_authed.is_admin, + is_operator: job_authed.is_operator, + groups: job_authed.groups, + folders: job_authed.folders, + label: Some(label.to_string()), + workspace_id: w_id.to_string(), + exp: (chrono::Utc::now() + chrono::Duration::seconds(expires_in as i64)).timestamp() + as usize, + job_id: Some(job_id.to_string()), + scopes: None, + }; + + let token = jwt::encode_with_internal_secret(&payload) + .await + .with_context(|| format!("Could not encode JWT token for job {job_id}"))?; + + Ok(format!("jwt_{}", token)) +} + +#[cfg(feature = "aws_auth")] +pub mod aws { + + use crate::error::to_anyhow; + + use super::*; + use crate::utils::empty_string_as_none; + use aws_config::{BehaviorVersion, Region}; + use aws_sdk_sts::{ + config::Credentials as AwsCredentials, + operation::{ + assume_role_with_saml::AssumeRoleWithSamlOutput, + assume_role_with_web_identity::AssumeRoleWithWebIdentityOutput, + }, + types::Credentials, + Client, + }; + + pub const AWS_OIDC_AUDIENCE: &'static str = "sts.amazonaws.com"; + + pub trait GetAuthenticationOutput { + fn get_credentials(&self) -> Result<&Credentials>; + } + + impl GetAuthenticationOutput for AssumeRoleWithSamlOutput { + fn get_credentials(&self) -> Result<&Credentials> { + let credentials = self.credentials.as_ref().ok_or(Error::BadGateway( + "Error fetching credentials from AWS STS".to_string(), + ))?; + Ok(credentials) + } + } + + impl GetAuthenticationOutput for AssumeRoleWithWebIdentityOutput { + fn get_credentials(&self) -> Result<&Credentials> { + let credentials = self.credentials.as_ref().ok_or(Error::BadGateway( + "Error fetching credentials from AWS STS".to_string(), + ))?; + Ok(credentials) + } + } + + #[derive(Debug, Clone, Serialize, Deserialize, sqlx::Type)] + #[sqlx(type_name = "AWS_AUTH_RESOURCE_TYPE", rename_all = "lowercase")] + #[serde(rename_all = "lowercase")] + pub enum AwsAuthResourceType { + Credentials, + Oidc, + } + + #[derive(Debug, Deserialize)] + pub struct CredentialsAuth { + #[serde(deserialize_with = "empty_string_as_none")] + pub region: Option, + #[serde(rename = "awsAccessKeyId")] + pub aws_access_key_id: String, + #[serde(rename = "awsSecretAccessKey")] + pub aws_secret_access_key: String, + } + + #[derive(Clone, Debug, Deserialize)] + #[serde(rename_all = "snake_case")] + pub struct OidcAuth { + #[serde(deserialize_with = "empty_string_as_none")] + pub region: Option, + #[serde(rename = "roleArn")] + pub role_arn: String, + } + + #[derive(Debug, Deserialize)] + #[serde(untagged)] + pub enum AWSAuthConfig { + Credentials(CredentialsAuth), + Oidc(OidcAuth), + } + + pub async fn get_oidc_authentication_data( + oidc_auth: OidcAuth, + role_session_name: Option, + token: String, + ) -> Result { + let region = oidc_auth.region.unwrap_or_else(|| "us-east-1".to_string()); + + let credentials = AwsCredentials::new("", "", None, None, "UserInput"); + + let config = aws_config::defaults(BehaviorVersion::latest()) + .credentials_provider(credentials) + .region(Region::new(region.clone())) + .load() + .await; + + let assume_role_with_web_identity_fluent_builder = Client::new(&config) + .assume_role_with_web_identity() + .set_role_arn(Some(oidc_auth.role_arn)) + .set_role_session_name(role_session_name.map(|str| str.to_string())) + .set_web_identity_token(Some(token)); + + let resp = assume_role_with_web_identity_fluent_builder + .clone() + .send() + .await + .map_err(to_anyhow)?; + + Ok(resp) + } +} diff --git a/backend/windmill-common/src/bench.rs b/backend/windmill-common/src/bench.rs index dafbfdb6e5..c851198b2a 100644 --- a/backend/windmill-common/src/bench.rs +++ b/backend/windmill-common/src/bench.rs @@ -41,8 +41,8 @@ impl BenchmarkInfo { self.total_duration = Some(total_duration as u64); println!( - "Writing benchmark {path}, duration of benchmark: {total_duration}s and RPS: {}", - self.iters as f64 / total_duration as f64 + "Writing benchmark {path}, duration of benchmark: {total_duration}ms and RPS: {}", + self.iters as f64 / total_duration as f64 * 1000.0 ); write_file(TMP_DIR, path, &serde_json::to_string(&self).unwrap()).expect("write profiling"); Ok(()) diff --git a/backend/windmill-common/src/cache.rs b/backend/windmill-common/src/cache.rs index 7447213605..03d8ec7f03 100644 --- a/backend/windmill-common/src/cache.rs +++ b/backend/windmill-common/src/cache.rs @@ -8,9 +8,13 @@ //! and there is only one test per thread, so using thread-local cache avoid unexpected results. use crate::{ - apps::AppScriptId, error, flows::FlowNodeId, flows::FlowValue, scripts::ScriptHash, - scripts::ScriptLang, + apps::AppScriptId, + error, + flows::{FlowNodeId, FlowValue}, + schema::SchemaValidator, + scripts::{ScriptHash, ScriptLang}, }; +use anyhow::anyhow; #[cfg(feature = "scoped_cache")] use std::thread::ThreadId; @@ -307,6 +311,8 @@ pub struct ScriptMetadata { pub language: Option, pub envs: Option>, pub codebase: Option, + pub schema: Option, + pub schema_validator: Option, } #[derive(Debug)] @@ -316,6 +322,25 @@ pub struct RawScript { pub meta: Option, } +#[derive(Debug, Deserialize, Serialize)] +pub struct RawScriptApi { + pub content: String, + pub lock: Option, + pub meta: Option, +} + +impl From for RawScriptApi { + fn from(value: RawScript) -> Self { + RawScriptApi { content: value.content, lock: value.lock, meta: value.meta } + } +} + +impl From for RawScript { + fn from(value: RawScriptApi) -> Self { + RawScript { content: value.content, lock: value.lock, meta: value.meta } + } +} + #[derive(Debug)] pub struct RawFlow { pub raw_flow: Box, @@ -328,6 +353,25 @@ pub struct RawNode { pub raw_flow: Option>, } +#[derive(Debug, Deserialize, Serialize)] +pub struct RawNodeApi { + pub raw_code: Option, + pub raw_lock: Option, + pub raw_flow: Option>, +} + +impl From for RawNodeApi { + fn from(value: RawNode) -> Self { + RawNodeApi { raw_code: value.raw_code, raw_lock: value.raw_lock, raw_flow: value.raw_flow } + } +} + +impl From for RawNode { + fn from(value: RawNodeApi) -> Self { + RawNode { raw_code: value.raw_code, raw_lock: value.raw_lock, raw_flow: value.raw_flow } + } +} + #[derive(Debug, Clone)] struct Entry(Arc); @@ -337,7 +381,7 @@ struct ScriptFull { pub meta: Arc, } -fn unwrap_or_error( +pub fn unwrap_or_error( at: &'static Location, entity: &'static str, key: Key, @@ -357,6 +401,11 @@ pub fn clear() { } pub mod flow { + use crate::{ + worker::{fetch_flow_node_query, Connection}, + DB, + }; + use super::*; make_static! { @@ -382,10 +431,10 @@ pub mod flow { /// This should be preferred over fetching the database directly. #[track_caller] pub fn fetch_script<'c>( - e: impl PgExecutor<'c>, + conn: &'c Connection, node: FlowNodeId, - ) -> impl Future>> { - let fetch_node = fetch_node(e, node); + ) -> impl Future>> + 'c { + let fetch_node = fetch_node(conn, node); async move { fetch_node.await.and_then(|data| match data { RawData::Script(data) => Ok(data), @@ -403,11 +452,12 @@ pub mod flow { /// This should be preferred over fetching the database directly. #[track_caller] pub fn fetch_flow<'c>( - e: impl PgExecutor<'c>, + db: &'c DB, node: FlowNodeId, - ) -> impl Future>> { - let fetch_node = fetch_node(e, node); + ) -> impl Future>> + 'c { async move { + let conn = Connection::Sql(db.clone()); + let fetch_node = fetch_node(&conn, node); fetch_node.await.and_then(|data| match data { RawData::Flow(data) => Ok(data), RawData::Script(_) => Err(error::Error::internal_err(format!( @@ -424,31 +474,23 @@ pub mod flow { /// This should be preferred over fetching the database directly. #[track_caller] pub(super) fn fetch_node<'c>( - e: impl PgExecutor<'c>, + conn: &'c Connection, node: FlowNodeId, - ) -> impl Future> { + ) -> impl Future> + 'c { let loc = Location::caller(); // If not present, `get_or_insert_async` will lock the key until the future completes, // so only one thread will be able to fetch the data from the database and write it to // the file system and cache, hence no race on the file system. NODES.get_or_insert_async(node, async move { - sqlx::query!( - "SELECT \ - code AS \"raw_code: String\", \ - lock AS \"raw_lock: String\", \ - flow AS \"raw_flow: Json>\" \ - FROM flow_node WHERE id = $1 LIMIT 1", - node.0, - ) - .fetch_optional(e) - .await - .map_err(Into::into) - .and_then(unwrap_or_error(&loc, "Flow node", node)) - .map(|r| RawNode { - raw_code: r.raw_code, - raw_lock: r.raw_lock, - raw_flow: r.raw_flow.map(|Json(raw_flow)| raw_flow), - }) + match conn { + Connection::Sql(db) => fetch_flow_node_query(db, node.0, loc).await, + Connection::Http(client) => { + let r = client + .get::(&format!("/api/agent_workers/flow_script/{}", node.0)) + .await?; + Ok(r.into()) + } + } }) } @@ -496,6 +538,8 @@ pub mod flow { } pub mod script { + use crate::{worker::Connection, DB}; + use super::*; make_static! { @@ -514,30 +558,52 @@ pub mod script { /// it to the file system and cache. /// This should be preferred over fetching the database directly. #[track_caller] - pub fn fetch<'c>( - e: impl PgExecutor<'c>, + pub fn fetch( + conn: &Connection, hash: ScriptHash, ) -> impl Future, Arc)>> { // If not present, `get_or_insert_async` will lock the key until the future completes, // so only one thread will be able to fetch the data from the database and write it to // the file system and cache, hence no race on the file system. let loc = Location::caller(); + let conn = conn.clone(); let fut = CACHE.get_or_insert_async(hash, async move { - sqlx::query!( + match conn { + Connection::Sql(db) => fetch_script_from_db(&db, hash, loc).await, + Connection::Http(client) => { + let r = client + .get::(&format!("/api/agent_workers/script/{}", hash.0)) + .await?; + Ok(r.into()) + } + } + }); + fut.map_ok(|ScriptFull { data, meta }| (data, meta)) + } + + pub async fn fetch_script_from_db( + db: &DB, + hash: ScriptHash, + loc: &'static Location<'_>, + ) -> error::Result { + sqlx::query!( "SELECT \ - content AS \"content!: String\", - lock AS \"lock: String\", \ - language AS \"language: Option\", \ - envs AS \"envs: Vec\", \ - codebase LIKE '%.tar' as use_tar \ - FROM script WHERE hash = $1 LIMIT 1", - hash.0 - ) - .fetch_optional(e) - .await - .map_err(Into::into) - .and_then(unwrap_or_error(&loc, "Script", hash)) - .map(|r| RawScript { + content AS \"content!: String\", + lock AS \"lock: String\", \ + language AS \"language: Option\", \ + envs AS \"envs: Vec\", \ + schema AS \"schema: String\", \ + schema_validation AS \"schema_validation: bool\", \ + codebase LIKE '%.tar' as use_tar \ + FROM script WHERE hash = $1 LIMIT 1", + hash.0 + ) + .fetch_optional(db) + .await + .map_err(Into::into) + .and_then(unwrap_or_error(&loc, "Script", hash)) + .and_then(|r| { + Ok(RawScript { content: r.content, lock: r.lock, meta: Some(ScriptMetadata { @@ -553,10 +619,20 @@ pub mod script { } else { None }, + schema_validator: if r.schema_validation { + r.schema + .as_ref() + .map(|schema_str| { + SchemaValidator::from_schema(schema_str).map_err(|e| anyhow!("Couldn't create schema validator for script requiring schema validation: {e}")) + }) + .transpose()? + } else { + None + }, + schema: r.schema, }), }) - }); - fut.map_ok(|ScriptFull { data, meta }| (data, meta)) + }) } /// Invalidate the script cache for the given `hash`. @@ -566,6 +642,8 @@ pub mod script { } pub mod app { + use crate::worker::{fetch_raw_script_from_app_query, Connection}; + use super::*; make_static! { @@ -584,23 +662,23 @@ pub mod app { /// This should be preferred over fetching the database directly. #[track_caller] pub fn fetch_script<'c>( - e: impl PgExecutor<'c>, + conn: &'c Connection, id: AppScriptId, - ) -> impl Future>> { + ) -> impl Future>> + 'c { // If not present, `get_or_insert_async` will lock the key until the future completes, // so only one thread will be able to fetch the data from the database and write it to // the file system and cache, hence no race on the file system. let loc = Location::caller(); let fut = CACHE.get_or_insert_async(id, async move { - sqlx::query!( - "SELECT lock, code FROM app_script WHERE id = $1 LIMIT 1", - id.0, - ) - .fetch_optional(e) - .await - .map_err(Into::into) - .and_then(unwrap_or_error(&loc, "Application script", id)) - .map(|r| RawScript { content: r.code, lock: r.lock, meta: None }) + match conn { + Connection::Sql(db) => fetch_raw_script_from_app_query(db, id.0, loc).await, + Connection::Http(client) => { + let r = client + .get::(&format!("/api/agent_workers/app_script/{}", id.0)) + .await?; + Ok(r.into()) + } + } }); fut.map_ok(|Entry(data)| data) } @@ -608,7 +686,7 @@ pub mod app { pub mod job { use super::*; - use crate::jobs::JobKind; + use crate::{jobs::JobKind, worker::Connection, DB}; #[cfg(not(feature = "scoped_cache"))] lazy_static! { @@ -628,15 +706,18 @@ pub mod job { } #[track_caller] - pub fn fetch_preview_flow<'a, 'c>( - e: impl PgExecutor<'c> + 'a, + pub fn fetch_preview_flow<'a>( + db: &'a DB, job: &'a Uuid, - // original raw values from `queue` or `completed_job` tables: - // kept for backward compatibility. raw_flow: Option>>, ) -> impl Future>> + 'a { - let fetch_preview = fetch_preview(e, job, None, None, raw_flow); + // Create the Connection first so it lives for the entire scope + async move { + let conn = Connection::from(db); + + let fetch_preview = fetch_preview(&conn, job, None, None, raw_flow); + fetch_preview.await.and_then(|data| match data { RawData::Flow(data) => Ok(data), RawData::Script(_) => Err(error::Error::internal_err(format!( @@ -648,7 +729,7 @@ pub mod job { #[track_caller] pub fn fetch_preview_script<'a, 'c>( - e: impl PgExecutor<'c> + 'a, + e: &'a Connection, job: &'a Uuid, // original raw values from `queue` or `completed_job` tables: // kept for backward compatibility. @@ -668,7 +749,7 @@ pub mod job { #[track_caller] pub fn fetch_preview<'a, 'c>( - e: impl PgExecutor<'c> + 'a, + e: &'a Connection, job: &'a Uuid, // original raw values from `queue` or `completed_job` tables: // kept for backward compatibility. @@ -679,16 +760,21 @@ pub mod job { let loc = Location::caller(); let fetch = async move { match (raw_lock, raw_code, raw_flow) { - (None, None, None) => sqlx::query!( - "SELECT raw_code, raw_lock, raw_flow AS \"raw_flow: Json>\" \ - FROM v2_job WHERE id = $1 LIMIT 1", - job - ) - .fetch_optional(e) - .await - .map_err(Into::into) - .and_then(unwrap_or_error(&loc, "Preview", job)) - .map(|r| (r.raw_lock, r.raw_code, r.raw_flow)), + (None, None, None) => match e { + Connection::Sql(pool) => sqlx::query!( + "SELECT raw_code, raw_lock, raw_flow AS \"raw_flow: Json>\" \ + FROM v2_job WHERE id = $1 LIMIT 1", + job + ) + .fetch_optional(pool) + .await + .map_err(Into::into) + .and_then(unwrap_or_error(&loc, "Preview", job)) + .map(|r| (r.raw_lock, r.raw_code, r.raw_flow)), + Connection::Http(_) => Err(error::Error::InternalErr(format!( + "Cannot fetch preview in HTTP mode" + ))), + }, (lock, code, flow) => Ok((lock, code, flow)), } .and_then(|(lock, code, flow)| match flow { @@ -709,8 +795,8 @@ pub mod job { } #[track_caller] - pub fn fetch_script<'c>( - e: impl PgExecutor<'c>, + pub fn fetch_script( + db: DB, kind: JobKind, hash: Option, ) -> impl Future>> { @@ -718,11 +804,15 @@ pub mod job { let loc = Location::caller(); async move { match (kind, hash.map(|ScriptHash(id)| id)) { - (FlowScript, Some(id)) => flow::fetch_script(e, FlowNodeId(id)).await, - (Script | Dependencies, Some(hash)) => script::fetch(e, ScriptHash(hash)) + (FlowScript, Some(id)) => { + flow::fetch_script(&Connection::Sql(db.clone()), FlowNodeId(id)).await + } + (Script | Dependencies, Some(hash)) => script::fetch(&db.into(), ScriptHash(hash)) .await .map(|(data, _meta)| data), - (AppScript, Some(id)) => app::fetch_script(e, AppScriptId(id)).await, + (AppScript, Some(id)) => { + app::fetch_script(&Connection::Sql(db.clone()), AppScriptId(id)).await + } _ => Err(error::Error::internal_err(format!( "Isn't a script job: {:?}", kind @@ -734,19 +824,19 @@ pub mod job { #[track_caller] pub fn fetch_flow<'c>( - e: impl PgExecutor<'c> + Copy, + db: &'c DB, kind: JobKind, hash: Option, - ) -> impl Future>> { + ) -> impl Future>> + 'c { use JobKind::*; let loc = Location::caller(); async move { match (kind, hash.map(|ScriptHash(id)| id)) { - (FlowDependencies, Some(id)) => flow::fetch_version(e, id).await, - (FlowNode, Some(id)) => flow::fetch_flow(e, FlowNodeId(id)).await, - (Flow, Some(id)) => match flow::fetch_version_lite(e, id).await { + (FlowDependencies, Some(id)) => flow::fetch_version(db, id).await, + (FlowNode, Some(id)) => flow::fetch_flow(db, FlowNodeId(id)).await, + (Flow, Some(id)) => match flow::fetch_version_lite(db, id).await { Ok(raw_flow) => Ok(raw_flow), - Err(_) => flow::fetch_version(e, id).await, + Err(_) => flow::fetch_version(db, id).await, }, _ => Err(error::Error::internal_err(format!( "Isn't a flow job {:?}", @@ -802,6 +892,12 @@ const _: () = { } } + impl ScriptMetadata { + fn export_metadata(&self, dst: &impl Storage) -> error::Result<()> { + Ok(dst.put("info.json", serde_json::to_vec(self)?)?) + } + } + impl Export for ScriptFull { type Untrusted = RawScript; @@ -817,7 +913,7 @@ const _: () = { fn export(&self, dst: &impl Storage) -> error::Result<()> { self.data.export(dst)?; - self.meta.export(dst)?; + self.meta.export_metadata(dst)?; Ok(()) } } @@ -933,6 +1029,7 @@ const _: () = { (u64, |x| format!("{:016x}", x)), (Uuid, |x| format!("{:032x}", x.as_u128())), (ScriptHash, |x| format!("{:016x}", x.0)), + ((u8, ScriptHash), |x| format!("{:02x}-{:016x}", x.0, x.1.0)), (FlowNodeId, |x| format!("{:016x}", x.0)), (AppScriptId, |x| format!("{:016x}", x.0)) } diff --git a/backend/windmill-common/src/db.rs b/backend/windmill-common/src/db.rs index a669c36173..47c698b05c 100644 --- a/backend/windmill-common/src/db.rs +++ b/backend/windmill-common/src/db.rs @@ -59,6 +59,10 @@ impl Authable for Authed { } } +lazy_static::lazy_static! { + pub static ref PG_SCHEMA: Option = std::env::var("PG_SCHEMA").ok(); +} + impl UserDB { pub fn new(db: DB) -> Self { Self { db } @@ -68,12 +72,6 @@ impl UserDB { where T: Authable, { - let user = if authed.is_admin() { - "windmill_admin" - } else { - "windmill_user" - }; - let (folders_write, folders_read): &(Vec<_>, Vec<_>) = &authed.folders().into_iter().partition(|x| x.1); @@ -91,58 +89,93 @@ impl UserDB { let mut tx = self.db.begin().await?; - sqlx::query(&format!("SET LOCAL ROLE {}", user)) - .execute(&mut *tx) - .await?; + if let Some(schema) = PG_SCHEMA.as_ref() { + sqlx::query(&format!("SET LOCAL search_path TO {}", schema)) + .execute(&mut *tx) + .await?; + } sqlx::query!( - "SELECT set_config('session.user', $1, true)", - authed.username() - ) - .fetch_optional(&mut *tx) - .await?; - - sqlx::query!( - "SELECT set_config('session.groups', $1, true)", - &authed.groups().join(",") - ) - .fetch_optional(&mut *tx) - .await?; - - sqlx::query!( - "SELECT set_config('session.pgroups', $1, true)", - &authed + "SELECT set_session_context($1, $2, $3, $4, $5, $6)", + authed.is_admin(), + authed.username(), + authed.groups().join(","), + authed .groups() .iter() .map(|x| format!("g/{}", x)) .collect::>() - .join(",") - ) - .fetch_optional(&mut *tx) - .await?; - - sqlx::query!( - "SELECT set_config('session.folders_read', $1, true)", + .join(","), folders_read .iter() .map(|x| x.0.clone()) .collect::>() - .join(",") - ) - .fetch_optional(&mut *tx) - .await?; - - sqlx::query!( - "SELECT set_config('session.folders_write', $1, true)", + .join(","), folders_write .iter() .map(|x| x.0.clone()) .collect::>() .join(",") ) - .fetch_optional(&mut *tx) + .execute(&mut *tx) .await?; + // set_session_context( + // username TEXT, + // groups TEXT, + // pgroups TEXT, + // folders_read TEXT, + // folders_write TEXT + // ) + + // sqlx::query!( + // "SELECT set_config('session.user', $1, true)", + // authed.username() + // ) + // .fetch_optional(&mut *tx) + // .await?; + + // sqlx::query!( + // "SELECT set_config('session.groups', $1, true)", + // &authed.groups().join(",") + // ) + // .fetch_optional(&mut *tx) + // .await?; + + // sqlx::query!( + // "SELECT set_config('session.pgroups', $1, true)", + // &authed + // .groups() + // .iter() + // .map(|x| format!("g/{}", x)) + // .collect::>() + // .join(",") + // ) + // .fetch_optional(&mut *tx) + // .await?; + + // sqlx::query!( + // "SELECT set_config('session.folders_read', $1, true)", + // folders_read + // .iter() + // .map(|x| x.0.clone()) + // .collect::>() + // .join(",") + // ) + // .fetch_optional(&mut *tx) + // .await?; + + // sqlx::query!( + // "SELECT set_config('session.folders_write', $1, true)", + // folders_write + // .iter() + // .map(|x| x.0.clone()) + // .collect::>() + // .join(",") + // ) + // .fetch_optional(&mut *tx) + // .await?; + Ok(tx) } } diff --git a/backend/windmill-common/src/error.rs b/backend/windmill-common/src/error.rs index faf1a72d1b..feced2b6d5 100644 --- a/backend/windmill-common/src/error.rs +++ b/backend/windmill-common/src/error.rs @@ -22,6 +22,8 @@ pub type JsonResult = std::result::Result, Error>; #[derive(Debug, Error)] pub enum Error { + #[error("Bad gateway: {0}")] + BadGateway(String), #[error("Bad config: {0}")] BadConfig(String), #[error("Connecting to database: {0}")] @@ -69,11 +71,15 @@ pub enum Error { #[error("Error: {0:#?}")] JsonErr(serde_json::Value), #[error("{0}")] - AiError(String), + AIError(String), #[error("{0}")] AlreadyCompleted(String), #[error("Find python error: {0}")] FindPythonError(String), + #[error("Problem with arguments: {0}")] + ArgumentErr(String), + #[error("{1}")] + Generic(StatusCode, String), } fn prettify_location(location: &'static Location<'static>) -> String { @@ -171,26 +177,29 @@ pub fn to_anyhow(e: T) -> anyhow:: impl IntoResponse for Error { fn into_response(self) -> axum::response::Response { - let e = &self; - let body = Body::from(e.to_string()); - let status = match self { Self::NotFound(_) => axum::http::StatusCode::NOT_FOUND, Self::NotAuthorized(_) => axum::http::StatusCode::UNAUTHORIZED, Self::RequireAdmin(_) => axum::http::StatusCode::FORBIDDEN, Self::SqlErr { .. } | Self::BadRequest(_) - | Self::AiError(_) + | Self::AIError(_) | Self::QuotaExceeded(_) => axum::http::StatusCode::BAD_REQUEST, + Self::BadGateway(_) => axum::http::StatusCode::BAD_GATEWAY, + Self::Generic(status_code, _) => status_code, _ => axum::http::StatusCode::INTERNAL_SERVER_ERROR, }; + let e = &self; + if matches!(status, axum::http::StatusCode::NOT_FOUND) { tracing::warn!(message = e.to_string()); } else { tracing::error!(message = e.to_string(), error = ?e); }; + let body = Body::from(e.to_string()); + axum::response::Response::builder() .header("Content-Type", "text/plain") .status(status) diff --git a/backend/windmill-common/src/flows.rs b/backend/windmill-common/src/flows.rs index 31152aa938..443461af64 100644 --- a/backend/windmill-common/src/flows.rs +++ b/backend/windmill-common/src/flows.rs @@ -22,7 +22,8 @@ use crate::{ error::Error, more_serde::{default_empty_string, default_id, default_null, default_true, is_default}, scripts::{Schema, ScriptHash, ScriptLang}, - worker::to_raw_value, + worker::{to_raw_value, Connection}, + DB, }; #[derive(Serialize, Deserialize, sqlx::FromRow)] @@ -60,6 +61,8 @@ pub struct FlowWithStarred { pub flow: Flow, #[serde(skip_serializing_if = "Option::is_none")] pub starred: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub lock_error_logs: Option, } fn is_none_or_false(b: &Option) -> bool { @@ -133,10 +136,11 @@ pub struct FlowValue { pub concurrency_key: Option, } -#[derive(Deserialize, Serialize, Debug, Clone)] +#[derive(Default, Deserialize, Serialize, Debug, Clone)] pub struct StopAfterIf { pub expr: String, pub skip_if_stopped: bool, + pub error_message: Option, } #[derive(Deserialize, Serialize, Debug, Clone, Default, PartialEq)] @@ -729,7 +733,7 @@ pub async fn resolve_maybe_value( } /// Resolve modules recursively. -pub async fn resolve_value( +async fn resolve_value( e: &sqlx::PgPool, workspace_id: &str, value: &mut Box, @@ -747,7 +751,7 @@ pub async fn resolve_value( /// Resolve module value recursively. pub async fn resolve_module( - e: &sqlx::PgPool, + db: &DB, workspace_id: &str, value: &mut Box, with_code: bool, @@ -781,7 +785,7 @@ pub async fn resolve_module( let (lock, content) = if !with_code { (Some("...".to_string()), "...".to_string()) } else { - cache::flow::fetch_script(e, id) + cache::flow::fetch_script(&Connection::Sql(db.clone()), id) .await .map(|data| (data.lock.clone(), data.code.clone()))? }; @@ -799,13 +803,13 @@ pub async fn resolve_module( }; } ForloopFlow { modules, modules_node, .. } | WhileloopFlow { modules, modules_node, .. } => { - resolve_modules(e, workspace_id, modules, modules_node.take(), with_code).await?; + resolve_modules(db, workspace_id, modules, modules_node.take(), with_code).await?; } BranchOne { branches, default, default_node } => { - resolve_modules(e, workspace_id, default, default_node.take(), with_code).await?; + resolve_modules(db, workspace_id, default, default_node.take(), with_code).await?; for branch in branches { resolve_modules( - e, + db, workspace_id, &mut branch.modules, branch.modules_node.take(), @@ -817,7 +821,7 @@ pub async fn resolve_module( BranchAll { branches, .. } => { for branch in branches { resolve_modules( - e, + db, workspace_id, &mut branch.modules, branch.modules_node.take(), diff --git a/backend/windmill-common/src/global_settings.rs b/backend/windmill-common/src/global_settings.rs index 31d8214e0c..61184b627c 100644 --- a/backend/windmill-common/src/global_settings.rs +++ b/backend/windmill-common/src/global_settings.rs @@ -11,6 +11,8 @@ pub const LICENSE_KEY_SETTING: &str = "license_key"; pub const NPM_CONFIG_REGISTRY_SETTING: &str = "npm_config_registry"; pub const BUNFIG_INSTALL_SCOPES_SETTING: &str = "bunfig_install_scopes"; pub const NUGET_CONFIG_SETTING: &str = "nuget_config"; +pub const MAVEN_REPOS_SETTING: &str = "maven_repos"; +pub const NO_DEFAULT_MAVEN_SETTING: &str = "no_default_maven"; pub const EXTRA_PIP_INDEX_URL_SETTING: &str = "pip_extra_index_url"; pub const PIP_INDEX_URL_SETTING: &str = "pip_index_url"; @@ -35,12 +37,13 @@ pub const HUB_BASE_URL_SETTING: &str = "hub_base_url"; pub const HUB_ACCESSIBLE_URL_SETTING: &str = "hub_accessible_url"; pub const CRITICAL_ERROR_CHANNELS_SETTING: &str = "critical_error_channels"; pub const CRITICAL_ALERT_MUTE_UI_SETTING: &str = "critical_alert_mute_ui"; +pub const CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING: &str = "critical_alerts_on_db_oversize"; pub const DEV_INSTANCE_SETTING: &str = "dev_instance"; pub const JWT_SECRET_SETTING: &str = "jwt_secret"; pub const EMAIL_DOMAIN_SETTING: &str = "email_domain"; pub const OTEL_SETTING: &str = "otel"; -pub const ENV_SETTINGS: [&str; 56] = [ +pub const ENV_SETTINGS: &[&str] = &[ "DISABLE_NSJAIL", "MODE", "NUM_WORKERS", @@ -58,8 +61,11 @@ pub const ENV_SETTINGS: [&str; 56] = [ "S3_CACHE_BUCKET", "COOKIE_DOMAIN", "PYTHON_PATH", + "NU_PATH", "DENO_PATH", "GO_PATH", + "JAVA_PATH", + // for related places search: ADD_NEW_LANG "GOPRIVATE", "GOPROXY", "NETRC", @@ -97,6 +103,8 @@ pub const ENV_SETTINGS: [&str; 56] = [ "OTEL_TRACING", "OTEL_LOGS", "DISABLE_S3_STORE", + "PG_SCHEMA", + "PG_LISTENER_REFRESH_PERIOD_SECS", ]; use crate::error; diff --git a/backend/windmill-common/src/job_metrics.rs b/backend/windmill-common/src/job_metrics.rs index 6577e34d6e..d416e4d44b 100644 --- a/backend/windmill-common/src/job_metrics.rs +++ b/backend/windmill-common/src/job_metrics.rs @@ -49,10 +49,7 @@ pub async fn register_metric_for_job( .await? .flatten(); if exists.unwrap_or(false) { - return Err(error::Error::BadRequest(format!( - "Metric {} is already registered for job {}", - metric_id, job_id - ))); + return Ok(metric_id); } let (scalar_int, scalar_float, timestamps, timeseries_int, timeseries_float) = match metric_kind diff --git a/backend/windmill-common/src/jobs.rs b/backend/windmill-common/src/jobs.rs index 49de1c2415..6cad6579fb 100644 --- a/backend/windmill-common/src/jobs.rs +++ b/backend/windmill-common/src/jobs.rs @@ -10,6 +10,7 @@ use tokio::io::AsyncReadExt; use uuid::Uuid; pub const ENTRYPOINT_OVERRIDE: &str = "_ENTRYPOINT_OVERRIDE"; +pub const LARGE_LOG_THRESHOLD_SIZE: usize = 9000; use crate::{ apps::AppScriptId, @@ -24,7 +25,7 @@ use crate::{ #[derive(sqlx::Type, Serialize, Deserialize, Debug, PartialEq, Copy, Clone)] #[sqlx(type_name = "JOB_KIND", rename_all = "lowercase")] -#[serde(rename_all(serialize = "lowercase"))] +#[serde(rename_all(serialize = "lowercase", deserialize = "lowercase"))] pub enum JobKind { Script, #[allow(non_camel_case_types)] @@ -51,6 +52,13 @@ impl JobKind { JobKind::Flow | JobKind::FlowPreview | JobKind::SingleScriptFlow | JobKind::FlowNode ) } + + pub fn is_dependency(&self) -> bool { + matches!( + self, + JobKind::FlowDependencies | JobKind::AppDependencies | JobKind::Dependencies + ) + } } #[derive(sqlx::FromRow, Debug, Serialize, Clone)] diff --git a/backend/windmill-common/src/jwt.rs b/backend/windmill-common/src/jwt.rs new file mode 100644 index 0000000000..8ebdb8dacb --- /dev/null +++ b/backend/windmill-common/src/jwt.rs @@ -0,0 +1,59 @@ +use crate::error::{self, to_anyhow, Error}; +use serde::{de::DeserializeOwned, Serialize}; +use std::{collections::HashSet, sync::Arc}; +use tokio::sync::RwLock; + +lazy_static::lazy_static! { + pub static ref JWT_SECRET: Arc> = Arc::new(RwLock::new("".to_string())); +} + +pub async fn encode_with_internal_secret(claims: T) -> error::Result { + let jwt_secret = JWT_SECRET.read().await; + + if jwt_secret.is_empty() { + return Err(Error::internal_err("JWT secret is not set".to_string())); + } + + let token = jsonwebtoken::encode( + &jsonwebtoken::Header::new(jsonwebtoken::Algorithm::HS256), + &claims, + &jsonwebtoken::EncodingKey::from_secret(jwt_secret.as_bytes()), + ) + .map_err(to_anyhow)?; + + Ok(token) +} + +pub async fn decode_with_internal_secret(token: &str) -> error::Result { + let jwt_secret = JWT_SECRET.read().await; + + if jwt_secret.is_empty() { + return Err(Error::internal_err("JWT secret is not set".to_string())); + } + + let result = jsonwebtoken::decode::( + token, + &jsonwebtoken::DecodingKey::from_secret(jwt_secret.as_bytes()), + &jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256), + ) + .map_err(to_anyhow)?; + + Ok(result.claims) +} + +pub fn decode_without_verify(token: &str) -> anyhow::Result { + // Create a validation that skips all checks + let mut validation = jsonwebtoken::Validation::default(); + validation.insecure_disable_signature_validation(); + validation.validate_exp = false; + validation.validate_nbf = false; + validation.required_spec_claims = HashSet::new(); + + // Use an empty key since we're not verifying + let key = jsonwebtoken::DecodingKey::from_secret(&[]); + + // Decode the token + let token_data = jsonwebtoken::decode::(token, &key, &validation)?; + + Ok(token_data.claims) +} diff --git a/backend/windmill-common/src/lib.rs b/backend/windmill-common/src/lib.rs index 4bb224e21b..e00e3c5c78 100644 --- a/backend/windmill-common/src/lib.rs +++ b/backend/windmill-common/src/lib.rs @@ -8,14 +8,21 @@ use std::{ net::SocketAddr, - sync::{atomic::AtomicBool, Arc}, + str::FromStr, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, }; +use tokio::sync::broadcast; + use ee::CriticalErrorChannel; use error::Error; use scripts::ScriptLang; use sqlx::{Pool, Postgres}; +pub mod agent_workers; pub mod apps; pub mod auth; #[cfg(feature = "benchmark")] @@ -33,13 +40,16 @@ pub mod indexer; pub mod job_metrics; #[cfg(feature = "parquet")] pub mod job_s3_helpers_ee; + pub mod jobs; +pub mod jwt; pub mod more_serde; pub mod oauth2; pub mod otel_ee; pub mod queue; pub mod s3_helpers; pub mod schedule; +pub mod schema; pub mod scripts; pub mod server; pub mod stats_ee; @@ -105,6 +115,7 @@ lazy_static::lazy_static! { pub static ref CRITICAL_ERROR_CHANNELS: Arc>> = Arc::new(RwLock::new(vec![])); + pub static ref CRITICAL_ALERTS_ON_DB_OVERSIZE: Arc>> = Arc::new(RwLock::new(None)); pub static ref JOB_RETENTION_SECS: Arc> = Arc::new(RwLock::new(0)); @@ -115,7 +126,7 @@ lazy_static::lazy_static! { } pub async fn shutdown_signal( - tx: tokio::sync::broadcast::Sender<()>, + tx: KillpillSender, mut rx: tokio::sync::broadcast::Receiver<()>, ) -> anyhow::Result<()> { use std::io; @@ -151,13 +162,11 @@ pub async fn shutdown_signal( } tracing::info!("signal received, starting graceful shutdown"); - let _ = tx.send(()); + let _ = tx.send(); Ok(()) } use tokio::sync::RwLock; -#[cfg(feature = "prometheus")] -use tokio::task::JoinHandle; use utils::rd_string; #[cfg(feature = "prometheus")] @@ -165,23 +174,31 @@ pub async fn serve_metrics( addr: SocketAddr, mut rx: tokio::sync::broadcast::Receiver<()>, ready_worker_endpoint: bool, -) -> JoinHandle<()> { - use std::sync::atomic::Ordering; - + metrics_endpoint: bool, +) -> anyhow::Result<()> { + if !metrics_endpoint && !ready_worker_endpoint { + return Ok(()); + } use axum::{ routing::{get, post}, Router, }; use hyper::StatusCode; - let router = Router::new() - .route("/metrics", get(metrics)) - .route("/reset", post(reset)); + let router = Router::new(); + + let router = if metrics_endpoint { + router + .route("/metrics", get(metrics)) + .route("/reset", post(reset)) + } else { + router + }; let router = if ready_worker_endpoint { router.route( "/ready", get(|| async { - if IS_READY.load(Ordering::Relaxed) { + if IS_READY.load(std::sync::atomic::Ordering::Relaxed) { (StatusCode::OK, "ready") } else { (StatusCode::INTERNAL_SERVER_ERROR, "not ready") @@ -194,8 +211,12 @@ pub async fn serve_metrics( tokio::spawn(async move { tracing::info!("Serving metrics at: {addr}"); - let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); - if let Err(e) = axum::serve(listener, router.into_make_service()) + let listener = tokio::net::TcpListener::bind(addr).await; + if let Err(e) = listener { + tracing::error!("Error binding to metrics address: {}", e); + return; + } + if let Err(e) = axum::serve(listener.unwrap(), router.into_make_service()) .with_graceful_shutdown(async move { rx.recv().await.ok(); tracing::info!("Graceful shutdown of metrics"); @@ -205,6 +226,8 @@ pub async fn serve_metrics( tracing::error!("Error serving metrics: {}", e); } }) + .await?; + Ok(()) } #[cfg(feature = "prometheus")] @@ -220,28 +243,42 @@ async fn reset() -> () { todo!() } -pub async fn connect_db( - server_mode: bool, - indexer_mode: bool, -) -> anyhow::Result> { - use anyhow::Context; +pub async fn get_database_url() -> Result { use std::env::var; use tokio::fs::File; use tokio::io::AsyncReadExt; - - let database_url = match var("DATABASE_URL_FILE") { + match var("DATABASE_URL_FILE") { Ok(file_path) => { let mut file = File::open(file_path).await?; let mut contents = String::new(); file.read_to_string(&mut contents).await?; - contents.trim().to_string() + Ok(contents.trim().to_string()) } Err(_) => var("DATABASE_URL").map_err(|_| { Error::BadConfig( "Either DATABASE_URL_FILE or DATABASE_URL env var is missing".to_string(), ) - })?, - }; + }), + } +} + +pub async fn initial_connection() -> Result, error::Error> { + let database_url = get_database_url().await?; + sqlx::postgres::PgPoolOptions::new() + .max_connections(2) + .connect_with(sqlx::postgres::PgConnectOptions::from_str(&database_url)?) + .await + .map_err(|err| Error::ConnectingToDatabase(err.to_string())) +} + +pub async fn connect_db( + server_mode: bool, + indexer_mode: bool, + worker_mode: bool, +) -> anyhow::Result> { + use anyhow::Context; + + let database_url = get_database_url().await?; let max_connections = match std::env::var("DATABASE_CONNECTIONS") { Ok(n) => n.parse::().context("invalid DATABASE_CONNECTIONS")?, @@ -262,20 +299,35 @@ pub async fn connect_db( } }; - Ok(connect(&database_url, max_connections).await?) + Ok(connect(&database_url, max_connections, worker_mode).await?) } pub async fn connect( database_url: &str, max_connections: u32, + worker_mode: bool, ) -> Result, error::Error> { use std::time::Duration; sqlx::postgres::PgPoolOptions::new() - .min_connections(3) + .min_connections((max_connections / 5).clamp(3, max_connections)) .max_connections(max_connections) .max_lifetime(Duration::from_secs(30 * 60)) // 30 mins - .connect(database_url) + .after_connect(move |conn, _| { + if worker_mode { + Box::pin(async move { + sqlx::query("SET enable_seqscan = OFF;") + .execute(conn) + .await?; + Ok(()) + }) + } else { + Box::pin(async move { Ok(()) }) + } + }) + .connect_with( + sqlx::postgres::PgConnectOptions::from_str(database_url)?.statement_cache_capacity(400), + ) .await .map_err(|err| Error::ConnectingToDatabase(err.to_string())) } @@ -379,3 +431,56 @@ pub async fn get_latest_hash_for_path<'c>( script.created_by, )) } + +pub struct KillpillSender { + tx: broadcast::Sender<()>, + already_sent: Arc, +} + +impl Clone for KillpillSender { + fn clone(&self) -> Self { + KillpillSender { tx: self.tx.clone(), already_sent: self.already_sent.clone() } + } +} + +impl KillpillSender { + pub fn new(capacity: usize) -> (Self, broadcast::Receiver<()>) { + let (tx, rx) = broadcast::channel(capacity); + let sender = KillpillSender { tx, already_sent: Arc::new(AtomicBool::new(false)) }; + (sender, rx) + } + + pub fn clone(&self) -> Self { + KillpillSender { tx: self.tx.clone(), already_sent: self.already_sent.clone() } + } + + pub fn subscribe(&self) -> broadcast::Receiver<()> { + self.tx.subscribe() + } + + // Try to send the killpill if it hasn't been sent already + pub fn send(&self) -> bool { + // Check if it's already been sent, and if not, set the flag to true + if !self.already_sent.swap(true, Ordering::SeqCst) { + // We're the first to set it to true, so send the signal + if let Err(e) = self.tx.send(()) { + tracing::error!("failed to send killpill: {:?}", e); + } + true + } else { + // Signal was already sent + false + } + } + + // // Force send a signal regardless of previous sends + // fn force_send(&self) -> Result> { + // self.already_sent.store(true, Ordering::SeqCst); + // self.tx.send(()) + // } + + // // Check if the killpill has been sent + // fn is_sent(&self) -> bool { + // self.already_sent.load(Ordering::SeqCst) + // } +} diff --git a/backend/windmill-common/src/s3_helpers.rs b/backend/windmill-common/src/s3_helpers.rs index 01507f5b6a..495f45912e 100644 --- a/backend/windmill-common/src/s3_helpers.rs +++ b/backend/windmill-common/src/s3_helpers.rs @@ -111,13 +111,15 @@ pub struct S3AwsOidcResource { pub audience: Option, } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, Default)] pub struct S3Object { pub s3: String, #[serde(skip_serializing_if = "Option::is_none")] pub storage: Option, #[serde(skip_serializing_if = "Option::is_none")] pub filename: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub presigned: Option, } #[cfg(feature = "parquet")] @@ -474,3 +476,7 @@ impl CredentialProvider for AwsCredentialAdapter { pub fn bundle(w_id: &str, hash: &str) -> String { format!("script_bundle/{}/{}", w_id, hash) } + +pub fn raw_app(w_id: &str, version: &i64) -> String { + format!("/home/rfiszel/raw_app/{}/{}", w_id, version) +} diff --git a/backend/windmill-common/src/schedule.rs b/backend/windmill-common/src/schedule.rs index b655f35c76..e78d3a211c 100644 --- a/backend/windmill-common/src/schedule.rs +++ b/backend/windmill-common/src/schedule.rs @@ -53,6 +53,8 @@ pub struct Schedule { #[serde(skip_serializing_if = "Option::is_none")] pub summary: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub tag: Option, #[serde(skip_serializing_if = "Option::is_none")] pub paused_until: Option>, diff --git a/backend/windmill-common/src/schema.rs b/backend/windmill-common/src/schema.rs new file mode 100644 index 0000000000..2b24ead0d3 --- /dev/null +++ b/backend/windmill-common/src/schema.rs @@ -0,0 +1,641 @@ +use anyhow::anyhow; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, str::FromStr}; + +use serde_json::{value::RawValue, Value}; + +use crate::{error::Error, scripts::ScriptLang}; + +#[derive(PartialEq, Serialize, Deserialize, Debug, Clone)] +pub enum JsonPrimitiveType { + String, + Number, + Integer, + Object, + Array, + Boolean, + Null, +} + +#[derive(PartialEq, Serialize, Deserialize, Debug, Clone)] +pub enum SchemaValidationRule { + StrictEnum(Vec), + IsNull, + IsInteger, + IsString, + IsBool, + IsDatetime, + IsNumber, + IsEmail, + IsObject(Vec<(String, Vec)>), + IsArray(Vec), + IsUnionType(Vec>), + IsOneOf(HashMap>), + IsBytes, +} + +impl SchemaValidationRule { + fn from_primitive(p: &JsonPrimitiveType, val: &Value) -> Result, anyhow::Error> { + let mut schema_rules = vec![]; + + match p { + JsonPrimitiveType::String => { + schema_rules.push(SchemaValidationRule::IsString); + + if let Some(format) = val.get("format").and_then(|f| f.as_str()) { + if format == "date" || format == "date-time" { + schema_rules.push(SchemaValidationRule::IsDatetime); + } + + if format == "email" { + schema_rules.push(SchemaValidationRule::IsEmail); + } + } + + if let Some(encoding) = val.get("contentEncoding").and_then(|e| e.as_str()) { + if encoding == "base64" { + schema_rules.push(SchemaValidationRule::IsBytes); + } + } + } + + JsonPrimitiveType::Number => { + schema_rules.push(SchemaValidationRule::IsNumber); + } + JsonPrimitiveType::Integer => { + schema_rules.push(SchemaValidationRule::IsInteger); + } + JsonPrimitiveType::Object => { + let mut obj_rules = vec![]; + + if let Some(properties) = val.get("properties") { + let properties = properties + .as_object() + .ok_or(anyhow!("Field properties should be an object"))?; + + for (key, v) in properties { + obj_rules.push((key.clone(), SchemaValidationRule::from_value(v)?)) + } + + schema_rules.push(SchemaValidationRule::IsObject(obj_rules)); + } else if let Some(one_of) = val.get("oneOf") { + let one_of = one_of + .as_array() + .ok_or(anyhow!("`oneOf` needs to be an array"))?; + let mut rules_map: HashMap> = HashMap::new(); + + for variant in one_of { + let variant_label = variant + .get("title") + .ok_or(anyhow!( + "oneOf variant definition should have a `title` field" + ))? + .as_str() + .ok_or(anyhow!( + "oneOf variant definition `title` field should be a string" + ))?; + if !rules_map.contains_key(variant_label) { + rules_map.insert( + variant_label.to_string(), + SchemaValidationRule::from_value(variant)?, + ); + } else { + return Err(anyhow!( + "oneOf definition has a duplicate variant `{variant_label}`" + )); + } + } + + schema_rules.push(SchemaValidationRule::IsOneOf(rules_map)) + } else { + let is_resource = val + .get("format") + .and_then(|f| f.as_str()) + .map(|f| f.starts_with("resource")) + .unwrap_or(false); + if !is_resource { + return Err(anyhow!( + "Object type should have a `properties` or `anyOf` field, or be a resource" + )); + } + } + } + JsonPrimitiveType::Array => { + let items = val + .get("items") + .ok_or(anyhow!("Array type should have field `items`"))?; + + let arr_rules = SchemaValidationRule::from_value(items)?; + + schema_rules.push(SchemaValidationRule::IsArray(arr_rules)); + } + JsonPrimitiveType::Boolean => { + schema_rules.push(SchemaValidationRule::IsBool); + } + JsonPrimitiveType::Null => { + schema_rules.push(SchemaValidationRule::IsNull); + } + } + + Ok(schema_rules) + } + + fn from_value(val: &Value) -> Result, Error> { + if let Some(any_of) = val.get("anyOf").and_then(|any_of| any_of.as_array()) { + let mut r = vec![]; + + for variant in any_of { + r.push(SchemaValidationRule::from_value(variant)?); + } + return Ok(vec![SchemaValidationRule::IsUnionType(r)]); + } + + let mut schema_rules = vec![]; + + let typ = val.get("type").ok_or(anyhow!("Missing `type` field"))?; + + if let Some(typ) = typ.as_str() { + schema_rules.append(&mut SchemaValidationRule::from_primitive( + &JsonPrimitiveType::from_str(typ)?, + val, + )?); + } else if let Some(typ_arr) = typ.as_array() { + let typ_arr = typ_arr + .into_iter() + .map(|v| { + SchemaValidationRule::from_primitive( + &JsonPrimitiveType::from_str( + v.as_str() + .ok_or(anyhow!("Expected array of strings for `type` field"))?, + )?, + v, + ) + }) + .collect::>, anyhow::Error>>()?; + + schema_rules.push(SchemaValidationRule::IsUnionType(typ_arr)); + } else { + return Err(anyhow!( + "Unsupported value for type field, expected string or string array" + ) + .into()); + } + + if let Some(enum_variants) = val.get("enum") { + let variants = enum_variants + .as_array() + .ok_or(anyhow!("enum variants are not in an array"))? + .clone(); + schema_rules.push(SchemaValidationRule::StrictEnum(variants)); + } + + Ok(schema_rules) + } + + fn apply_rule(&self, key: &str, val: &Value, required: bool) -> Result<(), Error> { + if val.is_null() { + if !required { + return Ok(()); + } + return Err(Error::ArgumentErr(format!("Argument {key} cannot be null"))); + } + match self { + SchemaValidationRule::IsNull => { + if !val.is_null() { + return Err(Error::ArgumentErr(format!( + "Argument `{key}` should be null" + ))); + } + } + SchemaValidationRule::StrictEnum(vec) => { + if !vec.contains(val) { + let options = vec.iter().map(|s| s.to_string()).join(", "); + return Err(Error::ArgumentErr(format!( + "Enum type argument `{key}` expected one of `[{options}]` but received {}", + val.to_string() + ))); + } + } + SchemaValidationRule::IsNumber => { + if !val.is_number() { + return Err(Error::ArgumentErr(format!( + "Argument `{key}` should be a numeric value" + ))); + } + } + SchemaValidationRule::IsInteger => { + if !val.is_i64() && !val.is_u64() { + return Err(Error::ArgumentErr(format!( + "Argument `{key}` should be an integer" + ))); + } + } + SchemaValidationRule::IsString => { + if !val.is_string() { + return Err(Error::ArgumentErr(format!( + "Argument `{key}` should be a string" + ))); + } + } + SchemaValidationRule::IsBool => { + if !val.is_boolean() { + return Err(Error::ArgumentErr(format!( + "Argument `{key}` should be a boolean" + ))); + } + } + SchemaValidationRule::IsObject(o) => { + if !val.is_object() { + return Err(Error::ArgumentErr(format!( + "Argument `{key}` should be an object" + ))); + } + + for (s, rules) in o { + let v = val + .get(&s) + .ok_or(Error::ArgumentErr(format!("Missing field {s} in {key}")))?; + for r in rules { + r.apply_rule(&format!("{key}.{s}"), v, true)?; + } + } + } + SchemaValidationRule::IsArray(vec) => { + if let Some(arr) = val.as_array() { + for (i, el) in arr.iter().enumerate() { + for r in vec { + r.apply_rule(&format!("{key}[{i}]"), el, true)?; + } + } + } else { + return Err(Error::ArgumentErr(format!( + "Argument `{key}` should be an array" + ))); + } + } + // TODO: For better error messages on OneOf, make a dedicated OneOf type that matches the label instead of trying the whole type. + SchemaValidationRule::IsUnionType(vec) => { + let mut match_count = 0; + + let mut errors = String::new(); + for typ in vec { + if let Some(e) = typ + .iter() + .map(|r| r.apply_rule(key, val, true)) + .find_map(Result::err) + { + errors.push_str(&format!("- {e}\n")); + } else { + match_count += 1; + } + } + + if match_count == 0 { + return Err(Error::ArgumentErr(format!( + "Argument `{key}` is not valid, failed matching to one of the expected types. Here is a list of possible errors:\n{errors}" + ))); + } + } + SchemaValidationRule::IsOneOf(vec) => { + let variant_label = val + .get("label") + .ok_or(Error::ArgumentErr(format!( + "oneOf Variant for argument `{key}` should have a label field" + )))? + .as_str() + .ok_or(Error::ArgumentErr(format!( + "Argument `{key}` of type oneOf expected the label to be a string" + )))?; + + let variant_rules = vec + .get(variant_label) + .ok_or_else(|| Error::ArgumentErr(format!( + "Argument `{key}` of type oneOf expected one of the following variants {}, but received `{variant_label}`", vec.keys().join(", ") + )))?; + + for r in variant_rules { + r.apply_rule(key, val, true).map_err(|e| Error::ArgumentErr(format!("Argument `{key}`: The schema for the selected oneOf variant `{variant_label}` was not respected: {e}")))?; + } + } + // TODO: Implement validation on these + SchemaValidationRule::IsDatetime => (), + SchemaValidationRule::IsEmail => (), + SchemaValidationRule::IsBytes => (), + } + + Ok(()) + } +} + +fn find_annotation(comm_lit: &str, annotation: &str, code: &str) -> bool { + let a = format!("{comm_lit} {annotation}"); + for l in code.lines() { + if !l.starts_with(comm_lit) { + break; + } + + if l.trim_end() == a { + return true; + } + } + + false +} + +pub fn should_validate_schema(code: &str, lang: &ScriptLang) -> bool { + let annotation = "schema_validation"; + use ScriptLang::*; + let comment = match lang { + Nativets | Bun | Bunnative | Deno | Php | CSharp | Java => "//", + Python3 | Go | Bash | Powershell | Graphql | Ansible | Nu => "#", + Postgresql | Mysql | Bigquery | Snowflake | Mssql | OracleDB => "--", + Rust => "//!", + // for related places search: ADD_NEW_LANG + }; + find_annotation(comment, annotation, code) +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SchemaValidator { + pub required: Vec, + pub rules: Vec<(String, Vec)>, +} + +impl SchemaValidator { + pub fn validate(&self, args: &HashMap>) -> Result<(), Error> { + for key in &self.required { + if !args.contains_key(key) { + return Err(Error::ArgumentErr(format!("Argument {key} is required"))); + } + } + + for (key, rules) in &self.rules { + if let Some(raw_val) = args.get(key) { + let parsed_val = Value::from_str(raw_val.get()).map_err(|e| { + Error::ArgumentErr(format!("Failed to parse `{key}` argument: {e}")) + })?; + for rule in rules { + rule.apply_rule(key, &parsed_val, self.required.contains(key))?; + } + } + } + + Ok(()) + } + + pub fn from_schema(schema: &str) -> Result { + let schema: Value = serde_json::from_str(schema)?; + + if let Some(draft_version) = schema.get("$schema") { + match draft_version.as_str() { + Some("https://json-schema.org/draft/2020-12/schema") => (), + _ => return Err(anyhow!("Supplied schema draft version is unsuported").into()), + } + } else { + return Err(anyhow!("No draft version supplied").into()); + } + + let required: Vec = schema + .get("required") + .ok_or(anyhow!("Missing `required` field on schema"))? + .as_array() + .ok_or(anyhow!("`required` field should be an array of strings"))? + .into_iter() + .map(|v| { + v.as_str() + .map(|s| s.to_string()) + .ok_or(anyhow!("required field key is not a string")) + }) + .collect::, anyhow::Error>>()?; + + let properties = schema + .get("properties") + .ok_or(anyhow!("Missing `properties` field on schema"))? + .as_object() + .ok_or(anyhow!("`properties` field should be an object"))?; + + let mut rules = vec![]; + + for (key, val) in properties { + rules.push(( + key.clone(), + SchemaValidationRule::from_value(val) + .map_err(|e| anyhow!("Problem making rule for {key}: {e}"))?, + )); + } + + Ok(Self { required, rules }) + } +} + +impl JsonPrimitiveType { + fn from_str(typ: &str) -> Result { + match typ { + "string" => { + return Ok(JsonPrimitiveType::String); + } + "number" => { + return Ok(JsonPrimitiveType::Number); + } + "integer" => { + return Ok(JsonPrimitiveType::Integer); + } + "object" => { + return Ok(JsonPrimitiveType::Object); + } + "array" => { + return Ok(JsonPrimitiveType::Array); + } + "boolean" => { + return Ok(JsonPrimitiveType::Boolean); + } + "null" => { + return Ok(JsonPrimitiveType::Null); + } + other => return Err(anyhow!("Received unsupported type `{other}`").into()), + } + } +} + +#[cfg(test)] +mod tests { + use serde_json::json; + + use super::*; + + fn value_to_rawvalue_map( + value: Value, + ) -> Result>, anyhow::Error> { + match value { + Value::Object(map) => { + let mut result = HashMap::new(); + for (key, val) in map { + let raw = serde_json::to_string(&val)?; // Serialize the Value to a string + let raw_value: Box = serde_json::from_str(&raw)?; // Convert string to Box + result.insert(key, raw_value); + } + Ok(result) + } + _ => Err(anyhow!("Expected a JSON object")), + } + } + #[test] + fn test_parse_and_validate_schema() { + let schema = r#"{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "a": { + "contentEncoding": "base64", + "default": null, + "description": "", + "originalType": "bytes", + "type": "string" + }, + "b": { + "default": null, + "description": "", + "enum": [ + "my", + "enum" + ], + "originalType": "enum", + "type": "string" + }, + "e": { + "default": "inferred type string from default arg", + "description": "", + "originalType": "string", + "type": "string" + }, + "f": { + "default": { + "nested": "object" + }, + "description": "", + "properties": { + "nested": { + "description": "", + "type": "string", + "originalType": "string" + } + }, + "type": "object" + }, + "g": { + "default": null, + "description": "", + "oneOf": [ + { + "type": "object", + "title": "Variant 1", + "properties": { + "label": { + "description": "", + "type": "string", + "originalType": "enum", + "enum": [ + "Variant 1" + ] + }, + "foo": { + "description": "", + "type": "string", + "originalType": "string" + } + } + }, + { + "type": "object", + "title": "Variant 2", + "properties": { + "label": { + "description": "", + "type": "string", + "originalType": "enum", + "enum": [ + "Variant 2" + ] + }, + "bar": { + "description": "", + "type": "number" + } + } + } + ], + "type": "object" + } + }, + "required": [ + "a", + "b", + "g" + ], + "type": "object" +} +"#; + + let validator = SchemaValidator::from_schema(schema) + .expect("Schema couldn't be built from a valid schema"); + + let args = json!( + { + "g": { + "label": "Variant 1", + "foo": "" + }, + "f": { + "nested": "object" + }, + "e": "inferred type string from default arg", + "b": "my", + "a": null + } + ); + + validator + .validate(&value_to_rawvalue_map(args).unwrap()) + .err() + .expect("Validation should not work for this"); + + let args = json!( + { + "g": { + "label": "Variant 1", + "foo": "" + }, + "f": { + "nested": "object" + }, + "e": "inferred type string from default arg", + "b": "not_enum", + "a": "123" + } + ); + + validator + .validate(&value_to_rawvalue_map(args).unwrap()) + .err() + .expect("Validation should not work for this"); + + let args = json!( + { + "g": { + "label": "Variant 1", + "foo": "" + }, + "f": { + "nested": "object" + }, + "e": "inferred type string from default arg", + "b": "my", + "a": "123" + } + ); + + validator + .validate(&value_to_rawvalue_map(args).unwrap()) + .expect("Validation should work for this"); + } +} diff --git a/backend/windmill-common/src/scripts.rs b/backend/windmill-common/src/scripts.rs index 9d896e7f5b..8b67284c4e 100644 --- a/backend/windmill-common/src/scripts.rs +++ b/backend/windmill-common/src/scripts.rs @@ -19,6 +19,8 @@ use crate::{ use crate::worker::HUB_CACHE_DIR; use anyhow::Context; +use backon::ConstantBuilder; +use backon::{BackoffBuilder, Retryable}; use serde::de::Error as _; use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize}; @@ -48,6 +50,9 @@ pub enum ScriptLang { Rust, Ansible, CSharp, + Nu, + Java, + // for related places search: ADD_NEW_LANG } impl ScriptLang { @@ -72,6 +77,9 @@ impl ScriptLang { ScriptLang::Rust => "rust", ScriptLang::Ansible => "ansible", ScriptLang::CSharp => "csharp", + ScriptLang::Nu => "nu", + ScriptLang::Java => "java", + // for related places search: ADD_NEW_LANG } } } @@ -257,7 +265,7 @@ pub struct ScriptHistoryUpdate { pub deployment_msg: Option, } -#[derive(Serialize, Deserialize, Debug, sqlx::Type)] +#[derive(Serialize, Deserialize, Debug, sqlx::Type, Clone)] #[sqlx(transparent)] #[serde(transparent)] pub struct Schema(pub sqlx::types::Json>); @@ -415,6 +423,8 @@ pub async fn get_hub_script_by_path( Some(db), ) .await? + .error_for_status() + .map_err(to_anyhow)? .text() .await .map_err(to_anyhow); @@ -440,6 +450,8 @@ pub async fn get_hub_script_by_path( Some(db), ) .await? + .error_for_status() + .map_err(to_anyhow)? .text() .await .map_err(to_anyhow)?; @@ -494,49 +506,67 @@ async fn get_full_hub_script_by_path_inner( ) -> crate::error::Result { let hub_base_url = HUB_BASE_URL.read().await.clone(); - let result = http_get_from_hub( - http_client, - &format!("{}/raw2/{}", hub_base_url, path), - true, - None, - db, - ) - .await? - .json::() - .await - .context("Decoding hub response to script"); + let response = (|| async { + let response = http_get_from_hub( + http_client, + &format!("{}/raw2/{}", hub_base_url, path), + true, + None, + db, + ) + .await + .and_then(|r| r.error_for_status().map_err(|e| to_anyhow(e).into())); - match result { - Ok(result) => Ok(result), - Err(e) => { - if hub_base_url != DEFAULT_HUB_BASE_URL - && path - .split("/") - .next() - .is_some_and(|x| x.parse::().is_ok_and(|x| x < 10_000_000)) - { - tracing::info!( - "Not found on private hub, fallback to default hub for {}", - path - ); - let value = http_get_from_hub( - http_client, - &format!("{}/raw2/{}", DEFAULT_HUB_BASE_URL, path), - true, - None, - db, - ) - .await? - .json::() - .await - .context("Decoding hub response to script")?; - - Ok(value) - } else { - Err(e)? + match response { + Ok(response) => Ok(response), + Err(e) => { + if hub_base_url != DEFAULT_HUB_BASE_URL + && path + .split("/") + .next() + .is_some_and(|x| x.parse::().is_ok_and(|x| x < 10_000_000)) + { + // TODO: should only fallback to default hub if status is 404 (hub returns 500 currently) + tracing::info!( + "Not found on private hub, fallback to default hub for {}", + path + ); + http_get_from_hub( + http_client, + &format!("{}/raw2/{}", DEFAULT_HUB_BASE_URL, path), + true, + None, + db, + ) + .await? + .error_for_status() + .map_err(|e| to_anyhow(e).into()) + } else { + Err(e) + } } } - } + }) + .retry( + ConstantBuilder::default() + .with_delay(std::time::Duration::from_secs(5)) + .with_max_times(2) + .build(), + ) + .notify(|err, dur| { + tracing::warn!( + "Could not get hub script at path {path}, retrying in {dur:#?}, err: {err:#?}" + ); + }) + .sleep(tokio::time::sleep) + .await?; + + let script = response + .json::() + .await + .context(format!("Decoding hub response for script at path {path}"))?; + + Ok(script) } #[derive(Deserialize, Serialize)] diff --git a/backend/windmill-common/src/tracing_init.rs b/backend/windmill-common/src/tracing_init.rs index 5e6ef6940e..5ed5cec752 100644 --- a/backend/windmill-common/src/tracing_init.rs +++ b/backend/windmill-common/src/tracing_init.rs @@ -60,8 +60,11 @@ pub fn initialize_tracing( "RUST_LOG", &format!("windmill={}", rust_log_env.as_ref().unwrap()), ) - } - let default_env_filter = if rust_log_env.is_ok_and(|x| x == "debug") { + } else if rust_log_env.as_ref().is_ok_and(|x| x == "sqlxdebug") { + std::env::set_var("RUST_LOG", "windmill=debug,sqlx=debug"); + }; + + let default_env_filter = if rust_log_env.is_ok_and(|x| x == "debug" || x == "sqlxdebug") { LevelFilter::DEBUG } else { LevelFilter::INFO diff --git a/backend/windmill-common/src/utils.rs b/backend/windmill-common/src/utils.rs index 735c3d4a27..ff3aea9a19 100644 --- a/backend/windmill-common/src/utils.rs +++ b/backend/windmill-common/src/utils.rs @@ -19,11 +19,10 @@ use git_version::git_version; use chrono::Utc; use croner::Cron; -use rand::distr::Alphanumeric; -use rand::{rng, Rng}; +use rand::{distr::Alphanumeric, rng, Rng}; use reqwest::Client; use semver::Version; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use sha2::{Digest, Sha256}; use sqlx::{Pool, Postgres}; use std::str::FromStr; @@ -47,9 +46,84 @@ lazy_static::lazy_static! { .connect_timeout(std::time::Duration::from_secs(10)) .build().unwrap(); pub static ref GIT_SEM_VERSION: Version = Version::parse( - // skip first `v` character. - GIT_VERSION.split_at(1).1 + if GIT_VERSION.starts_with('v') { + &GIT_VERSION[1..] + } else { + GIT_VERSION + } ).unwrap_or(Version::new(0, 1, 0)); + + + pub static ref MODE_AND_ADDONS: ModeAndAddons = { + let mut search_addon = false; + let mode = std::env::var("MODE") + .map(|x| x.to_lowercase()) + .map(|x| { + if &x == "server" { + println!("Binary is in 'server' mode"); + Mode::Server + } else if &x == "worker" { + tracing::info!("Binary is in 'worker' mode"); + #[cfg(windows)] + { + println!("It is highly recommended to use the agent mode instead on windows (MODE=agent) and to pass a BASE_INTERNAL_URL"); + } + Mode::Worker + } else if &x == "agent" { + println!("Binary is in 'agent' mode"); + if std::env::var("BASE_INTERNAL_URL").is_err() { + panic!("BASE_INTERNAL_URL is required in agent mode") + } + if std::env::var("AGENT_TOKEN").is_err() { + println!("AGENT_TOKEN is not passed. This is required for the agent to work and contains the JWT to authenticate with the server.") + } + + #[cfg(not(feature = "enterprise"))] + { + panic!("Agent mode is only available in the EE, ignoring..."); + } + #[cfg(feature = "enterprise")] + Mode::Agent + } else if &x == "indexer" { + tracing::info!("Binary is in 'indexer' mode"); + #[cfg(not(feature = "tantivy"))] + { + eprintln!("Cannot start the indexer because tantivy is not included in this binary/image. Make sure you are using the EE image if you want to access the full text search features."); + panic!("Indexer mode requires compiling with the tantivy feature flag."); + } + #[cfg(feature = "tantivy")] + Mode::Indexer + } else if &x == "standalone+search"{ + search_addon = true; + println!("Binary is in 'standalone' mode with search enabled"); + Mode::Standalone + } else if &x == "mcp" { + println!("Binary is in 'mcp' mode"); + Mode::MCP + } else { + if &x != "standalone" { + eprintln!("mode not recognized, defaulting to standalone: {x}"); + } else { + println!("Binary is in 'standalone' mode"); + } + Mode::Standalone + } + }) + .unwrap_or_else(|_| { + tracing::info!("Mode not specified, defaulting to standalone"); + Mode::Standalone + }); + ModeAndAddons { + indexer: search_addon, + mode, + } + }; +} + +#[derive(Clone)] +pub struct ModeAndAddons { + pub indexer: bool, + pub mode: Mode, } #[derive(Deserialize, Clone)] @@ -102,6 +176,28 @@ pub fn hostname() -> String { }) } +fn instance_name(hostname: &str) -> String { + hostname + .replace(" ", "") + .split("-") + .last() + .unwrap() + .to_ascii_lowercase() + .to_string() +} + +pub fn worker_suffix(hostname: &str, rd_string: &str) -> String { + format!("{}-{}", instance_name(hostname), rd_string) +} + +pub fn worker_name_with_suffix(is_agent: bool, worker_group: &str, suffix: &str) -> String { + if is_agent { + format!("ag-{}-{}", worker_group, suffix) + } else { + format!("wk-{}-{}", worker_group, suffix) + } +} + pub fn paginate(pagination: Pagination) -> (usize, usize) { let per_page = pagination .per_page @@ -252,6 +348,7 @@ pub enum Mode { Server, Standalone, Indexer, + MCP, } impl std::fmt::Display for Mode { @@ -262,6 +359,7 @@ impl std::fmt::Display for Mode { Mode::Server => write!(f, "server"), Mode::Standalone => write!(f, "standalone"), Mode::Indexer => write!(f, "indexer"), + Mode::MCP => write!(f, "mcp"), } } } @@ -373,6 +471,16 @@ pub async fn report_recovered_critical_error( } } +pub fn empty_string_as_none<'de, D>( + deserializer: D, +) -> std::result::Result, D::Error> +where + D: Deserializer<'de>, +{ + let option = as serde::Deserialize>::deserialize(deserializer)?; + Ok(option.filter(|s| !s.is_empty())) +} + pub async fn fetch_mute_workspace(_db: &DB, workspace_id: &str) -> Result { match sqlx::query!( "SELECT mute_critical_alerts FROM workspace_settings WHERE workspace_id = $1", @@ -421,7 +529,11 @@ impl ScheduleType { } } - pub fn from_str(schedule_str: &str, version: Option<&str>) -> Result { + pub fn from_str( + schedule_str: &str, + version: Option<&str>, + seconds_required: bool, + ) -> Result { tracing::debug!( "Attempting to parse schedule string: {}, with version: {:?}", schedule_str, @@ -445,7 +557,13 @@ impl ScheduleType { Some("v2") | Some(_) => { // Use Croner for v2 let schedule_type_result = panic::catch_unwind(AssertUnwindSafe(|| { - Cron::new(schedule_str).with_seconds_optional().parse() + let mut croner = Cron::new(schedule_str); + if seconds_required { + croner.with_seconds_required(); + } else { + croner.with_seconds_optional(); + }; + croner.parse() })) .map_err(|_| { tracing::error!( diff --git a/backend/windmill-common/src/variables.rs b/backend/windmill-common/src/variables.rs index 48746152ef..3fd2e0513b 100644 --- a/backend/windmill-common/src/variables.rs +++ b/backend/windmill-common/src/variables.rs @@ -7,9 +7,11 @@ */ use crate::error; +use crate::worker::Connection; use crate::{worker::WORKER_GROUP, BASE_URL, DB}; use chrono::{SecondsFormat, Utc}; use magic_crypt::{MagicCrypt256, MagicCryptError, MagicCryptTrait}; +use quick_cache::sync::Cache; use serde::{Deserialize, Serialize}; lazy_static::lazy_static! { @@ -160,8 +162,12 @@ pub fn decrypt(mc: &MagicCrypt256, value: String) -> error::Result { pub const WM_SCHEDULED_FOR: &str = "WM_SCHEDULED_FOR"; +lazy_static::lazy_static! { + pub static ref CUSTOM_ENVS_CACHE: Cache)> = Cache::new(100); +} + pub async fn get_reserved_variables( - db: &DB, + conn: &Connection, w_id: &str, token: &str, email: &str, @@ -201,6 +207,8 @@ pub async fn get_reserved_variables( } }; + let custom_envs = get_cached_workspace_envs(conn, w_id).await; + let joined_schedule_path = schedule_path .clone() .unwrap_or("manual".to_string()) @@ -223,132 +231,160 @@ pub async fn get_reserved_variables( }; vec![ - ContextualVariable { - name: "WM_WORKSPACE".to_string(), - value: w_id.to_string(), - description: "Workspace id of the current script".to_string(), + ContextualVariable { + name: "WM_WORKSPACE".to_string(), + value: w_id.to_string(), + description: "Workspace id of the current script".to_string(), + is_custom: false, + }, + ContextualVariable { + name: "WM_TOKEN".to_string(), + value: token.to_string(), + description: "Token ephemeral to the current script with equal permission to the \ + permission of the run (Usable as a bearer token)" + .to_string(), is_custom: false, - }, - ContextualVariable { - name: "WM_TOKEN".to_string(), - value: token.to_string(), - description: "Token ephemeral to the current script with equal permission to the \ - permission of the run (Usable as a bearer token)" - .to_string(), - is_custom: false, - }, - ContextualVariable { - name: "WM_EMAIL".to_string(), - value: email.to_string(), - description: "Email of the user that executed the current script".to_string(), - is_custom: false, - }, - ContextualVariable { - name: "WM_USERNAME".to_string(), - value: username.to_string(), - description: "Username of the user that executed the current script".to_string(), - is_custom: false, - }, - ContextualVariable { - name: "WM_BASE_URL".to_string(), - value: BASE_URL.read().await.clone(), - description: "base url of this instance".to_string(), - is_custom: false, - }, - ContextualVariable { - name: "WM_JOB_ID".to_string(), - value: job_id.to_string(), - description: "Job id of the current script".to_string(), - is_custom: false, - }, - ContextualVariable { - name: WM_SCHEDULED_FOR.to_string(), - value: scheduled_for - .map(|ts| ts.to_rfc3339_opts(SecondsFormat::Secs, true)) - .unwrap_or_else(|| "".to_string()), - description: "date-time in UTC (e.g: 2014-11-28T12:45:59.324310806Z) of when the job was scheduled".to_string(), - is_custom: false, - }, - ContextualVariable { - name: "WM_JOB_PATH".to_string(), - value: path.unwrap_or_else(|| "".to_string()), - description: "Path of the script or flow being run if any".to_string(), - is_custom: false, - }, - ContextualVariable { - name: "WM_FLOW_JOB_ID".to_string(), - value: flow_id.unwrap_or_else(|| "".to_string()), - description: "Job id of the encapsulating flow if the job is a flow step".to_string(), - is_custom: false, - }, - ContextualVariable { - name: "WM_ROOT_FLOW_JOB_ID".to_string(), - value: root_flow_id.unwrap_or_else(|| "".to_string()), - description: "Job id of the root flow if the job is a flow step".to_string(), - is_custom: false, - }, - ContextualVariable { - name: "WM_FLOW_PATH".to_string(), - value: flow_path.unwrap_or_else(|| "".to_string()), - description: "Path of the encapsulating flow if the job is a flow step".to_string(), - is_custom: false, - }, + }, + ContextualVariable { + name: "WM_EMAIL".to_string(), + value: email.to_string(), + description: "Email of the user that executed the current script".to_string(), + is_custom: false, + }, + ContextualVariable { + name: "WM_USERNAME".to_string(), + value: username.to_string(), + description: "Username of the user that executed the current script".to_string(), + is_custom: false, + }, + ContextualVariable { + name: "WM_BASE_URL".to_string(), + value: BASE_URL.read().await.clone(), + description: "base url of this instance".to_string(), + is_custom: false, + }, + ContextualVariable { + name: "WM_JOB_ID".to_string(), + value: job_id.to_string(), + description: "Job id of the current script".to_string(), + is_custom: false, + }, + ContextualVariable { + name: WM_SCHEDULED_FOR.to_string(), + value: scheduled_for + .map(|ts| ts.to_rfc3339_opts(SecondsFormat::Secs, true)) + .unwrap_or_else(|| "".to_string()), + description: "date-time in UTC (e.g: 2014-11-28T12:45:59.324310806Z) of when the job was scheduled".to_string(), + is_custom: false, + }, + ContextualVariable { + name: "WM_JOB_PATH".to_string(), + value: path.unwrap_or_else(|| "".to_string()), + description: "Path of the script or flow being run if any".to_string(), + is_custom: false, + }, + ContextualVariable { + name: "WM_FLOW_JOB_ID".to_string(), + value: flow_id.unwrap_or_else(|| "".to_string()), + description: "Job id of the encapsulating flow if the job is a flow step".to_string(), + is_custom: false, + }, + ContextualVariable { + name: "WM_ROOT_FLOW_JOB_ID".to_string(), + value: root_flow_id.unwrap_or_else(|| "".to_string()), + description: "Job id of the root flow if the job is a flow step".to_string(), + is_custom: false, + }, + ContextualVariable { + name: "WM_FLOW_PATH".to_string(), + value: flow_path.unwrap_or_else(|| "".to_string()), + description: "Path of the encapsulating flow if the job is a flow step".to_string(), + is_custom: false, + }, - ContextualVariable { - name: "WM_SCHEDULE_PATH".to_string(), - value: schedule_path.unwrap_or_else(|| "".to_string()), - description: "Path of the schedule if the job of the step or encapsulating step has \ - been triggered by a schedule" - .to_string(), - is_custom: false, - }, - ContextualVariable { - name: "WM_PERMISSIONED_AS".to_string(), - value: permissioned_as.to_string(), - description: "Fully Qualified (u/g) owner name of executor of the job".to_string(), + ContextualVariable { + name: "WM_SCHEDULE_PATH".to_string(), + value: schedule_path.unwrap_or_else(|| "".to_string()), + description: "Path of the schedule if the job of the step or encapsulating step has \ + been triggered by a schedule" + .to_string(), is_custom: false, - }, - ContextualVariable { - name: "WM_STATE_PATH".to_string(), - value: state_path.clone(), - description: "State resource path unique to a script and its trigger".to_string(), - is_custom: false, - }, - ContextualVariable { - name: "WM_FLOW_STEP_ID".to_string(), - value: step_id.unwrap_or_else(|| "".to_string()), - description: "The node id in a flow (like 'a', 'b', or 'f')".to_string(), - is_custom: false, - }, - ContextualVariable { - name: "WM_OBJECT_PATH".to_string(), - value: object_path, - description: "Script or flow step execution unique path, useful for storing results in an external service".to_string(), - is_custom: false, - }, - ContextualVariable { - name: "WM_OIDC_JWT".to_string(), - value: jwt_token.unwrap_or_else(|| "".to_string()), - description: "OIDC JWT token (EE only)".to_string(), - is_custom: false, - }, - ContextualVariable { - name: "WM_WORKER_GROUP".to_string(), - value: WORKER_GROUP.clone(), - description: "name of the worker group the job is running on".to_string(), - is_custom: false, - }, - ].into_iter().chain( sqlx::query_as::<_, (String, String)>( - "SELECT name, value FROM workspace_env WHERE workspace_id = $1", - ) - .bind(w_id) - .fetch_all(db) - .await - .unwrap_or_default() - .into_iter().map(|(name, value)| ContextualVariable { - name, - value, - description: "Custom workspace environment variable".to_string(), - is_custom: true, - })).collect() + }, + ContextualVariable { + name: "WM_PERMISSIONED_AS".to_string(), + value: permissioned_as.to_string(), + description: "Fully Qualified (u/g) owner name of executor of the job".to_string(), + is_custom: false, + }, + ContextualVariable { + name: "WM_STATE_PATH".to_string(), + value: state_path.clone(), + description: "State resource path unique to a script and its trigger".to_string(), + is_custom: false, + }, + ContextualVariable { + name: "WM_FLOW_STEP_ID".to_string(), + value: step_id.unwrap_or_else(|| "".to_string()), + description: "The node id in a flow (like 'a', 'b', or 'f')".to_string(), + is_custom: false, + }, + ContextualVariable { + name: "WM_OBJECT_PATH".to_string(), + value: object_path, + description: "Script or flow step execution unique path, useful for storing results in an external service".to_string(), + is_custom: false, + }, + ContextualVariable { + name: "WM_OIDC_JWT".to_string(), + value: jwt_token.unwrap_or_else(|| "".to_string()), + description: "OIDC JWT token (EE only)".to_string(), + is_custom: false, + }, + ContextualVariable { + name: "WM_WORKER_GROUP".to_string(), + value: WORKER_GROUP.clone(), + description: "name of the worker group the job is running on".to_string(), + is_custom: false, + }, +].into_iter().chain(custom_envs.into_iter().map(|(name, value)| ContextualVariable { + name, + value, + description: "Custom workspace environment variable".to_string(), + is_custom: true, +}) +).collect() +} + +async fn get_cached_workspace_envs(conn: &Connection, w_id: &str) -> Vec<(String, String)> { + let cached_envs_o = CUSTOM_ENVS_CACHE.get(w_id).and_then(|(ts, envs)| { + if ts > chrono::Utc::now().timestamp() - (60 * 15) { + Some(envs) + } else { + None + } + }); + + let custom_envs = if let Some(cached_envs) = cached_envs_o { + cached_envs + } else { + let custom_envs = match conn { + Connection::Sql(db) => sqlx::query_as::<_, (String, String)>( + "SELECT name, value FROM workspace_env WHERE workspace_id = $1", + ) + .bind(w_id) + .fetch_all(db) + .await + .unwrap_or_default(), + Connection::Http(client) => client + .get(&format!("/api/w/{w_id}/agent_workers/custom_envs")) + .await + .unwrap_or_default(), + }; + CUSTOM_ENVS_CACHE.insert( + w_id.to_string(), + (chrono::Utc::now().timestamp(), custom_envs.clone()), + ); + custom_envs + }; + custom_envs } diff --git a/backend/windmill-common/src/worker.rs b/backend/windmill-common/src/worker.rs index 65c77a5ee7..b65b50a33a 100644 --- a/backend/windmill-common/src/worker.rs +++ b/backend/windmill-common/src/worker.rs @@ -1,28 +1,57 @@ use anyhow::anyhow; +use bytes::Bytes; use const_format::concatcp; use itertools::Itertools; use regex::Regex; +use reqwest_middleware::ClientWithMiddleware; use semver::Version; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_json::value::RawValue; +use sqlx::{types::Json, Pool, Postgres}; use std::{ cmp::Reverse, collections::{HashMap, HashSet}, - fs::File, + fs::{self, File}, io::Write, + panic::Location, path::{Component, Path, PathBuf}, str::FromStr, sync::{atomic::AtomicBool, Arc}, }; use tokio::sync::RwLock; +use uuid::Uuid; use windmill_macros::annotations; use crate::{ - error, global_settings::CUSTOM_TAGS_SETTING, indexer::TantivyIndexerSettings, server::Smtp, DB, + agent_workers::{PingJobStatusResponse, BASE_INTERNAL_URL}, + cache::{unwrap_or_error, RawNode, RawScript}, + error::{self, to_anyhow}, + global_settings::CUSTOM_TAGS_SETTING, + indexer::TantivyIndexerSettings, + server::Smtp, + KillpillSender, DB, }; +pub const DEFAULT_CLOUD_TIMEOUT: u64 = 900; +pub const DEFAULT_SELFHOSTED_TIMEOUT: u64 = 604800; // 7 days + lazy_static::lazy_static! { - pub static ref WORKER_GROUP: String = std::env::var("WORKER_GROUP").unwrap_or_else(|_| "default".to_string()); + pub static ref WORKER_GROUP: String = std::env::var("WORKER_GROUP").unwrap_or_else(|_| { + #[cfg(not(feature = "enterprise"))] + { + "default".to_string() + } + + #[cfg(feature = "enterprise")] + { + if let Some(token) = crate::agent_workers::DECODED_AGENT_TOKEN.as_ref() { + token.worker_group.clone() + } else { + "default".to_string() + } + } + }); + pub static ref NO_LOGS: bool = std::env::var("NO_LOGS").ok().is_some_and(|x| x == "1" || x == "true"); pub static ref CGROUP_V2_PATH_RE: Regex = Regex::new(r#"(?m)^0::(/.*)$"#).unwrap(); @@ -48,6 +77,9 @@ lazy_static::lazy_static! { "rust".to_string(), "ansible".to_string(), "csharp".to_string(), + "nu".to_string(), + "java".to_string(), + // for related places search: ADD_NEW_LANG "dependency".to_string(), "flow".to_string(), "other".to_string() @@ -56,6 +88,15 @@ lazy_static::lazy_static! { pub static ref DEFAULT_TAGS_PER_WORKSPACE: AtomicBool = AtomicBool::new(false); pub static ref DEFAULT_TAGS_WORKSPACES: Arc>>> = Arc::new(RwLock::new(None)); + pub static ref MAX_TIMEOUT: u64 = std::env::var("TIMEOUT") + .ok() + .and_then(|x| x.parse::().ok()) + .unwrap_or_else(|| if *CLOUD_HOSTED { DEFAULT_CLOUD_TIMEOUT } else { DEFAULT_SELFHOSTED_TIMEOUT }); + + pub static ref SCRIPT_TOKEN_EXPIRY: u64 = std::env::var("SCRIPT_TOKEN_EXPIRY") + .ok() + .and_then(|x| x.parse::().ok()) + .unwrap_or(*MAX_TIMEOUT); pub static ref WORKER_CONFIG: Arc> = Arc::new(RwLock::new(WorkerConfig { worker_tags: Default::default(), @@ -95,93 +136,196 @@ lazy_static::lazy_static! { .unwrap_or(false); pub static ref MIN_VERSION: Arc> = Arc::new(RwLock::new(Version::new(0, 0, 0))); + pub static ref MIN_VERSION_IS_AT_LEAST_1_461: Arc> = Arc::new(RwLock::new(false)); pub static ref MIN_VERSION_IS_AT_LEAST_1_427: Arc> = Arc::new(RwLock::new(false)); pub static ref MIN_VERSION_IS_AT_LEAST_1_432: Arc> = Arc::new(RwLock::new(false)); pub static ref MIN_VERSION_IS_AT_LEAST_1_440: Arc> = Arc::new(RwLock::new(false)); // Features flags: pub static ref DISABLE_FLOW_SCRIPT: bool = std::env::var("DISABLE_FLOW_SCRIPT").ok().is_some_and(|x| x == "1" || x == "true"); + + pub static ref ROOT_STANDALONE_BUNDLE_DIR: String = format!("{}/.windmill/standalone_bundle/", std::env::var("HOME").unwrap_or_else(|_| "/root".to_string())); +} + +pub const ROOT_CACHE_NOMOUNT_DIR: &str = concatcp!(TMP_DIR, "/cache_nomount/"); + +pub static MIN_VERSION_IS_LATEST: AtomicBool = AtomicBool::new(false); + +#[derive(Clone)] +pub struct HttpClient(pub ClientWithMiddleware); + +impl HttpClient { + pub async fn post( + &self, + url: &str, + body: &T, + ) -> anyhow::Result { + let response = self + .0 + .post(format!("{}{}", *BASE_INTERNAL_URL, url)) + .json(body) + .send() + .await + .map_err(|e| anyhow::anyhow!(e))?; + let status = response.status(); + if status.is_success() { + Ok(response.json().await?) + } else { + Err(anyhow::anyhow!(format!( + "HTTP agent request POST {} failed {}", + url, + response.status() + ))) + } + } + + pub async fn get(&self, url: &str) -> anyhow::Result { + let response = self + .0 + .get(format!("{}{}", *BASE_INTERNAL_URL, url)) + .send() + .await + .map_err(|e| anyhow::anyhow!(e))?; + let status = response.status(); + if status.is_success() { + Ok(response.json().await?) + } else { + Err(anyhow::anyhow!(format!( + "HTTP agent request GET {} failed {}", + url, + response.status() + ))) + } + } +} + +#[derive(Clone)] +pub enum Connection { + Sql(Pool), + Http(HttpClient), +} + +impl std::fmt::Debug for Connection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Connection::Sql(_) => write!(f, "Sql"), + Connection::Http(_) => write!(f, "Http"), + } + } +} + +impl Connection { + pub fn as_sql(&self) -> Option<&Pool> { + match self { + Connection::Sql(db) => Some(db), + Connection::Http(_) => None, + } + } +} + +impl From> for Connection { + fn from(value: Pool) -> Self { + Connection::Sql(value) + } +} + +impl From<&Pool> for Connection { + fn from(value: &Pool) -> Self { + Connection::Sql(value.clone()) + } } fn format_pull_query(peek: String) -> String { - format!( + let r = format!( "WITH peek AS ( {} - ), q AS ( + ), q AS NOT MATERIALIZED ( UPDATE v2_job_queue SET running = true, started_at = coalesce(started_at, now()), - suspend_until = null + suspend_until = null, + worker = $1 WHERE id = (SELECT id FROM peek) RETURNING - started_at, scheduled_for, running, - canceled_by, canceled_reason, canceled_by IS NOT NULL AS canceled, - suspend, suspend_until - ), r AS ( + started_at, scheduled_for, + canceled_by, canceled_reason, worker + ), r AS NOT MATERIALIZED ( UPDATE v2_job_runtime SET ping = now() WHERE id = (SELECT id FROM peek) - RETURNING ping AS last_ping, memory_peak AS mem_peak - ), j AS ( + ), j AS NOT MATERIALIZED ( SELECT - id, workspace_id, parent_job, created_by, created_at, runnable_id AS script_hash, - runnable_path AS script_path, args, kind AS job_kind, - CASE WHEN trigger_kind = 'schedule' THEN trigger END AS schedule_path, - permissioned_as, permissioned_as_email AS email, script_lang AS language, - flow_innermost_root_job AS root_job, flow_step_id, flow_step_id IS NOT NULL AS is_flow_step, + id, workspace_id, parent_job, created_by, created_at, runnable_id, + runnable_path, args, kind, trigger, trigger_kind, + permissioned_as, permissioned_as_email, script_lang, + flow_innermost_root_job, flow_step_id, same_worker, pre_run_error, visible_to_owner, tag, concurrent_limit, concurrency_time_window_s, timeout, cache_ttl, priority, raw_code, raw_lock, raw_flow, script_entrypoint_override, preprocessed FROM v2_job WHERE id = (SELECT id FROM peek) - ) SELECT id, workspace_id, parent_job, created_by, created_at, started_at, scheduled_for, - running, script_hash, script_path, args, null as logs, canceled, canceled_by, - canceled_reason, last_ping, job_kind, schedule_path, permissioned_as, - flow_status, is_flow_step, language, suspend, suspend_until, - same_worker, pre_run_error, email, visible_to_owner, mem_peak, - root_job, flow_leaf_jobs as leaf_jobs, tag, concurrent_limit, concurrency_time_window_s, - timeout, flow_step_id, cache_ttl, priority, raw_code, raw_lock, raw_flow, - script_entrypoint_override, preprocessed - FROM q, r, j - LEFT JOIN v2_job_status f USING (id)", + ) SELECT j.id, j.workspace_id, j.parent_job, j.created_by, started_at, scheduled_for, + j.runnable_id, j.runnable_path, j.args, canceled_by, + canceled_reason, j.kind, j.trigger, j.trigger_kind, j.permissioned_as, + flow_status, j.script_lang, + j.same_worker, j.pre_run_error, j.visible_to_owner, + j.tag, j.concurrent_limit, j.concurrency_time_window_s, j.flow_innermost_root_job, + j.timeout, j.flow_step_id, j.cache_ttl, j.priority, j.raw_code, j.raw_lock, j.raw_flow, + j.script_entrypoint_override, j.preprocessed, pj.runnable_path as parent_runnable_path, + COALESCE(p.email, j.permissioned_as_email) as permissioned_as_email, p.username as permissioned_as_username, p.is_admin as permissioned_as_is_admin, + p.is_operator as permissioned_as_is_operator, p.groups as permissioned_as_groups, p.folders as permissioned_as_folders + FROM q, j + LEFT JOIN v2_job_status f USING (id) + LEFT JOIN job_perms p ON p.job_id = j.id + LEFT JOIN v2_job pj ON j.parent_job = pj.id", peek - ) + ); + tracing::debug!("pull query: {}", r); + r } -pub async fn make_suspended_pull_query(wc: &WorkerConfig) { - if wc.worker_tags.len() == 0 { - tracing::error!("Empty tags in worker tags, skipping"); - return; - } - let query = format_pull_query(format!( +pub fn make_suspended_pull_query(tags: &[String]) -> String { + format_pull_query(format!( "SELECT id FROM v2_job_queue WHERE suspend_until IS NOT NULL AND (suspend <= 0 OR suspend_until <= now()) AND tag IN ({}) ORDER BY priority DESC NULLS LAST, created_at FOR UPDATE SKIP LOCKED LIMIT 1", - wc.worker_tags.iter().map(|x| format!("'{x}'")).join(", ") - )); + tags.iter().map(|x| format!("'{x}'")).join(", ") + )) +} +// pub async fn make_suspended +pub async fn store_suspended_pull_query(wc: &WorkerConfig) { + if wc.worker_tags.len() == 0 { + tracing::error!("Empty tags in worker tags, skipping"); + return; + } + let query = make_suspended_pull_query(&wc.worker_tags); let mut l = WORKER_SUSPENDED_PULL_QUERY.write().await; *l = query; } -pub async fn make_pull_query(wc: &WorkerConfig) { +pub fn make_pull_query(tags: &[String]) -> String { + format_pull_query(format!( + "SELECT id + FROM v2_job_queue + WHERE running = false AND tag IN ({}) AND scheduled_for <= now() + ORDER BY priority DESC NULLS LAST, scheduled_for + FOR UPDATE SKIP LOCKED + LIMIT 1", + tags.iter().map(|x| format!("'{x}'")).join(", ") + )) +} + +pub async fn store_pull_query(wc: &WorkerConfig) { let mut queries = vec![]; for tags in wc.priority_tags_sorted.iter() { if tags.tags.len() == 0 { tracing::error!("Empty tags in priority tags, skipping"); continue; } - let query = format_pull_query(format!( - "SELECT id - FROM v2_job_queue - WHERE running = false AND tag IN ({}) AND scheduled_for <= now() - ORDER BY priority DESC NULLS LAST, scheduled_for - FOR UPDATE SKIP LOCKED - LIMIT 1", - tags.tags.iter().map(|x| format!("'{x}'")).join(", ") - )); + let query = make_pull_query(&tags.tags); queries.push(query); } let mut l = WORKER_PULL_QUERIES.write().await; @@ -203,6 +347,13 @@ pub fn write_file(dir: &str, path: &str, content: &str) -> error::Result { Ok(file) } +pub fn write_file_bytes(dir: &str, path: &str, content: &Bytes) -> error::Result { + let path = format!("{}/{}", dir, path); + let mut file = File::create(&path)?; + file.write_all(content)?; + file.flush()?; + Ok(file) +} /// from : https://github.com/rust-lang/cargo/blob/fede83ccf973457de319ba6fa0e36ead454d2e20/src/cargo/util/paths.rs#L61 fn normalize_path(path: &Path) -> PathBuf { let mut components = path.components().peekable(); @@ -230,11 +381,10 @@ fn normalize_path(path: &Path) -> PathBuf { } ret } -pub fn write_file_at_user_defined_location( + +pub fn is_allowed_file_location( job_dir: &str, user_defined_path: &str, - content: &str, - mode: Option, ) -> error::Result { let job_dir = Path::new(job_dir); let user_path = PathBuf::from(user_defined_path); @@ -254,6 +404,17 @@ pub fn write_file_at_user_defined_location( .into()); } + Ok(normalized_full_path) +} + +pub fn write_file_at_user_defined_location( + job_dir: &str, + user_defined_path: &str, + content: &str, + mode: Option, +) -> error::Result { + let normalized_full_path = is_allowed_file_location(job_dir, user_defined_path)?; + let full_path = normalized_full_path.as_path(); if let Some(parent_dir) = full_path.parent() { std::fs::create_dir_all(parent_dir)?; @@ -357,9 +518,6 @@ fn parse_file(path: &str) -> Option { #[annotations("#")] pub struct PythonAnnotations { pub no_cache: bool, - pub no_uv: bool, - pub no_uv_install: bool, - pub no_uv_compile: bool, pub no_postinstall: bool, pub py310: bool, pub py311: bool, @@ -384,8 +542,43 @@ pub struct SqlAnnotations { pub struct BashAnnotations { pub docker: bool, } +/// length = 5 +/// value = "foo" +/// output = "foo " +/// 12345 +pub fn pad_string(value: &str, total_length: usize) -> String { + if value.len() >= total_length { + value.to_string() // Return the original string if it's already long enough + } else { + let padding_needed = total_length - value.len(); + format!("{value}{}", " ".repeat(padding_needed)) // Pad with spaces + } +} +pub fn copy_dir_recursively(src: &Path, dst: &Path) -> error::Result<()> { + if !dst.exists() { + fs::create_dir_all(dst)?; + } -pub async fn load_cache(bin_path: &str, _remote_path: &str) -> (bool, String) { + tracing::debug!("Copying recursively from {:?} to {:?}", src, dst); + + for entry in fs::read_dir(src)? { + let entry = entry?; + let src_path = entry.path(); + let dst_path = dst.join(entry.file_name()); + + if src_path.is_dir() && !src_path.is_symlink() { + copy_dir_recursively(&src_path, &dst_path)?; + } else { + fs::copy(&src_path, &dst_path)?; + } + } + + tracing::debug!("Finished copying recursively from {:?} to {:?}", src, dst); + + Ok(()) +} + +pub async fn load_cache(bin_path: &str, _remote_path: &str, is_dir: bool) -> (bool, String) { if tokio::fs::metadata(&bin_path).await.is_ok() { (true, format!("loaded from local cache: {}\n", bin_path)) } else { @@ -399,12 +592,22 @@ pub async fn load_cache(bin_path: &str, _remote_path: &str) -> (bool, String) { use crate::s3_helpers::attempt_fetch_bytes; if let Ok(mut x) = attempt_fetch_bytes(os, _remote_path).await { - if let Err(e) = write_binary_file(bin_path, &mut x) { - tracing::error!("could not write bundle/bin file locally: {e:?}"); - return ( - false, - "error writing bundle/bin file from object store".to_string(), - ); + if is_dir { + if let Err(e) = extract_tar(x, bin_path).await { + tracing::error!("could not write tar archive locally: {e:?}"); + return ( + false, + "error writing tar archive from object store".to_string(), + ); + } + } else { + if let Err(e) = write_binary_file(bin_path, &mut x) { + tracing::error!("could not write bundle/bin file locally: {e:?}"); + return ( + false, + "error writing bundle/bin file from object store".to_string(), + ); + } } tracing::info!("loaded from object store {}", bin_path); return ( @@ -417,6 +620,7 @@ pub async fn load_cache(bin_path: &str, _remote_path: &str) -> (bool, String) { ); } } + let _ = is_dir; (false, "".to_string()) } } @@ -444,6 +648,7 @@ pub async fn save_cache( local_cache_path: &str, _remote_cache_path: &str, origin: &str, + is_dir: bool, ) -> crate::error::Result { let mut _cached_to_s3 = false; #[cfg(all(feature = "enterprise", feature = "parquet"))] @@ -453,11 +658,33 @@ pub async fn save_cache( .clone() { use object_store::path::Path; + let file_to_cache = if is_dir { + let tar_path = format!( + "{ROOT_CACHE_DIR}/tar/{}_tar.tar", + local_cache_path + .split("/") + .last() + .unwrap_or(&uuid::Uuid::new_v4().to_string()) + ); + let tar_file = std::fs::File::create(&tar_path)?; + let mut tar = tar::Builder::new(tar_file); + tar.append_dir_all(".", &origin)?; + let tar_metadata = tokio::fs::metadata(&tar_path).await; + if tar_metadata.is_err() || tar_metadata.as_ref().unwrap().len() == 0 { + tracing::info!("Failed to tar cache: {origin}"); + return Err(error::Error::ExecutionErr(format!( + "Failed to tar cache: {origin}" + ))); + } + tar_path + } else { + origin.to_owned() + }; if let Err(e) = os .put( &Path::from(_remote_cache_path), - std::fs::read(origin)?.into(), + std::fs::read(&file_to_cache)?.into(), ) .await { @@ -467,12 +694,19 @@ pub async fn save_cache( ); } else { _cached_to_s3 = true; + if is_dir { + tokio::fs::remove_dir_all(&file_to_cache).await?; + } } } // if !*CLOUD_HOSTED { if true { - std::fs::copy(origin, local_cache_path)?; + if is_dir { + copy_dir_recursively(&PathBuf::from(origin), &PathBuf::from(local_cache_path))?; + } else { + std::fs::copy(origin, local_cache_path)?; + } Ok(format!( "\nwrote cached binary: {} (backed by EE distributed object store: {_cached_to_s3})\n", local_cache_path @@ -487,6 +721,31 @@ pub async fn save_cache( } } +#[cfg(all(feature = "enterprise", feature = "parquet"))] +pub async fn extract_tar(tar: bytes::Bytes, folder: &str) -> error::Result<()> { + use std::time::Instant; + + use bytes::Buf; + use tokio::fs::{self}; + + let start: Instant = Instant::now(); + fs::create_dir_all(&folder).await?; + + let mut ar = tar::Archive::new(tar.reader()); + + if let Err(e) = ar.unpack(folder) { + tracing::info!("Failed to untar to {folder}. Error: {:?}", e); + fs::remove_dir_all(&folder).await?; + return Err(error::Error::ExecutionErr(format!( + "Failed to untar tar {folder}" + ))); + } + tracing::info!( + "Finished extracting tar to {folder}. Took {}ms", + start.elapsed().as_millis(), + ); + Ok(()) +} #[cfg(all(feature = "enterprise", feature = "parquet"))] fn write_binary_file(main_path: &str, byts: &mut bytes::Bytes) -> error::Result<()> { use std::fs::{File, Permissions}; @@ -637,29 +896,39 @@ pub fn get_windmill_memory_usage() -> Option { } } -pub async fn update_min_version<'c, E: sqlx::Executor<'c, Database = sqlx::Postgres>>( - executor: E, -) -> bool { +pub async fn update_min_version(conn: &Connection) -> bool { use crate::utils::{GIT_SEM_VERSION, GIT_VERSION}; - // fetch all pings with a different version than self from the last 5 minutes. - let pings = sqlx::query_scalar!( - "SELECT wm_version FROM worker_ping WHERE wm_version != $1 AND ping_at > now() - interval '5 minutes'", - GIT_VERSION - ).fetch_all(executor).await.unwrap_or_default(); - let cur_version = GIT_SEM_VERSION.clone(); - let min_version = pings - .iter() - .filter(|x| !x.is_empty()) - .filter_map(|x| semver::Version::parse(x.split_at(1).1).ok()) - .min() - .unwrap_or_else(|| cur_version.clone()); + + let min_version = match conn { + Connection::Sql(pool) => { + // fetch all pings with a different version than self from the last 5 minutes. + let pings = sqlx::query_scalar!( + "SELECT wm_version FROM worker_ping WHERE wm_version != $1 AND ping_at > now() - interval '5 minutes'", + GIT_VERSION + ).fetch_all(pool).await.unwrap_or_default(); + + pings + .iter() + .filter(|x| !x.is_empty()) + .filter_map(|x| { + semver::Version::parse(if x.starts_with('v') { &x[1..] } else { x }).ok() + }) + .min() + .unwrap_or_else(|| cur_version.clone()) + } + Connection::Http(_) => { + // TODO: get min version from server, for now we use the current version. Min version should be of no interest for http mode workers + cur_version.clone() + } + }; if min_version != cur_version { tracing::info!("Minimal worker version: {min_version}"); } + *MIN_VERSION_IS_AT_LEAST_1_461.write().await = min_version >= Version::new(1, 461, 0); *MIN_VERSION_IS_AT_LEAST_1_427.write().await = min_version >= Version::new(1, 427, 0); *MIN_VERSION_IS_AT_LEAST_1_432.write().await = min_version >= Version::new(1, 432, 0); *MIN_VERSION_IS_AT_LEAST_1_440.write().await = min_version >= Version::new(1, 440, 0); @@ -668,40 +937,333 @@ pub async fn update_min_version<'c, E: sqlx::Executor<'c, Database = sqlx::Postg min_version >= cur_version } -pub async fn update_ping(worker_instance: &str, worker_name: &str, ip: &str, db: &DB) { - let (tags, dw) = { - let wc = WORKER_CONFIG.read().await.clone(); - ( - wc.worker_tags, - wc.dedicated_worker - .as_ref() - .map(|x| format!("{}:{}", x.workspace_id, x.path)), - ) - }; +#[derive(Serialize, Deserialize)] +pub enum PingType { + Initial, + MainLoop, + Job, + InitScript, +} +#[derive(Serialize, Deserialize)] +pub struct Ping { + pub last_job_executed: Option, + pub last_job_workspace_id: Option, + pub worker_instance: Option, + pub ip: Option, + pub tags: Option>, + pub dw: Option, + pub version: Option, + pub vcpus: Option, + pub memory: Option, + pub memory_usage: Option, + pub wm_memory_usage: Option, + pub jobs_executed: Option, + pub occupancy_rate: Option, + pub occupancy_rate_15s: Option, + pub occupancy_rate_5m: Option, + pub occupancy_rate_30m: Option, + pub ping_type: PingType, +} +pub async fn update_ping_http( + insert_ping: Ping, + worker_name: &str, + worker_group: &str, + db: &DB, +) -> anyhow::Result<()> { + // tracing::info!("update ping: {}", insert_ping.tags.join(",")); + match insert_ping.ping_type { + PingType::MainLoop => { + update_worker_ping_main_loop_query( + worker_name, + insert_ping.tags.unwrap_or_default().as_slice(), + insert_ping.vcpus, + insert_ping.memory, + insert_ping.jobs_executed, + insert_ping.occupancy_rate, + insert_ping.memory_usage, + insert_ping.wm_memory_usage, + insert_ping.occupancy_rate_15s, + insert_ping.occupancy_rate_5m, + insert_ping.occupancy_rate_30m, + db, + ) + .await? + } + PingType::Initial => { + if insert_ping.worker_instance.is_none() + || insert_ping.version.is_none() + || insert_ping.ip.is_none() + { + return Err(anyhow::anyhow!( + "Worker instance, version and ip are required" + )); + } - let vcpus = get_vcpus(); - let memory = get_memory(); + insert_ping_query( + &insert_ping.worker_instance.unwrap(), + &worker_name, + worker_group, + &insert_ping.ip.unwrap(), + insert_ping.tags.unwrap_or_default().as_slice(), + insert_ping.dw, + &insert_ping.version.unwrap(), + insert_ping.vcpus, + insert_ping.memory, + db, + ) + .await?; + } + PingType::Job => { + update_worker_ping_from_job_query( + &insert_ping.last_job_executed.unwrap_or_default(), + &insert_ping.last_job_workspace_id.unwrap_or_default(), + worker_name, + insert_ping.memory_usage, + insert_ping.wm_memory_usage, + insert_ping.occupancy_rate, + insert_ping.occupancy_rate_15s, + insert_ping.occupancy_rate_5m, + insert_ping.occupancy_rate_30m, + db, + ) + .await?; + } + PingType::InitScript => { + update_ping_for_failed_init_script_query( + worker_name, + insert_ping.last_job_executed.unwrap_or_default(), + db, + ) + .await? + } + } + Ok(()) +} +#[derive(Serialize, Deserialize)] +pub struct JobCancelled { + pub canceled_by: String, + pub reason: String, +} + +pub async fn set_job_cancelled_query( + job_id: Uuid, + db: &DB, + canceled_by: &str, + reason: &str, +) -> anyhow::Result<()> { + sqlx::query!( + "UPDATE v2_job_queue + SET canceled_by = $1 + , canceled_reason = $2 +WHERE id = $3", + canceled_by, + reason, + job_id + ) + .execute(db) + .await?; + Ok(()) +} + +pub async fn update_ping_for_failed_init_script_query( + worker_name: &str, + last_job_id: Uuid, + db: &DB, +) -> anyhow::Result<()> { + sqlx::query!( + "UPDATE worker_ping SET +ping_at = now(), +jobs_executed = 1, +current_job_id = $1, +current_job_workspace_id = 'admins' +WHERE worker = $2", + last_job_id, + worker_name + ) + .execute(db) + .await?; + Ok(()) +} + +pub async fn fetch_flow_node_query( + db: &DB, + id: i64, + loc: &'static Location<'_>, +) -> error::Result { + let r = sqlx::query!( + "SELECT \ + code AS \"raw_code: String\", \ + lock AS \"raw_lock: String\", \ + flow AS \"raw_flow: Json>\" \ + FROM flow_node WHERE id = $1 LIMIT 1", + id, + ) + .fetch_optional(db) + .await + .map_err(Into::into) + .and_then(unwrap_or_error(loc, "Flow node", id)) + .map(|r| RawNode { + raw_code: r.raw_code, + raw_lock: r.raw_lock, + raw_flow: r.raw_flow.map(|Json(raw_flow)| raw_flow), + })?; + Ok(r) +} + +pub async fn fetch_raw_script_from_app_query( + db: &DB, + id: i64, + loc: &'static Location<'_>, +) -> error::Result { + sqlx::query!( + "SELECT lock, code FROM app_script WHERE id = $1 LIMIT 1", + id, + ) + .fetch_optional(db) + .await + .map_err(Into::into) + .and_then(unwrap_or_error(&loc, "Application script", id)) + .map(|r| RawScript { content: r.code, lock: r.lock, meta: None }) +} + +pub async fn insert_ping_query( + worker_instance: &str, + worker_name: &str, + worker_group: &str, + ip: &str, + tags: &[String], + dw: Option, + version: &str, + vcpus: Option, + memory: Option, + db: &DB, +) -> anyhow::Result<()> { sqlx::query!( "INSERT INTO worker_ping (worker_instance, worker, ip, custom_tags, worker_group, dedicated_worker, wm_version, vcpus, memory) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT (worker) DO UPDATE set ip = $3, custom_tags = $4, worker_group = $5", worker_instance, worker_name, ip, - tags.as_slice(), - *WORKER_GROUP, + tags, + worker_group, dw, - crate::utils::GIT_VERSION, + version, vcpus, memory + ) + .execute(db) + .await?; + Ok(()) +} + +pub async fn update_worker_ping_from_job_query( + job_id: &Uuid, + w_id: &str, + worker_name: &str, + memory_usage: Option, + wm_memory_usage: Option, + occupancy_rate: Option, + occupancy_rate_15s: Option, + occupancy_rate_5m: Option, + occupancy_rate_30m: Option, + db: &DB, +) -> anyhow::Result<()> { + sqlx::query!( + "UPDATE worker_ping SET ping_at = now(), current_job_id = $1, current_job_workspace_id = $2, memory_usage = $3, wm_memory_usage = $4, + occupancy_rate = $6, occupancy_rate_15s = $7, occupancy_rate_5m = $8, occupancy_rate_30m = $9 WHERE worker = $5", + job_id, + w_id, + memory_usage, + wm_memory_usage, + worker_name, + occupancy_rate, + occupancy_rate_15s, + occupancy_rate_5m, + occupancy_rate_30m, ) .execute(db) - .await - .expect("insert worker_ping initial value"); + .await?; + Ok(()) } +pub async fn update_job_ping_query( + job_id: &Uuid, + db: &DB, + mem_peak: Option, +) -> anyhow::Result { + let ro = sqlx::query!( + "UPDATE v2_job_runtime r SET + memory_peak = $1, + ping = now() + FROM v2_job_queue q + WHERE r.id = $2 AND q.id = r.id + RETURNING canceled_by, canceled_reason", + mem_peak, + job_id + ) + .map(|x| PingJobStatusResponse { + canceled_by: x.canceled_by, + canceled_reason: x.canceled_reason, + already_completed: false, + }) + .fetch_optional(db) + .await; + + // TODO: add memory metrics to memory time series + + if let Ok(r) = ro { + if let Some(i) = r { + Ok(i) + } else { + Err(anyhow::anyhow!("Job not found")) + } + } else { + Err(to_anyhow(ro.unwrap_err())) + } +} + +pub async fn update_worker_ping_main_loop_query( + worker_name: &str, + tags: &[String], + vcpus: Option, + memory: Option, + jobs_executed: Option, + occupancy_rate: Option, + memory_usage: Option, + wm_memory_usage: Option, + occupancy_rate_15s: Option, + occupancy_rate_5m: Option, + occupancy_rate_30m: Option, + db: &DB, +) -> anyhow::Result<()> { + sqlx::query!( + "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2, + occupancy_rate = $3, memory_usage = $4, wm_memory_usage = $5, vcpus = COALESCE($7, vcpus), + memory = COALESCE($8, memory), occupancy_rate_15s = $9, occupancy_rate_5m = $10, occupancy_rate_30m = $11 WHERE worker = $6", + jobs_executed, + tags, + occupancy_rate, + memory_usage, + wm_memory_usage, + worker_name, + vcpus, + memory, + occupancy_rate_15s, + occupancy_rate_5m, + occupancy_rate_30m, + ) + .execute(db) + .await?; + Ok(()) +} + +// "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2, +// occupancy_rate = $3, memory_usage = $4, wm_memory_usage = $5, vcpus = COALESCE($7, vcpus), +// memory = COALESCE($8, memory), occupancy_rate_15s = $9, occupancy_rate_5m = $10, occupancy_rate_30m = $11 WHERE worker = $6", + pub async fn load_worker_config( db: &DB, - killpill_tx: tokio::sync::broadcast::Sender<()>, + killpill_tx: KillpillSender, ) -> error::Result { tracing::info!("Loading config from WORKER_GROUP: {}", *WORKER_GROUP); let mut config: WorkerConfigOpt = sqlx::query_scalar!( @@ -735,7 +1297,7 @@ pub async fn load_worker_config( .map(|x| { let splitted = x.split(':').to_owned().collect_vec(); if splitted.len() != 2 { - killpill_tx.send(()).expect("send"); + killpill_tx.send(); return Err(anyhow::anyhow!( "Invalid dedicated_worker format. Got {x}, expects :" )); @@ -896,7 +1458,7 @@ pub struct WorkspacedPath { pub path: String, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct WorkerConfigOpt { pub worker_tags: Option>, pub priority_tags: Option>, diff --git a/backend/windmill-common/src/workspaces.rs b/backend/windmill-common/src/workspaces.rs index d674285479..1238d5b243 100644 --- a/backend/windmill-common/src/workspaces.rs +++ b/backend/windmill-common/src/workspaces.rs @@ -1,3 +1,4 @@ +use quick_cache::sync::Cache; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Default)] @@ -27,6 +28,7 @@ pub enum ObjectType { ResourceType, User, Group, + Trigger, } #[derive(Serialize, Deserialize, Debug)] @@ -37,3 +39,21 @@ pub struct GitRepositorySettings { pub group_by_folder: Option, pub exclude_types_override: Option>, } + +lazy_static::lazy_static! { + pub static ref IS_PREMIUM_CACHE: Cache = Cache::new(5000); +} + +#[cfg(feature = "cloud")] +pub async fn is_premium_workspace(_db: &crate::DB, _w_id: &str) -> bool { + let cached = IS_PREMIUM_CACHE.get(_w_id); + if let Some(cached) = cached { + return cached; + } + let premium = sqlx::query_scalar!("SELECT premium FROM workspace WHERE id = $1", _w_id) + .fetch_one(_db) + .await + .unwrap_or(false); + IS_PREMIUM_CACHE.insert(_w_id.to_string(), premium); + premium +} diff --git a/backend/windmill-queue/src/flow_status.rs b/backend/windmill-queue/src/flow_status.rs new file mode 100644 index 0000000000..3bc6b3c815 --- /dev/null +++ b/backend/windmill-queue/src/flow_status.rs @@ -0,0 +1,135 @@ +use uuid::Uuid; +use windmill_common::{ + error::{self, Error}, + utils::WarnAfterExt, + DB, +}; + +#[derive(Debug, Copy, Clone)] +pub enum Step { + Step(usize), + PreprocessorStep, + FailureStep, +} + +impl Step { + pub fn from_i32_and_len(step: i32, len: usize) -> Self { + if step < 0 { + Step::PreprocessorStep + } else if (step as usize) < len { + Step::Step(step as usize) + } else { + Step::FailureStep + } + } +} + +pub async fn update_flow_status_in_progress( + db: &DB, + _w_id: &str, + flow: Uuid, + job_in_progress: Uuid, +) -> error::Result { + let step = get_step_of_flow_status(db, flow).await?; + match step { + Step::Step(step) => { + sqlx::query!( + "UPDATE v2_job_status SET + flow_status = jsonb_set( + jsonb_set(flow_status, ARRAY['modules', $3::INTEGER::TEXT, 'job'], to_jsonb($1::UUID::TEXT)), + ARRAY['modules', $3::INTEGER::TEXT, 'type'], + to_jsonb('InProgress'::text) + ) + WHERE id = $2", + job_in_progress, + flow, + step as i32 + ) + .execute(db) + .await?; + } + Step::PreprocessorStep => { + sqlx::query!( + "UPDATE v2_job_status SET + flow_status = jsonb_set( + jsonb_set(flow_status, ARRAY['preprocessor_module', 'job'], to_jsonb($1::UUID::TEXT)), + ARRAY['preprocessor_module', 'type'], + to_jsonb('InProgress'::text) + ) + WHERE id = $2", + job_in_progress, + flow + ) + .execute(db) + .await?; + } + Step::FailureStep => { + sqlx::query!( + "UPDATE v2_job_status SET + flow_status = jsonb_set( + jsonb_set(flow_status, ARRAY['failure_module', 'job'], to_jsonb($1::UUID::TEXT)), + ARRAY['failure_module', 'type'], + to_jsonb('InProgress'::text) + ) + WHERE id = $2", + job_in_progress, + flow + ) + .execute(db) + .await?; + } + } + + Ok(step) +} + +pub async fn update_workflow_as_code_status( + db: &DB, + id: &Uuid, + parent_job: &Uuid, +) -> error::Result<()> { + let _ = sqlx::query_scalar!( + "UPDATE v2_job_status SET + workflow_as_code_status = jsonb_set( + jsonb_set( + COALESCE(workflow_as_code_status, '{}'::jsonb), + array[$1], + COALESCE(workflow_as_code_status->$1, '{}'::jsonb) + ), + array[$1, 'started_at'], + to_jsonb(now()::text) + ) + WHERE id = $2", + id.to_string(), + parent_job + ) + .execute(db) + .warn_after_seconds(5) + .await + .inspect_err(|e| { + tracing::error!( + "Could not update parent job `started_at` in workflow as code status: {}", + e + ) + }); + Ok(()) +} + +// TODO: merge as a CTE +#[tracing::instrument(level = "trace", skip_all)] +async fn get_step_of_flow_status(db: &DB, id: Uuid) -> error::Result { + let r = sqlx::query!( + "SELECT (flow_status->'step')::integer as step, jsonb_array_length(flow_status->'modules') as len + FROM v2_job_status WHERE id = $1", + id + ) + .fetch_one(db) + .await + .map_err(|e| Error::internal_err(format!("fetching step flow status: {e:#}")))?; + + if let Some(step) = r.step { + Ok(Step::from_i32_and_len(step, r.len.unwrap_or(0) as usize)) + } else { + Err(Error::internal_err("step is null".to_string())) + } +} diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index c76747af5a..93ea9d0bac 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -6,7 +6,8 @@ * LICENSE-AGPL for a copy of the license. */ -use std::{borrow::Borrow, collections::HashMap, sync::Arc, vec}; +use std::fmt; +use std::{collections::HashMap, sync::Arc, vec}; use anyhow::Context; use async_recursion::async_recursion; @@ -17,8 +18,10 @@ use itertools::Itertools; use prometheus::IntCounter; use regex::Regex; use reqwest::Client; +use serde::Deserialize; use serde::{ser::SerializeMap, Serialize}; use serde_json::{json, value::RawValue}; +use sqlx::PgExecutor; use sqlx::{types::Json, FromRow, Pool, Postgres, Transaction}; use tokio::{sync::RwLock, time::sleep}; use ulid::Ulid; @@ -26,7 +29,13 @@ use uuid::Uuid; use windmill_audit::audit_ee::{audit_log, AuditAuthor}; use windmill_audit::ActionKind; +#[cfg(feature = "benchmark")] +use windmill_common::add_time; +use windmill_common::auth::JobPerms; +#[cfg(feature = "benchmark")] +use windmill_common::bench::BenchmarkIter; use windmill_common::utils::now_from_db; +use windmill_common::worker::{Connection, SCRIPT_TOKEN_EXPIRY}; use windmill_common::{ auth::{fetch_authed_from_permissioned_as, permissioned_as_to_username}, cache::{self, FlowData}, @@ -46,7 +55,7 @@ use windmill_common::{ users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL}, utils::{not_found_if_none, report_critical_error, StripPath, WarnAfterExt}, worker::{ - to_raw_value, CLOUD_HOSTED, DEFAULT_TAGS_PER_WORKSPACE, DEFAULT_TAGS_WORKSPACES, + to_raw_value, CLOUD_HOSTED, DISABLE_FLOW_SCRIPT, MIN_VERSION_IS_AT_LEAST_1_432, MIN_VERSION_IS_AT_LEAST_1_440, NO_LOGS, WORKER_PULL_QUERIES, WORKER_SUSPENDED_PULL_QUERY, }, @@ -62,7 +71,10 @@ use windmill_common::BASE_URL; #[cfg(feature = "cloud")] use windmill_common::users::SUPERADMIN_SYNC_EMAIL; +use crate::flow_status::{update_flow_status_in_progress, update_workflow_as_code_status}; +use crate::jobs_ee::update_concurrency_counter; use crate::schedule::{get_schedule_opt, push_scheduled_job}; +use crate::tags::per_workspace_tag; #[cfg(feature = "prometheus")] lazy_static::lazy_static! { @@ -98,8 +110,6 @@ lazy_static::lazy_static! { .build().unwrap(); - pub static ref JOB_TOKEN: Option = std::env::var("JOB_TOKEN").ok(); - static ref JOB_ARGS_AUDIT_LOGS: bool = std::env::var("JOB_ARGS_AUDIT_LOGS") .ok() .and_then(|x| x.parse().ok()) @@ -121,12 +131,26 @@ const SCHEDULE_ERROR_HANDLER_USER_EMAIL: &str = "schedule_error_handler@windmill #[cfg(any(feature = "enterprise", feature = "cloud"))] const SCHEDULE_RECOVERY_HANDLER_USER_EMAIL: &str = "schedule_recovery_handler@windmill.dev"; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct CanceledBy { pub username: Option, pub reason: Option, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct JobCompleted { + pub job: Arc, + pub result: Arc>, + pub result_columns: Option>, + pub mem_peak: i32, + pub success: bool, + pub cached_res_path: Option, + pub token: String, + pub canceled_by: Option, + pub duration: Option, +} + + pub async fn cancel_single_job<'c>( username: &str, reason: Option, @@ -140,22 +164,24 @@ pub async fn cancel_single_job<'c>( let username = username.to_string(); let w_id = w_id.to_string(); let db = db.clone(); + tracing::info!("cancelling job {:?}", job_running.id); let job_running = job_running.clone(); tokio::task::spawn(async move { let reason: String = reason .clone() .unwrap_or_else(|| "unexplicited reasons".to_string()); let e = serde_json::json!({"message": format!("Job canceled: {reason} by {username}"), "name": "Canceled", "reason": reason, "canceler": username}); + append_logs( &job_running.id, w_id.to_string(), format!("canceled by {username}: (force cancel: {force_cancel})"), - &db, + &Connection::from(db.clone()), ) .await; let add_job = add_completed_job_error( &db, - &job_running, + &MiniPulledJob::from(&job_running), job_running.mem_peak.unwrap_or(0), Some(CanceledBy { username: Some(username.to_string()), reason: Some(reason) }), e, @@ -241,22 +267,40 @@ pub async fn cancel_job<'c>( let job = Arc::new(job); - // get all children - let mut jobs = vec![job.id]; - let mut jobs_to_cancel = vec![]; - while !jobs.is_empty() { - let p_job = jobs.pop(); - let new_jobs = sqlx::query_scalar!( - "SELECT id AS \"id!\" FROM v2_job WHERE parent_job = $1 AND workspace_id = $2", - p_job, - w_id - ) - .fetch_all(&mut *tx) - .await?; - jobs.extend(new_jobs.clone()); - jobs_to_cancel.extend(new_jobs); - } - jobs.reverse(); + // get all children using recursive CTE + let mut jobs_to_cancel = sqlx::query!( + r#" +WITH RECURSIVE job_tree AS ( + -- Base case: direct children of the given parent job + SELECT id, parent_job, 1 AS depth + FROM v2_job_queue + INNER JOIN v2_job USING (id) + WHERE parent_job = $1 AND v2_job.workspace_id = $2 + + UNION ALL + + -- Recursive case: fetch children of previously found jobs + SELECT q.id, j.parent_job, t.depth + 1 + FROM v2_job_queue q + INNER JOIN v2_job j USING (id) + INNER JOIN job_tree t ON t.id = j.parent_job + WHERE j.workspace_id = $2 AND t.depth < 500 -- Limit recursion depth to 500 +) +SELECT id AS id, depth +FROM job_tree +ORDER BY depth, id + "#, + job.id, + w_id + ) + .fetch_all(&mut *tx) + .await? + .into_iter() + .filter_map(|r| r.id.clone()) + .collect_vec(); + + jobs_to_cancel.reverse(); + tracing::info!("Found {} child jobs to cancel", jobs_to_cancel.len()); let (ntx, _) = cancel_single_job( username, @@ -270,7 +314,23 @@ pub async fn cancel_job<'c>( .await?; tx = ntx; - // cancel children + if !force_cancel { + // cancel children in batch first + if !jobs_to_cancel.is_empty() { + let updated = sqlx::query_scalar!( + "UPDATE v2_job_queue SET canceled_by = $1, canceled_reason = $2, scheduled_for = now(), suspend = 0 WHERE id = ANY($3) AND workspace_id = $4 AND (canceled_by IS NULL OR canceled_reason != $2) RETURNING id", + username, + reason, + jobs_to_cancel.as_slice(), + w_id + ) + .fetch_all(&mut *tx) + .await?; + + // Remove any jobs that were successfully updated + jobs_to_cancel.retain(|id| !updated.contains(&id)); + } + } for job_id in jobs_to_cancel { let job = get_queued_job_tx(job_id, &w_id, &mut tx).await?; @@ -297,7 +357,7 @@ pub async fn append_logs( job_id: &uuid::Uuid, workspace: impl AsRef, logs: impl AsRef, - db: impl Borrow>, + conn: &Connection, ) { if logs.as_ref().is_empty() { return; @@ -312,20 +372,81 @@ pub async fn append_logs( tracing::info!("NO LOGS [{job_id}]: {}", logs.as_ref()); return; } - if let Err(err) = sqlx::query!( - "INSERT INTO job_logs (logs, job_id, workspace_id) VALUES ($1, $2, $3) ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, $1::text)", - logs.as_ref(), - job_id, - workspace.as_ref(), - ) - .execute(db.borrow()) - .warn_after_seconds(1) - .await - { - tracing::error!(%job_id, %err, "error updating logs for large_log job {job_id}: {err}"); + match conn { + Connection::Sql(pool) => { + if let Err(err) = sqlx::query!( + "INSERT INTO job_logs (logs, job_id, workspace_id) VALUES ($1, $2, $3) ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, $1::text)", + logs.as_ref(), + job_id, + workspace.as_ref(), + ) + .execute(pool) + .warn_after_seconds(1) + .await + { + tracing::error!(%job_id, %err, "error updating logs for job {job_id}: {err}"); + } + } + Connection::Http(client) => { + if let Err(e) = client + .post::<_, String>( + &format!("/api/w/{}/agent_workers/push_logs/{}", workspace.as_ref(), job_id), + &logs.as_ref(), + ) + .await { + tracing::error!(%job_id, %e, "error sending logs for job {job_id}: {e}"); + }; + } } } +pub async fn push_init_job<'c>( + db: &Pool, + content: String, + worker_name: &str, +) -> error::Result { + let tx = PushIsolationLevel::IsolatedRoot(db.clone()); + let ehm = HashMap::new(); + let (uuid, inner_tx) = push( + &db, + tx, + "admins", + windmill_common::jobs::JobPayload::Code(windmill_common::jobs::RawCode { + hash: None, + content, + path: Some(format!("init_script_{worker_name}")), + language: ScriptLang::Bash, + lock: None, + custom_concurrency_key: None, + concurrent_limit: None, + concurrency_time_window_s: None, + cache_ttl: None, + dedicated_worker: None, + }), + PushArgs::from(&ehm), + worker_name, + "worker@windmill.dev", + SUPERADMIN_SECRET_EMAIL.to_string(), + None, + None, + None, + None, + None, + false, + true, + None, + true, + Some("init_script".to_string()), + None, + None, + None, + None, + ) + .await?; + inner_tx.commit().await?; + Ok(uuid) +} + pub async fn cancel_persistent_script_jobs<'c>( username: &str, reason: Option, @@ -487,7 +608,7 @@ where pub async fn add_completed_job_error( db: &Pool, - queued_job: &QueuedJob, + queued_job: &MiniPulledJob, mem_peak: i32, canceled_by: Option, e: serde_json::Value, @@ -543,7 +664,7 @@ lazy_static::lazy_static! { pub async fn add_completed_job( db: &Pool, - queued_job: &QueuedJob, + queued_job: &MiniPulledJob, success: bool, skipped: bool, result: Json<&T>, @@ -566,6 +687,9 @@ pub async fn add_completed_job( let result_columns = result_columns.as_ref(); let _job_id = queued_job.id; let (opt_uuid, _duration, _skip_downstream_error_handlers) = (|| async { + + // let start = std::time::Instant::now(); + let mut tx = db.begin().await?; let job_id = queued_job.id; @@ -577,7 +701,7 @@ pub async fn add_completed_job( serde_json::to_string(&result).unwrap_or_else(|_| "".to_string()) ); - let mem_peak = mem_peak.max(queued_job.mem_peak.unwrap_or(0)); + let mem_peak = mem_peak; // add_time!(bench, "add_completed_job query START"); let _duration = sqlx::query_scalar!( @@ -594,13 +718,15 @@ pub async fn add_completed_job( , workflow_as_code_status , memory_peak , status + , worker ) SELECT q.workspace_id, q.id, started_at, COALESCE($9::bigint, (EXTRACT('epoch' FROM (now())) - EXTRACT('epoch' FROM (COALESCE(started_at, now()))))*1000), $3, $10, $5, $6, flow_status, workflow_as_code_status, $8, CASE WHEN $4::BOOL THEN 'canceled'::job_status WHEN $7::BOOL THEN 'skipped'::job_status WHEN $2::BOOL THEN 'success'::job_status - ELSE 'failure'::job_status END AS status + ELSE 'failure'::job_status END AS status, + q.worker FROM v2_job_queue q LEFT JOIN v2_job_status USING (id) WHERE q.id = $1 ON CONFLICT (id) DO UPDATE SET status = EXCLUDED.status, result = $3 RETURNING duration_ms AS \"duration_ms!\"", /* $1 */ queued_job.id, @@ -632,7 +758,7 @@ pub async fn add_completed_job( .map_err(|e| Error::InternalErr(format!("Could not update job labels: {e:#}")))?; } - if !queued_job.is_flow_step { + if !queued_job.is_flow_step() { if let Some(parent_job) = queued_job.parent_job { let _ = sqlx::query_scalar!( "UPDATE v2_job_status SET @@ -661,10 +787,10 @@ pub async fn add_completed_job( // tracing::error!("Added completed job {:#?}", queued_job); let mut _skip_downstream_error_handlers = false; - tx = delete_job(tx, &queued_job.workspace_id, job_id).await?; + tx = delete_job(tx, &job_id).await?; // tracing::error!("3 {:?}", start.elapsed()); - if queued_job.is_flow_step { + if queued_job.is_flow_step() { if let Some(parent_job) = queued_job.parent_job { // persist the flow last progress timestamp to avoid zombie flow jobs tracing::debug!( @@ -698,12 +824,12 @@ pub async fn add_completed_job( } } } else { - if queued_job.schedule_path.is_some() && queued_job.script_path.is_some() { - let schedule_path = queued_job.schedule_path.as_ref().unwrap(); - let script_path = queued_job.script_path.as_ref().unwrap(); + if queued_job.schedule_path().is_some() && queued_job.runnable_path.is_some() { + let schedule_path = queued_job.schedule_path().unwrap(); + let script_path = queued_job.runnable_path.as_ref().unwrap(); let schedule = - get_schedule_opt(&mut *tx, &queued_job.workspace_id, schedule_path).await?; + get_schedule_opt(&mut *tx, &queued_job.workspace_id, &schedule_path).await?; if let Some(schedule) = schedule { #[cfg(feature = "enterprise")] @@ -737,7 +863,7 @@ pub async fn add_completed_job( db, queued_job, &schedule, - script_path, + &script_path, &queued_job.workspace_id, ) .await @@ -754,7 +880,7 @@ pub async fn add_completed_job( if let Err(err) = apply_schedule_handlers( db, &schedule, - script_path, + &script_path, &queued_job.workspace_id, success, result, @@ -793,27 +919,29 @@ pub async fn add_completed_job( } } if queued_job.concurrent_limit.is_some() { - let concurrency_key = match concurrency_key(db, queued_job).await { + let concurrency_key = match concurrency_key(db, &queued_job.id).await { Ok(c) => c, Err(e) => { tracing::error!( "Could not get concurrency key for job {} defaulting to default key: {e:?}", queued_job.id ); - legacy_concurrency_key(db, queued_job) - .await - .unwrap_or_else(|| queued_job.full_path_with_workspace()) + "".to_string() } }; - if let Err(e) = sqlx::query_scalar!( - "UPDATE concurrency_counter SET job_uuids = job_uuids - $2 WHERE concurrency_id = $1", - concurrency_key, - queued_job.id.hyphenated().to_string(), - ) - .execute(&mut *tx) - .await - { - tracing::error!("Could not decrement concurrency counter: {}", e); + if *DISABLE_CONCURRENCY_LIMIT || concurrency_key.is_empty() { + tracing::warn!("Concurrency limit is disabled, skipping"); + } else { + if let Err(e) = sqlx::query_scalar!( + "UPDATE concurrency_counter SET job_uuids = job_uuids - $2 WHERE concurrency_id = $1", + concurrency_key, + queued_job.id.hyphenated().to_string(), + ) + .execute(&mut *tx) + .await + { + tracing::error!("Could not decrement concurrency counter: {}", e); + } } if let Err(e) = sqlx::query_scalar!( @@ -832,30 +960,31 @@ pub async fn add_completed_job( tracing::debug!("decremented concurrency counter"); } - if JOB_TOKEN.is_none() { sqlx::query!("DELETE FROM job_perms WHERE job_id = $1", job_id) .execute(&mut *tx) .await?; - } + tx.commit().await?; tracing::info!( %job_id, - root_job = ?queued_job.root_job.map(|x| x.to_string()).unwrap_or_else(|| String::new()), - path = &queued_job.script_path(), - job_kind = ?queued_job.job_kind, + root_job = ?queued_job.flow_innermost_root_job.map(|x| x.to_string()).unwrap_or_else(|| String::new()), + path = &queued_job.runnable_path(), + job_kind = ?queued_job.kind, started_at = ?queued_job.started_at.map(|x| x.to_string()).unwrap_or_else(|| String::new()), duration = ?_duration, permissioned_as = ?queued_job.permissioned_as, - email = ?queued_job.email, + email = ?queued_job.permissioned_as_email, created_by = queued_job.created_by, - is_flow_step = queued_job.is_flow_step, - language = ?queued_job.language, + is_flow_step = queued_job.is_flow_step(), + language = ?queued_job.script_lang, + scheduled_for = ?queued_job.scheduled_for, success, "inserted completed job: {} (success: {success})", queued_job.id ); + // tracing::info!("completed job: {:?}", start.elapsed().as_micros()); Ok((None, _duration, _skip_downstream_error_handlers)) as windmill_common::error::Result<(Option, i64, bool)> }) .retry( @@ -882,13 +1011,7 @@ pub async fn add_completed_job( if *CLOUD_HOSTED && !queued_job.is_flow() && _duration > 1000 { let additional_usage = _duration / 1000; let w_id = &queued_job.workspace_id; - let premium_workspace = - sqlx::query_scalar!("SELECT premium FROM workspace WHERE id = $1", w_id) - .fetch_one(db) - .await - .map_err(|e| { - Error::internal_err(format!("fetching if {w_id} is premium: {e:#}")) - })?; + let premium_workspace = windmill_common::workspaces::is_premium_workspace(db, w_id).await; let _ = sqlx::query!( "INSERT INTO usage (id, is_workspace, month_, usage) VALUES ($1, TRUE, EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date), $2) @@ -905,7 +1028,7 @@ pub async fn add_completed_job( "INSERT INTO usage (id, is_workspace, month_, usage) VALUES ($1, FALSE, EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date), $2) ON CONFLICT (id, is_workspace, month_) DO UPDATE SET usage = usage.usage + $2", - queued_job.email, + queued_job.permissioned_as_email, additional_usage as i32 ) .execute(db) @@ -916,8 +1039,8 @@ pub async fn add_completed_job( #[cfg(feature = "enterprise")] if !success { - async fn has_failure_module(db: &Pool, job: &QueuedJob) -> bool { - if let Ok(flow) = cache::job::fetch_flow(db, job.job_kind, job.script_hash).await { + async fn has_failure_module(db: &Pool, job: &MiniPulledJob) -> bool { + if let Ok(flow) = cache::job::fetch_flow(db, job.kind, job.runnable_id).await { return flow.value().failure_module.is_some(); } sqlx::query_scalar!( @@ -930,7 +1053,7 @@ pub async fn add_completed_job( .unwrap_or(false) } - if queued_job.email == ERROR_HANDLER_USER_EMAIL { + if queued_job.permissioned_as_email == ERROR_HANDLER_USER_EMAIL { let base_url = BASE_URL.read().await; let w_id = &queued_job.workspace_id; report_critical_error( @@ -949,7 +1072,7 @@ pub async fn add_completed_job( None, ) .await; - } else if queued_job.email == SCHEDULE_ERROR_HANDLER_USER_EMAIL { + } else if queued_job.permissioned_as_email == SCHEDULE_ERROR_HANDLER_USER_EMAIL { let base_url = BASE_URL.read().await; let w_id = &queued_job.workspace_id; report_error_to_workspace_handler_or_critical_side_channel( @@ -968,8 +1091,8 @@ pub async fn add_completed_job( ) .await; } else if !_skip_downstream_error_handlers - && (matches!(queued_job.job_kind, JobKind::Script) - || matches!(queued_job.job_kind, JobKind::Flow) + && (matches!(queued_job.kind, JobKind::Script) + || matches!(queued_job.kind, JobKind::Flow) && !has_failure_module(db, queued_job).await) && queued_job.parent_job.is_none() { @@ -1019,8 +1142,8 @@ pub async fn add_completed_job( } } - if !queued_job.is_flow_step && queued_job.job_kind == JobKind::Script && canceled_by.is_none() { - if let Some(hash) = queued_job.script_hash { + if !queued_job.is_flow_step() && queued_job.kind == JobKind::Script && canceled_by.is_none() { + if let Some(hash) = queued_job.runnable_id { let p = sqlx::query_scalar!( "SELECT restart_unless_cancelled FROM script WHERE hash = $1 AND workspace_id = $2", hash.0, @@ -1043,7 +1166,7 @@ pub async fn add_completed_job( { let next_run = queued_job.started_at.unwrap_or(now) + chrono::Duration::try_seconds(10).unwrap(); - tracing::warn!("Perpetual script {:?} is running too fast, only 1 job per 10s it supported. Scheduling next run for {:?}", queued_job.script_path, next_run); + tracing::warn!("Perpetual script {:?} is running too fast, only 1 job per 10s it supported. Scheduling next run for {:?}", queued_job.runnable_path, next_run); Some(next_run) } else { None @@ -1056,14 +1179,14 @@ pub async fn add_completed_job( &queued_job.workspace_id, JobPayload::ScriptHash { hash, - path: queued_job.script_path().to_string(), - custom_concurrency_key: custom_concurrency_key(db, queued_job.id).await?, + path: queued_job.runnable_path().to_string(), + custom_concurrency_key: custom_concurrency_key(db, &queued_job.id).await?, concurrent_limit: queued_job.concurrent_limit, concurrency_time_window_s: queued_job.concurrency_time_window_s, cache_ttl: queued_job.cache_ttl, dedicated_worker: None, language: queued_job - .language + .script_lang .clone() .unwrap_or_else(|| ScriptLang::Deno), priority: queued_job.priority, @@ -1075,10 +1198,10 @@ pub async fn add_completed_job( .map(|x| PushArgs::from(&x.0)) .unwrap_or_else(|| PushArgs::from(&ehm)), &queued_job.created_by, - &queued_job.email, + &queued_job.permissioned_as_email, queued_job.permissioned_as.clone(), scheduled_for, - queued_job.schedule_path.clone(), + queued_job.schedule_path(), None, None, None, @@ -1105,7 +1228,7 @@ pub async fn add_completed_job( } pub async fn send_error_to_global_handler<'a, T: Serialize + Send + Sync>( - queued_job: &QueuedJob, + queued_job: &MiniPulledJob, db: &Pool, result: Json<&T>, ) -> Result<(), Error> { @@ -1120,8 +1243,8 @@ pub async fn send_error_to_global_handler<'a, T: Serialize + Send + Sync>( push_error_handler( db, queued_job.id, - queued_job.schedule_path.clone(), - queued_job.script_path.clone(), + queued_job.schedule_path(), + queued_job.runnable_path.clone(), queued_job.is_flow(), &queued_job.workspace_id, &prefixed_global_error_handler_path, @@ -1129,7 +1252,7 @@ pub async fn send_error_to_global_handler<'a, T: Serialize + Send + Sync>( None, queued_job.started_at, None, - &queued_job.email, + &queued_job.permissioned_as_email, false, true, None, @@ -1141,7 +1264,7 @@ pub async fn send_error_to_global_handler<'a, T: Serialize + Send + Sync>( } pub async fn report_error_to_workspace_handler_or_critical_side_channel( - queued_job: &QueuedJob, + queued_job: &MiniPulledJob, db: &Pool, error_message: String, ) -> () { @@ -1160,8 +1283,8 @@ pub async fn report_error_to_workspace_handler_or_critical_side_channel( if let Err(err) = push_error_handler( db, queued_job.id, - queued_job.schedule_path.clone(), - queued_job.script_path.clone(), + queued_job.schedule_path(), + queued_job.runnable_path.clone(), queued_job.is_flow(), w_id, &error_handler, @@ -1173,7 +1296,7 @@ pub async fn report_error_to_workspace_handler_or_critical_side_channel( None, queued_job.started_at, error_handler_extra_args, - &queued_job.email, + &queued_job.permissioned_as_email, false, false, None, @@ -1193,7 +1316,7 @@ pub async fn report_error_to_workspace_handler_or_critical_side_channel( } pub async fn send_error_to_workspace_handler<'a, 'c, T: Serialize + Send + Sync>( - queued_job: &QueuedJob, + queued_job: &MiniPulledJob, is_canceled: bool, db: &Pool, result: Json<&'a T>, @@ -1213,12 +1336,12 @@ pub async fn send_error_to_workspace_handler<'a, 'c, T: Serialize + Send + Sync> } if let Some(error_handler) = error_handler { - let ws_error_handler_muted: Option = match queued_job.job_kind { + let ws_error_handler_muted: Option = match queued_job.kind { JobKind::Script => { sqlx::query_scalar!( "SELECT ws_error_handler_muted FROM script WHERE workspace_id = $1 AND hash = $2", queued_job.workspace_id, - queued_job.script_hash.unwrap().0, + queued_job.runnable_id.map(|x| x.0), ) .fetch_optional(db) .await? @@ -1227,7 +1350,7 @@ pub async fn send_error_to_workspace_handler<'a, 'c, T: Serialize + Send + Sync> sqlx::query_scalar!( "SELECT ws_error_handler_muted FROM flow WHERE workspace_id = $1 AND path = $2", queued_job.workspace_id, - queued_job.script_path.as_ref().unwrap(), + queued_job.runnable_path.clone(), ) .fetch_optional(db) .await? @@ -1242,8 +1365,8 @@ pub async fn send_error_to_workspace_handler<'a, 'c, T: Serialize + Send + Sync> push_error_handler( db, queued_job.id, - queued_job.schedule_path.clone(), - queued_job.script_path.clone(), + queued_job.schedule_path(), + queued_job.runnable_path.clone(), queued_job.is_flow(), &queued_job.workspace_id, &error_handler, @@ -1251,7 +1374,7 @@ pub async fn send_error_to_workspace_handler<'a, 'c, T: Serialize + Send + Sync> None, queued_job.started_at, error_handler_extra_args, - &queued_job.email, + &queued_job.permissioned_as_email, false, false, None, @@ -1265,7 +1388,7 @@ pub async fn send_error_to_workspace_handler<'a, 'c, T: Serialize + Send + Sync> pub async fn handle_maybe_scheduled_job<'c>( db: &Pool, - job: &QueuedJob, + job: &MiniPulledJob, schedule: &Schedule, script_path: &str, w_id: &str, @@ -1375,12 +1498,17 @@ async fn apply_schedule_handlers<'a, 'c, T: Serialize + Send + Sync>( let exact = schedule.on_failure_exact.unwrap_or(false); if times > 1 || exact { let past_jobs = sqlx::query!( - "SELECT - success AS \"success!\", - result AS \"result: Json>\", - started_at AS \"started_at!\" - FROM v2_as_completed_job - WHERE workspace_id = $1 AND schedule_path = $2 AND script_path = $3 AND id != $4 + // Query plan: + // - use of the `ix_v2_job_root_by_path` index; + // hence the `parent_job IS NULL` clause. + // - select from `v2_job` first, then join with `v2_job_completed` to avoid a full + // table scan. + "SELECT status = 'success' AS \"success!\" + FROM v2_job j JOIN v2_job_completed USING (id) + WHERE j.workspace_id = $1 AND trigger_kind = 'schedule' AND trigger = $2 + AND parent_job IS NULL + AND runnable_path = $3 + AND j.id != $4 ORDER BY created_at DESC LIMIT $5", &schedule.workspace_id, @@ -1446,11 +1574,19 @@ async fn apply_schedule_handlers<'a, 'c, T: Serialize + Send + Sync>( let tx = db.begin().await?; let times = schedule.on_recovery_times.unwrap_or(1).max(1); let past_jobs = sqlx::query!( - "SELECT - success AS \"success!\", + // Query plan: + // - use of the `ix_v2_job_root_by_path` index; + // hence the `parent_job IS NULL` clause. + // - select from `v2_job` first, then join with `v2_job_completed` to avoid a full + // table scan. + "SELECT status = 'success' AS \"success!\", result AS \"result: Json>\", started_at AS \"started_at!\"\ - FROM v2_as_completed_job WHERE workspace_id = $1 AND schedule_path = $2 AND script_path = $3 AND id != $4 + FROM v2_job j JOIN v2_job_completed USING (id) + WHERE j.workspace_id = $1 AND trigger_kind = 'schedule' AND trigger = $2 + AND parent_job IS NULL + AND runnable_path = $3 + AND j.id != $4 ORDER BY created_at DESC LIMIT $5", &schedule.workspace_id, @@ -1807,34 +1943,428 @@ async fn handle_successful_schedule<'a, 'c, T: Serialize + Send + Sync>( Ok(()) } -#[derive(sqlx::FromRow)] +#[derive(sqlx::Type, Serialize, Deserialize, Debug, Clone)] +#[sqlx(type_name = "TRIGGER_KIND", rename_all = "lowercase")] +#[serde(rename_all = "lowercase")] +pub enum TriggerKind { + Webhook, + Http, + Websocket, + Kafka, + Email, + Nats, + Mqtt, + Sqs, + Postgres, + Gcp +} + +#[derive(sqlx::Type, Serialize, Deserialize, Debug, Clone)] +#[sqlx(type_name = "JOB_TRIGGER_KIND", rename_all = "lowercase")] +#[serde(rename_all = "lowercase")] +pub enum JobTriggerKind { + Webhook, + Http, + Websocket, + Kafka, + Email, + Nats, + Mqtt, + Sqs, + Postgres, + Schedule, + Gcp +} + +impl fmt::Display for TriggerKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + TriggerKind::Webhook => "webhook", + TriggerKind::Http => "http", + TriggerKind::Websocket => "websocket", + TriggerKind::Kafka => "kafka", + TriggerKind::Email => "email", + TriggerKind::Nats => "nats", + TriggerKind::Mqtt => "mqtt", + TriggerKind::Sqs => "sqs", + TriggerKind::Postgres => "postgres", + TriggerKind::Gcp => "gcp", + }; + write!(f, "{}", s) + } +} + +#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize)] +pub struct MiniPulledJob { + pub workspace_id: String, + pub id: Uuid, + pub args: Option>>>, + pub parent_job: Option, + pub created_by: String, + pub scheduled_for: chrono::DateTime, + pub started_at: Option>, + pub runnable_path: Option, + pub kind: JobKind, + pub runnable_id: Option, + pub canceled_reason: Option, + pub canceled_by: Option, + pub permissioned_as: String, + pub permissioned_as_email: String, + pub flow_status: Option>>, + pub tag: String, + pub script_lang: Option, + pub same_worker: bool, + pub pre_run_error: Option, + pub concurrent_limit: Option, + pub concurrency_time_window_s: Option, + pub flow_innermost_root_job: Option, + pub timeout: Option, + pub flow_step_id: Option, + pub cache_ttl: Option, + pub priority: Option, + pub preprocessed: Option, + pub script_entrypoint_override: Option, + pub trigger: Option, + pub trigger_kind: Option, + pub visible_to_owner: bool, +} + +impl MiniPulledJob { + pub fn runnable_path(&self) -> &str { + self.runnable_path + .as_ref() + .map(String::as_str) + .unwrap_or("tmp/main") + } + + pub fn is_flow_step(&self) -> bool { + self.flow_step_id.is_some() + } + + pub fn is_canceled(&self) -> bool { + self.canceled_by.is_some() + } + + pub fn parse_flow_status(&self) -> Option { + // tracing::error!("parse_flow_status: {:?}", self.flow_status); + + self.flow_status + .as_ref() + .and_then(|v| serde_json::from_str::((**v).get()).ok()) + } + + pub fn from(job: &QueuedJob) -> MiniPulledJob { + MiniPulledJob { + workspace_id: job.workspace_id.clone(), + id: job.id, + args: job.args.clone(), + parent_job: job.parent_job.clone(), + created_by: job.created_by.clone(), + started_at: job.started_at.clone(), + scheduled_for: job.scheduled_for, + runnable_path: job.script_path.clone(), + kind: job.job_kind, + runnable_id: job.script_hash.clone(), + canceled_reason: job.canceled_reason.clone(), + canceled_by: job.canceled_by.clone(), + permissioned_as: job.permissioned_as.clone(), + permissioned_as_email: job.email.clone(), + flow_status: job.flow_status.clone(), + tag: job.tag.clone(), + script_lang: job.language.clone(), + same_worker: job.same_worker, + pre_run_error: job.pre_run_error.clone(), + concurrent_limit: job.concurrent_limit.clone(), + concurrency_time_window_s: job.concurrency_time_window_s.clone(), + flow_innermost_root_job: job.root_job.clone(), + timeout: job.timeout.clone(), + flow_step_id: job.flow_step_id.clone(), + cache_ttl: job.cache_ttl.clone(), + priority: job.priority.clone(), + preprocessed: job.preprocessed.clone(), + script_entrypoint_override: job.script_entrypoint_override.clone(), + trigger: job.schedule_path.clone(), + trigger_kind: if job.schedule_path.is_some() { + Some(JobTriggerKind::Schedule) + } else { + None + }, + visible_to_owner: job.visible_to_owner.clone(), + } + } + pub fn is_flow(&self) -> bool { + self.kind.is_flow() + } + + pub fn is_dependency(&self) -> bool { + self.kind.is_dependency() + } + + pub fn schedule_path(&self) -> Option { + if self + .trigger_kind + .as_ref() + .is_some_and(|t| matches!(t, JobTriggerKind::Schedule)) + { + self.trigger.clone() + } else { + None + } + } + + + pub async fn mark_as_started_if_step(&self, db: &DB) -> Result<(), Error> { + if self.is_flow_step() { + let _ = update_flow_status_in_progress( + db, + &self.workspace_id, + self.parent_job + .ok_or_else(|| Error::internal_err(format!("expected parent job")))?, + self.id, + ) + .warn_after_seconds(5) + .await?; + } else if let Some(parent_job) = self.parent_job { + let _ = update_workflow_as_code_status( + db, + &self.id, + &parent_job, + ) + .await?; + } + Ok(()) + } + +} + + + +#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize)] pub struct PulledJob { #[sqlx(flatten)] - pub job: QueuedJob, + pub job: MiniPulledJob, pub raw_code: Option, pub raw_lock: Option, pub raw_flow: Option>>, + pub parent_runnable_path: Option, + pub permissioned_as_email: Option, + pub permissioned_as_username: Option, + pub permissioned_as_is_admin: Option, + pub permissioned_as_is_operator: Option, + pub permissioned_as_groups: Option>, + pub permissioned_as_folders: Option>, } +#[derive(Serialize, Deserialize)] +pub enum PrecomputedAgentInfo { + Bun { local: String, remote: String }, + Python { py_version: Option, requirements: Option }, +} + +#[derive(Serialize, Deserialize)] +pub struct JobAndPerms { + pub job: MiniPulledJob, + pub raw_code: Option, + pub raw_flow: Option>>, + pub raw_lock: Option, + pub parent_runnable_path: Option, + pub token: String, + pub precomputed_agent_info: Option, +} +impl PulledJob { + pub async fn get_job_and_perms(self, db: &DB) -> JobAndPerms { + let job_perms = match ( + self.permissioned_as_email, + self.permissioned_as_username, + self.permissioned_as_is_admin, + self.permissioned_as_is_operator, + self.permissioned_as_groups, + self.permissioned_as_folders, + ) { + ( + Some(email), + Some(username), + Some(is_admin), + Some(is_operator), + Some(groups), + Some(folders), + ) => Some(JobPerms { + email, + username, + is_admin, + is_operator, + groups, + folders, + }), + _ => None, + }; + + let token = create_token(&db, &self.job, job_perms).await; + JobAndPerms { + job: self.job, + raw_code: self.raw_code, + raw_flow: self.raw_flow, + raw_lock: self.raw_lock, + parent_runnable_path: self.parent_runnable_path, + token, + precomputed_agent_info: None, + } + } +} + +// struct Permission +pub async fn create_token(db: &DB, job: &MiniPulledJob, perms: Option) -> String { + // skipping test runs + if job.workspace_id != "" { + let label = if job.permissioned_as != format!("u/{}", job.created_by) + && job.permissioned_as != job.created_by + { + format!("ephemeral-script-end-user-{}", job.created_by) + } else { + "ephemeral-script".to_string() + }; + windmill_common::auth::create_token_for_owner( + db, + &job.workspace_id, + &job.permissioned_as, + &label, + *SCRIPT_TOKEN_EXPIRY, + &job.permissioned_as_email, + &job.id, + perms, + ) + .warn_after_seconds(5) + .await + .expect("could not create job token") + } else { + return "".to_string(); + } +} + + + + impl std::ops::Deref for PulledJob { - type Target = QueuedJob; + type Target = MiniPulledJob; fn deref(&self) -> &Self::Target { &self.job } } +lazy_static::lazy_static! { + static ref DISABLE_CONCURRENCY_LIMIT: bool = std::env::var("DISABLE_CONCURRENCY_LIMIT").is_ok_and(|s| s == "true"); +} + +pub async fn get_mini_pulled_job<'c>( + e: impl PgExecutor<'c>, + job_id: &Uuid, +) -> windmill_common::error::Result> { + let job = sqlx::query_as!( + MiniPulledJob, + "SELECT + v2_job_queue.workspace_id, + v2_job_queue.id, + v2_job.args as \"args: sqlx::types::Json>>\", + v2_job.parent_job, + v2_job.created_by, + v2_job_queue.started_at, + scheduled_for, + runnable_path, + kind as \"kind: JobKind\", + runnable_id as \"runnable_id: ScriptHash\", + canceled_reason, + canceled_by, + permissioned_as, + permissioned_as_email, + flow_status as \"flow_status: sqlx::types::Json>\", + v2_job.tag, + script_lang as \"script_lang: ScriptLang\", + same_worker, + pre_run_error, + concurrent_limit, + concurrency_time_window_s, + flow_innermost_root_job, + timeout, + flow_step_id, + cache_ttl, + v2_job_queue.priority, + preprocessed, + script_entrypoint_override, + trigger, + trigger_kind as \"trigger_kind: JobTriggerKind\", + visible_to_owner + FROM v2_job_queue INNER JOIN v2_job ON v2_job.id = v2_job_queue.id LEFT JOIN v2_job_status ON v2_job_status.id = v2_job_queue.id WHERE v2_job_queue.id = $1", + job_id, + ) + .fetch_optional(e) + .await?; + Ok(job) +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct PulledJobResult { + pub job: Option, + pub suspended: bool, +} + + + pub async fn pull( db: &Pool, suspend_first: bool, -) -> windmill_common::error::Result<(Option, bool)> { + worker_name: &str, + query_o: Option<(String, String)>, + #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, +) -> windmill_common::error::Result { loop { - let (job, suspended) = - pull_single_job_and_mark_as_running_no_concurrency_limit(db, suspend_first).await?; + if let Some((query_suspended, query_no_suspend)) = query_o.as_ref() { + let njob = { + let job = sqlx::query_as::<_, PulledJob>(query_suspended) + .bind(worker_name) + .fetch_optional(db) + .await?; + if let Some(job) = job { + PulledJobResult { job: Some(job), suspended: true } + } else { + let job = sqlx::query_as::<_, PulledJob>(query_no_suspend) + .bind(worker_name) + .fetch_optional(db) + .await?; + PulledJobResult { job, suspended: false } + } + }; + if let Some(job) = njob.job.as_ref() { + if job.is_flow() || job.is_dependency() { + let per_workspace = per_workspace_tag(&job.workspace_id).await; + let base_tag = if job.is_flow() { + "flow".to_string() + } else { + "dependency".to_string() + }; + let tag = if per_workspace { + format!("{}-{}", base_tag, job.workspace_id) + } else { + base_tag + }; + sqlx::query!("UPDATE v2_job_queue SET tag = $1, running = false WHERE id = $2", tag, job.id).execute(db).await?; + continue; + } + } + return Ok(njob); + }; + let (job, suspended) = pull_single_job_and_mark_as_running_no_concurrency_limit( + db, + suspend_first, + worker_name, + #[cfg(feature = "benchmark")] bench, + ) + .await?; let Some(job) = job else { - return Ok((None, suspended)); + return Ok(PulledJobResult { job: None, suspended }); }; + let has_concurent_limit = job.concurrent_limit.is_some(); #[cfg(not(feature = "enterprise"))] @@ -1847,29 +2377,25 @@ pub async fn pull( // concurrency check. If more than X jobs for this path are already running, we re-queue and pull another job from the queue let pulled_job = job; - if pulled_job.script_path.is_none() || !has_concurent_limit || pulled_job.canceled { + if pulled_job.runnable_path.is_none() + || !has_concurent_limit + || pulled_job.canceled_by.is_some() + { #[cfg(feature = "prometheus")] if METRICS_ENABLED.load(std::sync::atomic::Ordering::Relaxed) { QUEUE_PULL_COUNT.inc(); } - return Ok((Option::Some(pulled_job), suspended)); + return Ok(PulledJobResult { job: Some(pulled_job), suspended }); } - let mut tx = db.begin().await?; - - // Else the job is subject to concurrency limits - let job_script_path = pulled_job.script_path.clone().unwrap(); - - let job_concurrency_key = match concurrency_key(db, &pulled_job).await { + let job_concurrency_key = match concurrency_key(db, &pulled_job.id).await { Ok(key) => key, Err(e) => { tracing::error!( "Could not get concurrency key for job {} defaulting to default key: {e:?}", pulled_job.id ); - legacy_concurrency_key(db, &pulled_job) - .await - .unwrap_or_else(|| pulled_job.full_path_with_workspace()) + "".to_string() } }; tracing::debug!("Concurrency key is '{}'", job_concurrency_key); @@ -1883,110 +2409,68 @@ pub async fn pull( job_custom_concurrency_time_window_s ); - sqlx::query_scalar!( - "SELECT null FROM v2_job_queue WHERE id = $1 FOR UPDATE", - pulled_job.id - ) - .fetch_one(&mut *tx) - .await - .context("lock job in queue")?; - let jobs_uuids_init_json_value = serde_json::from_str::( format!("{{\"{}\": {{}}}}", pulled_job.id.hyphenated().to_string()).as_str(), ) .expect("Unable to serialize job_uuids column to proper JSON"); - let running_job = sqlx::query_scalar!( - "INSERT INTO concurrency_counter(concurrency_id, job_uuids) VALUES ($1, $2) - ON CONFLICT (concurrency_id) - DO UPDATE SET job_uuids = jsonb_set(concurrency_counter.job_uuids, array[$3], '{}') - RETURNING (SELECT COUNT(*) FROM jsonb_object_keys(job_uuids))", - job_concurrency_key, - jobs_uuids_init_json_value, - pulled_job.id.hyphenated().to_string(), - ) - .fetch_one(&mut *tx) - .await - .map_err(|e| { - Error::internal_err(format!( - "Error getting concurrency count for script path {job_script_path}: {e:#}" - )) - })?; - tracing::debug!("running_job: {}", running_job.unwrap_or(0)); - let completed_count = sqlx::query!( - "SELECT COUNT(*) as count, COALESCE(MAX(ended_at), now() - INTERVAL '1 second' * $2) as max_ended_at FROM concurrency_key WHERE key = $1 AND ended_at >= (now() - INTERVAL '1 second' * $2)", - job_concurrency_key, - f64::from(job_custom_concurrency_time_window_s), - ).fetch_one(&mut *tx).await.map_err(|e| { - Error::internal_err(format!( - "Error getting completed count for key {job_concurrency_key}: {e:#}" - )) - })?; - - let min_started_at = sqlx::query!( - "SELECT COALESCE((SELECT MIN(started_at) as min_started_at - FROM v2_as_queue - WHERE script_path = $1 AND job_kind != 'dependencies' AND running = true AND workspace_id = $2 AND canceled = false AND concurrent_limit > 0), $3) as min_started_at, now() AS now", - job_script_path, - &pulled_job.workspace_id, - completed_count.max_ended_at - ) - .fetch_one(&mut *tx) - .await - .map_err(|e| { - Error::internal_err(format!( - "Error getting concurrency count for script path {job_script_path}: {e:#}" - )) - })?; - - let concurrent_jobs_for_this_script = - completed_count.count.unwrap_or_default() as i32 + running_job.unwrap_or(0) as i32; - tracing::debug!( - "Current concurrent jobs for this script: {}", - concurrent_jobs_for_this_script - ); - if concurrent_jobs_for_this_script <= job_custom_concurrent_limit { + let (within_limit, max_ended_at) = + if *DISABLE_CONCURRENCY_LIMIT || job_concurrency_key.is_empty() { + tracing::warn!("Concurrency limit is disabled, skipping"); + (true, None) + } else { + update_concurrency_counter( + db, + &pulled_job.id, + job_concurrency_key.clone(), + jobs_uuids_init_json_value, + pulled_job.id.hyphenated().to_string(), + job_custom_concurrency_time_window_s, + job_custom_concurrent_limit, + ) + .await? + }; + if within_limit { #[cfg(feature = "prometheus")] if METRICS_ENABLED.load(std::sync::atomic::Ordering::Relaxed) { QUEUE_PULL_COUNT.inc(); } - tx.commit().await?; - return Ok((Option::Some(pulled_job), suspended)); + return Ok(PulledJobResult { job: Some(pulled_job), suspended }); } - let x = sqlx::query_scalar!( - "UPDATE concurrency_counter SET job_uuids = job_uuids - $2 WHERE concurrency_id = $1 RETURNING (SELECT COUNT(*) FROM jsonb_object_keys(job_uuids))", - job_concurrency_key, - pulled_job.id.hyphenated().to_string(), + let job_script_path = pulled_job.runnable_path.clone().unwrap_or_default(); + + let min_started_at = sqlx::query!( + "SELECT COALESCE((SELECT MIN(started_at) as min_started_at + FROM v2_job_queue INNER JOIN v2_job ON v2_job.id = v2_job_queue.id + WHERE v2_job.runnable_path = $1 AND v2_job.kind != 'dependencies' AND v2_job_queue.running = true AND v2_job_queue.workspace_id = $2 AND v2_job_queue.canceled_by IS NULL AND v2_job.concurrent_limit > 0), $3) as min_started_at, now() AS now", + job_script_path, + &pulled_job.workspace_id, + max_ended_at ) - .fetch_one(&mut *tx) + .fetch_one(db) .await .map_err(|e| { Error::internal_err(format!( - "Error decreasing concurrency count for script path {job_script_path}: {e:#}" + "Error getting min started at for script path {job_script_path}: {e:#}" )) })?; - tracing::debug!("running_job after decrease: {}", x.unwrap_or(0)); - let job_uuid: Uuid = pulled_job.id; let avg_script_duration: Option = sqlx::query_scalar!( "SELECT CAST(ROUND(AVG(duration_ms), 0) AS BIGINT) AS avg_duration_s FROM - (SELECT duration_ms FROM concurrency_key LEFT JOIN v2_as_completed_job ON v2_as_completed_job.id = concurrency_key.job_id WHERE key = $1 AND ended_at IS NOT NULL + (SELECT duration_ms FROM concurrency_key LEFT JOIN v2_job_completed ON v2_job_completed.id = concurrency_key.job_id WHERE key = $1 AND ended_at IS NOT NULL ORDER BY ended_at DESC LIMIT 10) AS t", job_concurrency_key ) - .fetch_one(&mut *tx) + .fetch_one(db) .await?; - tracing::info!("avg script duration computed: {:?}", avg_script_duration); + tracing::debug!( + "avg script duration computed: {}", + avg_script_duration.unwrap_or(0) + ); - // let before_me = sqlx::query!( - // "SELECT schedu FROM queue WHERE script_path = $1 AND job_kind != 'dependencies' AND running = true AND workspace_id = $2 AND canceled = false AND started_at < $3 ORDER BY started_at DESC LIMIT 1", - // job_script_path, - // &pulled_job.workspace_id, - // min_started_at.now.unwrap() - // ) // optimal scheduling is: 'older_job_in_concurrency_time_window_started_timestamp + script_avg_duration + concurrency_time_window_s' let inc = Duration::try_milliseconds( avg_script_duration.map(|x| i64::from(x + 100)).unwrap_or(0), @@ -1997,56 +2481,81 @@ pub async fn pull( .unwrap_or_default(); let now = min_started_at.now.unwrap(); - let min_started_p_inc = (min_started_at.min_started_at.unwrap_or(now) + inc) - .max(now + Duration::try_seconds(3).unwrap_or_default()); + let min_started_at_or_now = min_started_at.min_started_at.unwrap_or(now); + let min_started_p_inc = + (min_started_at_or_now + inc).max(now + Duration::try_seconds(3).unwrap_or_default()); let mut estimated_next_schedule_timestamp = min_started_p_inc; + let all_jobs = sqlx::query_scalar!( + "SELECT scheduled_for FROM v2_job_queue INNER JOIN concurrency_key ON concurrency_key.job_id = v2_job_queue.id + WHERE key = $1 AND running = false AND canceled_by IS NULL AND scheduled_for >= $2", + job_concurrency_key, + estimated_next_schedule_timestamp - inc + ).fetch_all(db).await?; + + tracing::debug!( + "all_jobs: {:?}, estimated_next_schedule_timestamp: {:?}, inc: {:?}", + all_jobs, + estimated_next_schedule_timestamp, + inc + ); + let mut i = 0; loop { - let nestimated = estimated_next_schedule_timestamp + inc; - let jobs_in_window = sqlx::query_scalar!( - "SELECT COUNT(*) FROM v2_as_queue LEFT JOIN concurrency_key ON concurrency_key.job_id = v2_as_queue.id - WHERE key = $1 AND running = false AND canceled = false AND scheduled_for >= $2 AND scheduled_for < $3", - job_concurrency_key, - estimated_next_schedule_timestamp, - nestimated - ).fetch_optional(&mut *tx).await?.flatten().unwrap_or(0) as i32; - tracing::info!("estimated_next_schedule_timestamp: {:?}, jobs_in_window: {jobs_in_window}, nestimated: {nestimated}, inc: {inc}", estimated_next_schedule_timestamp); - if jobs_in_window < job_custom_concurrent_limit { + let jobs_in_window = all_jobs + .iter() + .filter(|&scheduled_for| scheduled_for <= &estimated_next_schedule_timestamp) + .count() as i32 + - (job_custom_concurrent_limit * i); + + tracing::debug!("estimated_next_schedule_timestamp: {:?}, jobs_in_window: {jobs_in_window}, inc: {inc}", estimated_next_schedule_timestamp); + + if jobs_in_window < job_custom_concurrent_limit || *DISABLE_CONCURRENCY_LIMIT { break; } else { - estimated_next_schedule_timestamp = nestimated; + i += 1; + estimated_next_schedule_timestamp = estimated_next_schedule_timestamp + inc; } } - tracing::info!("Job '{}' from path '{}' with concurrency key '{}' has reached its concurrency limit of {} jobs run in the last {} seconds. This job will be re-queued for next execution at {}", - job_uuid, job_script_path, job_concurrency_key, job_custom_concurrent_limit, job_custom_concurrency_time_window_s, estimated_next_schedule_timestamp); + tracing::info!("Job '{}' from path '{}' with concurrency key '{}' has reached its concurrency limit of {} jobs run in the last {} seconds. This job will be re-queued for next execution at {} (min_started_at: {min_started_at_or_now}, avg script duration: {:?}, number of time windows full: {})", + job_uuid, job_script_path, job_concurrency_key, job_custom_concurrent_limit, job_custom_concurrency_time_window_s, estimated_next_schedule_timestamp, avg_script_duration, i); let job_log_event = format!( - "\nRe-scheduled job to {estimated_next_schedule_timestamp} due to concurrency limits with key {job_concurrency_key} and limit {job_custom_concurrent_limit} in the last {job_custom_concurrency_time_window_s} seconds", + "\nRe-scheduled job to {estimated_next_schedule_timestamp} due to concurrency limits with key {job_concurrency_key} and limit {job_custom_concurrent_limit} in the last {job_custom_concurrency_time_window_s} seconds (min_started_at: {min_started_at_or_now}, avg script duration: {:?}, number of time windows full: {})\n", + avg_script_duration, i ); - let _ = append_logs(&job_uuid, &pulled_job.workspace_id, job_log_event, db).await; + let _ = append_logs( + &job_uuid, + &pulled_job.workspace_id, + job_log_event, + &Connection::from(db.clone()), + ) + .await; - // if using posgtres, then we're able to re-queue the entire batch of scheduled job for this script_path, so we do it sqlx::query!( - "WITH ping AS (UPDATE v2_job_runtime SET ping = NULL WHERE id = $2 RETURNING id) + " + WITH ping AS ( + UPDATE v2_job_runtime SET ping = null WHERE id = $2 + ) UPDATE v2_job_queue SET running = false, started_at = null, scheduled_for = $1 - WHERE id = (SELECT id FROM ping)", + WHERE id = $2", estimated_next_schedule_timestamp, job_uuid, ) - .fetch_all(&mut *tx) + .execute(db) .await .map_err(|e| Error::internal_err(format!("Could not update and re-queue job {job_uuid}. The job will be marked as running but it is not running: {e:#}")))?; - tx.commit().await? } } async fn pull_single_job_and_mark_as_running_no_concurrency_limit<'c>( db: &Pool, suspend_first: bool, + worker_name: &str, + #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, ) -> windmill_common::error::Result<(Option, bool)> { let job_and_suspended: (Option, bool) = { /* Jobs can be started if they: @@ -2060,12 +2569,14 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit<'c>( if query.is_empty() { tracing::warn!("No suspended pull queries available"); + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; return Ok((None, false)); } let r = if suspend_first { // tracing::info!("Pulling job with query: {}", query); sqlx::query_as::<_, PulledJob>(&query) + .bind(worker_name) .fetch_optional(db) .await? } else { @@ -2080,16 +2591,28 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit<'c>( if queries.is_empty() { tracing::warn!("No pull queries available"); + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; return Ok((None, false)); } for query in queries.iter() { // tracing::info!("Pulling job with query: {}", query); + // let instant = std::time::Instant::now(); + + #[cfg(feature = "benchmark")] + add_time!(bench, "pre pull"); + let r = sqlx::query_as::<_, PulledJob>(query) + .bind(worker_name) .fetch_optional(db) .await?; + #[cfg(feature = "benchmark")] + add_time!(bench, "post pull"); + if let Some(pulled_job) = r { + // tracing::info!("pulled job: {:?}", instant.elapsed().as_micros()); + highest_priority_job = Some(pulled_job); break; } @@ -2108,56 +2631,18 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit<'c>( pub async fn custom_concurrency_key( db: &Pool, - job_id: Uuid, + job_id: &Uuid, ) -> Result, sqlx::Error> { sqlx::query_scalar!("SELECT key FROM concurrency_key WHERE job_id = $1", job_id) .fetch_optional(db) // this should no longer be fetch optional .await } -async fn legacy_concurrency_key(db: &Pool, queued_job: &QueuedJob) -> Option { - let r = if queued_job.is_flow() { - sqlx::query_scalar!( - "SELECT flow_version.value->>'concurrency_key' - FROM flow - LEFT JOIN flow_version - ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] - WHERE flow.path = $1 AND flow.workspace_id = $2", - queued_job.script_path, - queued_job.workspace_id - ) - .fetch_optional(db) - .await - } else { - sqlx::query_scalar!( - "SELECT concurrency_key FROM script WHERE hash = $1 AND workspace_id = $2", - queued_job.script_hash.unwrap_or(ScriptHash(0)).0, - queued_job.workspace_id - ) - .fetch_optional(db) - .await - } - .ok() - .flatten() - .flatten(); - - let ehm = HashMap::new(); - let push_args = queued_job - .args - .as_ref() - .map(|x| PushArgs::from(&x.0)) - .unwrap_or_else(|| PushArgs::from(&ehm)); - r.map(|x| interpolate_args(x, &push_args, &queued_job.workspace_id)) -} - -async fn concurrency_key( - db: &Pool, - queued_job: &QueuedJob, -) -> windmill_common::error::Result { +async fn concurrency_key(db: &Pool, id: &Uuid) -> windmill_common::error::Result { not_found_if_none( - custom_concurrency_key(db, queued_job.id).await?, + custom_concurrency_key(db, id).await?, "ConcurrencyKey", - queued_job.id.to_string(), + id.to_string(), ) } @@ -2168,13 +2653,38 @@ fn interpolate_args(x: String, args: &PushArgs, workspace_id: &str) -> String { let mut interpolated = workspaced.clone(); for cap in RE_ARG_TAG.captures_iter(&workspaced) { let arg_name = cap.get(1).unwrap().as_str(); - let arg_value = args - .args - .get(arg_name) - .or(args.extra.as_ref().and_then(|x| x.get(arg_name))) - .map(|x| x.get()) - .unwrap_or_default() - .trim_matches('"'); + let arg_value = if arg_name.contains('.') { + let parts: Vec<&str> = arg_name.split('.').collect(); + let root = parts[0]; + let mut value = args + .args + .get(root) + .or(args.extra.as_ref().and_then(|x| x.get(root))) + .map(|x| x.get()) + .unwrap_or_default().to_string(); + + for part in parts.iter().skip(1) { + if let Ok(obj) = serde_json::from_str::(&value) { + value = obj.get(part) + .and_then(|v| Some(v.to_string())) + .unwrap_or_default() + .as_str().to_string(); + } else { + value = "".to_string(); // Invalid JSON or missing field + break; + } + } + value.trim_matches('"').to_string() + } else { + args.args + .get(arg_name) + .or(args.extra.as_ref().and_then(|x| x.get(arg_name))) + .map(|x| x.get()) + .unwrap_or_default() + .trim_matches('"') + .to_string() + }; + tracing::error!("arg_value: {}", arg_value); interpolated = interpolated.replace(format!("$args[{}]", arg_name).as_str(), &arg_value); } @@ -2300,8 +2810,8 @@ pub async fn get_result_and_success_by_id_from_flow( let success = match &job_result { JobResult::SingleJob(job_id) => { sqlx::query_scalar!( - "SELECT success AS \"success!\" - FROM v2_as_completed_job WHERE id = $1 AND workspace_id = $2", + "SELECT status = 'success' OR status = 'skipped' AS \"success!\" + FROM v2_job_completed WHERE id = $1 AND workspace_id = $2", job_id, w_id ) @@ -2313,20 +2823,19 @@ pub async fn get_result_and_success_by_id_from_flow( r#"WITH modules AS ( SELECT jsonb_array_elements(flow_status->'modules') AS module FROM {} - WHERE id = $1 AND workspace_id = $2 + WHERE id = $1 ) SELECT module->>'type' = 'Success' FROM modules - WHERE module->>'id' = $3"#, + WHERE module->>'id' = $2"#, if completed { - "v2_as_completed_job" + "v2_job_completed" } else { - "v2_as_queue" + "v2_job_status" } ); sqlx::query_scalar(&query) .bind(flow_id) - .bind(w_id) .bind(node_id) .fetch_optional(db) .await? @@ -2410,7 +2919,8 @@ pub async fn get_result_by_id_from_running_flow_inner( async fn get_completed_flow_node_result_rec( db: &Pool, w_id: &str, - subflows: impl std::iter::Iterator, + created_at: DateTime, + subflows: Vec<(Uuid, FlowStatus)>, node_id: &str, ) -> error::Result> { for (id, flow_status) in subflows { @@ -2430,18 +2940,19 @@ async fn get_completed_flow_node_result_rec( }; } else { let subflows = sqlx::query!( - "SELECT id AS \"id!\", flow_status AS \"flow_status!: Json\" - FROM v2_as_completed_job - WHERE parent_job = $1 AND workspace_id = $2 AND flow_status IS NOT NULL", + "SELECT j.id, jc.flow_status AS \"flow_status!: Json\" + FROM v2_job j + JOIN v2_job_completed jc ON j.id = jc.id + WHERE j.parent_job = $1 AND j.workspace_id = $2 AND j.created_at >= $3 AND jc.flow_status IS NOT NULL", id, - w_id + w_id, + created_at ) .map(|record| (record.id, record.flow_status.0)) .fetch_all(db) - .await? - .into_iter(); + .await?; match Box::pin(get_completed_flow_node_result_rec( - db, w_id, subflows, node_id, + db, w_id, created_at, subflows, node_id, )) .await? { @@ -2461,22 +2972,26 @@ async fn get_result_by_id_from_original_flow_inner( node_id: &str, ) -> error::Result { let flow_job = sqlx::query!( - "SELECT id, flow_status AS \"flow_status!: Json\" - FROM v2_job_completed WHERE id = $1 AND workspace_id = $2", + "SELECT jc.id, jc.flow_status AS \"flow_status!: Json\", j.created_at + FROM v2_job_completed jc + JOIN v2_job j ON j.id = jc.id + WHERE jc.id = $1 AND jc.workspace_id = $2 AND jc.flow_status IS NOT NULL", completed_flow_id, w_id ) - .map(|record| (record.id, record.flow_status.0)) + .map(|record| (record.id, record.flow_status.0, record.created_at)) .fetch_optional(db) .await?; - let flow_job = not_found_if_none( + let (id, flow_status, created_at) = not_found_if_none( flow_job, "Root completed job", format!("root: {}, id: {}", completed_flow_id, node_id), )?; - match get_completed_flow_node_result_rec(db, w_id, [flow_job].into_iter(), node_id).await? { + match get_completed_flow_node_result_rec(db, w_id, created_at, vec![(id, flow_status)], node_id) + .await? + { Some(res) => Ok(res), None => Err(Error::NotFound(format!( "Flow result by id not found going top-down from {}, (id: {})", @@ -2570,21 +3085,17 @@ async fn extract_result_from_job_result( pub async fn delete_job<'c>( mut tx: Transaction<'c, Postgres>, - w_id: &str, - job_id: Uuid, + job_id: &Uuid, ) -> windmill_common::error::Result> { #[cfg(feature = "prometheus")] if METRICS_ENABLED.load(std::sync::atomic::Ordering::Relaxed) { QUEUE_DELETE_COUNT.inc(); } - let job_removed = sqlx::query_scalar!( - "DELETE FROM v2_job_queue WHERE workspace_id = $1 AND id = $2 RETURNING 1", - w_id, - job_id - ) - .fetch_optional(&mut *tx) - .await; + let job_removed = + sqlx::query_scalar!("DELETE FROM v2_job_queue WHERE id = $1 RETURNING 1", job_id,) + .fetch_optional(&mut *tx) + .await; if let Err(job_removed) = job_removed { tracing::error!( @@ -2741,7 +3252,7 @@ pub fn empty_result() -> Box { // } lazy_static::lazy_static! { - pub static ref RE_ARG_TAG: Regex = Regex::new(r#"\$args\[(\w+)\]"#).unwrap(); + pub static ref RE_ARG_TAG: Regex = Regex::new(r#"\$args\[((?:\w+\.)*\w+)\]"#).unwrap(); } // #[instrument(level = "trace", skip_all)] @@ -2772,15 +3283,7 @@ pub async fn push<'c, 'd>( #[cfg(feature = "cloud")] if *CLOUD_HOSTED { let premium_workspace = - sqlx::query_scalar!("SELECT premium FROM workspace WHERE id = $1", workspace_id) - .fetch_one(_db) - .await - .map_err(|e| { - Error::internal_err(format!( - "fetching if {workspace_id} is premium and overquota: {e:#}" - )) - })?; - + windmill_common::workspaces::is_premium_workspace(_db, workspace_id).await; // we track only non flow steps let (workspace_usage, user_usage) = if !matches!( job_payload, @@ -2866,7 +3369,7 @@ pub async fn push<'c, 'd>( .await? .unwrap_or(0); - if in_queue > MAX_FREE_EXECS.into() { + if in_queue > MAX_FREE_EXECS as i64 { return Err(error::Error::QuotaExceeded(format!( "User {email} has exceeded the jobs in queue limit of {MAX_FREE_EXECS} that applies outside of premium workspaces." ))); @@ -2880,7 +3383,7 @@ pub async fn push<'c, 'd>( .await? .unwrap_or(0); - if concurrent_runs > MAX_FREE_CONCURRENT_RUNS.into() { + if concurrent_runs > MAX_FREE_CONCURRENT_RUNS as i64 { return Err(error::Error::QuotaExceeded(format!( "User {email} has exceeded the concurrent runs limit of {MAX_FREE_CONCURRENT_RUNS} that applies outside of premium workspaces." ))); @@ -2922,7 +3425,7 @@ pub async fn push<'c, 'd>( .await? .unwrap_or(0); - if in_queue_workspace > MAX_FREE_EXECS.into() { + if in_queue_workspace > MAX_FREE_EXECS as i64 { return Err(error::Error::QuotaExceeded(format!( "Workspace {workspace_id} has exceeded the jobs in queue limit of {MAX_FREE_EXECS} that applies outside of premium workspaces." ))); @@ -2936,7 +3439,7 @@ pub async fn push<'c, 'd>( .await? .unwrap_or(0); - if concurrent_runs_workspace > MAX_FREE_CONCURRENT_RUNS.into() { + if concurrent_runs_workspace > MAX_FREE_CONCURRENT_RUNS as i64 { return Err(error::Error::QuotaExceeded(format!( "Workspace {workspace_id} has exceeded the concurrent runs limit of {MAX_FREE_CONCURRENT_RUNS} that applies outside of premium workspaces." ))); @@ -3074,7 +3577,9 @@ pub async fn push<'c, 'd>( None, ), JobPayload::ScriptHub { path } => { - if path == "hub/7771/slack" || path == "hub/7836/slack" { + if path == "hub/7771/slack" || path == "hub/7836/slack" || path == "hub/9084/slack" { + // these scripts send app reports to slack + // they use the slack bot token and should therefore be run with permissions to access it permissioned_as = SUPERADMIN_NOTIFICATION_EMAIL.to_string(); email = SUPERADMIN_NOTIFICATION_EMAIL; } @@ -3377,18 +3882,16 @@ pub async fn push<'c, 'd>( }?; tx = PushIsolationLevel::Transaction(ntx); - let value = data.value().clone(); + let mut value = data.value().clone(); let priority = value.priority; let cache_ttl = value.cache_ttl.map(|x| x as i32); let custom_concurrency_key = value.concurrency_key.clone(); let concurrency_time_window_s = value.concurrency_time_window_s; let concurrent_limit = value.concurrent_limit; - // this is a new flow being pushed, status is set to `value`. - let mut status = FlowStatus::new(&value); let extra = args.extra.get_or_insert_with(HashMap::new); if !apply_preprocessor { - status.preprocessor_module = None; + value.preprocessor_module = None; extra.remove("wm_trigger"); } else { preprocessed = Some(false); @@ -3398,6 +3901,10 @@ pub async fn push<'c, 'd>( })) }); } + + // this is a new flow being pushed, status is set to `value`. + let status = FlowStatus::new(&value); + // Keep inserting `value` if not all workers are updated. // Starting at `v1.440`, the value is fetched on pull from the version id. let value_o = if !*MIN_VERSION_IS_AT_LEAST_1_440.read().await { @@ -3406,9 +3913,6 @@ pub async fn push<'c, 'd>( if same_worker { value.same_worker = true; } - if !apply_preprocessor { - value.preprocessor_module = None; - } Some(value) } else { // `raw_flow` is fetched on pull, the mutations from the other branch are replaced @@ -3599,14 +4103,6 @@ pub async fn push<'c, 'd>( .map(|e| (Some(e.0), e.1)) .unwrap_or_else(|| (None, None)); - let per_workspace_workspaces = DEFAULT_TAGS_WORKSPACES.read().await; - let per_workspace = DEFAULT_TAGS_PER_WORKSPACE.load(std::sync::atomic::Ordering::Relaxed) - && (per_workspace_workspaces.is_none() - || per_workspace_workspaces - .as_ref() - .unwrap() - .contains(&workspace_id.to_string())); - let tag = if dedicated_worker.is_some_and(|x| x) { format!( "{}:{}{}", @@ -3624,6 +4120,7 @@ pub async fn push<'c, 'd>( } let interpolated_tag = tag.map(|x| interpolate_args(x, &args, workspace_id)); + let per_workspace = per_workspace_tag(&workspace_id).await; let default = || { let ntag = if job_kind.is_flow() || job_kind == JobKind::Identity { @@ -3631,6 +4128,7 @@ pub async fn push<'c, 'd>( } else if job_kind == JobKind::Dependencies || job_kind == JobKind::FlowDependencies || job_kind == JobKind::DeploymentCallback + || job_kind == JobKind::AppDependencies { // using the dependency tag for deployment callback for now. We can create a separate tag when we need "dependency".to_string() @@ -3690,7 +4188,12 @@ pub async fn push<'c, 'd>( &job_kind, )); sqlx::query!( - "INSERT INTO concurrency_key(key, job_id) VALUES ($1, $2)", + "WITH inserted_concurrency_counter AS ( + INSERT INTO concurrency_counter (concurrency_id, job_uuids) + VALUES ($1, '{}'::jsonb) + ON CONFLICT DO NOTHING + ) + INSERT INTO concurrency_key(key, job_id) VALUES ($1, $2)", concurrency_key, job_id, ) @@ -3715,16 +4218,77 @@ pub async fn push<'c, 'd>( _ => None, }); + let job_authed = match authed { + Some(authed) + if authed.email == email + && authed.username == permissioned_as_to_username(&permissioned_as) => + { + authed.clone() + } + _ => { + if authed.is_some() { + tracing::warn!("Authed passed to push is not the same as permissioned_as, refetching direclty permissions for job {job_id}...") + } + fetch_authed_from_permissioned_as( + permissioned_as.clone(), + email.to_string(), + workspace_id, + _db, + ) + .await + .map_err(|e| { + Error::internal_err(format!( + "Could not get permissions directly for job {job_id}: {e:#}" + )) + })? + } + }; + + let folders = job_authed + .folders + .iter() + .filter_map(|x| serde_json::to_value(x).ok()) + .collect::>(); + + // if let Err(err) = sqlx::query!("INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) + // values ($1, $2, $3, $4, $5, $6, $7, $8) + // ON CONFLICT (job_id) DO UPDATE SET email = $2, username = $3, is_admin = $4, is_operator = $5, folders = $6, groups = $7, workspace_id = $8", + // job_id, + // job_authed.email, + // job_authed.username, + // job_authed.is_admin, + // job_authed.is_operator, + // folders.as_slice(), + // job_authed.groups.as_slice(), + // workspace_id, + // ).execute(&mut *tx).await { + // tracing::error!("Could not insert job_perms for job {job_id}: {err:#}"); + // } + + sqlx::query!( - "INSERT INTO v2_job (id, workspace_id, raw_code, raw_lock, raw_flow, tag, parent_job, - created_by, permissioned_as, runnable_id, runnable_path, args, kind, trigger, + "WITH inserted_job AS ( + INSERT INTO v2_job (id, workspace_id, raw_code, raw_lock, raw_flow, tag, parent_job, + created_by, permissioned_as, runnable_id, runnable_path, args, kind, trigger, script_lang, same_worker, pre_run_error, permissioned_as_email, visible_to_owner, flow_innermost_root_job, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id, cache_ttl, priority, trigger_kind, script_entrypoint_override, preprocessed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, CASE WHEN $14::VARCHAR IS NOT NULL THEN 'schedule'::job_trigger_kind END, - ($12::JSONB)->>'_ENTRYPOINT_OVERRIDE', $27)", + ($12::JSONB)->>'_ENTRYPOINT_OVERRIDE', $27) + ), + inserted_runtime AS ( + INSERT INTO v2_job_runtime (id, ping) VALUES ($1, null) + ), + inserted_job_perms AS ( + INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) + values ($1, $32, $33, $34, $35, $36, $37, $2) + ON CONFLICT (job_id) DO UPDATE SET email = $32, username = $33, is_admin = $34, is_operator = $35, folders = $36, groups = $37, workspace_id = $2 + ) + INSERT INTO v2_job_queue + (workspace_id, id, running, scheduled_for, started_at, tag, priority) + VALUES ($2, $1, $28, COALESCE($29, now()), CASE WHEN $27 THEN now() END, $30, $31)", job_id, workspace_id, raw_code, @@ -3756,32 +4320,42 @@ pub async fn push<'c, 'd>( cache_ttl, final_priority, preprocessed, + is_running, + scheduled_for_o, + tag, + final_priority, + job_authed.email, + job_authed.username, + job_authed.is_admin, + job_authed.is_operator, + folders.as_slice(), + job_authed.groups.as_slice(), ) .execute(&mut *tx) .warn_after_seconds(1) .await?; - tracing::debug!("Pushing job {job_id} with tag {tag}, schedule_path {schedule_path:?}, script_path: {script_path:?}, email {email}, workspace_id {workspace_id}"); - let uuid = sqlx::query_scalar!( - "INSERT INTO v2_job_queue - (workspace_id, id, running, scheduled_for, started_at, tag, priority) - VALUES ($1, $2, $3, COALESCE($4, now()), CASE WHEN $3 THEN now() END, $5, $6) \ - RETURNING id AS \"id!\"", - workspace_id, - job_id, - is_running, - scheduled_for_o, - tag, - final_priority, - ) - .fetch_one(&mut *tx) - .warn_after_seconds(1) - .await - .map_err(|e| Error::internal_err(format!("Could not insert into queue {job_id} with tag {tag}, schedule_path {schedule_path:?}, script_path: {script_path:?}, email {email}, workspace_id {workspace_id}: {e:#}")))?; +// tracing::debug!("Pushing job {job_id} with tag {tag}, schedule_path {schedule_path:?}, script_path: {script_path:?}, email {email}, workspace_id {workspace_id}"); +// let uuid = sqlx::query_scalar!( +// "INSERT INTO v2_job_queue +// (workspace_id, id, running, scheduled_for, started_at, tag, priority) +// VALUES ($1, $2, $3, COALESCE($4, now()), CASE WHEN $3 THEN now() END, $5, $6) \ +// RETURNING id AS \"id!\"", +// workspace_id, +// job_id, +// , +// ) +// .fetch_one(&mut *tx) +// .warn_after_seconds(1) +// .await +// .map_err(|e| Error::internal_err(format!("Could not insert into queue {job_id} with tag {tag}, schedule_path {schedule_path:?}, script_path: {script_path:?}, email {email}, workspace_id {workspace_id}: {e:#}")))?; - sqlx::query!("INSERT INTO v2_job_runtime (id) VALUES ($1)", job_id) - .execute(&mut *tx) - .await?; + // sqlx::query!( + // "INSERT INTO v2_job_runtime (id, ping) VALUES ($1, null)", + // job_id + // ) + // .execute(&mut *tx) + // .await?; if let Some(flow_status) = flow_status { sqlx::query!( "INSERT INTO v2_job_status (id, flow_status) VALUES ($1, $2)", @@ -3799,54 +4373,7 @@ pub async fn push<'c, 'd>( QUEUE_PUSH_COUNT.inc(); } - if JOB_TOKEN.is_none() { - let job_authed = match authed { - Some(authed) - if authed.email == email - && authed.username == permissioned_as_to_username(&permissioned_as) => - { - authed.clone() - } - _ => { - if authed.is_some() { - tracing::warn!("Authed passed to push is not the same as permissioned_as, refetching direclty permissions for job {job_id}...") - } - fetch_authed_from_permissioned_as( - permissioned_as.clone(), - email.to_string(), - workspace_id, - _db, - ) - .await - .map_err(|e| { - Error::internal_err(format!( - "Could not get permissions directly for job {job_id}: {e:#}" - )) - })? - } - }; - let folders = job_authed - .folders - .iter() - .filter_map(|x| serde_json::to_value(x).ok()) - .collect::>(); - - if let Err(err) = sqlx::query!("INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) - values ($1, $2, $3, $4, $5, $6, $7, $8) - ON CONFLICT (job_id) DO UPDATE SET email = $2, username = $3, is_admin = $4, is_operator = $5, folders = $6, groups = $7, workspace_id = $8", - job_id, - job_authed.email, - job_authed.username, - job_authed.is_admin, - job_authed.is_operator, - folders.as_slice(), - job_authed.groups.as_slice(), - workspace_id, - ).execute(&mut *tx).await { - tracing::error!("Could not insert job_perms for job {job_id}: {err:#}"); - } - } { let uuid_string = job_id.to_string(); @@ -3907,10 +4434,10 @@ pub async fn push<'c, 'd>( .await?; } - Ok((uuid, tx)) + Ok((job_id, tx)) } -pub fn canceled_job_to_result(job: &QueuedJob) -> serde_json::Value { +pub fn canceled_job_to_result(job: &MiniPulledJob) -> serde_json::Value { let reason = job .canceled_reason .as_deref() @@ -3957,7 +4484,7 @@ async fn restarted_flows_resolution( })?; let flow_data = cache::job::fetch_flow(db, row.job_kind, row.script_hash) - .or_else(|_| cache::job::fetch_preview_flow(db, &completed_flow_id, row.raw_flow)) + .or_else(|_| cache::job::fetch_preview_flow(db.into(), &completed_flow_id, row.raw_flow)) .await?; let flow_value = flow_data.value(); let flow_status = row @@ -4110,3 +4637,78 @@ async fn restarted_flows_resolution( flow_status.cleanup_module, )) } + + +#[derive(Serialize, Deserialize)] +pub struct SameWorkerPayload { + pub job_id: Uuid, + pub recoverable: bool, +} + +pub async fn get_same_worker_job( + db: &DB, + same_worker_job: &SameWorkerPayload, +) -> windmill_common::error::Result> { + sqlx::query_as::<_, PulledJob>( + "WITH ping AS ( + UPDATE v2_job_runtime SET ping = NOW() WHERE id = $1 + ), + started_at AS ( + UPDATE v2_job_queue SET started_at = NOW() WHERE id = $1 + ) + SELECT + v2_job_queue.workspace_id, + v2_job_queue.id, + v2_job.args, + v2_job.parent_job, + v2_job.created_by, + v2_job_queue.started_at, + scheduled_for, + v2_job.runnable_path, + v2_job.kind, + v2_job.runnable_id, + v2_job_queue.canceled_reason, + v2_job_queue.canceled_by, + v2_job.permissioned_as, + v2_job.permissioned_as_email, + v2_job_status.flow_status, + v2_job.tag, + v2_job.script_lang, + v2_job.same_worker, + v2_job.pre_run_error, + v2_job.concurrent_limit, + v2_job.concurrency_time_window_s, + v2_job.flow_innermost_root_job, + v2_job.timeout, + v2_job.flow_step_id, + v2_job.cache_ttl, + v2_job_queue.priority, + v2_job.preprocessed, + v2_job.script_entrypoint_override, + v2_job.trigger, + v2_job.trigger_kind, + v2_job.visible_to_owner, + v2_job.raw_code, + v2_job.raw_lock, + v2_job.raw_flow, + pj.runnable_path as parent_runnable_path, + p.email as permissioned_as_email, p.username as permissioned_as_username, p.is_admin as permissioned_as_is_admin, + p.is_operator as permissioned_as_is_operator, p.groups as permissioned_as_groups, p.folders as permissioned_as_folders + FROM v2_job_queue + INNER JOIN v2_job ON v2_job.id = v2_job_queue.id + LEFT JOIN v2_job_status ON v2_job_status.id = v2_job_queue.id + LEFT JOIN job_perms p ON p.job_id = v2_job.id + LEFT JOIN v2_job pj ON v2_job.parent_job = pj.id + WHERE v2_job_queue.id = $1 +", + ) + .bind(same_worker_job.job_id) + .fetch_optional(db) + .await + .map_err(|e| { + Error::internal_err(format!( + "Impossible to fetch same_worker job {}: {}", + same_worker_job.job_id, e + )) + }) +} \ No newline at end of file diff --git a/backend/windmill-queue/src/lib.rs b/backend/windmill-queue/src/lib.rs index b47a3c3238..2496bdf818 100644 --- a/backend/windmill-queue/src/lib.rs +++ b/backend/windmill-queue/src/lib.rs @@ -7,6 +7,8 @@ */ mod jobs; +pub mod jobs_ee; pub mod schedule; - pub use jobs::*; +pub mod flow_status; +pub mod tags; diff --git a/backend/windmill-queue/src/schedule.rs b/backend/windmill-queue/src/schedule.rs index fd4bcab4b4..af1fca6f5d 100644 --- a/backend/windmill-queue/src/schedule.rs +++ b/backend/windmill-queue/src/schedule.rs @@ -9,7 +9,7 @@ use crate::push; use crate::PushIsolationLevel; use anyhow::Context; -use sqlx::{query_scalar, PgExecutor, Postgres, Transaction}; +use sqlx::{PgExecutor, Postgres, Transaction}; use std::collections::HashMap; use std::str::FromStr; use windmill_common::db::Authed; @@ -38,7 +38,8 @@ pub async fn push_scheduled_job<'c>( )); } - let sched = ScheduleType::from_str(&schedule.schedule, schedule.cron_version.as_deref())?; + let sched = + ScheduleType::from_str(&schedule.schedule, schedule.cron_version.as_deref(), false)?; let tz = chrono_tz::Tz::from_str(&schedule.timezone) .map_err(|e| error::Error::BadRequest(e.to_string()))?; @@ -63,18 +64,27 @@ pub async fn push_scheduled_job<'c>( }; let next = sched.find_next(&starting_from); - // println!("next event ({:?}): {}", tz, next); // println!("next event(UTC): {}", next.with_timezone(&chrono::Utc)); // Scheduled events must be stored in the database in UTC let next = next.with_timezone(&chrono::Utc); - - let already_exists: bool = query_scalar!( - "SELECT EXISTS (SELECT 1 FROM v2_as_queue WHERE workspace_id = $1 AND schedule_path = $2 AND scheduled_for = $3)", + // panic!("next: {}", next); + let already_exists: bool = sqlx::query_scalar!( + // Query plan: + // - use of the `ix_v2_job_root_by_path` index; hence the `parent_job IS NULL` clause. + // - select from `v2_job` first, then join with `v2_job_queue` to avoid a full table scan + // on `scheduled_for = $3`. + "SELECT EXISTS ( + SELECT 1 FROM v2_job j JOIN v2_job_queue USING (id) + WHERE j.workspace_id = $1 AND trigger_kind = 'schedule' AND trigger = $2 AND runnable_path = $4 + AND parent_job IS NULL + AND scheduled_for = $3 + )", &schedule.workspace_id, &schedule.path, - next + next, + &schedule.script_path ) .fetch_one(&mut *tx) .await? diff --git a/backend/windmill-queue/src/tags.rs b/backend/windmill-queue/src/tags.rs new file mode 100644 index 0000000000..6cf6620039 --- /dev/null +++ b/backend/windmill-queue/src/tags.rs @@ -0,0 +1,11 @@ +use windmill_common::worker::{DEFAULT_TAGS_PER_WORKSPACE, DEFAULT_TAGS_WORKSPACES}; + +pub async fn per_workspace_tag(workspace_id: &str) -> bool { + let per_workspace_workspaces = DEFAULT_TAGS_WORKSPACES.read().await; + DEFAULT_TAGS_PER_WORKSPACE.load(std::sync::atomic::Ordering::Relaxed) + && (per_workspace_workspaces.is_none() + || per_workspace_workspaces + .as_ref() + .unwrap() + .contains(&workspace_id.to_string())) +} diff --git a/backend/windmill-worker/Cargo.toml b/backend/windmill-worker/Cargo.toml index 7bec644550..e4de400d84 100644 --- a/backend/windmill-worker/Cargo.toml +++ b/backend/windmill-worker/Cargo.toml @@ -20,7 +20,7 @@ flow_testing = [] cloud = [] sqlx = [] deno_core = ["dep:deno_fetch", "dep:deno_webidl", "dep:deno_web", "dep:deno_net", "dep:deno_console", "dep:deno_url", "dep:deno_core", - "dep:deno_ast", "dep:deno_tls", "dep:deno_permissions"] + "dep:deno_ast", "dep:deno_tls", "dep:deno_permissions", "dep:deno_io", "dep:deno_runtime", "dep:deno_telemetry", "dep:deno_error", "dep:winapi"] otel = ["windmill-common/otel", "dep:opentelemetry"] dind = ["dep:bollard"] php = ["dep:windmill-parser-php"] @@ -29,16 +29,21 @@ oracledb = ["dep:oracle"] python = ["dep:windmill-parser-py", "dep:windmill-parser-py-imports"] csharp = ["dep:windmill-parser-csharp"] rust = ["dep:windmill-parser-rust"] +nu = ["dep:windmill-parser-nu"] +java = ["dep:windmill-parser-java"] [dependencies] windmill-queue.workspace = true windmill-audit.workspace = true # there isn't really a reason for audit-worth actions to happen in the worker. windmill-common = { workspace = true, default-features = false } +windmill-macros.workspace = true windmill-parser.workspace = true windmill-parser-ts.workspace = true windmill-parser-go.workspace = true windmill-parser-rust = { workspace = true, optional = true } windmill-parser-csharp = { workspace = true, optional = true } +windmill-parser-nu = { workspace = true, optional = true } +windmill-parser-java = { workspace = true, optional = true } windmill-parser-py = { workspace = true, optional = true } windmill-parser-yaml.workspace = true windmill-parser-py-imports = { workspace = true, optional = true } @@ -47,6 +52,7 @@ windmill-parser-sql.workspace = true windmill-parser-graphql.workspace = true windmill-parser-php = { workspace = true, optional = true } windmill-git-sync.workspace = true +flume.workspace = true sqlx.workspace = true uuid.workspace = true tracing.workspace = true @@ -70,6 +76,8 @@ dyn-iter.workspace = true once_cell.workspace = true tokio-postgres.workspace = true bit-vec.workspace = true +url.workspace = true +deno_telemetry = { workspace = true, optional = true } deno_fetch = { workspace = true, optional = true } deno_webidl = { workspace = true, optional = true } deno_web = { workspace = true, optional = true } @@ -80,6 +88,8 @@ deno_core = { workspace = true, optional = true } deno_ast = { workspace = true, optional = true } deno_tls = { workspace = true, optional = true } deno_permissions = { workspace = true, optional = true } +deno_io = { workspace = true, optional = true } +deno_error = { workspace = true, optional = true } postgres-native-tls.workspace = true native-tls.workspace = true @@ -94,6 +104,7 @@ urlencoding.workspace = true nix.workspace = true bytes.workspace = true reqwest.workspace = true +reqwest-middleware.workspace = true hex.workspace = true tiberius = { workspace = true, optional = true } tokio-util = { workspace = true, optional = true } @@ -102,6 +113,7 @@ object_store = { workspace = true, optional = true} convert_case.workspace = true yaml-rust.workspace = true backon.workspace = true +winapi = { workspace = true, optional = true } opentelemetry = { workspace = true, optional = true } bollard = { workspace = true, optional = true } @@ -118,3 +130,7 @@ deno_core = { workspace = true, optional = true } deno_ast = { workspace = true, optional = true } deno_tls = { workspace = true, optional = true } deno_permissions = { workspace = true, optional = true } +deno_io = { workspace = true, optional = true } +deno_runtime = { workspace = true, optional = true } +deno_telemetry = { workspace = true, optional = true } +winapi = { workspace = true, optional = true } diff --git a/backend/windmill-worker/build.rs b/backend/windmill-worker/build.rs index e48ecddb35..8534e10edf 100644 --- a/backend/windmill-worker/build.rs +++ b/backend/windmill-worker/build.rs @@ -32,9 +32,10 @@ impl FetchPermissions for PermissionsContainer { #[inline(always)] fn check_read<'a>( &mut self, + _resolved: bool, _p: &'a std::path::Path, _api_name: &str, - ) -> Result, deno_permissions::PermissionCheckError> { + ) -> Result, deno_io::fs::FsError> { unreachable!("snapshotting") } } @@ -95,7 +96,7 @@ fn main() { println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap()); let exts = vec![ - // deno_telemetry::deno_telemetry::init_ops_and_esm(), + deno_telemetry::deno_telemetry::init_ops_and_esm(), deno_webidl::deno_webidl::init_ops_and_esm(), deno_url::deno_url::init_ops_and_esm(), deno_console::deno_console::init_ops_and_esm(), @@ -117,7 +118,9 @@ fn main() { deno_core::snapshot::CreateSnapshotOptions { cargo_manifest_dir: env!("CARGO_MANIFEST_DIR"), startup_snapshot: None, - extension_transpiler: None, + extension_transpiler: Some(std::rc::Rc::new(|specifier, source| { + deno_runtime::transpile::maybe_transpile_source(specifier, source) + })), extensions: exts, with_runtime_cb: None, skip_op_registration: false, diff --git a/backend/windmill-worker/nsjail/download_deps.py.pip.sh b/backend/windmill-worker/nsjail/download_deps.py.pip.sh deleted file mode 100755 index efd6274832..0000000000 --- a/backend/windmill-worker/nsjail/download_deps.py.pip.sh +++ /dev/null @@ -1,24 +0,0 @@ -#/bin/sh - -INDEX_URL_ARG=$([ -z "$INDEX_URL" ] && echo ""|| echo "--index-url $INDEX_URL" ) -EXTRA_INDEX_URL_ARG=$([ -z "$EXTRA_INDEX_URL" ] && echo ""|| echo "--extra-index-url $EXTRA_INDEX_URL" ) -TRUSTED_HOST_ARG=$([ -z "$TRUSTED_HOST" ] && echo "" || echo "--trusted-host $TRUSTED_HOST") - -if [ ! -z "$INDEX_URL" ] -then - echo "\$INDEX_URL is set to $INDEX_URL" -fi - -if [ ! -z "$EXTRA_INDEX_URL" ] -then - echo "\$EXTRA_INDEX_URL is set to $EXTRA_INDEX_URL" -fi - -if [ ! -z "$TRUSTED_HOST" ] -then - echo "\$TRUSTED_HOST is set to $TRUSTED_HOST" -fi - -CMD="/usr/local/bin/python3 -m pip install -v \"$REQ\" -I -t \"$TARGET\" --no-cache --no-color --no-deps --isolated --no-warn-conflicts --disable-pip-version-check $INDEX_URL_ARG $EXTRA_INDEX_URL_ARG $TRUSTED_HOST_ARG" -echo $CMD -eval $CMD diff --git a/backend/windmill-worker/nsjail/run.ansible.config.proto b/backend/windmill-worker/nsjail/run.ansible.config.proto index 463de39770..65a8ea7006 100644 --- a/backend/windmill-worker/nsjail/run.ansible.config.proto +++ b/backend/windmill-worker/nsjail/run.ansible.config.proto @@ -16,6 +16,7 @@ clone_newuser: {CLONE_NEWUSER} keep_caps: false keep_env: true +mount_proc: true mount { src: "/bin" @@ -37,6 +38,12 @@ mount { mandatory: false } +mount { + src: "/root/.local/share/uv/tools/ansible" + dst: "/root/.local/share/uv/tools/ansible" + is_bind: true +} + mount { src: "/usr" dst: "/usr" @@ -126,6 +133,13 @@ mount { is_bind: true } +mount { + src: "{PY_INSTALL_DIR}" + dst: "{PY_INSTALL_DIR}" + is_bind: true +} + + {SHARED_MOUNT} {SHARED_DEPENDENCIES} diff --git a/backend/windmill-worker/nsjail/run.bash.config.proto b/backend/windmill-worker/nsjail/run.bash.config.proto index 4f86c66a32..63018f7655 100644 --- a/backend/windmill-worker/nsjail/run.bash.config.proto +++ b/backend/windmill-worker/nsjail/run.bash.config.proto @@ -21,10 +21,24 @@ mount { is_bind: true } +mount { + src: "/proc/self/fd" + dst: "/dev/fd" + is_symlink: true + mandatory: false +} + +mount { + src: "/bin" + dst: "/bin" + is_bind: true +} + mount { src: "/opt/microsoft" dst: "/opt/microsoft" is_bind: true + mandatory: false } mount { diff --git a/backend/windmill-worker/nsjail/run.csharp.config.proto b/backend/windmill-worker/nsjail/run.csharp.config.proto index 4a6de952a7..389448eff0 100644 --- a/backend/windmill-worker/nsjail/run.csharp.config.proto +++ b/backend/windmill-worker/nsjail/run.csharp.config.proto @@ -104,10 +104,9 @@ mount { iface_no_lo: true mount { - src: "{CACHE_DIR}" - dst: "/tmp/.cache/csharp" + src: "{CACHE_DIR}/{CACHE_HASH}" + dst: "/tmp/.cache/csharp/{CACHE_HASH}" is_bind: true - rw: true mandatory: false } diff --git a/backend/windmill-worker/nsjail/run.java.config.proto b/backend/windmill-worker/nsjail/run.java.config.proto new file mode 100644 index 0000000000..032072fb7c --- /dev/null +++ b/backend/windmill-worker/nsjail/run.java.config.proto @@ -0,0 +1,107 @@ +name: "java run script" + +mode: ONCE +hostname: "java" +log_level: ERROR + +disable_rl: true + +cwd: "/tmp" + +clone_newnet: false +clone_newuser: {CLONE_NEWUSER} + +keep_caps: false +keep_env: true +mount_proc: true + +mount { + src: "/bin" + dst: "/bin" + is_bind: true +} + +mount { + src: "/lib" + dst: "/lib" + is_bind: true +} + + +mount { + src: "/lib64" + dst: "/lib64" + is_bind: true + mandatory: false +} + + +mount { + src: "/usr" + dst: "/usr" + is_bind: true +} + +mount { + src: "/dev/null" + dst: "/dev/null" + is_bind: true + rw: true +} + +mount { + dst: "/tmp" + fstype: "tmpfs" + rw: true + options: "size=500000000" +} + + +mount { + src: "{JOB_DIR}/target" + dst: "/tmp/target" + is_bind: true + mandatory: false +} + +mount { + src: "{JOB_DIR}/args.json" + dst: "/tmp/args.json" + is_bind: true +} + +mount { + src: "{JOB_DIR}/result.json" + dst: "/tmp/result.json" + rw: true + is_bind: true +} + +mount { + src: "{CACHE_DIR}" + dst: "{CACHE_DIR}" + is_bind: true + mandatory: false +} + +mount { + src: "/etc" + dst: "/etc" + is_bind: true +} + +mount { + src: "/dev/random" + dst: "/dev/random" + is_bind: true +} + +mount { + src: "/dev/urandom" + dst: "/dev/urandom" + is_bind: true +} + +iface_no_lo: true + +{SHARED_MOUNT} diff --git a/backend/windmill-worker/nsjail/download.py.pip.config.proto b/backend/windmill-worker/nsjail/run.nu.config.proto similarity index 53% rename from backend/windmill-worker/nsjail/download.py.pip.config.proto rename to backend/windmill-worker/nsjail/run.nu.config.proto index 6e2a8a1974..8d27d92455 100644 --- a/backend/windmill-worker/nsjail/download.py.pip.config.proto +++ b/backend/windmill-worker/nsjail/run.nu.config.proto @@ -1,25 +1,19 @@ -name: "python download pip" +name: "nu run script" mode: ONCE -hostname: "python" +hostname: "nu" log_level: ERROR -time_limit: 900 -rlimit_as: 2048 -rlimit_cpu: 1000 -rlimit_fsize: 1024 -rlimit_nofile: 64 - -envar: "HOME=/user" -envar: "LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH" +disable_rl: true cwd: "/tmp" clone_newnet: false clone_newuser: {CLONE_NEWUSER} -keep_caps: true +keep_caps: false keep_env: true +mount_proc: true mount { src: "/bin" @@ -33,6 +27,7 @@ mount { is_bind: true } + mount { src: "/lib64" dst: "/lib64" @@ -40,18 +35,13 @@ mount { mandatory: false } + mount { src: "/usr" dst: "/usr" is_bind: true } -mount { - src: "/etc" - dst: "/etc" - is_bind: true -} - mount { src: "/dev/null" dst: "/dev/null" @@ -63,13 +53,43 @@ mount { dst: "/tmp" fstype: "tmpfs" rw: true - options: "size=500000000" + options: "size=800000000" } +mount { + src: "{NU_PATH}" + dst: "{NU_PATH}" + is_bind: true +} mount { - src: "{WORKER_DIR}/download_deps.py.pip.sh" - dst: "/download_deps.sh" + src: "{JOB_DIR}/main.nu" + dst: "/tmp/main.nu" + is_bind: true +} + +mount { + src: "{JOB_DIR}/result.json" + dst: "/tmp/result.json" + rw: true + is_bind: true +} + +mount { + src: "{JOB_DIR}/args.json" + dst: "/tmp/args.json" + rw: true + is_bind: true +} +mount { + src: "/etc" + dst: "/etc" + is_bind: true +} + +mount { + src: "/dev/random" + dst: "/dev/random" is_bind: true } @@ -79,8 +99,8 @@ mount { is_bind: true } -exec_bin { - path: "/bin/sh" - arg: "/download_deps.sh" -} +iface_no_lo: true +{SHARED_MOUNT} + +envar: "HOME=/tmp" diff --git a/backend/windmill-worker/nsjail/run.powershell.config.proto b/backend/windmill-worker/nsjail/run.powershell.config.proto index 27a36548b4..93a48d4fec 100644 --- a/backend/windmill-worker/nsjail/run.powershell.config.proto +++ b/backend/windmill-worker/nsjail/run.powershell.config.proto @@ -21,6 +21,13 @@ mount { is_bind: true } +mount { + src: "/proc/self/fd" + dst: "/dev/fd" + is_symlink: true + mandatory: false +} + mount { src: "/opt/microsoft" dst: "/opt/microsoft" diff --git a/backend/windmill-worker/nsjail/run.rust.config.proto b/backend/windmill-worker/nsjail/run.rust.config.proto index 502011049c..3357cd88a9 100644 --- a/backend/windmill-worker/nsjail/run.rust.config.proto +++ b/backend/windmill-worker/nsjail/run.rust.config.proto @@ -97,10 +97,9 @@ mount { iface_no_lo: true mount { - src: "{CACHE_DIR}" - dst: "/tmp/.cache/rust" + src: "{CACHE_DIR}/{CACHE_HASH}" + dst: "/tmp/.cache/rust/{CACHE_HASH}" is_bind: true - rw: true mandatory: false } diff --git a/backend/windmill-worker/src/agent_workers.rs b/backend/windmill-worker/src/agent_workers.rs new file mode 100644 index 0000000000..192ae7ae37 --- /dev/null +++ b/backend/windmill-worker/src/agent_workers.rs @@ -0,0 +1,31 @@ +use uuid::Uuid; +use windmill_common::{agent_workers::QueueInitJob, worker::HttpClient}; +use windmill_queue::{JobAndPerms, JobCompleted}; + +pub async fn queue_init_job(client: &HttpClient, content: &str) -> anyhow::Result { + client + .post( + "/api/agent_workers/queue_init_job", + &QueueInitJob { content: content.to_string() }, + ) + .await + .and_then(|x: String| Uuid::parse_str(&x).map_err(|e| anyhow::anyhow!(e))) +} + +pub async fn pull_job(client: &HttpClient) -> anyhow::Result> { + client.post("/api/agent_workers/pull_job", &()).await +} + +pub async fn send_result(client: &HttpClient, jc: JobCompleted) -> anyhow::Result { + client + .post( + &format!( + "/api/w/{}/agent_workers/send_result/{}", + jc.job.workspace_id, jc.job.id + ), + &jc, + ) + .await +} + +pub const UPDATE_PING_URL: &str = "/api/agent_workers/update_ping"; diff --git a/backend/windmill-worker/src/ansible_executor.rs b/backend/windmill-worker/src/ansible_executor.rs index fee2eaa119..a0aa683cb3 100644 --- a/backend/windmill-worker/src/ansible_executor.rs +++ b/backend/windmill-worker/src/ansible_executor.rs @@ -2,23 +2,25 @@ use std::{collections::HashMap, os::unix::fs::PermissionsExt, path::PathBuf, process::Stdio}; #[cfg(windows)] -use std::{ - collections::HashMap, - path::{Path, PathBuf}, - process::Stdio, -}; +use std::{collections::HashMap, path::PathBuf, process::Stdio}; use anyhow::anyhow; +use futures::future::try_join_all; use itertools::Itertools; +use serde::{Deserialize, Serialize}; use serde_json::value::RawValue; use tokio::process::Command; use uuid::Uuid; use windmill_common::{ error, - jobs::QueuedJob, - worker::{to_raw_value, write_file, write_file_at_user_defined_location, WORKER_CONFIG}, + worker::{ + is_allowed_file_location, to_raw_value, write_file, write_file_at_user_defined_location, + Connection, WORKER_CONFIG, + }, }; -use windmill_parser_yaml::{AnsibleRequirements, ResourceOrVariablePath}; +use windmill_queue::MiniPulledJob; + +use windmill_parser_yaml::{AnsibleRequirements, GitRepo, ResourceOrVariablePath}; use windmill_queue::{append_logs, CanceledBy}; use crate::{ @@ -29,8 +31,8 @@ use crate::{ }, handle_child::handle_child, python_executor::{create_dependencies_dir, handle_python_reqs, uv_pip_compile, PyVersion}, - AuthedClientBackgroundTask, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV, - PROXY_ENVS, TZ_ENV, + AuthedClient, DISABLE_NSJAIL, DISABLE_NUSER, GIT_PATH, HOME_ENV, NSJAIL_PATH, PATH_ENV, + PROXY_ENVS, PY_INSTALL_DIR, TZ_ENV, }; lazy_static::lazy_static! { @@ -42,14 +44,301 @@ lazy_static::lazy_static! { } const NSJAIL_CONFIG_RUN_ANSIBLE_CONTENT: &str = include_str!("../nsjail/run.ansible.config.proto"); +const WINDMILL_ANSIBLE_PASSWORD_FILENAME: &str = ".windmill.ansible_vault_password_file"; +async fn clone_repo( + repo: &GitRepo, + job_dir: &str, + job_id: &Uuid, + worker_name: &str, + conn: &Connection, + mem_peak: &mut i32, + canceled_by: &mut Option, + w_id: &str, + occupancy_metrics: &mut OccupancyMetrics, + git_ssh_cmd: &str, +) -> error::Result { + let target_path = is_allowed_file_location(job_dir, &repo.target_path)?; + + let mut clone_cmd = Command::new(GIT_PATH.as_str()); + clone_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .args(["clone", "--quiet"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + if let Some(branch) = &repo.branch { + clone_cmd.args(["--branch", branch]); + } + clone_cmd.arg(&repo.url); + clone_cmd.arg(&target_path); + + let clone_cmd_child = start_child_process(clone_cmd, GIT_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + clone_cmd_child, + false, + worker_name, + w_id, + "git clone", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + + // Checkout specific commit if provided + if let Some(commit) = &repo.commit { + let mut checkout_cmd = Command::new(GIT_PATH.as_str()); + checkout_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .arg("-C") + .arg(&target_path) + .args(["checkout", "--quiet", commit]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let checkout_cmd_child = start_child_process(checkout_cmd, GIT_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + checkout_cmd_child, + false, + worker_name, + w_id, + "git checkout", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + } + + let mut rev_parse_cmd = Command::new(GIT_PATH.as_str()); + + let commit_hash_output = rev_parse_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .arg("-C") + .arg(&target_path) + .args(["rev-parse", "HEAD"]) + .stderr(Stdio::piped()) + .output() + .await?; + + if !commit_hash_output.status.success() { + let stderr = String::from_utf8(commit_hash_output.stderr)?; + return Err(anyhow!("Error getting git repo commit hash: {stderr}").into()); + } + + let commit_hash = String::from_utf8(commit_hash_output.stdout)? + .trim() + .to_string(); + + Ok(commit_hash) +} + +pub fn create_empty_dir(path: &PathBuf) -> std::io::Result<()> { + if path.exists() { + if path.is_dir() { + let mut entries = std::fs::read_dir(&path)?; + if entries.next().is_some() { + return Err(std::io::Error::new( + std::io::ErrorKind::AlreadyExists, + format!( + "Directory '{}' already exists and is not empty", + path.display() + ), + )); + } + Ok(()) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::AlreadyExists, + format!("Path '{}' exists and is not a directory", path.display()), + )) + } + } else { + std::fs::create_dir_all(path) + } +} + +async fn clone_repo_without_history( + repo: &GitRepo, + full_commit: &str, + job_dir: &str, + job_id: &Uuid, + worker_name: &str, + conn: &Connection, + mem_peak: &mut i32, + canceled_by: &mut Option, + w_id: &str, + occupancy_metrics: &mut OccupancyMetrics, + git_ssh_cmd: &str, +) -> error::Result<()> { + let target_path = is_allowed_file_location(job_dir, &repo.target_path)?; + + create_empty_dir(&target_path)?; + + let mut init_cmd = Command::new(GIT_PATH.as_str()); + init_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .arg("-C") + .arg(&target_path) + .args(["init", "--quiet"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + if let Some(branch) = &repo.branch { + init_cmd.args(["--initial-branch", branch]); + } + + let init_cmd_child = start_child_process(init_cmd, GIT_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + init_cmd_child, + false, + worker_name, + w_id, + "git init", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + + let mut add_remote_cmd = Command::new(GIT_PATH.as_str()); + add_remote_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .arg("-C") + .arg(&target_path) + .args(vec!["remote", "add", "origin", &repo.url]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let add_remote_cmd_child = start_child_process(add_remote_cmd, GIT_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + add_remote_cmd_child, + false, + worker_name, + w_id, + "git add remote", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + + let mut fetch_cmd = Command::new(GIT_PATH.as_str()); + fetch_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .arg("-C") + .arg(&target_path) + .args(vec!["fetch", "--depth=1", "--quiet", "origin", full_commit]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let fetch_cmd_child = start_child_process(fetch_cmd, GIT_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + fetch_cmd_child, + false, + worker_name, + w_id, + "git fetch", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + + let mut checkout_cmd = Command::new(GIT_PATH.as_str()); + checkout_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .arg("-C") + .arg(&target_path) + .args(["checkout", "--quiet", "FETCH_HEAD"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let checkout_cmd_child = start_child_process(checkout_cmd, GIT_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + checkout_cmd_child, + false, + worker_name, + w_id, + "git checkout", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + + Ok(()) +} async fn handle_ansible_python_deps( job_dir: &str, requirements_o: Option<&String>, ansible_reqs: Option<&AnsibleRequirements>, w_id: &str, job_id: &Uuid, - db: &sqlx::Pool, + conn: &Connection, worker_name: &str, worker_dir: &str, mem_peak: &mut i32, @@ -80,13 +369,12 @@ async fn handle_ansible_python_deps( mem_peak, canceled_by, job_dir, - db, + conn, worker_name, w_id, &mut Some(occupancy_metrics), PyVersion::Py311, false, - false, ) .await .map_err(|e| { @@ -107,13 +395,12 @@ async fn handle_ansible_python_deps( w_id, mem_peak, canceled_by, - db, + conn, worker_name, job_dir, worker_dir, &mut Some(occupancy_metrics), crate::python_executor::PyVersion::Py311, - false, ) .await?; additional_python_paths.append(&mut venv_path); @@ -121,7 +408,7 @@ async fn handle_ansible_python_deps( Ok(additional_python_paths) } -async fn install_galaxy_collections( +pub async fn install_galaxy_collections( collections_yml: &str, job_dir: &str, job_id: &Uuid, @@ -129,8 +416,9 @@ async fn install_galaxy_collections( w_id: &str, mem_peak: &mut i32, canceled_by: &mut Option, - db: &sqlx::Pool, + conn: &Connection, occupancy_metrics: &mut OccupancyMetrics, + git_ssh_cmd: &str, ) -> anyhow::Result<()> { write_file(job_dir, "requirements.yml", collections_yml)?; @@ -138,18 +426,55 @@ async fn install_galaxy_collections( job_id, w_id, "\n\n--- ANSIBLE GALAXY INSTALL ---\n".to_string(), - db, + conn, ) .await; - let mut galaxy_command = Command::new(ANSIBLE_GALAXY_PATH.as_str()); - galaxy_command + + let mut galaxy_roles_cmd = Command::new(ANSIBLE_GALAXY_PATH.as_str()); + galaxy_roles_cmd .current_dir(job_dir) .env_clear() .envs(PROXY_ENVS.clone()) .env("PATH", PATH_ENV.as_str()) .env("TZ", TZ_ENV.as_str()) - // .env("BASE_INTERNAL_URL", base_internal_url) - // .env("HOME", HOME_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .args(vec![ + "role", + "install", + "-r", + "requirements.yml", + "-p", + "./roles", + ]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let child = start_child_process(galaxy_roles_cmd, ANSIBLE_GALAXY_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + child, + !*DISABLE_NSJAIL, + worker_name, + w_id, + "ansible-galaxy role install", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + + let mut galaxy_collections_cmd = Command::new(ANSIBLE_GALAXY_PATH.as_str()); + galaxy_collections_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) .args(vec![ "collection", "install", @@ -161,36 +486,290 @@ async fn install_galaxy_collections( .stdout(Stdio::piped()) .stderr(Stdio::piped()); - let child = start_child_process(galaxy_command, ANSIBLE_GALAXY_PATH.as_str()).await?; + let child = start_child_process(galaxy_collections_cmd, ANSIBLE_GALAXY_PATH.as_str()).await?; handle_child( job_id, - db, + conn, mem_peak, canceled_by, child, !*DISABLE_NSJAIL, worker_name, w_id, - "ansible galaxy install", + "ansible-galaxy collection install", None, false, &mut Some(occupancy_metrics), + None, ) .await?; Ok(()) } +#[derive(Serialize, Deserialize)] +pub struct AnsibleDependencyLocks { + pub python_lockfile: String, + pub git_repos: HashMap, // URL to full commit hash + pub collections_and_roles: String, + pub collections_and_roles_logs: String, + // pub collection_versions: HashMap, // + // pub role_versions: HashMap, +} + +pub async fn get_collection_locks( + job_dir: &str, +) -> anyhow::Result<(HashMap, String)> { + let mut ansible_cmd = Command::new(ANSIBLE_GALAXY_PATH.as_str()); + + ansible_cmd + .current_dir(job_dir) + .args(["collection", "list", "--format", "json", "-p", "./"]); + + let output = ansible_cmd.output().await?; + + let mut ret = HashMap::new(); + let mut logs = String::new(); + + if !output.status.success() { + let stderr = String::from_utf8(output.stderr)?; + return Err(anyhow!( + "Error getting ansible collection versions: {stderr}" + )); + } + + let stdout = String::from_utf8(output.stdout)?; + + let val: serde_json::Value = serde_json::from_str(&stdout)?; + + let Some(own_collections) = val.get(format!("{}/ansible_collections", job_dir)) else { + return Ok((ret, logs)); + }; + + let collections = own_collections.as_object().ok_or(anyhow!( + "Expected an object (map) for the `ansible-galaxy collection list` command output and got {}", + own_collections + ))?; + + for (c_name, c) in collections.iter() { + if let Some(v) = c.get("version").and_then(|v| v.as_str()) { + // TODO: Check if version is not something like `(undefined)` + ret.insert(c_name.clone(), v.to_string()); + } else { + logs.push_str(&format!("Failed to get version for collection `{}`. Expected an object with a string in the `version` field but received {}\n", c_name, c)); + } + } + + Ok((ret, logs)) +} + +pub async fn get_role_locks(job_dir: &str) -> anyhow::Result<(HashMap, String)> { + let mut ansible_cmd = Command::new(ANSIBLE_GALAXY_PATH.as_str()); + + ansible_cmd + .current_dir(job_dir) + .args(["role", "list", "-p", "./roles"]); + + let output = ansible_cmd.output().await?; + let mut ret = HashMap::new(); + let mut logs = String::new(); + + if !output.status.success() { + let stderr = String::from_utf8(output.stderr)?; + logs.push_str(&format!("Error getting ansible role versions: {stderr}")); + return Ok((ret, logs)); + } + + let stdout = String::from_utf8(output.stdout)?; + + let mut lines = stdout.lines(); + + while let Some(line) = lines.next() { + if line == format!("# {}/roles", job_dir) { + break; + } + } + + for line in lines { + let line = line.strip_prefix("-").unwrap_or(line); + let mut cols = line.split(","); + + if let Some(name) = cols.next().map(|n| n.trim()) { + if let Some(version) = cols.next().map(|v| v.trim()) { + // TODO: Check if version is not something like `(undefined)` + ret.insert(name.to_string(), version.to_string()); + } else { + logs.push_str(&format!("Failed to get version for role `{}`.", name)); + } + } + } + + Ok((ret, logs)) +} + +pub async fn get_git_repo_full_head_commit_hash( + repo: &GitRepo, + git_ssh_cmd: &str, +) -> anyhow::Result { + let mut git_cmd = Command::new(GIT_PATH.as_str()); + + git_cmd + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .args(["ls-remote", &repo.url, "HEAD"]); + + let output = git_cmd.stderr(Stdio::piped()).output().await?; + + if !output.status.success() { + let stderr = String::from_utf8(output.stderr)?; + return Err(anyhow!("Error getting git repo commit hash: {stderr}")); + } + + let stdout = String::from_utf8(output.stdout)?; + + let lines: Vec<&str> = stdout.lines().collect(); + + if lines.len() != 1 { + return Err(anyhow!("Unexpected output format for git ls-remote",)); + } + + Ok(lines + .first() + .ok_or(anyhow!( + "The HEAD commit hash was not found for repo `{}`", + &repo.url + ))? + .split_whitespace() + .next() + .map(|s| s.to_string()) + .ok_or(anyhow!("Unexpected output format for git ls-remote"))?) +} + +pub async fn get_git_repos_lock( + repos: &Vec, + job_dir: &str, + job_id: &Uuid, + worker_name: &str, + conn: &Connection, + mem_peak: &mut i32, + canceled_by: &mut Option, + w_id: &str, + occupancy_metrics: &mut OccupancyMetrics, + git_ssh_cmd: &str, +) -> anyhow::Result> { + let mut ret = HashMap::new(); + + for repo in repos { + if repo.commit.is_some() { + ret.insert( + repo.url.to_string(), + clone_repo( + repo, + job_dir, + job_id, + worker_name, + conn, + mem_peak, + canceled_by, + w_id, + occupancy_metrics, + git_ssh_cmd, + ) + .await?, + ); + } else { + ret.insert( + repo.url.to_string(), + get_git_repo_full_head_commit_hash(repo, git_ssh_cmd).await?, + ); + } + } + + Ok(ret) +} + +pub fn create_ansible_cfg( + reqs: Option<&AnsibleRequirements>, + job_dir: &str, + vault_password_file_exists: bool, +) -> error::Result<()> { + let mut passwords_cfg = String::new(); + if vault_password_file_exists { + passwords_cfg.push_str(&format!( + "vault_password_file = {WINDMILL_ANSIBLE_PASSWORD_FILENAME}\n" + )); + } + if let Some(vault_ids) = reqs.as_ref().map(|r| &r.vault_id) { + if !vault_ids.is_empty() { + let password_files = vault_ids.join(","); + + passwords_cfg.push_str(&format!("vault_identity_list = {password_files}\n")); + } + } + let ansible_cfg_content = format!( + r#" +[defaults] +collections_path = ./ +roles_path = ./roles +home={job_dir}/.ansible +local_tmp={job_dir}/.ansible/tmp +remote_tmp={job_dir}/.ansible/tmp +{passwords_cfg} +"# + ); + + write_file(job_dir, "ansible.cfg", &ansible_cfg_content)?; + + Ok(()) +} + +pub async fn get_git_ssh_cmd( + reqs: &AnsibleRequirements, + job_dir: &str, + client: &AuthedClient, +) -> error::Result { + let ssh_id_files = try_join_all(reqs.git_ssh_identity.iter().enumerate().map( + async |(i, var_path)| -> error::Result { + let id_file_name = format!(".ssh_id_priv_{}", i); + let loc = is_allowed_file_location(job_dir, &id_file_name)?; + + let mut content = client.get_variable_value(var_path).await.map_err(|e| { + error::Error::NotFound(format!( + "Variable {var_path} not found for git ssh identity: {e:#}" + )) + })?; + content.push_str("\n"); + + let file = write_file(job_dir, &id_file_name, &content)?; + + #[cfg(unix)] + { + let perm = std::os::unix::fs::PermissionsExt::from_mode(0o600); + file.set_permissions(perm)?; + } + + Ok(format!( + " -i '{}'", + loc.to_string_lossy().replace('\'', r"'\''") + )) + }, + )) + .await?; + + let git_ssh_cmd = format!("ssh -o StrictHostKeyChecking=no{}", ssh_id_files.join("")); + Ok(git_ssh_cmd) +} + pub async fn handle_ansible_job( requirements_o: Option<&String>, job_dir: &str, worker_dir: &str, worker_name: &str, - job: &QueuedJob, + job: &MiniPulledJob, mem_peak: &mut i32, canceled_by: &mut Option, - db: &sqlx::Pool, - client: &AuthedClientBackgroundTask, + conn: &Connection, + client: &AuthedClient, + parent_runnable_path: Option, inner_content: &String, shared_mount: &str, base_internal_url: &str, @@ -203,17 +782,21 @@ pub async fn handle_ansible_job( "ansible", )?; + let req_lockfiles: Option = requirements_o + .map(|s| serde_json::from_str(s)) + .transpose()?; + let (logs, reqs, playbook) = windmill_parser_yaml::parse_ansible_reqs(inner_content)?; - append_logs(&job.id, &job.workspace_id, logs, db).await; + append_logs(&job.id, &job.workspace_id, logs, conn).await; write_file(job_dir, "main.yml", &playbook)?; let additional_python_paths = handle_ansible_python_deps( job_dir, - requirements_o, + req_lockfiles.as_ref().map(|r| &r.python_lockfile), reqs.as_ref(), &job.workspace_id, &job.id, - db, + conn, worker_name, worker_dir, mem_peak, @@ -222,6 +805,11 @@ pub async fn handle_ansible_job( ) .await?; + let git_ssh_cmd = &match &reqs { + Some(r) => get_git_ssh_cmd(r, job_dir, client).await?, + None => "ssh".to_string(), + }; + let interpolated_args; if let Some(args) = &job.args { let mut args = args.0.clone(); @@ -230,7 +818,7 @@ pub async fn handle_ansible_job( args.insert(name.clone(), to_raw_value(path)); } } - if let Some(x) = transform_json(client, &job.workspace_id, &args, job, db).await? { + if let Some(x) = transform_json(client, &job.workspace_id, &args, job, conn).await? { write_file( job_dir, "args.json", @@ -268,55 +856,129 @@ pub async fn handle_ansible_job( }) .unwrap_or_else(|| vec![]); - let authed_client = client.get_authed().await; let mut nsjail_extra_mounts = vec![]; - if let Some(r) = reqs { + if let Some(r) = reqs.as_ref() { nsjail_extra_mounts = create_file_resources( &job.id, &job.workspace_id, job_dir, interpolated_args.as_ref(), &r, - &authed_client, - db, + &client, + conn, ) .await?; - if let Some(collections) = r.collections { + for repo in &r.git_repos { + append_logs( + &job.id, + &job.workspace_id, + format!("\nCloning {}...\n", &repo.url), + conn, + ) + .await; + if let Some(full_commit_hash) = req_lockfiles + .as_ref() + .and_then(|r| r.git_repos.get(&repo.url)) + { + clone_repo_without_history( + repo, + full_commit_hash, + job_dir, + &job.id, + worker_name, + conn, + mem_peak, + canceled_by, + &job.workspace_id, + occupancy_metrics, + git_ssh_cmd, + ) + .await + .map_err(|e| anyhow!("Failed to clone git repo `{}`: {e}", repo.url))?; + } else { + if req_lockfiles.is_some() { + append_logs( + &job.id, + &job.workspace_id, + format!("Warning: `{}` is using latest commit because the lockfile didn't store a commit hash for this repo. Updates to the repo could break the deployed playbook.\n", &repo.url), + conn, + ) + .await; + } + clone_repo( + repo, + job_dir, + &job.id, + worker_name, + conn, + mem_peak, + canceled_by, + &job.workspace_id, + occupancy_metrics, + git_ssh_cmd, + ) + .await + .map_err(|e| anyhow!("Failed to clone git repo `{}`: {e}", repo.url))?; + } + + append_logs( + &job.id, + &job.workspace_id, + format!("Cloned {} into {}\n", &repo.url, &repo.target_path), + conn, + ) + .await; + } + + if let Some(collections) = r.roles_and_collections.as_ref() { + let empty = String::new(); + let (lockfile, logs) = req_lockfiles + .as_ref() + .map(|r| (&r.collections_and_roles, &r.collections_and_roles_logs)) + .unwrap_or((collections, &empty)); + + if !logs.is_empty() { + append_logs(&job.id, &job.workspace_id, logs, conn).await; + } + install_galaxy_collections( - collections.as_str(), + lockfile, job_dir, &job.id, worker_name, &job.workspace_id, mem_peak, canceled_by, - db, + conn, occupancy_metrics, + git_ssh_cmd, ) .await?; } } + append_logs( &job.id, &job.workspace_id, "\n\n--- ANSIBLE PLAYBOOK EXECUTION ---\n".to_string(), - db, + conn, ) .await; - let ansible_cfg_content = format!( - r#" -[defaults] -collections_path = ./ -roles_path = ./roles -home={job_dir}/.ansible -local_tmp={job_dir}/.ansible/tmp -remote_tmp={job_dir}/.ansible/tmp -"# - ); - write_file(job_dir, "ansible.cfg", &ansible_cfg_content)?; - let mut reserved_variables = get_reserved_variables(job, &authed_client.token, db).await?; + let vault_password_file_exists = match reqs.as_ref().and_then(|x| x.vault_password.as_ref()) { + Some(var_path) => { + let password = client.get_variable_value(&var_path).await?; + write_file(job_dir, WINDMILL_ANSIBLE_PASSWORD_FILENAME, &password)?; + true + } + None => false, + }; + + create_ansible_cfg(reqs.as_ref(), job_dir, vault_password_file_exists)?; + + let mut reserved_variables = + get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?; let additional_python_paths_folders = additional_python_paths.join(":"); if !*DISABLE_NSJAIL { @@ -339,6 +1001,7 @@ mount {{ job_dir, "run.config.proto", &NSJAIL_CONFIG_RUN_ANSIBLE_CONTENT + .replace("{PY_INSTALL_DIR}", PY_INSTALL_DIR) .replace("{JOB_DIR}", job_dir) .replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string()) .replace("{SHARED_MOUNT}", shared_mount) @@ -424,7 +1087,7 @@ fi handle_child( &job.id, - db, + conn, mem_peak, canceled_by, child, @@ -435,6 +1098,7 @@ fi job.timeout, false, &mut Some(occupancy_metrics), + None, ) .await?; read_and_check_result(job_dir).await @@ -495,7 +1159,7 @@ async fn create_file_resources( args: Option<&HashMap>>, r: &AnsibleRequirements, client: &crate::AuthedClient, - db: &sqlx::Pool, + conn: &Connection, ) -> error::Result> { let mut logs = String::new(); let mut nsjail_mounts: Vec = vec![]; @@ -565,7 +1229,7 @@ async fn create_file_resources( file_res.target_path, file_res.resource_path )); } - append_logs(job_id, w_id, logs, db).await; + append_logs(job_id, w_id, logs, conn).await; Ok(nsjail_mounts) } diff --git a/backend/windmill-worker/src/bash_executor.rs b/backend/windmill-worker/src/bash_executor.rs index 7b536afdae..7a7a78282c 100644 --- a/backend/windmill-worker/src/bash_executor.rs +++ b/backend/windmill-worker/src/bash_executor.rs @@ -15,17 +15,13 @@ use tokio::process::Command; use uuid::Uuid; use windmill_common::{ error::Error, - jobs::QueuedJob, - worker::{to_raw_value, write_file}, + worker::{to_raw_value, write_file, Connection}, }; -#[cfg(feature = "dind")] -use windmill_common::DB; - #[cfg(feature = "dind")] use windmill_common::error::to_anyhow; -use windmill_queue::{append_logs, CanceledBy}; +use windmill_queue::{append_logs, CanceledBy, MiniPulledJob}; lazy_static::lazy_static! { pub static ref BIN_BASH: String = std::env::var("BASH_PATH").unwrap_or_else(|_| "/bin/bash".to_string()); @@ -47,7 +43,7 @@ use crate::{ OccupancyMetrics, }, handle_child::handle_child, - AuthedClientBackgroundTask, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV, + AuthedClient, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV, POWERSHELL_CACHE_DIR, POWERSHELL_PATH, PROXY_ENVS, TZ_ENV, }; @@ -63,9 +59,10 @@ lazy_static::lazy_static! { pub async fn handle_bash_job( mem_peak: &mut i32, canceled_by: &mut Option, - job: &QueuedJob, - db: &sqlx::Pool, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + conn: &Connection, + client: &AuthedClient, + parent_runnable_path: Option, content: &str, job_dir: &str, shared_mount: &str, @@ -81,7 +78,7 @@ pub async fn handle_bash_job( if annotation.docker { logs1.push_str("docker mode\n"); } - append_logs(&job.id, &job.workspace_id, logs1, db).await; + append_logs(&job.id, &job.workspace_id, logs1, &conn).await; write_file(job_dir, "main.sh", &format!("set -e\n{content}"))?; let script = format!( @@ -96,8 +93,10 @@ cleanup() {{ # Ignore SIGTERM and SIGINT trap '' SIGTERM SIGINT + rm -f bp 2>/dev/null + # Kill the process group of the script (negative PID value) - pkill -P $$ + pkill -P $$ 2>/dev/null || true exit }} @@ -110,16 +109,19 @@ mkfifo bp # Start background processes cat bp | tail -1 >> ./result2.out & +tail_pid=$! # Run main.sh in the same process group {bash} ./main.sh "$@" 2>&1 | tee bp & - pid=$! # Wait for main.sh to finish and capture its exit status wait $pid exit_status=$? +# Ensure tail has finished before cleanup +wait $tail_pid 2>/dev/null || true + # Clean up the named pipe and background processes rm -f bp pkill -P $$ || true @@ -131,11 +133,11 @@ exit $exit_status ); write_file(job_dir, "wrapper.sh", &script)?; - let token = client.get_token().await; - let mut reserved_variables = get_reserved_variables(job, &token, db).await?; + let mut reserved_variables = + get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?; reserved_variables.insert("RUST_LOG".to_string(), "info".to_string()); - let args = build_args_map(job, client, db).await?.map(Json); + let args = build_args_map(job, client, conn).await?.map(Json); let job_args = if args.is_some() { args.as_ref() } else { @@ -156,7 +158,13 @@ exit $exit_status let _ = write_file(job_dir, "result.out", "")?; let _ = write_file(job_dir, "result2.out", "")?; - let child = if !*DISABLE_NSJAIL { + let nsjail = !*DISABLE_NSJAIL + && job + .runnable_path + .as_ref() + .map(|x| !x.starts_with("init_script_")) + .unwrap_or(true); + let child = if nsjail { let _ = write_file( job_dir, "run.config.proto", @@ -204,17 +212,18 @@ exit $exit_status }; handle_child( &job.id, - db, + conn, mem_peak, canceled_by, child, - !*DISABLE_NSJAIL, + nsjail, worker_name, &job.workspace_id, "bash run", job.timeout, true, &mut Some(occupancy_metrics), + None, ) .await?; @@ -223,7 +232,7 @@ exit $exit_status return handle_docker_job( job.id, &job.workspace_id, - db, + conn, job.timeout, mem_peak, canceled_by, @@ -268,7 +277,7 @@ exit $exit_status async fn handle_docker_job( job_id: Uuid, workspace_id: &str, - db: &DB, + conn: &Connection, job_timeout: Option, mem_peak: &mut i32, canceled_by: &mut Option, @@ -303,7 +312,7 @@ async fn handle_docker_job( let ncontainer_id = container_id.to_string(); let w_id = workspace_id.to_string(); let j_id = job_id.clone(); - let db2 = db.clone(); + let conn2 = conn.clone(); let (tx, mut rx) = tokio::sync::broadcast::channel::<()>(1); let mut killpill_rx = killpill_rx.resubscribe(); @@ -325,13 +334,13 @@ async fn handle_docker_job( log = log_stream.next() => { match log { Some(Ok(log)) => { - append_logs(&j_id, w_id.clone(), log.to_string(), db2.clone()).await; + append_logs(&j_id, w_id.clone(), log.to_string(), &conn2).await; } Some(Err(e)) => { tracing::error!("Error getting logs: {:?}", e); } _ => { - tracing::error!("End of stream"); + tracing::info!("End of docker logs stream"); return } }; @@ -359,7 +368,7 @@ async fn handle_docker_job( let result = run_future_with_polling_update_job_poller( job_id, job_timeout, - db, + conn, mem_peak, canceled_by, wait_f, @@ -459,9 +468,10 @@ fn raw_to_string(x: &str) -> String { pub async fn handle_powershell_job( mem_peak: &mut i32, canceled_by: &mut Option, - job: &QueuedJob, - db: &sqlx::Pool, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + db: &Connection, + client: &AuthedClient, + parent_runnable_path: Option, content: &str, job_dir: &str, shared_mount: &str, @@ -471,7 +481,7 @@ pub async fn handle_powershell_job( occupancy_metrics: &mut OccupancyMetrics, ) -> Result, Error> { let pwsh_args = { - let args = build_args_map(job, client, db).await?.map(Json); + let args = build_args_map(job, client, &db).await?.map(Json); let job_args = if args.is_some() { args.as_ref() } else { @@ -557,6 +567,7 @@ pub async fn handle_powershell_job( job.timeout, false, &mut Some(occupancy_metrics), + None, ) .await?; } @@ -643,8 +654,8 @@ $env:PSModulePath = \"{};$PSModulePathBackup\"", ), )?; - let token = client.get_token().await; - let mut reserved_variables = get_reserved_variables(job, &token, db).await?; + let mut reserved_variables = + get_reserved_variables(job, &client.token, db, parent_runnable_path).await?; reserved_variables.insert("RUST_LOG".to_string(), "info".to_string()); let _ = write_file(job_dir, "result.json", "")?; @@ -768,6 +779,7 @@ $env:PSModulePath = \"{};$PSModulePathBackup\"", job.timeout, false, &mut Some(occupancy_metrics), + None, ) .await?; diff --git a/backend/windmill-worker/src/bigquery_executor.rs b/backend/windmill-worker/src/bigquery_executor.rs index e4674ed6ee..9cc9fd6c90 100644 --- a/backend/windmill-worker/src/bigquery_executor.rs +++ b/backend/windmill-worker/src/bigquery_executor.rs @@ -5,7 +5,7 @@ use futures::{FutureExt, TryFutureExt}; use reqwest::Client; use serde_json::{json, value::RawValue, Value}; use windmill_common::error::to_anyhow; -use windmill_common::jobs::QueuedJob; +use windmill_common::worker::Connection; use windmill_common::{error::Error, worker::to_raw_value}; use windmill_parser_sql::{ parse_bigquery_sig, parse_db_resource, parse_sql_blocks, parse_sql_statement_named_params, @@ -16,9 +16,10 @@ use serde::Deserialize; use crate::common::{build_http_client, OccupancyMetrics}; use crate::handle_child::run_future_with_polling_update_job_poller; +use crate::sanitized_sql_params::sanitize_and_interpolate_unsafe_sql_args; use crate::{ common::{build_args_values, resolve_job_timeout}, - AuthedClientBackgroundTask, + AuthedClient, }; use gcp_auth::{AuthenticationManager, CustomServiceAccount}; @@ -203,26 +204,26 @@ fn do_bigquery_inner<'a>( Ok(result_f.boxed()) } +use windmill_queue::MiniPulledJob; + pub async fn do_bigquery( - job: &QueuedJob, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + client: &AuthedClient, query: &str, - db: &sqlx::Pool, + conn: &Connection, mem_peak: &mut i32, canceled_by: &mut Option, worker_name: &str, column_order: &mut Option>, occupancy_metrics: &mut OccupancyMetrics, ) -> windmill_common::error::Result> { - let bigquery_args = build_args_values(job, client, db).await?; + let bigquery_args = build_args_values(job, client, conn).await?; let inline_db_res_path = parse_db_resource(&query); let db_arg = if let Some(inline_db_res_path) = inline_db_res_path { Some( client - .get_authed() - .await .get_resource_value_interpolated::( &inline_db_res_path, Some(job.id.to_string()), @@ -252,7 +253,7 @@ pub async fn do_bigquery( .map_err(|e| Error::ExecutionErr(e.to_string()))?; let (timeout_duration, _, _) = - resolve_job_timeout(&db, &job.workspace_id, job.id, job.timeout).await; + resolve_job_timeout(&conn, &job.workspace_id, job.id, job.timeout).await; let timeout_ms = timeout_duration.as_millis() as u64; let http_client = build_http_client(timeout_duration)?; @@ -261,15 +262,21 @@ pub async fn do_bigquery( .await .map_err(|e| Error::ExecutionErr(e.to_string()))?; - let queries = parse_sql_blocks(query); - - let mut statement_values: HashMap = HashMap::new(); - let sig = parse_bigquery_sig(&query) .map_err(|x| Error::ExecutionErr(x.to_string()))? .args; + let (query, args_to_skip) = + &sanitize_and_interpolate_unsafe_sql_args(query, &sig, &bigquery_args)?; + + let queries = parse_sql_blocks(query); + + let mut statement_values: HashMap = HashMap::new(); + for arg in &sig { + if args_to_skip.contains(&arg.name) { + continue; + } let arg_t = arg.otyp.clone().unwrap_or_else(|| "string".to_string()); let arg_n = arg.clone().name; let arg_v = bigquery_args.get(&arg.name).cloned().unwrap_or(json!("")); @@ -360,7 +367,7 @@ pub async fn do_bigquery( let r = run_future_with_polling_update_job_poller( job.id, job.timeout, - db, + conn, mem_peak, canceled_by, result_f.map_err(to_anyhow), diff --git a/backend/windmill-worker/src/bun_executor.rs b/backend/windmill-worker/src/bun_executor.rs index 4fc471df91..6a6ad120e7 100644 --- a/backend/windmill-worker/src/bun_executor.rs +++ b/backend/windmill-worker/src/bun_executor.rs @@ -1,17 +1,15 @@ #[cfg(feature = "deno_core")] use std::time::Instant; -use std::{collections::HashMap, fs, path::Path, process::Stdio}; +use std::{collections::HashMap, fs, process::Stdio}; -use anyhow::Context; use base64::Engine; use itertools::Itertools; use serde_json::value::RawValue; -use sha2::Digest; use uuid::Uuid; use windmill_parser_ts::remove_pinned_imports; -use windmill_queue::{append_logs, CanceledBy}; +use windmill_queue::{append_logs, CanceledBy, MiniPulledJob, PrecomputedAgentInfo}; #[cfg(feature = "enterprise")] use crate::common::build_envs_map; @@ -22,9 +20,9 @@ use crate::{ read_file_content, read_result, start_child_process, write_file_binary, OccupancyMetrics, }, handle_child::handle_child, - AuthedClientBackgroundTask, BUNFIG_INSTALL_SCOPES, BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR, - BUN_DEPSTAR_CACHE_DIR, BUN_PATH, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NODE_BIN_PATH, - NODE_PATH, NPM_CONFIG_REGISTRY, NPM_PATH, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, TZ_ENV, + AuthedClient, BUNFIG_INSTALL_SCOPES, BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR, BUN_PATH, + DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NODE_BIN_PATH, NODE_PATH, NPM_CONFIG_REGISTRY, + NPM_PATH, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, TZ_ENV, }; #[cfg(windows)] @@ -43,9 +41,8 @@ use windmill_common::variables; use windmill_common::{ error::{self, Result}, get_latest_hash_for_path, - jobs::QueuedJob, scripts::ScriptLang, - worker::{exists_in_cache, save_cache, write_file}, + worker::{exists_in_cache, save_cache, to_raw_value, write_file, Connection, DISABLE_BUNDLING}, DB, }; @@ -99,7 +96,7 @@ pub async fn gen_bun_lockfile( canceled_by: &mut Option, job_id: &Uuid, w_id: &str, - db: Option<&sqlx::Pool>, + db: Option<&Connection>, token: &str, script_path: &str, job_dir: &str, @@ -114,7 +111,7 @@ pub async fn gen_bun_lockfile( let mut empty_deps = false; - if let Some(raw_deps) = raw_deps { + if let Some(raw_deps) = raw_deps.as_ref() { gen_bunfig(job_dir).await?; write_file(job_dir, "package.json", raw_deps.as_str())?; } else { @@ -169,6 +166,7 @@ pub async fn gen_bun_lockfile( None, false, occupancy_metrics, + None, ) .await?; } else { @@ -203,10 +201,21 @@ pub async fn gen_bun_lockfile( } if export_pkg { - let mut content = "".to_string(); + let mut content; { let mut file = File::open(format!("{job_dir}/package.json")).await?; - file.read_to_string(&mut content).await?; + let mut buf = String::default(); + file.read_to_string(&mut buf).await?; + if raw_deps.is_some() { + let mut json_map: HashMap> = serde_json::from_str(&buf)?; + json_map.insert( + "generatedFromPackageJson".to_string(), + to_raw_value(&"true".to_string()), + ); + content = serde_json::to_string_pretty(&json_map)?; + } else { + content = buf; + } } if !npm_mode { #[cfg(any(target_os = "linux", target_os = "macos"))] @@ -275,7 +284,7 @@ pub async fn install_bun_lockfile( canceled_by: &mut Option, job_id: &Uuid, w_id: &str, - db: Option<&sqlx::Pool>, + db: Option<&Connection>, job_dir: &str, worker_name: &str, common_bun_proc_envs: HashMap, @@ -352,6 +361,7 @@ pub async fn install_bun_lockfile( None, false, occupancy_metrics, + None, ) .await? } else { @@ -489,7 +499,7 @@ pub async fn generate_wrapper_mjs( w_id: &str, job_id: &Uuid, worker_name: &str, - db: &sqlx::Pool, + db: &Connection, timeout: Option, mem_peak: &mut i32, canceled_by: &mut Option, @@ -523,6 +533,7 @@ pub async fn generate_wrapper_mjs( timeout, false, occupancy_metrics, + None, ) .await?; fs::rename( @@ -538,7 +549,7 @@ pub async fn generate_bun_bundle( w_id: &str, job_id: &Uuid, worker_name: &str, - db: Option>, + db: Option<&Connection>, timeout: Option, mem_peak: &mut i32, canceled_by: &mut Option, @@ -573,6 +584,7 @@ pub async fn generate_bun_bundle( timeout, false, occupancy_metrics, + None, ) .await?; } else { @@ -581,100 +593,66 @@ pub async fn generate_bun_bundle( Ok(()) } -#[cfg(all(feature = "enterprise", feature = "parquet"))] pub async fn pull_codebase(w_id: &str, id: &str, job_dir: &str) -> Result<()> { - use crate::global_cache::extract_tar; - let path = windmill_common::s3_helpers::bundle(&w_id, &id); - let bun_cache_path = format!("{}/{}", crate::ROOT_CACHE_NOMOUNT_DIR, path); + let bun_cache_path = format!( + "{}/{}", + windmill_common::worker::ROOT_CACHE_NOMOUNT_DIR, + path + ); let is_tar = id.ends_with(".tar"); let dst = format!( "{job_dir}/{}", if is_tar { "codebase.tar" } else { "main.js" } ); - let dirs_splitted = bun_cache_path.split("/").collect_vec(); - tokio::fs::create_dir_all(dirs_splitted[..dirs_splitted.len() - 1].join("/")).await?; - if tokio::fs::metadata(&bun_cache_path).await.is_ok() { + + if std::fs::metadata(&bun_cache_path).is_ok() { tracing::info!("loading {bun_cache_path} from cache"); - if is_tar { - extract_tar(fs::read(bun_cache_path)?.into(), job_dir).await?; - } else { - #[cfg(unix)] - tokio::fs::symlink(&bun_cache_path, dst).await?; + extract_saved_codebase(job_dir, &bun_cache_path, is_tar, &dst, false)?; + } else { + #[cfg(all(feature = "enterprise", feature = "parquet"))] + let object_store = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS + .read() + .await + .clone(); - #[cfg(windows)] - std::os::windows::fs::symlink_dir(&bun_cache_path, &dst)?; - } - } else if let Some(os) = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS - .read() - .await - .clone() - { - let bytes = attempt_fetch_bytes(os, &path).await?; + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + let object_store: Option<()> = None; - tokio::fs::write(&bun_cache_path, &bytes).await?; - if is_tar { - extract_tar(bytes, job_dir).await?; - } else { - #[cfg(unix)] - tokio::fs::symlink(bun_cache_path, dst).await?; - - #[cfg(windows)] - std::os::windows::fs::symlink_dir(&bun_cache_path, &dst)?; - } - - // extract_tar(bytes, job_dir).await?; - } - - return Ok(()); -} - -#[cfg(not(all(feature = "enterprise", feature = "parquet")))] -pub async fn pull_codebase(_w_id: &str, _id: &str, _job_dir: &str) -> Result<()> { - return Err(error::Error::ExecutionErr( - "codebase is an EE feature".to_string(), - )); -} - -#[cfg(unix)] -pub fn copy_recursively( - source: impl AsRef, - destination: impl AsRef, - skip: Option<&Vec>, -) -> Result<()> { - let mut stack = Vec::new(); - stack.push(( - source.as_ref().to_path_buf(), - destination.as_ref().to_path_buf(), - 0, - )); - while let Some((current_source, current_destination, level)) = stack.pop() { - for entry in fs::read_dir(¤t_source) - .context(format!("reading directory {current_source:?}"))? + if &windmill_common::utils::MODE_AND_ADDONS.mode + == &windmill_common::utils::Mode::Standalone + && object_store.is_none() { - let entry = entry?; - let filetype = entry.file_type()?; - let destination = current_destination.join(entry.file_name()); - if level == 0 { - if let Some(skip) = skip { - if skip.contains(&entry.file_name().to_string_lossy().to_string()) { - continue; - } - } - } - - let original = entry.path(); - - if filetype.is_dir() { - fs::create_dir_all(&destination)?; - stack.push((entry.path(), destination, level + 1)); + let bun_cache_path = format!( + "{}{}", + *windmill_common::worker::ROOT_STANDALONE_BUNDLE_DIR, + id + ); + if std::fs::metadata(&bun_cache_path).is_ok() { + tracing::info!("loading {bun_cache_path} from standalone bundle cache"); + extract_saved_codebase(job_dir, &bun_cache_path, is_tar, &dst, true)?; } else { - fs::hard_link(&original, &destination).map_err(|e| { - error::Error::internal_err(format!( - "hard linking from {original:?} to {destination:?}: {e:#}" - )) - })?; + return Err(error::Error::ExecutionErr(format!( + "(standalone bundle test mode) could not find codebase at {bun_cache_path}" + ))); + } + } else { + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + return Err(error::Error::ExecutionErr( + "codebase is an EE feature".to_string(), + )); + + #[cfg(all(feature = "enterprise", feature = "parquet"))] + if let Some(os) = object_store { + let dirs_splitted = bun_cache_path.split("/").collect_vec(); + std::fs::create_dir_all(dirs_splitted[..dirs_splitted.len() - 1].join("/"))?; + + let bytes = attempt_fetch_bytes(os, &path).await?; + tracing::info!("loading {bun_cache_path} from object store"); + + std::fs::write(&bun_cache_path, &bytes)?; + extract_saved_codebase(job_dir, &bun_cache_path, is_tar, &dst, false)?; } } } @@ -682,32 +660,50 @@ pub fn copy_recursively( Ok(()) } +fn extract_saved_codebase( + job_dir: &str, + bun_cache_path: &String, + is_tar: bool, + dst: &str, + copy: bool, +) -> Result<()> { + use crate::global_cache::extract_tar; + + Ok(if is_tar { + extract_tar(fs::read(bun_cache_path)?.into(), job_dir)?; + } else { + if copy { + std::fs::copy(bun_cache_path, dst)?; + } else { + #[cfg(unix)] + std::os::unix::fs::symlink(bun_cache_path, dst)?; + + #[cfg(windows)] + std::os::windows::fs::symlink_dir(bun_cache_path, dst)?; + } + }) +} + pub async fn prebundle_bun_script( inner_content: &str, lockfile: Option<&String>, script_path: &str, job_id: &Uuid, w_id: &str, - db: Option, + db: Option<&DB>, job_dir: &str, base_internal_url: &str, worker_name: &str, token: &str, occupancy_metrics: &mut Option<&mut OccupancyMetrics>, ) -> Result<()> { - let (local_path, remote_path) = compute_bundle_local_and_remote_path( - inner_content, - lockfile, - script_path, - db.clone(), - w_id, - ) - .await; + let (local_path, remote_path) = + compute_bundle_local_and_remote_path(inner_content, lockfile, script_path, db, w_id).await; if exists_in_cache(&local_path, &remote_path).await { return Ok(()); } let annotation = windmill_common::worker::TypeScriptAnnotations::parse(inner_content); - if annotation.nobundling { + if annotation.nobundling || *DISABLE_BUNDLING { return Ok(()); } let origin = format!("{job_dir}/main.js"); @@ -736,7 +732,7 @@ pub async fn prebundle_bun_script( w_id, job_id, worker_name, - db.clone(), + db.map(|x| Connection::from(x.clone())).as_ref(), None, &mut 0, &mut None, @@ -745,7 +741,7 @@ pub async fn prebundle_bun_script( ) .await?; - save_cache(&local_path, &remote_path, &origin).await?; + save_cache(&local_path, &remote_path, &origin, false).await?; Ok(()) } @@ -764,11 +760,11 @@ async fn get_script_import_updated_at(db: &DB, w_id: &str, script_path: &str) -> Ok(last_updated_at.to_string()) } -async fn compute_bundle_local_and_remote_path( +pub async fn compute_bundle_local_and_remote_path( inner_content: &str, requirements_o: Option<&String>, script_path: &str, - db: Option, + db: Option<&DB>, w_id: &str, ) -> (String, String) { let mut input_src = format!( @@ -835,9 +831,10 @@ pub async fn handle_bun_job( codebase: Option<&String>, mem_peak: &mut i32, canceled_by: &mut Option, - job: &QueuedJob, - db: &sqlx::Pool, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + conn: &Connection, + client: &AuthedClient, + parent_runnable_path: Option, job_dir: &str, inner_content: &String, base_internal_url: &str, @@ -846,27 +843,46 @@ pub async fn handle_bun_job( shared_mount: &str, new_args: &mut Option>>, occupancy_metrics: &mut OccupancyMetrics, + precomputed_agent_info: Option, ) -> error::Result> { let mut annotation = windmill_common::worker::TypeScriptAnnotations::parse(inner_content); - let (mut has_bundle_cache, cache_logs, local_path, remote_path) = - if requirements_o.is_some() && !annotation.nobundling && codebase.is_none() { - let (local_path, remote_path) = compute_bundle_local_and_remote_path( - inner_content, - requirements_o, - job.script_path(), - Some(db.clone()), - &job.workspace_id, - ) - .await; - - let (cache, logs) = - windmill_common::worker::load_cache(&local_path, &remote_path).await; - (cache, logs, local_path, remote_path) - } else { - (false, "".to_string(), "".to_string(), "".to_string()) + let (mut has_bundle_cache, cache_logs, local_path, remote_path) = if requirements_o.is_some() + && !annotation.nobundling + && !*DISABLE_BUNDLING + && codebase.is_none() + { + let (local_path, remote_path) = match conn { + Connection::Sql(db) => { + compute_bundle_local_and_remote_path( + inner_content, + requirements_o, + job.runnable_path(), + Some(db), + &job.workspace_id, + ) + .await + } + Connection::Http(_) => { + let (local_path, remote_path) = match precomputed_agent_info { + Some(PrecomputedAgentInfo::Bun { local, remote }) => (local, remote), + _ => { + return Err(error::Error::ExecutionErr( + "bun bundle is missing the precomputed agent info".to_string(), + )) + } + }; + (local_path, remote_path) + } }; + let (cache, logs) = + windmill_common::worker::load_cache(&local_path, &remote_path, false).await; + (cache, logs, local_path, remote_path) + } else { + (false, "".to_string(), "".to_string(), "".to_string()) + }; + if !codebase.is_some() && !has_bundle_cache { let _ = write_file(job_dir, "main.ts", inner_content)?; } else if !annotation.native && codebase.is_none() { @@ -880,16 +896,8 @@ pub async fn handle_bun_job( annotation.nodejs = true } let main_override = job.script_entrypoint_override.as_deref(); - let apply_preprocessor = !job.is_flow_step && job.preprocessed == Some(false); + let apply_preprocessor = !job.is_flow_step() && job.preprocessed == Some(false); - #[cfg(not(feature = "enterprise"))] - if annotation.nodejs || annotation.npm { - return Err(error::Error::ExecutionErr( - "Nodejs / npm mode is an EE feature".to_string(), - )); - } - - let mut gbuntar_name: Option = None; if has_bundle_cache { let target; let symlink; @@ -924,80 +932,36 @@ pub async fn handle_bun_job( let _ = write_file(job_dir, "package.json", pkg)?; let lock = if annotation.npm { "" } else { lock.unwrap() }; if !empty { - let mut skip_install = false; - let mut create_buntar = false; - let mut buntar_path = "".to_string(); - if !annotation.npm { let _ = write_lock(lock, job_dir, is_binary).await?; - - let mut sha_path = sha2::Sha256::new(); - sha_path.update(lock.as_bytes()); - - let buntar_name = - base64::engine::general_purpose::URL_SAFE.encode(sha_path.finalize()); - buntar_path = format!("{BUN_DEPSTAR_CACHE_DIR}/{buntar_name}"); - - #[cfg(unix)] - if tokio::fs::metadata(&buntar_path).await.is_ok() { - if let Err(e) = copy_recursively(&buntar_path, job_dir, None) { - tracing::error!("Could not extract buntar: {e:#}"); - } else { - gbuntar_name = Some(buntar_name.clone()); - skip_install = true; - } - } else { - create_buntar = true; - } } - if !skip_install { - install_bun_lockfile( - mem_peak, - canceled_by, - &job.id, - &job.workspace_id, - Some(db), - job_dir, - worker_name, - common_bun_proc_envs.clone(), - annotation.npm, - &mut Some(occupancy_metrics), - ) - .await?; - - #[cfg(unix)] - if create_buntar { - fs::create_dir_all(&buntar_path)?; - if let Err(e) = copy_recursively( - job_dir, - &buntar_path, - Some(&vec![ - "main.ts".to_string(), - "package.json".to_string(), - if is_binary { "bun.lockb" } else { "bun.lock" }.to_string(), - "shared".to_string(), - "bunfig.toml".to_string(), - ]), - ) { - fs::remove_dir_all(&buntar_path).context("deleting buntar directory")?; - tracing::error!("Could not create buntar: {e}"); - } - } - } + install_bun_lockfile( + mem_peak, + canceled_by, + &job.id, + &job.workspace_id, + Some(conn), + job_dir, + worker_name, + common_bun_proc_envs.clone(), + annotation.npm, + &mut Some(occupancy_metrics), + ) + .await?; } } else { // if !*DISABLE_NSJAIL || !empty_trusted_deps || has_custom_config_registry { let logs1 = "\n\n--- BUN INSTALL ---\n".to_string(); - append_logs(&job.id, &job.workspace_id, logs1, db).await; + append_logs(&job.id, &job.workspace_id, logs1, conn).await; let _ = gen_bun_lockfile( mem_peak, canceled_by, &job.id, &job.workspace_id, - Some(db), - &client.get_token().await, - &job.script_path(), + Some(conn), + &client.token, + job.runnable_path(), job_dir, base_internal_url, worker_name, @@ -1031,13 +995,6 @@ pub async fn handle_bun_job( "\n\n--- BUN CODE EXECUTION ---\n".to_string() }; - if let Some(gbuntar_name) = gbuntar_name { - init_logs = format!( - "\nskipping install, using cached buntar based on lockfile hash: {gbuntar_name}{}", - init_logs - ); - } - if has_bundle_cache { init_logs = format!("\n{}{}", cache_logs, init_logs); } @@ -1050,6 +1007,7 @@ pub async fn handle_bun_job( let args = windmill_parser_ts::parse_deno_signature( inner_content, true, + false, main_override.map(ToString::to_string), )? .args; @@ -1059,6 +1017,7 @@ pub async fn handle_bun_job( windmill_parser_ts::parse_deno_signature( inner_content, true, + false, Some("preprocessor".to_string()), )? .args, @@ -1170,13 +1129,13 @@ try {{ let reserved_variables_args_out_f = async { let args_and_out_f = async { if !annotation.native { - create_args_and_out_file(&client, job, job_dir, db).await?; + create_args_and_out_file(&client, job, job_dir, conn).await?; } Ok(()) as Result<()> }; let reserved_variables_f = async { - let client = client.get_authed().await; - let vars = get_reserved_variables(job, &client.token, db).await?; + let vars = + get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?; Ok(vars) as Result> }; let (_, reserved_variables) = tokio::try_join!(args_and_out_f, reserved_variables_f)?; @@ -1185,6 +1144,7 @@ try {{ let build_cache = !has_bundle_cache && !annotation.nobundling + && !*DISABLE_BUNDLING && !codebase.is_some() && (requirements_o.is_some() || annotation.native); @@ -1193,9 +1153,9 @@ try {{ build_loader( job_dir, base_internal_url, - &client.get_token().await, + &client.token, &job.workspace_id, - &job.script_path(), + job.runnable_path(), if annotation.nodejs { LoaderMode::NodeBundle } else if annotation.native { @@ -1211,9 +1171,9 @@ try {{ build_loader( job_dir, base_internal_url, - &client.get_token().await, + &client.token, &job.workspace_id, - &job.script_path(), + job.runnable_path(), if annotation.nodejs { LoaderMode::Node } else { @@ -1238,7 +1198,7 @@ try {{ &job.workspace_id, &job.id, worker_name, - Some(db.clone()), + Some(conn), job.timeout, mem_peak, canceled_by, @@ -1247,7 +1207,14 @@ try {{ ) .await?; if !local_path.is_empty() { - match save_cache(&local_path, &remote_path, &format!("{job_dir}/main.js")).await { + match save_cache( + &local_path, + &remote_path, + &format!("{job_dir}/main.js"), + false, + ) + .await + { Err(e) => { let em = format!("could not save {local_path} to bundle cache: {e:?}"); tracing::error!(em) @@ -1280,7 +1247,7 @@ try {{ &job.workspace_id, &job.id, worker_name, - db, + conn, job.timeout, mem_peak, canceled_by, @@ -1310,7 +1277,7 @@ try {{ .join("\n")); let js_code = read_file_content(&format!("{job_dir}/main.js")).await?; let started_at = Instant::now(); - let args = crate::common::build_args_map(job, client, db) + let args = crate::common::build_args_map(job, client, conn) .await? .map(sqlx::types::Json); let job_args = if args.is_some() { @@ -1319,16 +1286,17 @@ try {{ job.args.as_ref() }; - append_logs(&job.id, &job.workspace_id, format!("{init_logs}\n"), db).await; + append_logs(&job.id, &job.workspace_id, format!("{init_logs}\n"), conn).await; let result = crate::js_eval::eval_fetch_timeout( env_code, inner_content.clone(), js_code, job_args, + job.script_entrypoint_override.clone(), job.id, job.timeout, - db, + conn, mem_peak, canceled_by, worker_name, @@ -1344,7 +1312,7 @@ try {{ return Ok(result); } } - append_logs(&job.id, &job.workspace_id, init_logs, db).await; + append_logs(&job.id, &job.workspace_id, init_logs, conn).await; //do not cache local dependencies let child = if !*DISABLE_NSJAIL { @@ -1476,7 +1444,7 @@ try {{ handle_child( &job.id, - db, + conn, mem_peak, canceled_by, child, @@ -1487,6 +1455,7 @@ try {{ job.timeout, false, &mut Some(occupancy_metrics), + None, ) .await?; @@ -1565,7 +1534,7 @@ pub async fn start_worker( script_path: &str, token: &str, job_completed_tx: JobCompletedSender, - jobs_rx: Receiver>, + jobs_rx: Receiver>, killpill_rx: tokio::sync::broadcast::Receiver<()>, ) -> Result<()> { let mut logs = "".to_string(); @@ -1585,7 +1554,7 @@ pub async fn start_worker( annotation.nodejs = true; let context = variables::get_reserved_variables( - db, + &Connection::from(db.clone()), w_id, &token, "dedicated_worker@windmill.dev", @@ -1636,7 +1605,7 @@ pub async fn start_worker( &mut canceled_by, &Uuid::nil(), &w_id, - Some(db), + Some(&Connection::from(db.clone())), job_dir, worker_name, common_bun_proc_envs.clone(), @@ -1653,7 +1622,7 @@ pub async fn start_worker( &mut canceled_by, &Uuid::nil(), &w_id, - Some(db), + Some(&Connection::from(db.clone())), token, &script_path, job_dir, @@ -1672,7 +1641,7 @@ pub async fn start_worker( { // let mut start = Instant::now(); - let args = windmill_parser_ts::parse_deno_signature(inner_content, true, None)?.args; + let args = windmill_parser_ts::parse_deno_signature(inner_content, true, false, None)?.args; let dates = args .iter() .filter_map(|x| { @@ -1754,7 +1723,7 @@ for await (const line of Readline.createInterface({{ input: process.stdin }})) { w_id, &Uuid::nil(), worker_name, - db, + &Connection::from(db.clone()), None, &mut mem_peak, &mut canceled_by, diff --git a/backend/windmill-worker/src/common.rs b/backend/windmill-worker/src/common.rs index a64a32c869..2699b89d02 100644 --- a/backend/windmill-worker/src/common.rs +++ b/backend/windmill-worker/src/common.rs @@ -11,6 +11,7 @@ use sha2::Digest; use sqlx::types::Json; use sqlx::{Pool, Postgres}; use tokio::process::Command; +use tokio::sync::{RwLock, Semaphore}; use tokio::{fs::File, io::AsyncReadExt}; #[cfg(feature = "parquet")] @@ -19,18 +20,20 @@ use windmill_common::s3_helpers::{ }; use windmill_common::variables::{build_crypt_with_key_suffix, decrypt}; use windmill_common::worker::{ - to_raw_value, write_file, CLOUD_HOSTED, ROOT_CACHE_DIR, WORKER_CONFIG, + to_raw_value, update_ping_for_failed_init_script_query, write_file, Connection, Ping, PingType, + CLOUD_HOSTED, ROOT_CACHE_DIR, WORKER_CONFIG, }; use windmill_common::{ cache::{Cache, RawData}, error::{self, Error}, - jobs::QueuedJob, scripts::ScriptHash, variables::ContextualVariable, }; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, bail, Result}; +use windmill_queue::MiniPulledJob; +use std::ops::AsyncFn; use std::path::Path; use std::{collections::HashMap, sync::Arc, time::Duration}; @@ -39,18 +42,19 @@ use windmill_common::{variables, DB}; use tokio::{io::AsyncWriteExt, process::Child, time::Instant}; +use crate::agent_workers::UPDATE_PING_URL; use crate::{ - AuthedClient, AuthedClientBackgroundTask, JOB_DEFAULT_TIMEOUT, MAX_RESULT_SIZE, - MAX_TIMEOUT_DURATION, PATH_ENV, + AuthedClient, DISABLE_NSJAIL, JOB_DEFAULT_TIMEOUT, MAX_RESULT_SIZE, MAX_TIMEOUT_DURATION, + PATH_ENV, }; pub async fn build_args_map<'a>( - job: &'a QueuedJob, - client: &AuthedClientBackgroundTask, - db: &Pool, + job: &'a MiniPulledJob, + client: &AuthedClient, + conn: &Connection, ) -> error::Result>>> { if let Some(args) = &job.args { - return transform_json(client, &job.workspace_id, &args.0, &job, db).await; + return transform_json(client, &job.workspace_id, &args.0, &job, conn).await; } return Ok(None); } @@ -73,12 +77,12 @@ pub fn check_executor_binary_exists( } pub async fn build_args_values( - job: &QueuedJob, - client: &AuthedClientBackgroundTask, - db: &Pool, + job: &MiniPulledJob, + client: &AuthedClient, + conn: &Connection, ) -> error::Result> { if let Some(args) = &job.args { - transform_json_as_values(client, &job.workspace_id, &args.0, &job, db).await + transform_json_as_values(client, &job.workspace_id, &args.0, job, conn).await } else { Ok(HashMap::new()) } @@ -86,13 +90,13 @@ pub async fn build_args_values( #[tracing::instrument(level = "trace", skip_all)] pub async fn create_args_and_out_file( - client: &AuthedClientBackgroundTask, - job: &QueuedJob, + client: &AuthedClient, + job: &MiniPulledJob, job_dir: &str, - db: &Pool, + conn: &Connection, ) -> Result<(), Error> { if let Some(args) = job.args.as_ref() { - if let Some(x) = transform_json(client, &job.workspace_id, &args.0, job, db).await? { + if let Some(x) = transform_json(client, &job.workspace_id, &args.0, job, conn).await? { write_file( job_dir, "args.json", @@ -126,11 +130,11 @@ lazy_static::lazy_static! { } pub async fn transform_json<'a>( - client: &AuthedClientBackgroundTask, + client: &AuthedClient, workspace: &str, vs: &'a HashMap>, - job: &QueuedJob, - db: &Pool, + job: &MiniPulledJob, + db: &Connection, ) -> error::Result>>> { let mut has_match = false; for (_, v) in vs { @@ -150,9 +154,7 @@ pub async fn transform_json<'a>( let value = serde_json::from_str(inner_vs).map_err(|e| { error::Error::internal_err(format!("Error while parsing inner arg: {e:#}")) })?; - let transformed = - transform_json_value(&k, &client.get_authed().await, workspace, value, job, db) - .await?; + let transformed = transform_json_value(&k, &client, workspace, value, job, db).await?; let as_raw = serde_json::from_value(transformed).map_err(|e| { error::Error::internal_err(format!("Error while parsing inner arg: {e:#}")) })?; @@ -165,11 +167,11 @@ pub async fn transform_json<'a>( } pub async fn transform_json_as_values<'a>( - client: &AuthedClientBackgroundTask, + client: &AuthedClient, workspace: &str, vs: &'a HashMap>, - job: &QueuedJob, - db: &Pool, + job: &MiniPulledJob, + db: &Connection, ) -> error::Result> { let mut r: HashMap = HashMap::new(); for (k, v) in vs { @@ -178,9 +180,7 @@ pub async fn transform_json_as_values<'a>( let value = serde_json::from_str(inner_vs).map_err(|e| { error::Error::internal_err(format!("Error while parsing inner arg: {e:#}")) })?; - let transformed = - transform_json_value(&k, &client.get_authed().await, workspace, value, job, db) - .await?; + let transformed = transform_json_value(&k, &client, workspace, value, job, db).await?; let as_raw = serde_json::from_value(transformed).map_err(|e| { error::Error::internal_err(format!("Error while parsing inner arg: {e:#}")) })?; @@ -213,14 +213,33 @@ pub fn parse_npm_config(s: &str) -> (String, Option) { return (url, token_opt); } +#[async_recursion] +pub async fn get_root_job_id(job: &Uuid, db: &Pool) -> anyhow::Result { + let njob = sqlx::query_scalar!( + "SELECT flow_innermost_root_job FROM v2_job WHERE id = $1", + job + ) + .fetch_optional(db) + .await? + .flatten(); + if let Some(root_job) = njob { + if root_job == *job { + return Ok(job.to_owned()); + } + get_root_job_id(&root_job, db).await + } else { + Ok(job.to_owned()) + } +} + #[async_recursion] pub async fn transform_json_value( name: &str, client: &AuthedClient, workspace: &str, v: Value, - job: &QueuedJob, - db: &Pool, + job: &MiniPulledJob, + conn: &Connection, ) -> error::Result { match v { Value::String(y) if y.starts_with("$var:") => { @@ -251,50 +270,39 @@ pub async fn transform_json_value( }) } Value::String(y) if y.starts_with("$encrypted:") => { - let encrypted = y.strip_prefix("$encrypted:").unwrap(); - let mc = - build_crypt_with_key_suffix(&db, &job.workspace_id, &job.id.to_string()).await?; - decrypt(&mc, encrypted.to_string()).and_then(|x| { - serde_json::from_str(&x).map_err(|e| Error::internal_err(e.to_string())) - }) + match conn { + Connection::Sql(db) => { + let encrypted = y.strip_prefix("$encrypted:").unwrap(); + + let root_job_id = + get_root_job_id(&job.flow_innermost_root_job.unwrap_or_else(|| job.id), db) + .await?; + let mc = build_crypt_with_key_suffix( + &db, + &job.workspace_id, + &root_job_id.to_string(), + ) + .await?; + decrypt(&mc, encrypted.to_string()).and_then(|x| { + serde_json::from_str(&x).map_err(|e| Error::internal_err(e.to_string())) + }) + } + Connection::Http(_) => { + Err(Error::NotFound("Http connection not supported".to_string())) + } + } // let path = y.strip_prefix("$res:").unwrap(); } Value::String(y) if y.starts_with("$") => { - let flow_path = if let Some(uuid) = job.parent_job { - sqlx::query_scalar!("SELECT runnable_path FROM v2_job WHERE id = $1", uuid) - .fetch_optional(db) - .await? - .flatten() - } else { - None - }; - - let variables = variables::get_reserved_variables( - db, - &job.workspace_id, - &client.token, - &job.email, - &job.created_by, - &job.id.to_string(), - &job.permissioned_as, - job.script_path.clone(), - job.parent_job.map(|x| x.to_string()), - flow_path, - job.schedule_path.clone(), - job.flow_step_id.clone(), - job.root_job.clone().map(|x| x.to_string()), - None, - Some(job.scheduled_for.clone()), - ) - .await; + let variables = get_reserved_variables(job, &client.token, conn, None).await?; let name = y.strip_prefix("$").unwrap(); let value = variables .iter() - .find(|x| x.name == name) - .map(|x| x.value.clone()) + .find(|x| x.0 == name) + .map(|x| x.1.clone()) .unwrap_or_else(|| y); Ok(json!(value)) } @@ -302,7 +310,7 @@ pub async fn transform_json_value( for (a, b) in m.clone().into_iter() { m.insert( a.clone(), - transform_json_value(&a, client, workspace, b, job, &db).await?, + transform_json_value(&a, client, workspace, b, job, conn).await?, ); } Ok(Value::Object(m)) @@ -333,7 +341,8 @@ pub fn unsafe_raw(json: String) -> Box { fn check_result_too_big(size: usize) -> error::Result<()> { if *CLOUD_HOSTED && size > MAX_RESULT_SIZE { return Err(error::Error::ExecutionErr("Result is too large for the cloud app (limit 2MB). - If using this script as part of the flow, use the shared folder to pass heavy data between steps.".to_owned())); +We highly recommend using object to store and pass heavy data (https://www.windmill.dev/docs/core_concepts/object_storage_in_windmill#read-a-file-from-s3-within-a-script) +Alternatively, if using this script as part of a flow, activate shared folder and use the shared folder to pass heavy data between steps.".to_owned())); }; Ok(()) } @@ -391,15 +400,23 @@ pub fn capitalize(s: &str) -> String { #[tracing::instrument(level = "trace", skip_all)] pub async fn get_reserved_variables( - job: &QueuedJob, + job: &MiniPulledJob, token: &str, - db: &sqlx::Pool, + db: &Connection, + parent_runnable_path: Option, ) -> Result, Error> { - let flow_path = if let Some(uuid) = job.parent_job { - sqlx::query_scalar!("SELECT runnable_path FROM v2_job WHERE id = $1", uuid) - .fetch_optional(db) - .await? - .flatten() + let flow_path = if parent_runnable_path.is_some() { + parent_runnable_path + } else if let Some(uuid) = job.parent_job { + match db { + Connection::Sql(db) => { + sqlx::query_scalar!("SELECT runnable_path FROM v2_job WHERE id = $1", uuid) + .fetch_optional(db) + .await? + .flatten() + } + Connection::Http(_) => None, + } } else { None }; @@ -408,16 +425,16 @@ pub async fn get_reserved_variables( db, &job.workspace_id, token, - &job.email, + &job.permissioned_as_email, &job.created_by, &job.id.to_string(), &job.permissioned_as, - job.script_path.clone(), + job.runnable_path.clone(), job.parent_job.map(|x| x.to_string()), flow_path, - job.schedule_path.clone(), + job.schedule_path(), job.flow_step_id.clone(), - job.root_job.clone().map(|x| x.to_string()), + job.flow_innermost_root_job.clone().map(|x| x.to_string()), None, Some(job.scheduled_for.clone()), ) @@ -460,26 +477,57 @@ pub fn sizeof_val(v: &serde_json::Value) -> usize { } pub async fn update_worker_ping_for_failed_init_script( - db: &DB, + conn: &Connection, worker_name: &str, last_job_id: Uuid, ) { - if let Err(e) = sqlx::query!( - "UPDATE worker_ping SET - ping_at = now(), - jobs_executed = 1, - current_job_id = $1, - current_job_workspace_id = 'admins' - WHERE worker = $2", - last_job_id, - worker_name - ) - .execute(db) - .await - { - tracing::error!("Error updating worker ping for failed init script: {e:?}"); + match conn { + Connection::Sql(db) => { + if let Err(e) = + update_ping_for_failed_init_script_query(worker_name, last_job_id, db).await + { + tracing::error!("Error updating worker ping for failed init script: {e:?}"); + } + } + Connection::Http(client) => { + if let Err(e) = client + .post::<_, ()>( + UPDATE_PING_URL, + &Ping { + last_job_executed: Some(last_job_id), + last_job_workspace_id: None, + worker_instance: None, + ip: None, + tags: None, + dw: None, + jobs_executed: None, + occupancy_rate: None, + occupancy_rate_15s: None, + occupancy_rate_5m: None, + occupancy_rate_30m: None, + version: None, + vcpus: None, + memory: None, + memory_usage: None, + wm_memory_usage: None, + ping_type: PingType::InitScript, + }, + ) + .await + { + tracing::error!("Error updating worker ping for failed init script: {e:?}"); + } + } } } + +pub fn error_to_value(err: Error) -> serde_json::Value { + match err { + Error::JsonErr(err) => err, + _ => json!({"message": err.to_string(), "name": "InternalErr"}), + } +} + pub struct OccupancyMetrics { pub running_job_started_at: Option, pub total_duration_of_running_jobs: f32, @@ -487,6 +535,13 @@ pub struct OccupancyMetrics { pub start_time: Instant, } +pub struct OccupancyResult { + pub occupancy_rate: f32, + pub occupancy_rate_15s: Option, + pub occupancy_rate_5m: Option, + pub occupancy_rate_30m: Option, +} + impl OccupancyMetrics { pub fn new(start_time: Instant) -> Self { OccupancyMetrics { @@ -497,7 +552,7 @@ impl OccupancyMetrics { } } - pub fn update_occupancy_metrics(&mut self) -> (f32, Option, Option, Option) { + pub fn update_occupancy_metrics(&mut self) -> OccupancyResult { let metrics = self; let current_occupied_duration = metrics .running_job_started_at @@ -548,12 +603,12 @@ impl OccupancyMetrics { .worker_occupancy_rate_history .push((total_occupation, elapsed)); - ( + OccupancyResult { occupancy_rate, occupancy_rate_15s, occupancy_rate_5m, occupancy_rate_30m, - ) + } } } @@ -564,7 +619,7 @@ pub async fn start_child_process(mut cmd: Command, executable: &str) -> Result, + _conn: &Connection, _w_id: &str, _job_id: Uuid, custom_timeout_secs: Option, @@ -572,13 +627,11 @@ pub async fn resolve_job_timeout( let mut warn_msg: Option = None; #[cfg(feature = "cloud")] let cloud_premium_workspace = *CLOUD_HOSTED - && sqlx::query_scalar!("SELECT premium FROM workspace WHERE id = $1", _w_id) - .fetch_one(_db) - .await - .map_err(|e| { - tracing::error!(%e, "error getting premium workspace for job {_job_id}: {e:#}"); - }) - .unwrap_or(false); + && windmill_common::workspaces::is_premium_workspace( + _conn.as_sql().expect("cloud cannot use http connection"), + _w_id, + ) + .await; #[cfg(not(feature = "cloud"))] let cloud_premium_workspace = false; @@ -647,15 +700,15 @@ async fn hash_args( pub async fn cached_result_path( db: &DB, client: &AuthedClient, - job: &QueuedJob, + job: &MiniPulledJob, raw_data: Option<&RawData>, ) -> String { let mut hasher = sha2::Sha256::new(); - hasher.update(&[job.job_kind as u8]); - if let Some(ScriptHash(hash)) = job.script_hash { + hasher.update(&[job.kind as u8]); + if let Some(ScriptHash(hash)) = job.runnable_id { hasher.update(&hash.to_le_bytes()) } else { - job.script_path + job.runnable_path .as_ref() .inspect(|x| hasher.update(x.as_bytes())); match raw_data { @@ -850,7 +903,7 @@ pub async fn get_cached_resource_value_if_valid( S3Object { s3: s3_file_key.clone(), storage: resource.storage.clone(), - filename: None, + ..Default::default() }, ) .await; @@ -868,7 +921,7 @@ pub async fn get_cached_resource_value_if_valid( pub async fn save_in_cache( db: &Pool, _client: &AuthedClient, - job: &QueuedJob, + job: &MiniPulledJob, cached_path: String, r: Arc>, ) { @@ -914,18 +967,18 @@ pub async fn save_in_cache( fn tentatively_improve_error(err: Error, executable: &str) -> Error { #[cfg(unix)] - let err_msg = "No such file or directory (os error 2)"; + let err_msgs = vec!["os error 2", "os error 3", "No such file or directory"]; #[cfg(windows)] - let err_msg = "program not found"; + let err_msgs = vec!["program not found", "os error 2", "os error 3"]; - if err.to_string().contains(&err_msg) { + if err_msgs.iter().any(|msg| err.to_string().contains(msg)) { return Error::internal_err(format!( "Executable {executable} not found on worker. PATH: {}", *PATH_ENV )); } - return err; + return Error::ExecutionErr(format!("Error executing {executable}: {err:#}")); } pub async fn clean_cache() -> error::Result<()> { @@ -959,3 +1012,570 @@ pub fn build_http_client(timeout_duration: std::time::Duration) -> error::Result .build() .map_err(|e| Error::internal_err(format!("Error building http client: {e:#}"))) } + +#[derive(Clone)] +pub struct RequiredDependency { + /// Expected directory of dependency in cache + /// For example: + /// /tmp/windmill/cache/python_311/rich==0.0.0 + /// IMPORTANT!: path should not end with '/' + pub path: String, + /// Name to use for S3 tars + /// If not specified will use top level directory of path. + pub custom_name: Option, + /// Display name + /// Name that will be used for console output and logging + /// If not specified will either use custom_name or top level directory of path. + pub short_name: Option, +} + +pub enum InstallStrategy { + /// Will invoke callback to install single dependency + Single(Arc Result + Send + Sync>), + /// Will try to pull S3 first and will invoke closure to install the rest + AllAtOnce(Arc) -> Result + Send + Sync>), +} +/// # General +/// +/// Languages that compile usually include dependencies in final executable. +/// When dynamic languages do not and runtime dependencies provided separately. +/// +/// This helper implies that the language is dynamic. +/// Python, Ruby, Java are dynamic and they can use this helper. +/// +/// # Features +/// +/// This helper will install all specified dependencies in parallel and if it is EE, cache to S3 +/// It has atomic success file, allowing to distinguish failed installations from succesfull. +/// +/// Besides that it provides console output and does logging. +/// +/// # Usage +/// +/// Most important arguments in this helper are `deps` and `install_fn` +/// +/// In `deps` you specify all dependencies that are needed to be on worker in order to execute script. +/// You don't know which are actually installed and which are not. +/// +/// `deps` is a vector of RequiredDependency. Check [RequiredDependency] for more context. +/// +/// After `deps` are provided helper will check each dependency and check if it is in cache, if not it will try to pull from S3 +/// and if it does not work either, it will invoke `install_fn` closure. +/// Closure arguments has dependency name as well as it`s expected path in cache. +/// Closure should return Command that will install dependency to asked place. +pub async fn par_install_language_dependencies<'a>( + deps: Vec, + language_name: &'a str, + installer_executable_name: &'a str, + platform_agnostic: bool, + concurrent_downloads: usize, + stdout_on_err: bool, + install_fn: InstallStrategy, + postinstall_cb: impl AsyncFn(Vec) -> Result<(), error::Error>, + job_id: &'a Uuid, + w_id: &'a str, + worker_name: &'a str, + conn: &Connection, +) -> anyhow::Result<()> { + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + let _ = (platform_agnostic, language_name); + + let total_time = std::time::Instant::now(); + + // Total to install + let mut not_installed = vec![]; + let total_to_install; + // let mut not_installed = vec![]; + let counter_arc = Arc::new(tokio::sync::Mutex::new(0)); + // Append logs with line like this: + // [9/21] + requests==2.32.3 << (S3) | in 57ms + #[allow(unused_assignments)] + async fn print_success( + mut s3_pull: bool, + mut s3_push: bool, + job_id: &Uuid, + w_id: &str, + req: &str, + req_tl: usize, + counter_arc: Arc>, + total_to_install: usize, + instant: std::time::Instant, + conn: &Connection, + ) { + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + { + (s3_pull, s3_push) = (false, false); + } + + #[cfg(all(feature = "enterprise", feature = "parquet"))] + if windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS + .read() + .await + .is_none() + { + (s3_pull, s3_push) = (false, false); + } + + let mut counter = counter_arc.lock().await; + *counter += 1; + + windmill_queue::append_logs( + job_id, + w_id, + format!( + "\n{}+ {}{}{}| in {}ms", + windmill_common::worker::pad_string( + &format!("[{}/{total_to_install}]", counter), + 9 + ), + // Because we want to align to max len [999/999] we take 9 + // 123456789 + windmill_common::worker::pad_string(&req, req_tl + 1), + // Margin to the right ^ + if s3_pull { "<< (S3) " } else { "" }, + if s3_push { " > (S3) " } else { "" }, + instant.elapsed().as_millis(), + ), + conn, + ) + .await; + // Drop lock, so next print success can fire + } + + let mut name_tl = 0; + struct NotInstalledDependency { + path: String, + custom_name: Option, + short_name: Option, + display_name: String, + } + { + let mut to_be_installed_is_used = false; + for RequiredDependency { + path, // + custom_name, + short_name, + } in deps.into_iter() + { + if path.ends_with("/") { + anyhow::bail!("Internal error: path should not end with '/'") + } + let display_name = short_name + .as_ref() + .or(custom_name.as_ref()) + .or(path.split("/").last().map(|e| e.to_owned()).as_ref()) + .unwrap_or_else(|| { + tracing::warn!( + workspace_id = %w_id, + job_id = %job_id, + "failed to parse top level directory name for {path}, fallback to full path.", + ); + + &path + }) + .to_owned(); + { + // Later will help us align text in log console + if display_name.len() > name_tl { + name_tl = display_name.len(); + } + } + // Will look like: /tmp/windmill/cache/lang/dependency.valid.windmill + if tokio::fs::metadata(path.clone() + ".valid.windmill") + .await + .is_err() + { + if !to_be_installed_is_used { + windmill_queue::append_logs( + job_id, + w_id, + format!("\n--- INSTALLATION ---\n\nTo be installed:\n\n"), + conn, + ) + .await; + to_be_installed_is_used = true; + } + windmill_queue::append_logs(job_id, w_id, format!("- {display_name}\n"), conn) + .await; + not_installed.push(NotInstalledDependency { + path, + custom_name, + short_name, + display_name, + }); + } + } + } + total_to_install = not_installed.len(); + if total_to_install == 0 { + return Ok(()); + } + #[cfg(all(feature = "enterprise", feature = "parquet"))] + let is_not_pro = !matches!( + windmill_common::ee::get_license_plan().await, + windmill_common::ee::LicensePlan::Pro + ); + #[cfg(all(feature = "enterprise", feature = "parquet"))] + if is_not_pro && matches!(install_fn, InstallStrategy::AllAtOnce(_)) { + windmill_queue::append_logs( + job_id, + w_id, + format!("\nLooking for packages on S3:\n"), + conn, + ) + .await; + } + + // Parallelism level (N) + let parallel_limit = // Semaphore will panic if value less then 1 + concurrent_downloads.clamp(1, 30); + + tracing::info!( + workspace_id = %w_id, + "Install parallel limit: {}, job: {}", + parallel_limit, + job_id + ); + + let mut handles = vec![]; + let semaphore = Arc::new(Semaphore::new(parallel_limit)); + let not_pulled = Arc::new(RwLock::new(vec![])); + // let mut handles = Vec::with_capacity(total_to_install); + for NotInstalledDependency { + // + path, + custom_name, + short_name, + display_name, + } in not_installed + { + let permit = semaphore.clone().acquire_owned().await; // Acquire a permit + + if let Err(_) = permit { + tracing::error!( + workspace_id = %w_id, + "Cannot acquire permit on semaphore, that can only mean that semaphore has been closed." + ); + break; + } + + let permit = permit.unwrap(); + + #[cfg(all(feature = "enterprise", feature = "parquet"))] + let s3_pull_future = if is_not_pro { + if let Some(os) = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS + .read() + .await + .clone() + { + Some(crate::global_cache::pull_from_tar( + os, + path.clone(), + language_name.to_owned(), + custom_name.clone(), + platform_agnostic, + )) + } else { + None + } + } else { + None + }; + let child = { + if let InstallStrategy::Single(ref callback, ..) = install_fn { + let cmd = callback(RequiredDependency { + path: path.clone(), + custom_name: custom_name.clone(), + short_name: short_name.clone(), + })?; + tracing::debug!("{:?}", &cmd); + Some(start_child_process(cmd, &installer_executable_name).await?) + } else { + None + } + }; + + let ( + worker_name_2, + path_2, + display_name_2, + custom_name, + job_id_2, + w_id_2, + conn_2, + counter_arc, + language_name, + installer_executable_name, + not_pulled, + ) = ( + worker_name.to_owned(), + path.clone(), + display_name.clone(), + custom_name.clone(), + job_id.clone(), + w_id.to_owned(), + conn.clone(), + counter_arc.clone(), + language_name.to_owned(), + installer_executable_name.to_owned(), + not_pulled.clone(), + ); + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + let _ = language_name; + + let start = std::time::Instant::now(); + let handle = tokio::spawn(async move { + let _permit = permit; + + #[cfg(all(feature = "enterprise", feature = "parquet"))] + if let Some(s3_pull_future) = s3_pull_future { + if let Err(e) = s3_pull_future.await { + tracing::info!( + workspace_id = %w_id_2, + "No tarball was found for {:?} on S3 or different problem occured {job_id_2}:\n{e}", + &custom_name.clone().unwrap_or(path) + ); + } else { + // TODO: Refactor + // Create a file to indicate that installation was successfull + let valid_path = path_2.clone() + ".valid.windmill"; + // This is atomic operation, meaning, that it either completes and dependency is valid, + // or it does not and dependency is invalid and will be reinstalled next run + if let Err(e) = File::create(&valid_path).await { + tracing::error!( + workspace_id = %w_id_2, + job_id = %job_id_2, + "Failed to create {}!\n{e}\n + This file needed for jobs to function", + valid_path + ); + }; + print_success( + true, + false, + &job_id_2, + &w_id_2, + &display_name_2, + name_tl, + counter_arc, + total_to_install, + start, + &conn_2, + ) + .await; + return; + } + } + + let Some(child) = child else { + let mut lock = not_pulled.write().await; + lock.push(RequiredDependency { + path: path_2.clone(), + custom_name: custom_name.clone(), + short_name: short_name.clone(), + }); + return; + }; + if let Err(e) = crate::handle_child::handle_child( + &job_id_2, + &conn_2, + // TODO: Return mem_peak + &mut 0, + // TODO: Return canceld_by_ref + &mut None, + child, + !*DISABLE_NSJAIL, + &worker_name_2, + &w_id_2, + &installer_executable_name, + None, + false, + &mut None, + None, + ) + .await + { + windmill_queue::append_logs( + &job_id_2, + &w_id_2, + format!("error while installing {}: {e:?}", &display_name_2), + &conn_2, + ) + .await; + } else { + // if let Some(cb) = postinstall_cb { + // if let Err(e) = cb(vec![RequiredDependency { + // path: path_2.clone(), + // custom_name: custom_name.clone(), + // short_name: short_name.clone(), + // }]) + // .await + // { + // tracing::error!( + // workspace_id = %w_id_2, + // job_id = %job_id_2, + // "Postinstall callback failed!\n{e}\n + // This might affect execution", + // ); + // } + // } + print_success( + false, + true, + &job_id_2, + &w_id_2, + &display_name_2, + name_tl, + counter_arc, + total_to_install, + start, + &conn_2, + ) + .await; + // TODO: Refactor + // Create a file to indicate that installation was successfull + let valid_path = path_2.clone() + ".valid.windmill"; + // This is atomic operation, meaning, that it either completes and dependency is valid, + // or it does not and dependency is invalid and will be reinstalled next run + if let Err(e) = File::create(&valid_path).await { + tracing::error!( + workspace_id = %w_id_2, + job_id = %job_id_2, + "Failed to create {}!\n{e}\n + This file needed for jobs to function", + valid_path + ); + }; + #[cfg(all(feature = "enterprise", feature = "parquet"))] + { + if let Some(os) = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS + .read() + .await + .clone() + { + tokio::spawn(async move { + if let Err(e) = crate::global_cache::build_tar_and_push( + os, + path_2, + language_name, + custom_name, + platform_agnostic, + ) + .await + { + tracing::warn!("failed to build tar and push: {e:?}"); + } + }); + } + } + } + }); + handles.push(handle); + } + + for handle in handles { + if let Err(e) = handle.await { + tracing::error!("Error joining handles: {e:?}"); + } + } + if !not_pulled.read().await.is_empty() { + if let InstallStrategy::AllAtOnce(ref callback, ..) = install_fn { + let not_pulled_copy = not_pulled.read().await.clone(); + windmill_queue::append_logs( + job_id, + w_id, + format!("\n\nFetching {} packages...\n", not_pulled_copy.len()), + &conn, + ) + .await; + let cmd = callback(not_pulled_copy.clone())?; + tracing::debug!("{:?}", &cmd); + let child = start_child_process(cmd, &installer_executable_name).await?; + let mut buf = "".to_owned(); + let pipe_stdout = if stdout_on_err { Some(&mut buf) } else { None }; + if let Err(e) = crate::handle_child::handle_child( + // &job_id, + &Uuid::nil(), + &conn, + // TODO: Return mem_peak + &mut 0, + // TODO: Return canceld_by_ref + &mut None, + child, + !*DISABLE_NSJAIL, + &worker_name, + &w_id, + &installer_executable_name, + None, + false, + &mut None, + pipe_stdout, + ) + .await + { + bail!(format!( + "error while installing dependencies: {e:?}\n{}", + buf + )); + } + { + postinstall_cb(not_pulled_copy.clone()).await?; + } + for RequiredDependency { path, custom_name: _custom_name, .. } in + not_pulled_copy.into_iter() + { + // TODO: Refactor + // Create a file to indicate that installation was successfull + let valid_path = path.clone() + ".valid.windmill"; + // This is atomic operation, meaning, that it either completes and dependency is valid, + // or it does not and dependency is invalid and will be reinstalled next run + if let Err(e) = File::create(&valid_path).await { + tracing::error!( + workspace_id = %w_id, + job_id = %job_id, + "Failed to create {}!\n{e}\n + This file needed for jobs to function", + valid_path + ); + }; + #[cfg(all(feature = "enterprise", feature = "parquet"))] + { + if let Some(os) = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS + .read() + .await + .clone() + { + let language_name = language_name.to_owned(); + tokio::spawn(async move { + if let Err(e) = crate::global_cache::build_tar_and_push( + os, + path, + language_name, + _custom_name, + platform_agnostic, + ) + .await + { + tracing::warn!("failed to build tar and push: {e:?}"); + } + }); + } + } + } + } + } + { + let total_time = total_time.elapsed().as_millis(); + windmill_queue::append_logs( + &job_id, + w_id, + format!( + "\nDone. Time spent on installation phase: {}ms\n", + total_time + ), + conn, + ) + .await; + } + Ok(()) +} diff --git a/backend/windmill-worker/src/csharp_executor.rs b/backend/windmill-worker/src/csharp_executor.rs index 61f53ab65a..2054e279db 100644 --- a/backend/windmill-worker/src/csharp_executor.rs +++ b/backend/windmill-worker/src/csharp_executor.rs @@ -19,7 +19,6 @@ use windmill_common::{ }; use windmill_common::error::{self, Error}; -use windmill_common::jobs::QueuedJob; #[cfg(feature = "csharp")] use windmill_queue::append_logs; @@ -37,7 +36,7 @@ use crate::{ }; use crate::common::OccupancyMetrics; -use crate::AuthedClientBackgroundTask; +use crate::AuthedClient; #[cfg(windows)] use crate::SYSTEM_ROOT; @@ -68,7 +67,7 @@ pub async fn generate_nuget_lockfile( mem_peak: &mut i32, canceled_by: &mut Option, job_dir: &str, - db: &sqlx::Pool, + conn: &Connection, worker_name: &str, w_id: &str, occupancy_metrics: &mut OccupancyMetrics, @@ -116,7 +115,7 @@ pub async fn generate_nuget_lockfile( let gen_lockfile_process = start_child_process(gen_lockfile_cmd, DOTNET_PATH.as_str()).await?; handle_child( job_id, - db, + conn, mem_peak, canceled_by, gen_lockfile_process, @@ -127,6 +126,7 @@ pub async fn generate_nuget_lockfile( None, false, &mut Some(occupancy_metrics), + None, ) .await?; @@ -150,7 +150,7 @@ pub async fn generate_nuget_lockfile( _mem_peak: &mut i32, _canceled_by: &mut Option, _job_dir: &str, - _db: &sqlx::Pool, + _conn: &Connection, _worker_name: &str, _w_id: &str, _occupancy_metrics: &mut OccupancyMetrics, @@ -311,7 +311,7 @@ async fn build_cs_proj( mem_peak: &mut i32, canceled_by: &mut Option, job_dir: &str, - db: &sqlx::Pool, + conn: &Connection, worker_name: &str, w_id: &str, base_internal_url: &str, @@ -371,7 +371,7 @@ async fn build_cs_proj( let build_cs_process = start_child_process(build_cs_cmd, DOTNET_PATH.as_str()).await?; handle_child( job_id, - db, + conn, mem_peak, canceled_by, build_cs_process, @@ -382,9 +382,10 @@ async fn build_cs_proj( None, false, &mut Some(occupancy_metrics), + None, ) .await?; - append_logs(job_id, w_id, "\n\n", db).await; + append_logs(job_id, w_id, "\n\n", conn).await; if let Err(e) = std::fs::remove_file(Path::new(job_dir).join("nuget.config")) { if e.kind() != io::ErrorKind::NotFound { Err(anyhow!("Error erasing nuget.config: {}", e))?; @@ -401,6 +402,7 @@ async fn build_cs_proj( &bin_path, &format!("{CSHARP_OBJECT_STORE_PREFIX}{hash}"), &target, + false, ) .await { @@ -426,13 +428,17 @@ fn remove_lines_from_text(contents: &str, indices_to_remove: Vec) -> Stri result.join("\n") } +use windmill_common::worker::Connection; +use windmill_queue::MiniPulledJob; + #[cfg(not(feature = "csharp"))] pub async fn handle_csharp_job( _mem_peak: &mut i32, _canceled_by: &mut Option, - _job: &QueuedJob, - _db: &sqlx::Pool, - _client: &AuthedClientBackgroundTask, + _job: &MiniPulledJob, + _conn: &Connection, + _client: &AuthedClient, + _parent_runnable_path: Option, _inner_content: &str, _job_dir: &str, _requirements_o: Option<&String>, @@ -444,14 +450,14 @@ pub async fn handle_csharp_job( ) -> Result, Error> { Err(anyhow!("C# is not available because the feature is not enabled").into()) } - #[cfg(feature = "csharp")] pub async fn handle_csharp_job( mem_peak: &mut i32, canceled_by: &mut Option, - job: &QueuedJob, - db: &sqlx::Pool, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + conn: &Connection, + client: &AuthedClient, + parent_runnable_path: Option, inner_content: &str, job_dir: &str, requirements_o: Option<&String>, @@ -471,7 +477,8 @@ pub async fn handle_csharp_job( let bin_path = format!("{}/{hash}", CSHARP_CACHE_DIR); let remote_path = format!("{CSHARP_OBJECT_STORE_PREFIX}{hash}"); - let (cache, cache_logs) = windmill_common::worker::load_cache(&bin_path, &remote_path).await; + let (cache, cache_logs) = + windmill_common::worker::load_cache(&bin_path, &remote_path, false).await; let cache_logs = if cache { #[cfg(unix)] @@ -488,7 +495,7 @@ pub async fn handle_csharp_job( cache_logs } else { let logs1 = format!("{cache_logs}\n\n--- DOTNET BUILD ---\n"); - append_logs(&job.id, &job.workspace_id, logs1, db).await; + append_logs(&job.id, &job.workspace_id, logs1, conn).await; let (reqs, lines_to_remove) = parse_csharp_reqs(inner_content); for req in &reqs { @@ -500,7 +507,7 @@ pub async fn handle_csharp_job( req.0, req.1.as_ref().unwrap_or(&"".to_string()) ), - db, + conn, ) .await; } @@ -518,7 +525,7 @@ pub async fn handle_csharp_job( mem_peak, canceled_by, job_dir, - db, + conn, worker_name, &job.workspace_id, base_internal_url, @@ -528,13 +535,13 @@ pub async fn handle_csharp_job( .await? }; - create_args_and_out_file(client, job, job_dir, db).await?; + create_args_and_out_file(client, job, job_dir, conn).await?; let logs2 = format!("{cache_logs}\n\n--- C# CODE EXECUTION ---\n"); - append_logs(&job.id, &job.workspace_id, format!("{}\n", logs2), db).await; + append_logs(&job.id, &job.workspace_id, format!("{}\n", logs2), conn).await; - let client = &client.get_authed().await; - let reserved_variables = get_reserved_variables(job, &client.token, db).await?; + let reserved_variables = + get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?; let child = if !*DISABLE_NSJAIL { write_file( @@ -543,6 +550,7 @@ pub async fn handle_csharp_job( &NSJAIL_CONFIG_RUN_CSHARP_CONTENT .replace("{JOB_DIR}", job_dir) .replace("{CACHE_DIR}", CSHARP_CACHE_DIR) + .replace("{CACHE_HASH}", &hash) .replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string()) .replace("{SHARED_MOUNT}", shared_mount), )?; @@ -619,7 +627,7 @@ pub async fn handle_csharp_job( handle_child( &job.id, - db, + conn, mem_peak, canceled_by, child, @@ -630,6 +638,7 @@ pub async fn handle_csharp_job( job.timeout, false, &mut Some(occupancy_metrics), + None, ) .await?; read_result(job_dir).await diff --git a/backend/windmill-worker/src/dedicated_worker.rs b/backend/windmill-worker/src/dedicated_worker.rs index fb3a30176c..33a83a1492 100644 --- a/backend/windmill-worker/src/dedicated_worker.rs +++ b/backend/windmill-worker/src/dedicated_worker.rs @@ -15,22 +15,21 @@ use tokio::{ use windmill_common::error::Error; use windmill_common::flows::FlowValue; use windmill_common::worker::WORKER_CONFIG; +use windmill_common::KillpillSender; use windmill_common::{ cache, error, flows::{FlowModule, FlowModuleValue}, - jobs::QueuedJob, scripts::{ScriptHash, ScriptLang}, variables, worker::to_raw_value, DB, }; use windmill_queue::append_logs; +use windmill_queue::MiniPulledJob; use anyhow::Context; -use crate::{ - common::start_child_process, JobCompleted, JobCompletedSender, MAX_BUFFERED_DEDICATED_JOBS, -}; +use crate::{common::start_child_process, JobCompletedSender, MAX_BUFFERED_DEDICATED_JOBS}; use futures::{future, Future}; use std::{collections::HashMap, task::Poll}; @@ -69,7 +68,7 @@ pub async fn handle_dedicated_process( mut killpill_rx: tokio::sync::broadcast::Receiver<()>, job_completed_tx: JobCompletedSender, token: &str, - mut jobs_rx: Receiver>, + mut jobs_rx: Receiver>, worker_name: &str, db: &DB, script_path: &str, @@ -77,6 +76,8 @@ pub async fn handle_dedicated_process( ) -> std::result::Result<(), error::Error> { //do not cache local dependencies + use windmill_queue::{JobCompleted, MiniPulledJob}; + use crate::{handle_child::process_status, PROXY_ENVS}; let cmd_name = format!("dedicated {command_path}"); let mut child = { @@ -133,7 +134,8 @@ pub async fn handle_dedicated_process( } }); - let mut jobs: VecDeque> = VecDeque::with_capacity(MAX_BUFFERED_DEDICATED_JOBS); + let mut jobs: VecDeque> = + VecDeque::with_capacity(MAX_BUFFERED_DEDICATED_JOBS); // let mut i = 0; // let mut j = 0; let mut alive = true; @@ -178,21 +180,21 @@ pub async fn handle_dedicated_process( } tracing::debug!("processed job: |{line}|"); if line.starts_with("wm_res[") { - let job: Arc = jobs.pop_front().expect("pop"); + let job: Arc = jobs.pop_front().expect("pop"); tracing::info!("job completed on dedicated worker {script_path}: {}", job.id); match serde_json::from_str::>(&line.replace("wm_res[success]:", "").replace("wm_res[error]:", "")) { Ok(result) => { let result = Arc::new(result); - append_logs(&job.id, &job.workspace_id, logs.clone(), db).await; + append_logs(&job.id, &job.workspace_id, logs.clone(), &db.into()).await; if line.starts_with("wm_res[success]:") { - job_completed_tx.send(JobCompleted { job , result, result_columns: None, mem_peak: 0, canceled_by: None, success: true, cached_res_path: None, token: token.to_string(), duration: None }).await.unwrap() + job_completed_tx.send_job(JobCompleted { job , result, result_columns: None, mem_peak: 0, canceled_by: None, success: true, cached_res_path: None, token: token.to_string(), duration: None }).await.unwrap() } else { - job_completed_tx.send(JobCompleted { job , result, result_columns: None, mem_peak: 0, canceled_by: None, success: false, cached_res_path: None, token: token.to_string(), duration: None }).await.unwrap() + job_completed_tx.send_job(JobCompleted { job , result, result_columns: None, mem_peak: 0, canceled_by: None, success: false, cached_res_path: None, token: token.to_string(), duration: None }).await.unwrap() } }, Err(e) => { tracing::error!("Could not deserialize job result `{line}`: {e:?}"); - job_completed_tx.send(JobCompleted { job , result: Arc::new(to_raw_value(&serde_json::json!({"error": format!("Could not deserialize job result `{line}`: {e:?}")}))), result_columns: None, mem_peak: 0, canceled_by: None, success: false, cached_res_path: None, token: token.to_string(), duration: None }).await.unwrap(); + job_completed_tx.send_job(JobCompleted { job , result: Arc::new(to_raw_value(&serde_json::json!({"error": format!("Could not deserialize job result `{line}`: {e:?}")}))), result_columns: None, mem_peak: 0, canceled_by: None, success: false, cached_res_path: None, token: token.to_string(), duration: None }).await.unwrap(); }, }; logs = init_log.clone(); @@ -241,7 +243,7 @@ pub async fn handle_dedicated_process( type DedicatedWorker = ( String, - Sender>, + Sender>, Option>, ); @@ -252,7 +254,7 @@ async fn spawn_dedicated_workers_for_flow( modules: &Vec, w_id: &str, path: &str, - killpill_tx: tokio::sync::broadcast::Sender<()>, + killpill_tx: KillpillSender, killpill_rx: &tokio::sync::broadcast::Receiver<()>, db: &DB, worker_dir: &str, @@ -261,7 +263,7 @@ async fn spawn_dedicated_workers_for_flow( job_completed_tx: &JobCompletedSender, ) -> Vec { let mut workers = vec![]; - let mut script_path_to_worker: HashMap>> = + let mut script_path_to_worker: HashMap>> = HashMap::new(); for module in modules.iter() { let value = module.get_value(); @@ -393,13 +395,16 @@ async fn spawn_dedicated_workers_for_flow( } } FlowModuleValue::FlowScript { id, language, .. } => { - let spawn = cache::flow::fetch_script(db, *id).await.map(|data| { - SpawnWorker::RawScript { - path: "".to_string(), - content: data.code.clone(), - lock: data.lock.clone(), - lang: *language, - } + let spawn = cache::flow::fetch_script( + &windmill_common::worker::Connection::Sql(db.clone()), + *id, + ) + .await + .map(|data| SpawnWorker::RawScript { + path: "".to_string(), + content: data.code.clone(), + lock: data.lock.clone(), + lang: *language, }); match spawn { Ok(spawn) => { @@ -438,7 +443,7 @@ async fn spawn_dedicated_workers_for_flow( } pub async fn create_dedicated_worker_map( - killpill_tx: &tokio::sync::broadcast::Sender<()>, + killpill_tx: &KillpillSender, killpill_rx: &tokio::sync::broadcast::Receiver<()>, db: &DB, worker_dir: &str, @@ -446,7 +451,7 @@ pub async fn create_dedicated_worker_map( worker_name: &str, job_completed_tx: &JobCompletedSender, ) -> ( - HashMap>>, + HashMap>>, bool, Vec>, ) { @@ -551,7 +556,7 @@ pub enum SpawnWorker { async fn spawn_dedicated_worker( sw: SpawnWorker, w_id: &str, - killpill_tx: tokio::sync::broadcast::Sender<()>, + killpill_tx: KillpillSender, killpill_rx: &tokio::sync::broadcast::Receiver<()>, db: &DB, worker_dir: &str, @@ -565,8 +570,9 @@ async fn spawn_dedicated_worker( scripts::{ScriptHash, ScriptLang}, utils::rd_string, }; + use windmill_queue::MiniPulledJob; - use crate::{build_envs, get_script_content_by_hash, ContentReqLangEnvs, JOB_TOKEN}; + use crate::{build_envs, get_script_content_by_hash, ContentReqLangEnvs}; #[cfg(not(feature = "enterprise"))] { @@ -577,10 +583,11 @@ async fn spawn_dedicated_worker( #[cfg(feature = "enterprise")] { - let (dedicated_worker_tx, dedicated_worker_rx) = - tokio::sync::mpsc::channel::>(MAX_BUFFERED_DEDICATED_JOBS); + let (dedicated_worker_tx, dedicated_worker_rx) = tokio::sync::mpsc::channel::< + std::sync::Arc, + >(MAX_BUFFERED_DEDICATED_JOBS); let killpill_rx = killpill_rx.resubscribe(); - let db = db.clone(); + let db2 = db.clone(); let base_internal_url = base_internal_url.to_string(); let worker_name = worker_name.to_string(); let job_completed_tx = job_completed_tx.clone(); @@ -607,11 +614,11 @@ async fn spawn_dedicated_worker( let (content, lock, language, envs, codebase) = match sw.clone() { SpawnWorker::Script { path, hash } => { let q = if let Some(hash) = hash { - get_script_content_by_hash(&hash, &w_id, &db).await.map( - |r: ContentReqLangEnvs| { + get_script_content_by_hash(&hash, &w_id, &db2.into()) + .await + .map(|r: ContentReqLangEnvs| { Some((r.content, r.lockfile, r.language, r.envs, r.codebase)) - }, - ) + }) } else { sqlx::query_as::<_, (String, Option, Option, Option>, bool, Option)>( "SELECT content, lock, language, envs, codebase IS NOT NULL, hash FROM script WHERE path = $1 AND workspace_id = $2 AND @@ -620,7 +627,7 @@ async fn spawn_dedicated_worker( ) .bind(&path) .bind(&w_id) - .fetch_optional(&db) + .fetch_optional(&db2) .await .map_err(|e| Error::internal_err(format!("expected content and lock: {e:#}"))) .map(|x| x.map(|y| (y.0, y.1, y.2, y.3, if y.4 { y.5.map(|z| z.to_string()) } else { None }))) @@ -638,7 +645,7 @@ async fn spawn_dedicated_worker( } } else { tracing::error!("Failed to fetch script for dedicated worker"); - killpill_tx.send(()).expect("send"); + killpill_tx.send(); return None; } } @@ -652,10 +659,9 @@ async fn spawn_dedicated_worker( _ => return None, } + let db = db.clone(); let handle = tokio::spawn(async move { - let token = if let Some(token) = JOB_TOKEN.as_ref() { - token.clone() - } else { + let token = { let token = rd_string(32); if let Err(e) = sqlx::query_scalar!( "INSERT INTO token @@ -670,7 +676,7 @@ async fn spawn_dedicated_worker( .await { tracing::error!("failed to create token for dedicated worker: {:?}", e); - killpill_tx.clone().send(()).expect("send"); + killpill_tx.clone().send(); }; token }; @@ -682,7 +688,7 @@ async fn spawn_dedicated_worker( #[cfg(not(feature = "python"))] { tracing::error!("Python requires the python feature to be enabled"); - killpill_tx.send(()).expect("send"); + killpill_tx.send(); return; } @@ -744,9 +750,7 @@ async fn spawn_dedicated_worker( } { tracing::error!("error in dedicated worker for {sw:#?}: {:?}", e); }; - if let Err(e) = killpill_tx.clone().send(()) { - tracing::error!("failed to send final killpill to dedicated worker: {:?}", e); - } + killpill_tx.clone().send(); }); return Some((node_id.unwrap_or(path2), dedicated_worker_tx, Some(handle))); // (Some(dedi_path), Some(dedicated_worker_tx), Some(handle)) diff --git a/backend/windmill-worker/src/deno_executor.rs b/backend/windmill-worker/src/deno_executor.rs index 6a7bed5c38..696957f463 100644 --- a/backend/windmill-worker/src/deno_executor.rs +++ b/backend/windmill-worker/src/deno_executor.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, process::Stdio}; use itertools::Itertools; use serde_json::value::RawValue; use uuid::Uuid; -use windmill_queue::{append_logs, CanceledBy}; +use windmill_queue::{append_logs, CanceledBy, MiniPulledJob}; use crate::{ common::{ @@ -11,14 +11,14 @@ use crate::{ start_child_process, OccupancyMetrics, }, handle_child::handle_child, - AuthedClientBackgroundTask, DENO_CACHE_DIR, DENO_PATH, DISABLE_NSJAIL, HOME_ENV, - NPM_CONFIG_REGISTRY, PATH_ENV, TZ_ENV, + AuthedClient, DENO_CACHE_DIR, DENO_PATH, DISABLE_NSJAIL, HOME_ENV, NPM_CONFIG_REGISTRY, + PATH_ENV, TZ_ENV, }; use tokio::{fs::File, io::AsyncReadExt, process::Command}; use windmill_common::{error::Result, worker::write_file, BASE_URL}; use windmill_common::{ error::{self}, - jobs::QueuedJob, + worker::Connection, }; use windmill_parser::Typ; @@ -100,7 +100,7 @@ pub async fn generate_deno_lock( mem_peak: &mut i32, canceled_by: &mut Option, job_dir: &str, - db: Option<&sqlx::Pool>, + db: Option<&Connection>, w_id: &str, worker_name: &str, base_internal_url: &str, @@ -159,6 +159,7 @@ pub async fn generate_deno_lock( None, false, occupancy_metrics, + None, ) .await?; } else { @@ -180,9 +181,10 @@ pub async fn handle_deno_job( requirements_o: Option<&String>, mem_peak: &mut i32, canceled_by: &mut Option, - job: &QueuedJob, - db: &sqlx::Pool, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + conn: &Connection, + client: &AuthedClient, + parent_runnable_path: Option, job_dir: &str, inner_content: &String, base_internal_url: &str, @@ -193,10 +195,10 @@ pub async fn handle_deno_job( ) -> error::Result> { // let mut start = Instant::now(); let logs1 = "\n\n--- DENO CODE EXECUTION ---\n".to_string(); - append_logs(&job.id, &job.workspace_id, logs1, db).await; + append_logs(&job.id, &job.workspace_id, logs1, conn).await; let main_override = job.script_entrypoint_override.as_deref(); - let apply_preprocessor = !job.is_flow_step && job.preprocessed == Some(false); + let apply_preprocessor = !job.is_flow_step() && job.preprocessed == Some(false); write_file(job_dir, "main.ts", inner_content)?; @@ -205,6 +207,7 @@ pub async fn handle_deno_job( let args = windmill_parser_ts::parse_deno_signature( inner_content, true, + false, main_override.map(ToString::to_string), )? .args; @@ -214,6 +217,7 @@ pub async fn handle_deno_job( windmill_parser_ts::parse_deno_signature( inner_content, true, + false, Some("preprocessor".to_string()), )? .args, @@ -308,32 +312,33 @@ try {{ let write_import_map_f = build_import_map( &job.workspace_id, - job.script_path(), + job.runnable_path(), base_internal_url, job_dir, ); let reserved_variables_args_out_f = async { let args_and_out_f = async { - create_args_and_out_file(&client, job, job_dir, db).await?; + create_args_and_out_file(&client, job, job_dir, conn).await?; Ok(()) as Result<()> }; let reserved_variables_f = async { - let client = client.get_authed().await; - let vars = get_reserved_variables(job, &client.token, db).await?; - Ok((vars, client.token)) as Result<(HashMap, String)> + let vars = + get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?; + Ok(vars) as Result> }; let (_, reserved_variables) = tokio::try_join!(args_and_out_f, reserved_variables_f)?; - Ok(reserved_variables) as error::Result<(HashMap, String)> + Ok(reserved_variables) as error::Result> }; - let ((reserved_variables, token), _, _) = tokio::try_join!( + let (reserved_variables, _, _) = tokio::try_join!( reserved_variables_args_out_f, write_wrapper_f, write_import_map_f )?; - let mut common_deno_proc_envs = get_common_deno_proc_envs(&token, base_internal_url).await; + let mut common_deno_proc_envs = + get_common_deno_proc_envs(&client.token, base_internal_url).await; if !*DISABLE_NSJAIL { common_deno_proc_envs.insert("HOME".to_string(), job_dir.to_string()); } @@ -403,7 +408,7 @@ try {{ // start = Instant::now(); handle_child( &job.id, - db, + conn, mem_peak, canceled_by, child, @@ -414,6 +419,7 @@ try {{ job.timeout, false, &mut Some(occupancy_metrics), + None, ) .await?; // logs.push_str(format!("execute: {:?}\n", start.elapsed().as_millis()).as_str()); @@ -500,7 +506,7 @@ pub async fn start_worker( script_path: &str, token: &str, job_completed_tx: JobCompletedSender, - jobs_rx: Receiver>, + jobs_rx: Receiver>, killpill_rx: tokio::sync::broadcast::Receiver<()>, db: &sqlx::Pool, ) -> Result<()> { @@ -512,7 +518,7 @@ pub async fn start_worker( let common_deno_proc_envs = get_common_deno_proc_envs(&token, base_internal_url).await; let context = variables::get_reserved_variables( - db, + &db.into(), w_id, &token, "dedicated_worker@windmill.dev", @@ -533,7 +539,7 @@ pub async fn start_worker( { // let mut start = Instant::now(); - let args = windmill_parser_ts::parse_deno_signature(inner_content, true, None)?.args; + let args = windmill_parser_ts::parse_deno_signature(inner_content, true, false, None)?.args; let dates = args .iter() .filter_map(|x| { diff --git a/backend/windmill-worker/src/global_cache.rs b/backend/windmill-worker/src/global_cache.rs index 8239ab77d4..155e6db8c1 100644 --- a/backend/windmill-worker/src/global_cache.rs +++ b/backend/windmill-worker/src/global_cache.rs @@ -1,44 +1,41 @@ // #[cfg(feature = "enterprise")] // use rand::Rng; -#[cfg(all(feature = "enterprise", feature = "parquet"))] use tokio::time::Instant; +use windmill_common::error; -#[cfg(all(feature = "enterprise", feature = "parquet", unix))] +#[cfg(all(feature = "enterprise", feature = "parquet"))] use object_store::ObjectStore; #[cfg(all(feature = "enterprise", feature = "parquet"))] -use windmill_common::error; - -#[cfg(all(feature = "enterprise", feature = "parquet", unix))] use std::sync::Arc; #[cfg(all(feature = "enterprise", feature = "parquet"))] pub const TARGET: &str = const_format::concatcp!(std::env::consts::OS, "_", std::env::consts::ARCH); -#[cfg(all(feature = "enterprise", feature = "parquet", unix))] +#[cfg(all(feature = "enterprise", feature = "parquet"))] pub async fn build_tar_and_push( s3_client: Arc, folder: String, - // python_311 - python_xyz: String, - no_uv: bool, + lang: String, + custom_folder_name: Option, + platform_agnostic: bool, ) -> error::Result<()> { use object_store::path::Path; - use crate::{TAR_PIP_CACHE_DIR, TAR_PYBASE_CACHE_DIR}; + use crate::TAR_PYBASE_CACHE_DIR; tracing::info!("Started building and pushing piptar {folder}"); let start = Instant::now(); // e.g. tiny==1.0.0 - let folder_name = folder.split("/").last().unwrap(); - - let prefix = if no_uv { - TAR_PIP_CACHE_DIR + let folder_name = if let Some(name) = custom_folder_name { + name } else { - &format!("{TAR_PYBASE_CACHE_DIR}/{}", python_xyz) + folder.split("/").last().unwrap().to_owned() }; + + let prefix = &format!("{TAR_PYBASE_CACHE_DIR}/{}", lang); let tar_path = format!("{prefix}/{folder_name}_tar.tar",); let tar_file = std::fs::File::create(&tar_path)?; @@ -60,8 +57,8 @@ pub async fn build_tar_and_push( if let Err(e) = s3_client .put( &Path::from(format!( - "/tar/{TARGET}/{}/{folder_name}.tar", - if no_uv { "pip" } else { &python_xyz } + "/tar/{}/{lang}/{folder_name}.tar", + if platform_agnostic { "" } else { TARGET } )), std::fs::read(&tar_path)?.into(), ) @@ -86,29 +83,33 @@ pub async fn build_tar_and_push( Ok(()) } -#[cfg(all(feature = "enterprise", feature = "parquet", unix))] +#[cfg(all(feature = "enterprise", feature = "parquet"))] pub async fn pull_from_tar( client: Arc, folder: String, - // python_311 - python_xyz: String, - no_uv: bool, + lang: String, + custom_folder_name: Option, + platform_agnostic: bool, ) -> error::Result<()> { use windmill_common::s3_helpers::attempt_fetch_bytes; - let folder_name = folder.split("/").last().unwrap(); + let folder_name = if let Some(name) = custom_folder_name { + name + } else { + folder.split("/").last().unwrap().to_owned() + }; - tracing::info!("Attempting to pull piptar {folder_name} from bucket"); + tracing::info!("Attempting to pull tar {folder_name} from bucket"); let start = Instant::now(); let tar_path = format!( - "tar/{TARGET}/{}/{folder_name}.tar", - if no_uv { "pip".to_owned() } else { python_xyz } + "tar/{}/{lang}/{folder_name}.tar", + if platform_agnostic { "" } else { TARGET } ); let bytes = attempt_fetch_bytes(client, &tar_path).await?; - extract_tar(bytes, &folder).await.map_err(|e| { + extract_tar(bytes, &folder).map_err(|e| { tracing::error!("Failed to extract piptar {folder_name}. Error: {:?}", e); e })?; @@ -121,21 +122,20 @@ pub async fn pull_from_tar( Ok(()) } -#[cfg(all(feature = "enterprise", feature = "parquet"))] -pub async fn extract_tar(tar: bytes::Bytes, folder: &str) -> error::Result<()> { +pub fn extract_tar(tar: bytes::Bytes, folder: &str) -> error::Result<()> { use bytes::Buf; - use tokio::fs::{self}; let start: Instant = Instant::now(); - fs::create_dir_all(&folder).await?; + std::fs::create_dir_all(&folder)?; let mut ar = tar::Archive::new(tar.reader()); if let Err(e) = ar.unpack(folder) { tracing::info!("Failed to untar to {folder}. Error: {:?}", e); - fs::remove_dir_all(&folder).await?; + std::fs::remove_dir_all(&folder)?; return Err(error::Error::ExecutionErr(format!( - "Failed to untar tar {folder}" + "Failed to untar tar {folder}. Error: {:?}", + e ))); } tracing::info!( diff --git a/backend/windmill-worker/src/go_executor.rs b/backend/windmill-worker/src/go_executor.rs index 1a8156a1bb..c0fc8a8085 100644 --- a/backend/windmill-worker/src/go_executor.rs +++ b/backend/windmill-worker/src/go_executor.rs @@ -7,12 +7,11 @@ use tokio::{fs::File, io::AsyncReadExt, process::Command}; use uuid::Uuid; use windmill_common::{ error::{self, Error}, - jobs::QueuedJob, utils::calculate_hash, - worker::{save_cache, write_file}, + worker::{save_cache, write_file, Connection}, }; use windmill_parser_go::{parse_go_imports, REQUIRE_PARSE}; -use windmill_queue::{append_logs, CanceledBy}; +use windmill_queue::{append_logs, CanceledBy, MiniPulledJob}; use crate::{ common::{ @@ -20,8 +19,8 @@ use crate::{ start_child_process, OccupancyMetrics, }, handle_child::handle_child, - AuthedClientBackgroundTask, DISABLE_NSJAIL, DISABLE_NUSER, GOPRIVATE, GOPROXY, - GO_BIN_CACHE_DIR, GO_CACHE_DIR, HOME_ENV, NSJAIL_PATH, PATH_ENV, TZ_ENV, + AuthedClient, DISABLE_NSJAIL, DISABLE_NUSER, GOPRIVATE, GOPROXY, GO_BIN_CACHE_DIR, + GO_CACHE_DIR, HOME_ENV, NSJAIL_PATH, PATH_ENV, TZ_ENV, }; const GO_REQ_SPLITTER: &str = "//go.sum\n"; @@ -36,9 +35,10 @@ pub const GO_OBJECT_STORE_PREFIX: &str = "gobin/"; pub async fn handle_go_job( mem_peak: &mut i32, canceled_by: &mut Option, - job: &QueuedJob, - db: &sqlx::Pool, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + conn: &Connection, + client: &AuthedClient, + parent_runnable_path: Option, inner_content: &str, job_dir: &str, requirements_o: Option<&String>, @@ -65,7 +65,8 @@ pub async fn handle_go_job( )); let bin_path = format!("{}/{hash}", GO_BIN_CACHE_DIR); let remote_path = format!("{GO_OBJECT_STORE_PREFIX}{hash}"); - let (cache, cache_logs) = windmill_common::worker::load_cache(&bin_path, &remote_path).await; + let (cache, cache_logs) = + windmill_common::worker::load_cache(&bin_path, &remote_path, false).await; let (skip_go_mod, skip_tidy) = if cache { (true, true) @@ -77,7 +78,7 @@ pub async fn handle_go_job( let cache_logs = if !cache { let logs1 = format!("{cache_logs}\n\n--- GO DEPENDENCIES SETUP ---\n"); - append_logs(&job.id, &job.workspace_id, logs1, db).await; + append_logs(&job.id, &job.workspace_id, logs1, conn).await; install_go_dependencies( &job.id, @@ -85,7 +86,7 @@ pub async fn handle_go_job( mem_peak, canceled_by, job_dir, - db, + conn, true, skip_go_mod, skip_tidy, @@ -95,7 +96,7 @@ pub async fn handle_go_job( ) .await?; - create_args_and_out_file(client, job, job_dir, db).await?; + create_args_and_out_file(client, job, job_dir, conn).await?; { let sig = windmill_parser_go::parse_go_sig(&inner_content)?; @@ -201,7 +202,7 @@ func Run(req Req) (interface{{}}, error){{ let build_go_process = start_child_process(build_go_cmd, GO_PATH.as_str()).await?; handle_child( &job.id, - db, + conn, mem_peak, canceled_by, build_go_process, @@ -212,6 +213,7 @@ func Run(req Req) (interface{{}}, error){{ None, false, &mut Some(occupation_metrics), + None, ) .await?; @@ -219,6 +221,7 @@ func Run(req Req) (interface{{}}, error){{ &bin_path, &format!("{GO_OBJECT_STORE_PREFIX}{hash}"), &format!("{job_dir}/main"), + false, ) .await { @@ -242,16 +245,15 @@ func Run(req Req) (interface{{}}, error){{ )) })?; - create_args_and_out_file(client, job, job_dir, db).await?; + create_args_and_out_file(client, job, job_dir, conn).await?; cache_logs }; let logs2 = format!("{cache_logs}\n\n--- GO CODE EXECUTION ---\n"); - append_logs(&job.id, &job.workspace_id, logs2, db).await; + append_logs(&job.id, &job.workspace_id, logs2, conn).await; - let client = &client.get_authed().await; - - let reserved_variables = get_reserved_variables(job, &client.token, db).await?; + let reserved_variables = + get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?; let child = if !*DISABLE_NSJAIL { let _ = write_file( @@ -304,7 +306,7 @@ func Run(req Req) (interface{{}}, error){{ }; handle_child( &job.id, - db, + conn, mem_peak, canceled_by, child, @@ -315,6 +317,7 @@ func Run(req Req) (interface{{}}, error){{ job.timeout, false, &mut Some(occupation_metrics), + None, ) .await?; @@ -348,7 +351,7 @@ pub async fn install_go_dependencies( mem_peak: &mut i32, canceled_by: &mut Option, job_dir: &str, - db: &sqlx::Pool, + conn: &Connection, non_dep_job: bool, skip_go_mod: bool, has_sum: bool, @@ -368,7 +371,7 @@ pub async fn install_go_dependencies( handle_child( job_id, - db, + conn, mem_peak, canceled_by, child_process, @@ -379,6 +382,7 @@ pub async fn install_go_dependencies( None, false, &mut Some(occupation_metrics), + None, ) .await?; @@ -405,20 +409,22 @@ pub async fn install_go_dependencies( let mut skip_tidy = has_sum; if !has_sum { - if let Some(cached) = sqlx::query_scalar!( - "SELECT lockfile FROM pip_resolution_cache WHERE hash = $1", - hash - ) - .fetch_optional(db) - .await? - { - let logs1 = format!("\nfound cached resolution: {}", hash); - append_logs(&job_id, w_id, logs1, db).await; - gen_go_mod(code, job_dir, &cached).await?; - skip_tidy = true; - new_lockfile = false; - } else { - new_lockfile = true; + if let Some(db) = conn.as_sql() { + if let Some(cached) = sqlx::query_scalar!( + "SELECT lockfile FROM pip_resolution_cache WHERE hash = $1", + hash + ) + .fetch_optional(db) + .await? + { + let logs1 = format!("\nfound cached resolution: {}", hash); + append_logs(&job_id, w_id, logs1, conn).await; + gen_go_mod(code, job_dir, &cached).await?; + skip_tidy = true; + new_lockfile = false; + } else { + new_lockfile = true; + } } } @@ -434,7 +440,7 @@ pub async fn install_go_dependencies( handle_child( job_id, - db, + conn, mem_peak, canceled_by, child_process, @@ -445,6 +451,7 @@ pub async fn install_go_dependencies( None, false, &mut Some(occupation_metrics), + None, ) .await?; @@ -464,11 +471,15 @@ pub async fn install_go_dependencies( } if non_dep_job { - sqlx::query!( - "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('3 days')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2", - hash, - req_content - ).fetch_optional(db).await?; + if let Some(db) = conn.as_sql() { + sqlx::query!( + "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('3 days')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2", + hash, + req_content + ) + .fetch_optional(db) + .await?; + } return Ok(String::new()); } else { diff --git a/backend/windmill-worker/src/graphql_executor.rs b/backend/windmill-worker/src/graphql_executor.rs index 929235cccf..5da8b2d193 100644 --- a/backend/windmill-worker/src/graphql_executor.rs +++ b/backend/windmill-worker/src/graphql_executor.rs @@ -4,17 +4,16 @@ use anyhow::anyhow; use futures::{stream, TryStreamExt}; use serde_json::{json, value::RawValue}; use sqlx::types::Json; -use windmill_common::jobs::QueuedJob; -use windmill_common::worker::to_raw_value; +use windmill_common::worker::{to_raw_value, Connection}; use windmill_common::{error::Error, worker::CLOUD_HOSTED}; use windmill_parser_graphql::parse_graphql_sig; -use windmill_queue::CanceledBy; +use windmill_queue::{CanceledBy, MiniPulledJob}; use serde::Deserialize; use crate::common::{build_http_client, resolve_job_timeout, OccupancyMetrics}; use crate::handle_child::run_future_with_polling_update_job_poller; -use crate::{common::build_args_map, AuthedClientBackgroundTask}; +use crate::{common::build_args_map, AuthedClient}; #[derive(Deserialize)] struct GraphqlApi { @@ -35,16 +34,16 @@ struct GraphqlError { } pub async fn do_graphql( - job: &QueuedJob, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + client: &AuthedClient, query: &str, - db: &sqlx::Pool, + conn: &Connection, mem_peak: &mut i32, canceled_by: &mut Option, worker_name: &str, occupation_metrics: &mut OccupancyMetrics, ) -> windmill_common::error::Result> { - let args = build_args_map(job, client, db).await?.map(Json); + let args = build_args_map(job, client, conn).await?.map(Json); let job_args = if args.is_some() { args.as_ref() } else { @@ -82,7 +81,7 @@ pub async fn do_graphql( } } let (timeout_duration, _, _) = - resolve_job_timeout(&db, &job.workspace_id, job.id, job.timeout).await; + resolve_job_timeout(&conn, &job.workspace_id, job.id, job.timeout).await; let http_client = build_http_client(timeout_duration)?; @@ -151,7 +150,7 @@ pub async fn do_graphql( let r = run_future_with_polling_update_job_poller( job.id, job.timeout, - db, + conn, mem_peak, canceled_by, result_f, diff --git a/backend/windmill-worker/src/handle_child.rs b/backend/windmill-worker/src/handle_child.rs index 7294950363..713046207f 100644 --- a/backend/windmill-worker/src/handle_child.rs +++ b/backend/windmill-worker/src/handle_child.rs @@ -4,8 +4,9 @@ use futures::Future; use nix::sys::signal::{self, Signal}; #[cfg(any(target_os = "linux", target_os = "macos"))] use nix::unistd::Pid; +use windmill_common::agent_workers::PingJobStatusResponse; +use windmill_common::jobs::LARGE_LOG_THRESHOLD_SIZE; -use sqlx::{Pool, Postgres}; #[cfg(windows)] use std::process::Stdio; use tokio::fs::File; @@ -15,7 +16,10 @@ use windmill_common::error::to_anyhow; use windmill_common::error::{self, Error}; -use windmill_common::worker::{get_windmill_memory_usage, get_worker_memory_usage, CLOUD_HOSTED}; +use windmill_common::worker::{ + get_windmill_memory_usage, get_worker_memory_usage, set_job_cancelled_query, Connection, + JobCancelled, CLOUD_HOSTED, +}; use windmill_queue::{append_logs, CanceledBy}; @@ -29,7 +33,6 @@ use std::{io, panic, time::Duration}; use tracing::{trace_span, Instrument}; use uuid::Uuid; -use windmill_common::DB; #[cfg(feature = "enterprise")] use windmill_common::job_metrics; @@ -49,8 +52,9 @@ use futures::{ }; use crate::common::{resolve_job_timeout, OccupancyMetrics}; -use crate::job_logger::{append_job_logs, append_with_limit, LARGE_LOG_THRESHOLD_SIZE}; +use crate::job_logger::{append_job_logs, append_with_limit}; use crate::job_logger_ee::process_streaming_log_lines; +use crate::worker_utils::{ping_job_status, update_worker_ping_from_job}; use crate::{MAX_RESULT_SIZE, MAX_WAIT_FOR_SIGINT, MAX_WAIT_FOR_SIGTERM}; lazy_static::lazy_static! { @@ -92,7 +96,7 @@ async fn kill_process_tree(pid: Option) -> Result<(), String> { #[tracing::instrument(name="run_subprocess", level = "info", skip_all, fields(otel.name = %child_name))] pub async fn handle_child( job_id: &Uuid, - db: &Pool, + conn: &Connection, mem_peak: &mut i32, canceled_by_ref: &mut Option, mut child: Child, @@ -103,6 +107,8 @@ pub async fn handle_child( custom_timeout: Option, sigterm: bool, occupancy_metrics: &mut Option<&mut OccupancyMetrics>, + // Do not print logs to output, but instead save to string. + pipe_stdout: Option<&mut String>, ) -> error::Result<()> { let start = Instant::now(); @@ -136,7 +142,7 @@ pub async fn handle_child( * waiting for the child to exit normally */ let update_job = update_job_poller( job_id, - db, + conn, mem_peak, canceled_by_ref, Box::pin(stream::unfold((), move |_| async move { @@ -182,15 +188,13 @@ pub async fn handle_child( } let (timeout_duration, timeout_warn_msg, is_job_specific) = - resolve_job_timeout(&db, w_id, job_id, custom_timeout).await; + resolve_job_timeout(&conn, w_id, job_id, custom_timeout).await; if let Some(msg) = timeout_warn_msg { - append_logs(&job_id, w_id, msg.as_str(), db).await; + append_logs(&job_id, w_id, msg.as_str(), conn).await; } /* a future that completes when the child process exits */ let wait_on_child = async { - let db = db.clone(); - let kill_reason = tokio::select! { biased; result = child.wait() => return result.map(Ok), @@ -206,18 +210,33 @@ pub async fn handle_child( let set_reason = async { if matches!(kill_reason, KillReason::Timeout { .. }) { - if let Err(err) = sqlx::query!( - "UPDATE v2_job_queue - SET canceled_by = 'timeout' - , canceled_reason = $1 - WHERE id = $2", - format!("duration > {}", timeout_duration.as_secs()), - job_id - ) - .execute(&db) - .await - { - tracing::error!(%job_id, %err, "error setting cancelation reason for job {job_id}: {err}"); + match conn { + Connection::Sql(db) => { + if let Err(err) = set_job_cancelled_query( + job_id, + db, + "timeout", + &format!("duration > {}", timeout_duration.as_secs()), + ) + .await + { + tracing::error!(%job_id, %err, "error setting cancelation reason for job {job_id}: {err}"); + } + } + Connection::Http(client) => { + if let Err(err) = client + .post::<_, ()>( + &format!("/api/agent_workers/set_job_cancelled/{}", job_id), + &JobCancelled { + canceled_by: "timeout".to_string(), + reason: format!("duration > {}", timeout_duration.as_secs()), + }, + ) + .await + { + tracing::error!(%job_id, %err, "error setting cancelation reason for job using http {job_id}: {err}"); + } + } } } }; @@ -304,6 +323,8 @@ pub async fn handle_child( let mut log_total_size: u64 = 0; let pg_log_total_size = Arc::new(AtomicU32::new(0)); + let mut pipe_stdout = pipe_stdout; + while let Some(line) = output.by_ref().next().await { let do_write_ = do_write.shared(); @@ -385,9 +406,14 @@ pub async fn handle_child( let worker_name = worker.to_string(); let w_id2 = w_id.to_string(); - (do_write, write_result) = tokio::spawn(append_job_logs(job_id, w_id2, joined, db.clone(), compact_logs, pg_log_total_size.clone(), worker_name)).remote_handle(); + if let Some(buf) = &mut pipe_stdout { + buf.push_str(&joined); + (do_write, write_result) = tokio::spawn(async { }).remote_handle(); + } else { + (do_write, write_result) = tokio::spawn(append_job_logs(job_id, w_id2, joined, conn.clone(), compact_logs, pg_log_total_size.clone(), worker_name)).remote_handle(); + } if let Err(err) = result { tracing::error!(%job_id, %err, "error reading output for job {job_id} '{child_name}': {err}"); @@ -490,7 +516,7 @@ pub(crate) async fn get_mem_peak(pid: Option, nsjail: bool) -> i32 { pub async fn run_future_with_polling_update_job_poller( job_id: Uuid, timeout: Option, - db: &DB, + conn: &Connection, mem_peak: &mut i32, canceled_by_ref: &mut Option, result_f: Fut, @@ -507,7 +533,7 @@ where let update_job = update_job_poller( job_id, - db, + conn, mem_peak, canceled_by_ref, get_mem, @@ -518,7 +544,7 @@ where ); let timeout_ms = u64::try_from( - resolve_job_timeout(&db, &w_id, job_id, timeout) + resolve_job_timeout(&conn, &w_id, job_id, timeout) .await .0 .as_millis(), @@ -553,7 +579,7 @@ pub enum UpdateJobPollingExit { pub async fn update_job_poller( job_id: Uuid, - db: &DB, + conn: &Connection, mem_peak: &mut i32, canceled_by_ref: &mut Option, mut get_mem: S, @@ -567,8 +593,7 @@ where { let update_job_interval = Duration::from_millis(500); - let db = db.clone(); - + let conn = conn.clone(); let mut interval = interval(update_job_interval); interval.set_missed_tick_behavior(MissedTickBehavior::Skip); @@ -590,22 +615,9 @@ where tracing::info!("job {job_id} on {worker_name} in {w_id} worker memory snapshot {}kB/{}kB", memory_usage.unwrap_or_default()/1024, wm_memory_usage.unwrap_or_default()/1024); let occupancy = occupancy_metrics.as_mut().map(|x| x.update_occupancy_metrics()); if job_id != Uuid::nil() { - sqlx::query!( - "UPDATE worker_ping SET ping_at = now(), current_job_id = $1, current_job_workspace_id = $2, memory_usage = $3, wm_memory_usage = $4, - occupancy_rate = $6, occupancy_rate_15s = $7, occupancy_rate_5m = $8, occupancy_rate_30m = $9 WHERE worker = $5", - &job_id, - &w_id, - memory_usage, - wm_memory_usage, - &worker_name, - occupancy.map(|x| x.0), - occupancy.and_then(|x| x.1), - occupancy.and_then(|x| x.2), - occupancy.and_then(|x| x.3), - ) - .execute(&db) - .await - .expect("update worker ping"); + if let Err(err) = update_worker_ping_from_job(&conn, &job_id, w_id, worker_name, memory_usage, wm_memory_usage, occupancy).await { + tracing::error!("Unable to update worker ping for job {} in workspace {}. Error was: {:?}", job_id, w_id, err); + } } } let current_mem = get_mem.next().await.unwrap_or(0); @@ -620,55 +632,49 @@ where #[cfg(feature = "enterprise")] { if job_id != Uuid::nil() { - - // tracking metric starting at i >= 2 b/c first point it useless and we don't want to track metric for super fast jobs - if i == 2 { - memory_metric_id = job_metrics::register_metric_for_job( - &db, - w_id.to_string(), - job_id, - "memory_kb".to_string(), - job_metrics::MetricKind::TimeseriesInt, - Some("Job Memory Footprint (kB)".to_string()), - ) - .await; - } - if let Ok(ref metric_id) = memory_metric_id { - if let Err(err) = job_metrics::record_metric(&db, w_id.to_string(), job_id, metric_id.to_owned(), job_metrics::MetricNumericValue::Integer(current_mem)).await { - tracing::error!("Unable to save memory stat for job {} in workspace {}. Error was: {:?}", job_id, w_id, err); + if let Connection::Sql(ref db) = conn { + // tracking metric starting at i >= 2 b/c first point it useless and we don't want to track metric for super fast jobs + if i == 2 { + memory_metric_id = job_metrics::register_metric_for_job( + &db, + w_id.to_string(), + job_id, + "memory_kb".to_string(), + job_metrics::MetricKind::TimeseriesInt, + Some("Job Memory Footprint (kB)".to_string()), + ) + .await; + } + if let Ok(ref metric_id) = memory_metric_id { + if let Err(err) = job_metrics::record_metric(&db, w_id.to_string(), job_id, metric_id.to_owned(), job_metrics::MetricNumericValue::Integer(current_mem)).await { + tracing::error!("Unable to save memory stat for job {} in workspace {}. Error was: {:?}", job_id, w_id, err); + } } } } } if job_id != Uuid::nil() { - let (canceled_by, canceled_reason, already_completed) = sqlx::query!( - "UPDATE v2_job_runtime r SET - memory_peak = $1, - ping = now() - FROM v2_job_queue q - WHERE r.id = $2 AND q.id = r.id - RETURNING canceled_by, canceled_reason", - *mem_peak, - job_id - ) - .map(|x| (x.canceled_by, x.canceled_reason, false)) - .fetch_optional(&db) - .await - .unwrap_or_else(|e| { - tracing::error!(%e, "error updating job {job_id}: {e:#}"); - Some((None, None, false)) - }) - .unwrap_or_else(|| { - // if the job is not in queue, it can only be in the completed_job so it is already complete - (None, None, true) - }); - if already_completed { + if matches!(conn, Connection::Http(_)) { + if i % 4 != 0 { + // only ping every 4th time (2s) on http agent mode + continue; + } + } + let ping_job_status = ping_job_status(&conn, &job_id, Some(*mem_peak), if current_mem > 0 { Some(current_mem) } else { None }).await.unwrap_or_else(|e| { + tracing::error!("Unable to ping job status for job {job_id}. Error was: {:?}", e); + PingJobStatusResponse { + canceled_by: None, + canceled_reason: None, + already_completed: false, + } + }); + if ping_job_status.already_completed { return UpdateJobPollingExit::AlreadyCompleted } - if canceled_by.is_some() { + if ping_job_status.canceled_by.is_some() { canceled_by_ref.replace(CanceledBy { - username: canceled_by.clone(), - reason: canceled_reason.clone(), + username: ping_job_status.canceled_by.clone(), + reason: ping_job_status.canceled_reason.clone(), }); break } diff --git a/backend/windmill-worker/src/java_executor.rs b/backend/windmill-worker/src/java_executor.rs new file mode 100644 index 0000000000..c589252005 --- /dev/null +++ b/backend/windmill-worker/src/java_executor.rs @@ -0,0 +1,921 @@ +use std::{collections::HashMap, path::PathBuf, process::Stdio, sync::Arc}; + +use anyhow::{anyhow, bail}; +use async_recursion::async_recursion; +use itertools::Itertools; +use serde_json::value::RawValue; +use tokio::{ + fs::{create_dir_all, metadata, remove_dir_all, File}, + io::AsyncWriteExt, + process::Command, +}; +use uuid::Uuid; +use windmill_common::{ + error::{self, Error}, + utils::calculate_hash, + worker::{copy_dir_recursively, save_cache, write_file, Connection}, +}; +use windmill_parser::Arg; +use windmill_parser_java::parse_java_sig_meta; +use windmill_queue::{append_logs, CanceledBy, MiniPulledJob}; + +use crate::{ + common::{ + create_args_and_out_file, get_reserved_variables, par_install_language_dependencies, + read_result, start_child_process, OccupancyMetrics, RequiredDependency, + }, + handle_child, AuthedClient, COURSIER_CACHE_DIR, DISABLE_NSJAIL, DISABLE_NUSER, JAVA_CACHE_DIR, + JAVA_REPOSITORY_DIR, MAVEN_REPOS, NO_DEFAULT_MAVEN, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, +}; +lazy_static::lazy_static! { + static ref JAVA_CONCURRENT_DOWNLOADS: usize = std::env::var("JAVA_CONCURRENT_DOWNLOADS").ok().map(|flag| flag.parse().unwrap_or(20)).unwrap_or(20); + static ref JAVA_PATH: String = std::env::var("JAVA_PATH").unwrap_or_else(|_| "/usr/bin/java".to_string()); + static ref JAVAC_PATH: String = std::env::var("JAVAC_PATH").unwrap_or_else(|_| "/usr/bin/javac".to_string()); + static ref CS_PATH: String = std::env::var("COURSIER_PATH").unwrap_or_else(|_| "/usr/bin/coursier".to_string()); + static ref STOREPASS: String = std::env::var("JAVA_STOREPASS").unwrap_or("123456".into()); + static ref TRUST_STORE_PATH: String = std::env::var("JAVA_TRUST_STORE_PATH").unwrap_or("/usr/local/share/ca-certificates/truststore.jks".into()); +} + +const NSJAIL_CONFIG_RUN_JAVA_CONTENT: &str = include_str!("../nsjail/run.java.config.proto"); + +#[allow(dead_code)] +pub(crate) struct JobHandlerInput<'a> { + pub base_internal_url: &'a str, + pub canceled_by: &'a mut Option, + pub client: &'a AuthedClient, + pub parent_runnable_path: Option, + pub conn: &'a Connection, + pub envs: HashMap, + pub inner_content: &'a str, + pub job: &'a MiniPulledJob, + pub job_dir: &'a str, + pub mem_peak: &'a mut i32, + pub occupancy_metrics: &'a mut OccupancyMetrics, + pub requirements_o: Option<&'a String>, + pub shared_mount: &'a str, + pub worker_name: &'a str, +} + +pub async fn handle_java_job<'a>(mut args: JobHandlerInput<'a>) -> Result, Error> { + // --- Prepare --- + { + prepare(&mut args).await?; + } + // --- Generate Lockfile --- + + let deps = resolve( + &args.job.id, + &args.inner_content, + &args.job_dir, + &args.conn, + &args.job.workspace_id, + ) + .await?; + + // --- Install --- + + let classpath = install(&mut args, deps).await?; + + // --- Build .java files --- + { + compile(&mut args, &classpath).await?; + } + // --- Run --- + { + run(&mut args, &classpath).await?; + } + // --- Retrieve results --- + { + read_result(&args.job_dir).await + } +} + +async fn prepare<'a>( + JobHandlerInput { job, conn, job_dir, client, inner_content, .. }: &mut JobHandlerInput<'a>, +) -> Result<(), Error> { + // Create needed files + { + create_args_and_out_file(&client, job, job_dir, conn).await?; + let app_path = format!("{}/src/main/java/net/script/", job_dir); + create_dir_all(&app_path).await?; + File::create(format!("{app_path}/App.java")) + .await? + .write_all(&wrap(inner_content)?.into_bytes()) + .await?; + File::create(format!("{app_path}/Main.java")) + .await? + .write_all( + &format!( + "package net.script;\n{MINI_CLIENT_IMPORTS}\n{}\n{MINI_CLIENT}", + inner_content + ) + .into_bytes(), + ) + .await?; + } + Ok(()) +} + +pub async fn resolve<'a>( + job_id: &Uuid, + code: &str, + job_dir: &str, + conn: &Connection, + w_id: &str, +) -> Result { + let deps = { + let find_requirements = code.lines().find_position(|x| { + x.starts_with("//requirements:") || x.starts_with("// requirements:") + }); + + let specified_deps = if let Some((pos, _)) = find_requirements { + code.lines() + .skip(pos + 1) + .map_while(|x| { + if x.starts_with("//") { + Some(x.replace("//", "").trim().to_owned()) + } else { + None + } + }) + .collect::>() + } else { + Default::default() + }; + + let mut deps = vec![ + // Default requirements + "com.fasterxml.jackson.core:jackson-databind:2.9.8".to_owned(), + ]; + deps.extend(specified_deps); + deps.join("\n") + }; + + let req_hash = format!("java-{}", calculate_hash(&deps)); + if let Connection::Sql(db) = conn { + if let Some(cached) = sqlx::query_scalar!( + "SELECT lockfile FROM pip_resolution_cache WHERE hash = $1", + req_hash + ) + .fetch_optional(db) + .await? + { + return Ok(cached); + } + } + let lock = { + append_logs( + job_id, + w_id, + format!("\n--- RESOLVING LOCKFILE ---\n"), + &conn, + ) + .await; + + let mut cmd = Command::new(if cfg!(windows) { + "java" + } else { + JAVA_PATH.as_str() + }); + cmd.env_clear() + .current_dir(job_dir.to_owned()) + .env("PATH", PATH_ENV.as_str()) + .envs(PROXY_ENVS.clone()); + + // Configure proxies + { + let jps = parse_proxy()?; + if let Some(val) = jps.https_host { + cmd.arg(&format!("-Dhttps.proxyHost={}", val)); + } + if let Some(val) = jps.https_port { + cmd.arg(&format!("-Dhttps.proxyPort={}", val)); + } + if let Some(val) = jps.http_host { + cmd.arg(&format!("-Dhttp.proxyHost={}", val)); + } + if let Some(val) = jps.http_port { + cmd.arg(&format!("-Dhttp.proxyPort={}", val)); + } + if let Some(val) = jps.no_proxy { + cmd.arg(&format!("-Dhttp.nonProxyHosts=\"{}\"", val)); + } + } + if metadata(TRUST_STORE_PATH.clone()).await.is_ok() { + cmd.args(&[ + &format!("-Djavax.net.ssl.trustStore={}", *TRUST_STORE_PATH), + &format!("-Djavax.net.ssl.trustStorePassword={}", *STOREPASS), + ]); + } + cmd.args(&[ + "-jar", + &CS_PATH, + "resolve", + &get_no_default(), + "--parallel", + &format!("{}", *JAVA_CONCURRENT_DOWNLOADS), + "--cache", + COURSIER_CACHE_DIR, + ]) + .args(&get_repos().await) + .args(&deps.split("\n").collect_vec()) + .stderr(Stdio::piped()); + + #[cfg(windows)] + { + cmd.env("SystemRoot", crate::SYSTEM_ROOT.as_str()) + .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()) + .env( + "TMP", + std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), + ); + } + let output = cmd.output().await?; + // Check if the command was successful + if output.status.success() { + String::from_utf8(output.stdout).expect("Failed to convert output to String") + } else { + let stderr = + String::from_utf8(output.stderr).expect("Failed to convert error output to String"); + return Err(error::Error::internal_err(stderr)); + } + }; + + if let Connection::Sql(db) = conn { + sqlx::query!( + "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('3 days')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2", + req_hash, + lock.clone(), + ) + .fetch_optional(db) + .await?; + } + + append_logs(job_id, w_id, format!("\n{}", &lock), &conn).await; + Ok(lock) +} + +async fn install<'a>( + JobHandlerInput { worker_name, job, conn, job_dir, .. }: &mut JobHandlerInput<'a>, + deps: String, +) -> Result { + let deps = deps + .lines() + .map(|line| { + let unparsed_dep = line.replace(":jar", "").replace(":lib", ""); + let mut it = unparsed_dep.split(":"); + + match (it.next(), it.next(), it.next()) { + (Some(group_id), Some(artifact_id), Some(version)) => { + let path = format!( + "{JAVA_REPOSITORY_DIR}/{}/{artifact_id}/{version}", + group_id.replace(".", "/") + ); + Ok(RequiredDependency { + path, + custom_name: Some(format!("{group_id}:{artifact_id}:{version}")), + short_name: Some(format!("{artifact_id}:{version}")), + }) + } + _ => anyhow::bail!("{line} is not parsable"), + } + }) + .collect::>>()?; + + let classpath = deps + .clone() + .into_iter() + .map(|RequiredDependency { path, .. }| path + "/*") + .collect_vec() + .join(":") + + ":target"; + + #[cfg(windows)] + let classpath = classpath.replace(":", ";"); + + tracing::debug!( + workspace_id = %job.workspace_id, + "JAVA classpath: {}", &classpath + ); + let (repos, no_default, trust_store_metadata) = ( + get_repos().await, + get_no_default(), + metadata(TRUST_STORE_PATH.clone()).await, + ); + let job_dir = job_dir.to_owned(); + let fetch_dir = format!("{JAVA_CACHE_DIR}/tmp-fetch-{}", Uuid::new_v4()); + let fetch_dir2 = fetch_dir.clone(); + par_install_language_dependencies( + deps, + "java", + "java", + true, + *JAVA_CONCURRENT_DOWNLOADS, + true, + crate::common::InstallStrategy::AllAtOnce(Arc::new(move |dependencies| { + let mut cmd = Command::new(if cfg!(windows) { + "java" + } else { + JAVA_PATH.as_str() + }); + let artifacts = dependencies + .into_iter() + .map(|e| { + e.custom_name.ok_or(anyhow::anyhow!( + "Internal Error: Artifact name should be Some!" + )) + }) + .collect::>>()?; + cmd.env_clear() + .current_dir(&job_dir) + .env("PATH", PATH_ENV.as_str()) + .envs(PROXY_ENVS.clone()); + // Configure proxies + { + let jps = parse_proxy()?; + if let Some(val) = jps.https_host { + cmd.arg(&format!("-Dhttps.proxyHost={}", val)); + } + if let Some(val) = jps.https_port { + cmd.arg(&format!("-Dhttps.proxyPort={}", val)); + } + if let Some(val) = jps.http_host { + cmd.arg(&format!("-Dhttp.proxyHost={}", val)); + } + if let Some(val) = jps.http_port { + cmd.arg(&format!("-Dhttp.proxyPort={}", val)); + } + if let Some(val) = jps.no_proxy { + cmd.arg(&format!("-Dhttp.nonProxyHosts=\"{}\"", val)); + } + } + + if trust_store_metadata.is_ok() { + cmd.args(&[ + &format!("-Djavax.net.ssl.trustStore={}", *TRUST_STORE_PATH), + &format!("-Djavax.net.ssl.trustStorePassword={}", *STOREPASS), + ]); + } + cmd.args(&[ + "-jar", + &CS_PATH, + "fetch", + &no_default, + "--quiet", + "--parallel", + &format!("{}", *JAVA_CONCURRENT_DOWNLOADS), + "--cache", + &fetch_dir, + ]) + .args(&repos) + .arg("--intransitive") + .args(artifacts) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + #[cfg(windows)] + { + cmd.env("SystemRoot", crate::SYSTEM_ROOT.as_str()) + .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()) + .env( + "TMP", + std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), + ); + } + + Ok(cmd) + })), + async move |_| { + move_to_repository(&fetch_dir2, 0).await?; + remove_dir_all(&fetch_dir2).await?; + #[async_recursion] + async fn move_to_repository(path: &str, depth: u8) -> anyhow::Result<()> { + if depth == 3 { + copy_dir_recursively( + &PathBuf::from(path), + &PathBuf::from(JAVA_REPOSITORY_DIR), + )?; + + return Ok(()); + } + let mut entries = tokio::fs::read_dir(path).await?; + loop { + let Some(entry) = entries.next_entry().await? else { + break Ok(()); + }; + + let path = entry + .path() + .to_str() + .ok_or(anyhow!("Internal Error: Cannot convert Path to Str"))? + .to_owned(); + + move_to_repository(&path, depth + 1).await?; + } + } + Ok(()) + }, + &job.id, + &job.workspace_id, + worker_name, + conn, + ) + .await?; + Ok(classpath) +} + +async fn compile<'a>( + JobHandlerInput { + occupancy_metrics, + mem_peak, + canceled_by, + worker_name, + job, + conn, + job_dir, + client, + envs, + base_internal_url, + inner_content, + requirements_o, + parent_runnable_path, + .. + }: &mut JobHandlerInput<'a>, + classpath: &'a str, + // plugins: Vec<&'a str>, +) -> Result<(), Error> { + fn compute_hash(code: &str, requirements_o: Option<&String>) -> String { + calculate_hash(&format!( + "{}{}", + code, + requirements_o + .as_ref() + .map(|x| x.to_string()) + .unwrap_or_default() + )) + } + let reserved_variables = + get_reserved_variables(job, &client.token, conn, parent_runnable_path.clone()).await?; + let hash = compute_hash(inner_content, *requirements_o); + let bin_path = format!("{}/{hash}", JAVA_CACHE_DIR); + let remote_path = format!("java_jar/{hash}"); + let (cache, ..) = windmill_common::worker::load_cache(&bin_path, &remote_path, true).await; + + if cache { + let target = format!("{job_dir}/target"); + + #[cfg(unix)] + let symlink = std::os::unix::fs::symlink(&bin_path, &target); + #[cfg(windows)] + let symlink = copy_dir_recursively(&PathBuf::from(&bin_path), &PathBuf::from(&target)); + + symlink.map_err(|e| { + Error::ExecutionErr(format!( + "could not copy cached binary from {bin_path} to {job_dir}/main: {e:?}" + )) + })?; + } else { + // let plugin_registry = format!("{job_dir}/plugin-registry"); + let child = { + append_logs( + &job.id, + &job.workspace_id, + format!("\n--- COMPILING .JAVA FILES\n"), + &conn, + ) + .await; + + let mut cmd = Command::new(if cfg!(windows) { + "javac" + } else { + JAVAC_PATH.as_str() + }); + cmd.env_clear() + .current_dir(job_dir.to_owned()) + .env("PATH", PATH_ENV.as_str()) + .env("BASE_INTERNAL_URL", base_internal_url) + .envs(envs) + .envs(reserved_variables) + .envs(PROXY_ENVS.clone()) + .args(&[ + "-classpath", + &classpath, + "src/main/java/net/script/Main.java", + "src/main/java/net/script/App.java", + "-d", + "./target", + ]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + #[cfg(windows)] + { + cmd.env("SystemRoot", crate::SYSTEM_ROOT.as_str()) + .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()) + .env( + "TMP", + std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), + ); + } + start_child_process(cmd, "javac").await? + }; + handle_child::handle_child( + &job.id, + conn, + mem_peak, + canceled_by, + child, + !*DISABLE_NSJAIL, + worker_name, + &job.workspace_id, + "javac", + job.timeout, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + + match save_cache( + &bin_path, + &format!("java_jar/{hash}"), + &format!("{job_dir}/target"), + true, + ) + .await + { + Err(e) => { + let em = format!( + "could not save {bin_path} to {} to java cache: {e:?}", + format!("{job_dir}/main"), + ); + tracing::error!(em); + } + Ok(logs) => { + tracing::trace!(logs); + } + } + }; + + Ok(()) +} +async fn run<'a>( + JobHandlerInput { + occupancy_metrics, + mem_peak, + canceled_by, + worker_name, + job, + conn, + job_dir, + shared_mount, + client, + envs, + base_internal_url, + parent_runnable_path, + .. + }: &mut JobHandlerInput<'a>, + classpath: &'a str, +) -> Result<(), Error> { + let reserved_variables = + get_reserved_variables(job, &client.token, conn, parent_runnable_path.clone()).await?; + + let child = if !cfg!(windows) && !*DISABLE_NSJAIL { + append_logs( + &job.id, + &job.workspace_id, + format!("\n--- ISOLATED JAVA CODE EXECUTION ---\n"), + &conn, + ) + .await; + + write_file( + job_dir, + "run.config.proto", + &NSJAIL_CONFIG_RUN_JAVA_CONTENT + .replace("{JOB_DIR}", job_dir) + .replace("{CACHE_DIR}", JAVA_CACHE_DIR) + .replace("{SHARED_MOUNT}", &shared_mount) + // .replace("{CACHED_TARGET}", &shared_mount) + .replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string()), + )?; + let mut cmd = Command::new(NSJAIL_PATH.as_str()); + cmd.env_clear() + .current_dir(job_dir) + .env("PATH", PATH_ENV.as_str()) + .env("BASE_INTERNAL_URL", base_internal_url) + .envs(envs) + .envs(reserved_variables) + .envs(PROXY_ENVS.clone()) + .args(vec![ + "--config", + "run.config.proto", + "--", + JAVA_PATH.as_str(), + ]); + if metadata(TRUST_STORE_PATH.clone()).await.is_ok() { + cmd.args(&[ + &format!("-Djavax.net.ssl.trustStore={}", *TRUST_STORE_PATH), + &format!("-Djavax.net.ssl.trustStorePassword={}", *STOREPASS), + ]); + } + // Configure proxies + { + let jps = parse_proxy()?; + if let Some(val) = jps.https_host { + cmd.arg(&format!("-Dhttps.proxyHost={}", val)); + } + if let Some(val) = jps.https_port { + cmd.arg(&format!("-Dhttps.proxyPort={}", val)); + } + if let Some(val) = jps.http_host { + cmd.arg(&format!("-Dhttp.proxyHost={}", val)); + } + if let Some(val) = jps.http_port { + cmd.arg(&format!("-Dhttp.proxyPort={}", val)); + } + if let Some(val) = jps.no_proxy { + cmd.arg(&format!("-Dhttp.nonProxyHosts=\"{}\"", val)); + } + } + cmd.args(vec!["-classpath", &classpath, "net.script.App"]); + cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); + + start_child_process(cmd, NSJAIL_PATH.as_str()).await? + } else { + append_logs( + &job.id, + &job.workspace_id, + format!("\n--- JAVA CODE EXECUTION ---\n"), + &conn, + ) + .await; + + let mut cmd = Command::new(if cfg!(windows) { + "java" + } else { + JAVA_PATH.as_str() + }); + cmd.env_clear() + .current_dir(job_dir.to_owned()) + .env("PATH", PATH_ENV.as_str()) + .env("BASE_INTERNAL_URL", base_internal_url) + .envs(envs) + .envs(reserved_variables); + if metadata(TRUST_STORE_PATH.clone()).await.is_ok() { + cmd.args(&[ + &format!("-Djavax.net.ssl.trustStore={}", *TRUST_STORE_PATH), + &format!("-Djavax.net.ssl.trustStorePassword={}", *STOREPASS), + ]); + } + // Configure proxies + { + let jps = parse_proxy()?; + if let Some(val) = jps.https_host { + cmd.arg(&format!("-Dhttps.proxyHost={}", val)); + } + if let Some(val) = jps.https_port { + cmd.arg(&format!("-Dhttps.proxyPort={}", val)); + } + if let Some(val) = jps.http_host { + cmd.arg(&format!("-Dhttp.proxyHost={}", val)); + } + if let Some(val) = jps.http_port { + cmd.arg(&format!("-Dhttp.proxyPort={}", val)); + } + if let Some(val) = jps.no_proxy { + cmd.arg(&format!("-Dhttp.nonProxyHosts=\"{}\"", val)); + } + } + cmd.args(&["-classpath", &classpath, "net.script.App"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + #[cfg(windows)] + { + cmd.env("SystemRoot", crate::SYSTEM_ROOT.as_str()) + .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()) + .env( + "TMP", + std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), + ); + } + start_child_process(cmd, "java").await? + }; + handle_child::handle_child( + &job.id, + conn, + mem_peak, + canceled_by, + child, + !*DISABLE_NSJAIL, + worker_name, + &job.workspace_id, + "java", + job.timeout, + false, + &mut Some(occupancy_metrics), + None, + ) + .await +} + +#[derive(Default, Debug)] +struct JavaProxySettings { + http_host: Option, + http_port: Option, + https_host: Option, + https_port: Option, + no_proxy: Option, +} +fn parse_proxy() -> anyhow::Result { + let mut jps = JavaProxySettings::default(); + for (ident, mut val) in PROXY_ENVS.clone() { + match ident { + "HTTPS_PROXY" => { + if val.contains("http://") { + bail!("HTTPS_PROXY url cannot contain http scheme."); + } + if !val.contains("https://") { + val = format!("https://{val}"); + } + let mut url = url::Url::parse(&val)?; + let port = url.port(); + // Make sure port and schema is not included in final url + { + url.set_port(None).unwrap_or_default(); + jps.https_host = Some(url.as_str().replace("https://", "")); + if let Some(port) = port { + jps.https_port = Some(format!("{}", port)); + } + } + } + "HTTP_PROXY" => { + if val.contains("https://") { + bail!("HTTP_PROXY url cannot contain https scheme."); + } + if !val.contains("http://") { + val = format!("http://{val}"); + } + let mut url = url::Url::parse(&val)?; + let port = url.port(); + // Make sure port and schema is not included in final url + { + url.set_port(None).unwrap_or_default(); + jps.http_host = Some(url.as_str().replace("http://", "")); + if let Some(port) = port { + jps.https_port = Some(format!("{}", port)); + } + } + } + // Java uses | instead of , + "NO_PROXY" => jps.no_proxy = Some(val.replace(",", "|")), + _ => {} + } + } + + Ok(jps) +} +async fn get_repos() -> Vec { + MAVEN_REPOS + .read() + .await + .as_ref() + .map(|repos| { + repos + .trim() + .split_whitespace() + .into_iter() + .map(|el| vec!["--repository".to_owned(), el.to_owned()]) + .collect_vec() + }) + .unwrap_or_default() + .concat() +} + +fn get_no_default() -> String { + if NO_DEFAULT_MAVEN.load(std::sync::atomic::Ordering::Relaxed) { + "--no-default" + } else { + // Command does not take empty arguments + "-q" + } + .into() +} + +/// Wraps content script +/// that upon execution reads args.json (which are piped and transformed from previous flow step or top level inputs) +/// Also wrapper takes output of program and serializes to result.json (Which windmill will know how to use later) +fn wrap(inner_content: &str) -> Result { + let sig = parse_java_sig_meta(inner_content)?; + let ret_void = sig.returns_void; + let spread = sig + .main_sig + .args + .clone() + .into_iter() + .map(|Arg { name, .. }| { + // Apply additional input transformation + format!(" parsedArgs.{name}") + }) + .collect_vec() + .join(","); + let args = sig + .main_sig + .args + .clone() + .into_iter() + .map(|Arg { name, otyp, .. }| { + // Apply additional input transformation + format!("public {} {name};\n", otyp.unwrap()) + }) + .collect_vec() + .join(" "); + Ok(r#" +package net.script; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.FileOutputStream; +import net.script.Main; + +public class App{ + + public static class Args {ARGS} + + public static void main(String[] args) { + try { + InputStream fileInputStream = new FileInputStream("args.json"); + ObjectMapper mapper = new ObjectMapper(); + Args parsedArgs = mapper.readValue(fileInputStream, Args.class); + fileInputStream.close(); + {MAIN_HANDLER} + FileOutputStream fileOutputStream = new FileOutputStream("result.json"); + mapper.writeValue(fileOutputStream, res); + fileOutputStream.close(); + + } catch (Exception e) { // Catching general Exception + e.printStackTrace(); // Handle the exception + } + } +} + "# + .replace( + "{MAIN_HANDLER}", + if ret_void { + " + Main.main(SPREAD); + Object res = null; + " + } else { + " + Object res = Main.main(SPREAD); + " + }, + ) + .replace("SPREAD", &spread) + .replace("ARGS", &args)) +} +const MINI_CLIENT_IMPORTS: &str = r#" +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +"#; +const MINI_CLIENT: &str = r#" +class Wmill { + public static String getVariable(String path) { + var baseUrl = System.getenv("BASE_INTERNAL_URL"); + var workspace = System.getenv("WM_WORKSPACE"); + var uri = java.text.MessageFormat.format("{0}/api/w/{1}/variables/get_value/{2}", baseUrl, workspace, path); + + // Create an HttpRequest + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(uri)) + .header("Authorization", "Bearer " + System.getenv("WM_TOKEN")) // Add the Authorization header + .GET() // Set the request method to GET + .build(); + + // Send the request and get the response + return HttpClient.newHttpClient().sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::body) + .join(); // Wait for the completion + } + public static String getResource(String path) { + var baseUrl = System.getenv("BASE_INTERNAL_URL"); + var workspace = System.getenv("WM_WORKSPACE"); + var uri = java.text.MessageFormat.format("{0}/api/w/{1}/resources/get_value_interpolated/{2}", baseUrl, workspace, path); + + // Create an HttpRequest + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(uri)) + .header("Authorization", "Bearer " + System.getenv("WM_TOKEN")) // Add the Authorization header + .GET() // Set the request method to GET + .build(); + + // Send the request and get the response + return HttpClient.newHttpClient().sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::body) + .join(); // Wait for the completion + } +} +"#; diff --git a/backend/windmill-worker/src/job_logger.rs b/backend/windmill-worker/src/job_logger.rs index fa4ac15383..97784340c5 100644 --- a/backend/windmill-worker/src/job_logger.rs +++ b/backend/windmill-worker/src/job_logger.rs @@ -1,6 +1,7 @@ use regex::Regex; -use windmill_common::worker::CLOUD_HOSTED; +pub use windmill_common::jobs::LARGE_LOG_THRESHOLD_SIZE; +use windmill_common::worker::{Connection, CLOUD_HOSTED}; use windmill_queue::append_logs; @@ -8,7 +9,6 @@ use std::sync::atomic::AtomicU32; use std::sync::Arc; use uuid::Uuid; -use windmill_common::DB; #[cfg(not(all(feature = "enterprise", feature = "parquet")))] use crate::job_logger_ee::default_disk_log_storage; @@ -25,39 +25,40 @@ pub enum CompactLogs { S3, } -pub(crate) async fn append_job_logs( +pub async fn append_job_logs( job_id: Uuid, w_id: String, logs: String, - db: DB, + conn: Connection, must_compact_logs: bool, total_size: Arc, worker_name: String, ) -> () { - if must_compact_logs { - #[cfg(all(feature = "enterprise", feature = "parquet"))] - s3_storage(job_id, &w_id, &db, logs, total_size, &worker_name).await; + match conn { + Connection::Sql(db) if must_compact_logs => { + #[cfg(all(feature = "enterprise", feature = "parquet"))] + s3_storage(job_id, &w_id, &db, logs, total_size, &worker_name).await; - #[cfg(not(all(feature = "enterprise", feature = "parquet")))] - { - default_disk_log_storage( - job_id, - &w_id, - &db, - logs, - total_size, - CompactLogs::NotEE, - &worker_name, - ) - .await; + #[cfg(not(all(feature = "enterprise", feature = "parquet")))] + { + default_disk_log_storage( + job_id, + &w_id, + &db, + logs, + total_size, + CompactLogs::NotEE, + &worker_name, + ) + .await; + } + } + _ => { + append_logs(&job_id, w_id, logs, &conn).await; } - } else { - append_logs(&job_id, w_id, logs, db).await; } } -pub const LARGE_LOG_THRESHOLD_SIZE: usize = 9000; - lazy_static::lazy_static! { static ref RE_00: Regex = Regex::new('\u{00}'.to_string().as_str()).unwrap(); pub static ref NO_LOGS_AT_ALL: bool = std::env::var("NO_LOGS_AT_ALL").ok().is_some_and(|x| x == "1" || x == "true"); diff --git a/backend/windmill-worker/src/js_eval.rs b/backend/windmill-worker/src/js_eval.rs index 9aab144296..33a9b57690 100644 --- a/backend/windmill-worker/src/js_eval.rs +++ b/backend/windmill-worker/src/js_eval.rs @@ -45,7 +45,7 @@ use windmill_common::error::Error; #[cfg(feature = "deno_core")] use windmill_common::worker::{write_file, TMP_DIR}; -use windmill_common::{flow_status::JobResult, DB}; +use windmill_common::flow_status::JobResult; use windmill_queue::CanceledBy; use crate::{common::OccupancyMetrics, AuthedClient}; @@ -108,9 +108,10 @@ impl FetchPermissions for PermissionsContainer { #[inline(always)] fn check_read<'a>( &mut self, + _resolved: bool, p: &'a std::path::Path, _api_name: &str, - ) -> Result, deno_permissions::PermissionCheckError> { + ) -> Result, deno_io::fs::FsError> { Ok(Cow::Borrowed(p)) } } @@ -375,9 +376,10 @@ fn replace_with_await(expr: String, fn_name: &str) -> String { } lazy_static! { static ref RE: Regex = - Regex::new(r#"(?m)(?Presults(?:(?:\.[a-zA-Z_0-9]+)|(?:\[\".*?\"\])))"#).unwrap(); + Regex::new(r#"(?m)(?Presults(?:\?)?(?:(?:\.[a-zA-Z_0-9]+)|(?:\[\".*?\"\])))"#).unwrap(); static ref RE_FULL: Regex = - Regex::new(r"(?m)^results\.([a-zA-Z_0-9]+)(?:\[(\d+)\])?((?:\.[a-zA-Z_0-9]+)+)?$").unwrap(); + Regex::new(r"(?m)^results(?:\?)?\.([a-zA-Z_0-9]+)(?:\[(\d+)\])?((?:\.[a-zA-Z_0-9]+)+)?$") + .unwrap(); static ref RE_PROXY: Regex = Regex::new(r"^(https?)://(([^:@\s]+):([^:@\s]+)@)?([^:@\s]+)(:(\d+))?$").unwrap(); } @@ -554,12 +556,17 @@ function get_from_env(name) {{ async fn op_variable( op_state: Rc>, #[string] path: String, -) -> Result { +) -> Result { let client = op_state.borrow().borrow::().0.clone(); if let Some(client) = client { - Ok(client.get_variable_value(&path).await?) + Ok(client + .get_variable_value(&path) + .await + .map_err(|e| deno_error::JsErrorBox::generic(e.to_string()))?) } else { - anyhow::bail!("No client found in op state"); + Err(deno_error::JsErrorBox::generic( + "No client found in op state", + )) } } @@ -569,16 +576,18 @@ async fn op_variable( async fn op_get_result( op_state: Rc>, #[string] id: String, -) -> Result { +) -> Result { let client = op_state.borrow().borrow::().0.clone(); if let Some(client) = client { - let result = client + client .get_completed_job_result::>(&id, None) - .await? - .clone(); - Ok(result.get().to_string()) + .await + .map_err(|e| deno_error::JsErrorBox::generic(e.to_string())) + .map(|x| x.get().to_string()) } else { - anyhow::bail!("No client found in op state"); + Err(deno_error::JsErrorBox::generic( + "No client found in op state", + )) } } @@ -589,7 +598,7 @@ async fn op_get_id( op_state: Rc>, #[string] flow_job_id: String, #[string] node_id: String, -) -> Result, anyhow::Error> { +) -> Result, deno_error::JsErrorBox> { let client = op_state.borrow().borrow::().0.clone(); if let Some(client) = client { let result = client @@ -602,7 +611,9 @@ async fn op_get_id( Ok(None) } } else { - anyhow::bail!("No client found in op state"); + Err(deno_error::JsErrorBox::generic( + "No client found in op state", + )) } } @@ -612,15 +623,18 @@ async fn op_get_id( async fn op_resource( op_state: Rc>, #[string] path: String, -) -> Result, anyhow::Error> { +) -> Result, deno_error::JsErrorBox> { let client = op_state.borrow().borrow::().0.clone(); if let Some(client) = client { client .get_resource_value_interpolated::>>(&path, None) .await .map(|x| x.map(|x| x.get().to_string())) + .map_err(|e| deno_error::JsErrorBox::generic(e.to_string())) } else { - anyhow::bail!("No client found in op state"); + Err(deno_error::JsErrorBox::generic( + "No client found in op state", + )) } } @@ -735,15 +749,19 @@ fn capture_proxy(s: &str) -> Option<(String, Option<(String, String)>)> { ) }) } + +use windmill_common::worker::Connection; + #[cfg(not(feature = "deno_core"))] pub async fn eval_fetch_timeout( _env_code: String, _ts_expr: String, _js_expr: String, _args: Option<&Json>>>, + _script_entrypoint_override: Option, _job_id: Uuid, _job_timeout: Option, - _db: &DB, + _conn: &Connection, _mem_peak: &mut i32, _canceled_by: &mut Option, _worker_name: &str, @@ -761,9 +779,10 @@ pub async fn eval_fetch_timeout( ts_expr: String, js_expr: String, args: Option<&Json>>>, + script_entrypoint_override: Option, job_id: Uuid, job_timeout: Option, - db: &DB, + conn: &Connection, mem_peak: &mut i32, canceled_by: &mut Option, worker_name: &str, @@ -775,7 +794,13 @@ pub async fn eval_fetch_timeout( let (sender, mut receiver) = oneshot::channel::(); - let parsed_args = windmill_parser_ts::parse_deno_signature(&ts_expr, true, None)?.args; + let parsed_args = windmill_parser_ts::parse_deno_signature( + &ts_expr, + true, + false, + script_entrypoint_override.clone(), + )? + .args; let spread = parsed_args .into_iter() .map(|x| { @@ -803,7 +828,7 @@ pub async fn eval_fetch_timeout( )); } - let db_ = db.clone(); + let conn_ = conn.clone(); let w_id_ = w_id.to_string(); let result_f = tokio::task::spawn_blocking(move || { let ops = vec![op_get_static_args(), op_log()]; @@ -822,6 +847,7 @@ pub async fn eval_fetch_timeout( }; let exts: Vec = vec![ + deno_telemetry::deno_telemetry::init_ops(), deno_webidl::deno_webidl::init_ops(), deno_url::deno_url::init_ops(), deno_console::deno_console::init_ops(), @@ -887,7 +913,7 @@ pub async fn eval_fetch_timeout( let future = async { let r = tokio::select! { - r = eval_fetch(&mut js_runtime, &js_expr, Some(env_code), load_client, &job_id) => Ok(r), + r = eval_fetch(&mut js_runtime, &js_expr, Some(env_code), script_entrypoint_override, load_client, &job_id) => Ok(r), _ = memory_limit_rx.recv() => Err(Error::ExecutionErr("Memory limit reached, killing isolate".to_string())) }; @@ -898,7 +924,7 @@ pub async fn eval_fetch_timeout( "{extra_logs}{}", js_runtime.op_state().borrow().borrow::().s ), - db_, + &conn_, ) .await; @@ -913,7 +939,7 @@ pub async fn eval_fetch_timeout( let res = run_future_with_polling_update_job_poller( job_id, job_timeout, - db, + conn, mem_peak, canceled_by, async { result_f.await? }, @@ -953,6 +979,9 @@ fn write_error_expr(expr: &str, uuid: &Uuid) { } }; + if std::env::var("PRINT_NATIVE_ERRORS").is_ok() { + tracing::info!("native error for job {uuid}: {expr}"); + } if dir_entries >= 100 { tracing::info!("Too many error files in {ERROR_DIR}, skipping write"); return; @@ -972,6 +1001,7 @@ async fn eval_fetch( js_runtime: &mut JsRuntime, expr: &str, env_code: Option, + script_entrypoint_override: Option, load_client: bool, job_id: &Uuid, ) -> anyhow::Result> { @@ -998,13 +1028,16 @@ async fn eval_fetch( }) .context("failed to load module")?; + let main_override = script_entrypoint_override.unwrap_or("main".to_string()); let script = js_runtime .execute_script( "", - r#" + format!( + r#" let args = Deno.core.ops.op_get_static_args().map(JSON.parse) -import("file:///eval.ts").then((module) => module.main(...args)).then(JSON.stringify) -"#, +import("file:///eval.ts").then((module) => module.{main_override}(...args)).then(JSON.stringify) +"# + ), ) .map_err(|e| { write_error_expr(expr, &job_id); diff --git a/backend/windmill-worker/src/lib.rs b/backend/windmill-worker/src/lib.rs index cc34dd72ab..64a9cf2c88 100644 --- a/backend/windmill-worker/src/lib.rs +++ b/backend/windmill-worker/src/lib.rs @@ -5,10 +5,14 @@ mod mssql_executor; #[cfg(feature = "enterprise")] mod snowflake_executor; +mod agent_workers; #[cfg(feature = "python")] mod ansible_executor; mod bash_executor; +#[cfg(feature = "java")] +mod java_executor; + mod bun_executor; pub mod common; mod config; @@ -20,29 +24,39 @@ mod global_cache; mod go_executor; mod graphql_executor; mod handle_child; -mod job_logger; +pub mod job_logger; mod job_logger_ee; mod js_eval; #[cfg(feature = "mysql")] mod mysql_executor; +#[cfg(feature = "nu")] +mod nu_executor; #[cfg(feature = "oracledb")] mod oracledb_executor; +mod otel_ee; mod pg_executor; #[cfg(feature = "php")] mod php_executor; #[cfg(feature = "python")] mod python_executor; -mod result_processor; +pub mod result_processor; #[cfg(feature = "rust")] mod rust_executor; +mod sanitized_sql_params; +mod schema; mod worker; mod worker_flow; mod worker_lockfiles; +mod worker_utils; + +pub use worker_lockfiles::process_relative_imports; + pub use worker::*; pub use result_processor::handle_job_error; pub use bun_executor::{ - get_common_bun_proc_envs, install_bun_lockfile, prebundle_bun_script, prepare_job_dir, + compute_bundle_local_and_remote_path, get_common_bun_proc_envs, install_bun_lockfile, + prebundle_bun_script, prepare_job_dir, }; pub use deno_executor::generate_deno_lock; diff --git a/backend/windmill-worker/src/mssql_executor.rs b/backend/windmill-worker/src/mssql_executor.rs index 42d9c3aa8c..d827e57095 100644 --- a/backend/windmill-worker/src/mssql_executor.rs +++ b/backend/windmill-worker/src/mssql_executor.rs @@ -8,24 +8,41 @@ use tiberius::{AuthMethod, Client, ColumnData, Config, FromSqlOwned, Query, Row, use tokio::net::TcpStream; use tokio_util::compat::TokioAsyncWriteCompatExt; use uuid::Uuid; -use windmill_common::error::{self, Error}; -use windmill_common::worker::to_raw_value; -use windmill_common::{error::to_anyhow, jobs::QueuedJob}; +use windmill_common::{ + error::{self, to_anyhow, Error}, + utils::empty_string_as_none, + worker::{to_raw_value, Connection}, +}; use windmill_parser_sql::{parse_db_resource, parse_mssql_sig}; +use windmill_queue::MiniPulledJob; use windmill_queue::{append_logs, CanceledBy}; use crate::common::{build_args_values, OccupancyMetrics}; use crate::handle_child::run_future_with_polling_update_job_poller; -use crate::AuthedClientBackgroundTask; +use crate::sanitized_sql_params::sanitize_and_interpolate_unsafe_sql_args; +use crate::AuthedClient; + +use serde::Deserializer; #[derive(Deserialize)] struct MssqlDatabase { host: String, - user: String, - password: String, + user: Option, + password: Option, port: Option, dbname: String, instance_name: Option, + #[serde(default, deserialize_with = "deserialize_aad_token")] + aad_token: Option, + trust_cert: Option, + #[serde(default, deserialize_with = "empty_string_as_none")] + ca_cert: Option, +} + +#[derive(Debug, Deserialize)] +struct AadToken { + #[serde(default, deserialize_with = "empty_string_as_none")] + token: Option, } lazy_static::lazy_static! { @@ -33,24 +50,23 @@ lazy_static::lazy_static! { } pub async fn do_mssql( - job: &QueuedJob, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + client: &AuthedClient, query: &str, - db: &sqlx::Pool, + conn: &Connection, mem_peak: &mut i32, canceled_by: &mut Option, worker_name: &str, occupancy_metrics: &mut OccupancyMetrics, + job_dir: &str, ) -> error::Result> { - let mssql_args = build_args_values(job, client, db).await?; + let mssql_args = build_args_values(job, client, conn).await?; let inline_db_res_path = parse_db_resource(&query); let db_arg = if let Some(inline_db_res_path) = inline_db_res_path { Some( client - .get_authed() - .await .get_resource_value_interpolated::( &inline_db_res_path, Some(job.id.to_string()), @@ -90,12 +106,42 @@ pub async fn do_mssql( if readonly_intent { let logs = format!("\nSetting ApplicationIntent to ReadOnly"); - append_logs(&job.id, &job.workspace_id, logs, db).await; + append_logs(&job.id, &job.workspace_id, logs, conn).await; } - // Using SQL Server authentication. - config.authentication(AuthMethod::sql_server(database.user, database.password)); - config.trust_cert(); // on production, it is not a good idea to do this + // Handle authentication based on available credentials + if let Some(token_value) = &database.aad_token { + if let Some(token) = &token_value.token { + config.authentication(AuthMethod::aad_token(token)); + } else { + return Err(Error::BadRequest( + "Invalid AAD token format - expected { token: string }".to_string(), + )); + } + } else if let (Some(user), Some(password)) = (&database.user, &database.password) { + config.authentication(AuthMethod::sql_server(user.clone(), password.clone())); + } else { + return Err(Error::BadRequest( + "Neither AAD token nor username/password credentials are set".to_string(), + )); + } + + // Handle certificate trust configuration + if database.trust_cert.unwrap_or(true) { + // If trust_cert is true, ignore ca_cert and trust any certificate + config.trust_cert(); + tracing::info!("MSSQL: disabling certificate validation"); + } else if let Some(ca_cert) = &database.ca_cert { + // Only use ca_cert if trust_cert is false + let cert_path = format!("{}/ca_cert.pem", job_dir); + + std::fs::write(&cert_path, ca_cert) + .map_err(|e| Error::ExecutionErr(format!("Failed to write CA certificate: {}", e)))?; + + // Use the CA certificate for trust + config.trust_cert_ca(cert_path); + tracing::info!("MSSQL: using provided CA certificate for trust"); + } let tcp = if use_instance_name { TcpStream::connect_named(&config).await.map_err(to_anyhow)? // named instance @@ -131,8 +177,14 @@ pub async fn do_mssql( .map_err(|x| Error::ExecutionErr(x.to_string()))? .args; + let (query, args_to_skip) = + &sanitize_and_interpolate_unsafe_sql_args(query, &sig, &mssql_args)?; + let mut prepared_query = Query::new(query.to_owned()); for arg in &sig { + if args_to_skip.contains(&arg.name) { + continue; + } let arg_t = arg.otyp.clone().unwrap_or_else(|| "string".to_string()); let arg_v = mssql_args .get(&arg.name) @@ -172,7 +224,7 @@ pub async fn do_mssql( let raw_result = run_future_with_polling_update_job_poller( job.id, job.timeout, - db, + conn, mem_peak, canceled_by, result_f, @@ -328,3 +380,15 @@ fn sql_to_json_value(val: ColumnData) -> Result { ), } } + +fn deserialize_aad_token<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let result = AadToken::deserialize(deserializer); + + match result { + Ok(token) if token.token.is_some() => Ok(Some(token)), + _ => Ok(None), + } +} diff --git a/backend/windmill-worker/src/mysql_executor.rs b/backend/windmill-worker/src/mysql_executor.rs index d733901f93..7148e7b961 100644 --- a/backend/windmill-worker/src/mysql_executor.rs +++ b/backend/windmill-worker/src/mysql_executor.rs @@ -6,25 +6,27 @@ use itertools::Itertools; use mysql_async::{ consts::ColumnType, prelude::*, FromValueError, OptsBuilder, Params, Row, SslOpts, }; +use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use serde_json::{json, value::RawValue, Value}; -use sqlx::types::Json; +use std::str::FromStr; use tokio::sync::Mutex; use windmill_common::{ error::{to_anyhow, Error}, - jobs::QueuedJob, - worker::to_raw_value, + worker::{to_raw_value, Connection}, }; use windmill_parser_sql::{ parse_db_resource, parse_mysql_sig, parse_sql_blocks, parse_sql_statement_named_params, RE_ARG_MYSQL_NAMED, }; use windmill_queue::CanceledBy; +use windmill_queue::MiniPulledJob; use crate::{ - common::{build_args_map, OccupancyMetrics}, + common::{build_args_values, OccupancyMetrics}, handle_child::run_future_with_polling_update_job_poller, - AuthedClientBackgroundTask, + sanitized_sql_params::sanitize_and_interpolate_unsafe_sql_args, + AuthedClient, }; #[derive(Deserialize)] @@ -103,46 +105,35 @@ pub fn do_mysql_inner<'a>( } pub async fn do_mysql( - job: &QueuedJob, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + client: &AuthedClient, query: &str, - db: &sqlx::Pool, + conn: &Connection, mem_peak: &mut i32, canceled_by: &mut Option, worker_name: &str, column_order: &mut Option>, occupancy_metrics: &mut OccupancyMetrics, ) -> windmill_common::error::Result> { - let args = build_args_map(job, client, db).await?.map(Json); - let job_args = if args.is_some() { - args.as_ref() - } else { - job.args.as_ref() - }; + let job_args = build_args_values(job, client, conn).await?; let inline_db_res_path = parse_db_resource(&query); let db_arg = if let Some(inline_db_res_path) = inline_db_res_path { - let val = client - .get_authed() - .await - .get_resource_value_interpolated::( - &inline_db_res_path, - Some(job.id.to_string()), - ) - .await?; - - let as_raw = serde_json::from_value(val).map_err(|e| { - Error::internal_err(format!("Error while parsing inline resource: {e:#}")) - })?; - - Some(as_raw) + Some( + client + .get_resource_value_interpolated::( + &inline_db_res_path, + Some(job.id.to_string()), + ) + .await?, + ) } else { - job_args.and_then(|x| x.get("database").cloned()) + job_args.get("database").cloned() }; let database = if let Some(db) = db_arg { - serde_json::from_str::(db.get()) + serde_json::from_value::(db) .map_err(|e| Error::ExecutionErr(e.to_string()))? } else { return Err(Error::BadRequest("Missing database argument".to_string())); @@ -171,6 +162,8 @@ pub async fn do_mysql( .map_err(|x| Error::ExecutionErr(x.to_string()))? .args; + let (query, args_to_skip) = &sanitize_and_interpolate_unsafe_sql_args(query, &sig, &job_args)?; + let using_named_params = RE_ARG_MYSQL_NAMED.captures_iter(query).count() > 0; let mut statement_values: Params = match using_named_params { @@ -178,18 +171,17 @@ pub async fn do_mysql( false => Params::Positional(vec![]), }; for arg in &sig { + if args_to_skip.contains(&arg.name) { + continue; + } let arg_t = arg.otyp.clone().unwrap_or_else(|| "text".to_string()); let arg_n = arg.name.clone(); let mysql_v = match job_args - .and_then(|x| { - x.get(arg.name.as_str()) - .map(|x| serde_json::from_str::(x.get()).ok()) - }) - .flatten() - .unwrap_or_else(|| json!(null)) + .get(arg.name.as_str()) + .unwrap_or_else(|| &json!(null)) { Value::Null => mysql_async::Value::NULL, - Value::Bool(b) => mysql_async::Value::Int(if b { 1 } else { 0 }), + Value::Bool(b) => mysql_async::Value::Int(if *b { 1 } else { 0 }), Value::String(s) if arg_t == "timestamp" || arg_t == "datetime" @@ -244,8 +236,8 @@ pub async fn do_mysql( } let pool = mysql_async::Pool::new(opts); - let conn = pool.get_conn().await.map_err(to_anyhow)?; - let conn_a = Arc::new(Mutex::new(conn)); + let mysql_conn = pool.get_conn().await.map_err(to_anyhow)?; + let conn_a = Arc::new(Mutex::new(mysql_conn)); let queries = parse_sql_blocks(query); @@ -291,7 +283,7 @@ pub async fn do_mysql( let result = run_future_with_polling_update_job_poller( job.id, job.timeout, - db, + conn, mem_peak, canceled_by, result_f, @@ -313,26 +305,38 @@ pub async fn do_mysql( return Ok(raw_result); } +// 2023-12-01T16:18:00.000Z +static DATE_REGEX_TZ: Lazy = Lazy::new(|| { + regex::Regex::new(r"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d+)Z").unwrap() +}); +// 2025-04-21 10:08:00 +static DATE_REGEX: Lazy = + Lazy::new(|| regex::Regex::new(r"(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})").unwrap()); + fn string_date_to_mysql_date(s: &str) -> mysql_async::Value { - // 2023-12-01T16:18:00.000Z - let re = regex::Regex::new(r"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d+)Z").unwrap(); - let caps = re.captures(s); + let caps = DATE_REGEX_TZ.captures(s).or_else(|| DATE_REGEX.captures(s)); if let Some(caps) = caps { mysql_async::Value::Date( - caps.get(1).unwrap().as_str().parse().unwrap_or_default(), - caps.get(2).unwrap().as_str().parse().unwrap_or_default(), - caps.get(3).unwrap().as_str().parse().unwrap_or_default(), - caps.get(4).unwrap().as_str().parse().unwrap_or_default(), - caps.get(5).unwrap().as_str().parse().unwrap_or_default(), - caps.get(6).unwrap().as_str().parse().unwrap_or_default(), - caps.get(7).unwrap().as_str().parse().unwrap_or_default(), + get_capture_by_index(&caps, 1), + get_capture_by_index(&caps, 2), + get_capture_by_index(&caps, 3), + get_capture_by_index(&caps, 4), + get_capture_by_index(&caps, 5), + get_capture_by_index(&caps, 6), + get_capture_by_index(&caps, 7), ) } else { mysql_async::Value::Date(0, 0, 0, 0, 0, 0, 0) } } +fn get_capture_by_index(caps: ®ex::Captures, n: usize) -> T { + caps.get(n) + .and_then(|s| s.as_str().parse::().ok()) + .unwrap_or_default() +} + fn convert_row_to_value(row: Row) -> serde_json::Value { let mut map = serde_json::Map::new(); diff --git a/backend/windmill-worker/src/nu_executor.rs b/backend/windmill-worker/src/nu_executor.rs new file mode 100644 index 0000000000..b2f6590c41 --- /dev/null +++ b/backend/windmill-worker/src/nu_executor.rs @@ -0,0 +1,361 @@ +use std::{collections::HashMap, process::Stdio}; + +use itertools::Itertools; +use serde_json::value::RawValue; +use tokio::{fs::File, io::AsyncWriteExt, process::Command}; +use windmill_common::{ + error::Error, + worker::{write_file, Connection}, +}; +use windmill_parser::Arg; +use windmill_parser_nu::parse_nu_signature; +use windmill_queue::{append_logs, CanceledBy, MiniPulledJob}; + +use crate::{ + common::{ + create_args_and_out_file, get_reserved_variables, read_result, start_child_process, + OccupancyMetrics, + }, + handle_child, AuthedClient, DISABLE_NSJAIL, DISABLE_NUSER, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, +}; + +const NSJAIL_CONFIG_RUN_NU_CONTENT: &str = include_str!("../nsjail/run.nu.config.proto"); +lazy_static::lazy_static! { + static ref NU_PATH: String = std::env::var("NU_PATH").unwrap_or_else(|_| "/usr/bin/nu".to_string()); + // TODO(v1): + // static ref PLUGIN_USE_RE: Regex = Regex::new(r#"(?:plugin use )(?.*)"#).unwrap(); +} + +// TODO: Can be generalized and used for other handlers +#[allow(dead_code)] +pub(crate) struct JobHandlerInput<'a> { + pub base_internal_url: &'a str, + pub canceled_by: &'a mut Option, + pub client: &'a AuthedClient, + pub parent_runnable_path: Option, + pub conn: &'a Connection, + pub envs: HashMap, + pub inner_content: &'a str, + pub job: &'a MiniPulledJob, + pub job_dir: &'a str, + pub mem_peak: &'a mut i32, + pub occupancy_metrics: &'a mut OccupancyMetrics, + pub requirements_o: Option<&'a String>, + pub shared_mount: &'a str, + pub worker_name: &'a str, +} + +pub async fn handle_nu_job<'a>(mut args: JobHandlerInput<'a>) -> Result, Error> { + // TODO(v1): + // --- Handle plugins --- + // let plugins = get_plugins(&mut args).await?; + // TODO(v1): + // --- Handle imports --- + // TODO(v1): + // --- Handle relative --- + // --- Wrap and write to fs --- + { + create_args_and_out_file(&args.client, args.job, args.job_dir, args.conn).await?; + File::create(format!("{}/main.nu", args.job_dir)) + .await? + .write_all(&wrap(args.inner_content)?.into_bytes()) + .await?; + } + // --- Execute --- + { + run(&mut args).await?; + } + // --- Retrieve results --- + { + read_result(&args.job_dir).await + } +} + +// async fn get_plugins<'a>( +// JobHandlerInput { +// occupancy_metrics, +// mem_peak, +// canceled_by, +// worker_name, +// job, +// db, +// inner_content, +// .. +// }: &mut JobHandlerInput<'a>, +// ) -> Result, Error> { +// let plugins_dir = concatcp!(NU_CACHE_DIR, "/plugins"); +// let nu_version = from_utf8_mut( +// Command::new(NU_PATH.as_str()) +// .arg("--version") +// .output() +// .await? +// .stdout +// .as_mut_slice(), +// ) +// .map_err(|e| windmill_common::error::Error::ExecutionErr(e.to_string()))? +// .to_owned(); + +// let plugins = parse_plugin_use(inner_content); + +// for plugin in &plugins { +// let mut run_cmd = Command::new(CARGO_PATH.as_str()); +// // cargo install nu_plugin_query --version (nu --version); plugin add ~/.cargo/bin/nu_plugin_query +// run_cmd +// // TODO: make it work with env_clear +// // .env_clear() +// .args(&[ +// "install", +// "--root", +// plugins_dir, +// "--locked", +// &format!("nu_plugin_{plugin}"), +// "--version", +// &nu_version, +// ]) +// .stdout(Stdio::piped()) +// .stderr(Stdio::piped()); + +// #[cfg(windows)] +// nsjail_cmd.env("SystemRoot", SYSTEM_ROOT.as_str()); +// let child = start_child_process(run_cmd, "cargo").await?; +// // handle_child::handle_child( +// // &job.id, +// // db, +// // mem_peak, +// // canceled_by, +// // child, +// // !*DISABLE_NSJAIL, +// // worker_name, +// // &job.workspace_id, +// // "cargo", +// // job.timeout, +// // false, +// // &mut Some(occupancy_metrics), +// // ) +// // .await?; +// } +// Ok(plugins) +// } + +// fn parse_plugin_use(inner_content: &str) -> Vec<&str> { +// let mut plugins = vec![]; +// // TODO: Ignore plugins with # in the beginning +// for cap in PLUGIN_USE_RE.captures_iter(inner_content).into_iter() { +// if let Some(mat) = cap.name("plugin") { +// plugins.push(mat.as_str()); +// } +// } +// plugins +// } + +/// Wraps content script +/// that upon execution reads args.json (which are piped and transformed from previous flow step or top level inputs) +/// Also wrapper takes output of program and serializes to result.json (Which windmill will know how to use later) +fn wrap(inner_content: &str) -> Result { + let sig = parse_nu_signature(inner_content)?; + let spread = sig + .args + .clone() + .into_iter() + .map(|Arg { name, typ, has_default, .. }| { + // Apply additional input transformation + let transformation = format!( + "| if $in != null {{ {} }} else {{ $in }}", + match typ { + // JSON converts X.0 to X and nu can't coerce type automatically + windmill_parser::Typ::Datetime => "into datetime", + windmill_parser::Typ::Bytes => "into binary", + windmill_parser::Typ::Float => "into float", + // Ident + _ => "$in", + } + ); + let nullguard = if has_default || matches!(typ, windmill_parser::Typ::Unknown) { + "".to_owned() + } else { + format!("| nullguard {name}") + }; + format!("\n\t\t\t($parsed_args.{name}? {nullguard} {transformation}) ",) + }) + .collect_vec() + .join(" "); + Ok( + r#" +$env.config.table.mode = 'basic' + +def nullguard [ name: string ] { + if ($in == null) { + panic $"argument `($name)` of main function can't be null" + } + $in +} + +# TODO: Probably needs rework in order for LSP to work +def get_variable [ pat ] { + let addr = $"($env.BASE_INTERNAL_URL)/api/w/($env.WM_WORKSPACE)/variables/get_value/($pat)" ; + http get -H ["Authorization", $"Bearer ($env.WM_TOKEN)"] $addr | return $in +} +def get_resource [ pat ] { + let addr = $"($env.BASE_INTERNAL_URL)/api/w/($env.WM_WORKSPACE)/resources/get_value_interpolated/($pat)" ; + http get -H ["Authorization", $"Bearer ($env.WM_TOKEN)"] $addr | return $in +} + +def 'main --wrapped' [] { + let parsed_args = open args.json + (main SPREAD + ) | to json | save -f result.json +} + +INNER_CONTENT + "# + .replace("INNER_CONTENT", inner_content) + .replace("SPREAD", &spread), // .replace("TRANSFORM", transform) + ) +} + +async fn run<'a>( + JobHandlerInput { + occupancy_metrics, + mem_peak, + canceled_by, + worker_name, + job, + conn, + job_dir, + shared_mount, + client, + parent_runnable_path, + envs, + base_internal_url, + .. + }: &mut JobHandlerInput<'a>, + // plugins: Vec<&'a str>, +) -> Result<(), Error> { + let reserved_variables = + get_reserved_variables(job, &client.token, conn, parent_runnable_path.clone()).await?; + let child = if !cfg!(windows) && !*DISABLE_NSJAIL { + append_logs( + &job.id, + &job.workspace_id, + format!("\n\n--- ISOLATED NU CODE EXECUTION ---\n"), + conn, + ) + .await; + + write_file( + job_dir, + "run.config.proto", + &NSJAIL_CONFIG_RUN_NU_CONTENT + .replace("{JOB_DIR}", job_dir) + .replace("{NU_PATH}", &NU_PATH) + .replace("{SHARED_MOUNT}", &shared_mount) + .replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string()), + )?; + let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str()); + nsjail_cmd + .env_clear() + .current_dir(job_dir) + .env("PATH", PATH_ENV.as_str()) + .env("BASE_INTERNAL_URL", base_internal_url) + .envs(envs) + .envs(reserved_variables) + .envs(PROXY_ENVS.clone()) + .args(vec![ + "--config", + "run.config.proto", + "--", + NU_PATH.as_str(), + "/tmp/main.nu", + "--wrapped", + ]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + start_child_process(nsjail_cmd, NSJAIL_PATH.as_str()).await? + } else { + append_logs( + &job.id, + &job.workspace_id, + format!("\n\n--- NU CODE EXECUTION ---\n"), + &conn, + ) + .await; + + // let plugin_registry = format!("{job_dir}/plugin-registry"); + // File::create(&plugin_registry).await?; + // + let mut cmd = Command::new(if cfg!(windows) { + "nu" + } else { + NU_PATH.as_str() + }); + cmd.env_clear() + .current_dir(job_dir.to_owned()) + .env("PATH", PATH_ENV.as_str()) + .env("BASE_INTERNAL_URL", base_internal_url) + .envs(envs) + .envs(reserved_variables) + .envs(PROXY_ENVS.clone()) + .args(&[ + "main.nu", + "--wrapped", + // TODO(v1): + // "--plugins", + // &format!( + // "[{}]", + // plugins + // .into_iter() + // .map(|pl| format!("{NU_CACHE_DIR}/plugins/bin/nu_plugin_{pl}")) + // .collect_vec() + // .join(",") + // ), + ]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + #[cfg(windows)] + { + cmd.env("SystemRoot", crate::SYSTEM_ROOT.as_str()) + .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()) + .env( + "TMP", + std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), + ); + } + start_child_process(cmd, "nu").await? + }; + handle_child::handle_child( + &job.id, + conn, + mem_peak, + canceled_by, + child, + !*DISABLE_NSJAIL, + worker_name, + &job.workspace_id, + "nu", + job.timeout, + false, + &mut Some(occupancy_metrics), + None, + ) + .await +} +// #[cfg(test)] +// mod test { +// use super::parse_plugin_use; + +// #[test] +// fn test_nu_plugin_use() { +// let content = r#" +// plugin use foo +// plugin use bar +// plugin use baz +// plugin use meh +// "#; +// assert_eq!( +// vec!["foo", "bar", "baz", "meh"], // +// parse_plugin_use(content) +// ); +// } +// } diff --git a/backend/windmill-worker/src/oracledb_executor.rs b/backend/windmill-worker/src/oracledb_executor.rs index e6292181b2..7c63e02c81 100644 --- a/backend/windmill-worker/src/oracledb_executor.rs +++ b/backend/windmill-worker/src/oracledb_executor.rs @@ -8,21 +8,22 @@ use itertools::Itertools; use oracle::sql_type::{InnerValue, OracleType, ToSql}; use serde::{Deserialize, Serialize}; use serde_json::{json, value::RawValue, Value}; -use sqlx::types::Json; use windmill_common::{ error::{to_anyhow, Error}, - jobs::QueuedJob, - worker::to_raw_value, + worker::{to_raw_value, Connection}, }; +use windmill_queue::MiniPulledJob; + use windmill_parser_sql::{ parse_db_resource, parse_oracledb_sig, parse_sql_blocks, parse_sql_statement_named_params, }; use windmill_queue::CanceledBy; use crate::{ - common::{build_args_map, check_executor_binary_exists, OccupancyMetrics}, + common::{build_args_values, check_executor_binary_exists, OccupancyMetrics}, handle_child::run_future_with_polling_update_job_poller, - AuthedClientBackgroundTask, + sanitized_sql_params::sanitize_and_interpolate_unsafe_sql_args, + AuthedClient, }; #[derive(Deserialize)] @@ -220,24 +221,24 @@ fn convert_oracledb_value_to_json(v: &oracle::SqlValue, c: &OracleType) -> serde fn get_statement_values( sig: Vec, - job_args: Option<&Json>>>, + job_args: &HashMap, + args_to_skip: &Vec, ) -> (Vec<(String, Box)>, Vec) { let mut statement_values = vec![]; let mut errors = vec![]; for arg in &sig { + if args_to_skip.contains(&arg.name) { + continue; + } let arg_t = arg.otyp.clone().unwrap_or_else(|| "text".to_string()); let arg_n = arg.name.clone(); let oracle_v: Box = match job_args - .and_then(|x| { - x.get(arg.name.as_str()) - .map(|x| serde_json::from_str::(x.get()).ok()) - }) - .flatten() - .unwrap_or_else(|| json!(null)) + .get(arg.name.as_str()) + .unwrap_or_else(|| &json!(null)) { // Value::Null => todo!(), - Value::Bool(b) => Box::new(b), + Value::Bool(b) => Box::new(*b), Value::String(s) if arg_t == "timestamp" || arg_t == "datetime" @@ -247,10 +248,10 @@ fn get_statement_values( if let Ok(d) = chrono::DateTime::::from_str(s.as_str()) { Box::new(d) } else { - Box::new(s) + Box::new(s.clone()) } } - Value::String(s) => Box::new(s), + Value::String(s) => Box::new(s.clone()), Value::Number(n) if n.is_i64() && (arg_t == "int" @@ -292,10 +293,10 @@ fn get_statement_values( } pub async fn do_oracledb( - job: &QueuedJob, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + client: &AuthedClient, query: &str, - db: &sqlx::Pool, + conn: &Connection, mem_peak: &mut i32, canceled_by: &mut Option, worker_name: &str, @@ -308,36 +309,25 @@ pub async fn do_oracledb( "Oracle Database", )?; - let args = build_args_map(job, client, db).await?.map(Json); - let job_args = if args.is_some() { - args.as_ref() - } else { - job.args.as_ref() - }; + let job_args = build_args_values(job, client, conn).await?; let inline_db_res_path = parse_db_resource(&query); let db_arg = if let Some(inline_db_res_path) = inline_db_res_path { - let val = client - .get_authed() - .await - .get_resource_value_interpolated::( - &inline_db_res_path, - Some(job.id.to_string()), - ) - .await?; - - let as_raw = serde_json::from_value(val).map_err(|e| { - Error::internal_err(format!("Error while parsing inline resource: {e:#}")) - })?; - - Some(as_raw) + Some( + client + .get_resource_value_interpolated::( + &inline_db_res_path, + Some(job.id.to_string()), + ) + .await?, + ) } else { - job_args.and_then(|x| x.get("database").cloned()) + job_args.get("database").cloned() }; let database = if let Some(db) = db_arg { - serde_json::from_str::(db.get()) + serde_json::from_value::(db) .map_err(|e| Error::ExecutionErr(e.to_string()))? } else { return Err(Error::BadRequest("Missing database argument".to_string())); @@ -349,7 +339,9 @@ pub async fn do_oracledb( .map_err(|x| Error::ExecutionErr(x.to_string()))? .args; - let (statement_values, errors) = get_statement_values(sig.clone(), job_args); + let (query, args_to_skip) = sanitize_and_interpolate_unsafe_sql_args(query, &sig, &job_args)?; + + let (statement_values, errors) = get_statement_values(sig.clone(), &job_args, &args_to_skip); if !errors.is_empty() { return Err(Error::ExecutionErr(errors.join("\n"))); @@ -362,22 +354,22 @@ pub async fn do_oracledb( .init(); } - let conn = tokio::task::spawn_blocking(|| { + let oracle_conn = tokio::task::spawn_blocking(|| { oracle::Connection::connect(database.user, database.password, database.database) .map_err(|e| Error::ExecutionErr(e.to_string())) }) .await .map_err(to_anyhow)??; - let conn_a = Arc::new(std::sync::Mutex::new(conn)); + let conn_a = Arc::new(std::sync::Mutex::new(oracle_conn)); - let queries = parse_sql_blocks(query); + let queries = parse_sql_blocks(&query); let result_f = if queries.len() > 1 { let f = async { let mut res: Vec> = vec![]; for (i, q) in queries.iter().enumerate() { - let (vals, _) = get_statement_values(sig.clone(), job_args); + let (vals, _) = get_statement_values(sig.clone(), &job_args, &args_to_skip); let r = do_oracledb_inner( q, vals, @@ -398,13 +390,13 @@ pub async fn do_oracledb( f.boxed() } else { - do_oracledb_inner(query, statement_values, conn_a, Some(column_order), false)? + do_oracledb_inner(&query, statement_values, conn_a, Some(column_order), false)? }; let result = run_future_with_polling_update_job_poller( job.id, job.timeout, - db, + conn, mem_peak, canceled_by, result_f, diff --git a/backend/windmill-worker/src/pg_executor.rs b/backend/windmill-worker/src/pg_executor.rs index 2ad6eb4038..79002e4d1f 100644 --- a/backend/windmill-worker/src/pg_executor.rs +++ b/backend/windmill-worker/src/pg_executor.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use std::net::IpAddr; -use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Duration; @@ -17,7 +17,7 @@ use serde::Deserialize; use serde_json::value::RawValue; use serde_json::Map; use serde_json::Value; -use tokio::sync::Mutex; +use tokio::sync::{Mutex, RwLock}; use tokio_postgres::Client; use tokio_postgres::{types::ToSql, NoTls, Row}; use tokio_postgres::{ @@ -25,18 +25,19 @@ use tokio_postgres::{ Column, }; use uuid::Uuid; +use windmill_common::error::to_anyhow; use windmill_common::error::{self, Error}; -use windmill_common::worker::{to_raw_value, CLOUD_HOSTED}; -use windmill_common::{error::to_anyhow, jobs::QueuedJob}; +use windmill_common::worker::{to_raw_value, Connection, CLOUD_HOSTED}; use windmill_parser::{Arg, Typ}; use windmill_parser_sql::{ parse_db_resource, parse_pg_statement_arg_indices, parse_pgsql_sig, parse_sql_blocks, }; -use windmill_queue::CanceledBy; +use windmill_queue::{CanceledBy, MiniPulledJob}; use crate::common::{build_args_values, sizeof_val, OccupancyMetrics}; use crate::handle_child::run_future_with_polling_update_job_poller; -use crate::{AuthedClientBackgroundTask, MAX_RESULT_SIZE}; +use crate::sanitized_sql_params::sanitize_and_interpolate_unsafe_sql_args; +use crate::{AuthedClient, MAX_RESULT_SIZE}; use bytes::Buf; use lazy_static::lazy_static; use urlencoding::encode; @@ -55,8 +56,9 @@ struct PgDatabase { lazy_static! { pub static ref CONNECTION_CACHE: Arc>> = Arc::new(Mutex::new(None)); + pub static ref CONNECTION_COUNTER: Arc>> = + Arc::new(RwLock::new(HashMap::new())); pub static ref LAST_QUERY: AtomicU64 = AtomicU64::new(0); - pub static ref RUNNING: AtomicBool = AtomicBool::new(false); } fn do_postgresql_inner<'a>( @@ -156,25 +158,23 @@ fn do_postgresql_inner<'a>( } pub async fn do_postgresql( - job: &QueuedJob, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + client: &AuthedClient, query: &str, - db: &sqlx::Pool, + conn: &Connection, mem_peak: &mut i32, canceled_by: &mut Option, worker_name: &str, column_order: &mut Option>, occupancy_metrics: &mut OccupancyMetrics, ) -> error::Result> { - let pg_args = build_args_values(job, client, db).await?; + let pg_args = build_args_values(job, client, conn).await?; let inline_db_res_path = parse_db_resource(&query); let db_arg = if let Some(inline_db_res_path) = inline_db_res_path { Some( client - .get_authed() - .await .get_resource_value_interpolated::( &inline_db_res_path, Some(job.id.to_string()), @@ -211,14 +211,10 @@ pub async fn do_postgresql( ); let database_string_clone = database_string.clone(); - RUNNING.store(true, std::sync::atomic::Ordering::Relaxed); - LAST_QUERY.store( - chrono::Utc::now().timestamp().try_into().unwrap_or(0), - std::sync::atomic::Ordering::Relaxed, - ); let mtex; if !*CLOUD_HOSTED { - mtex = Some(CONNECTION_CACHE.lock().await); + mtex = CONNECTION_CACHE.try_lock().ok(); + increment_connection_counter(&database_string).await; } else { mtex = None; } @@ -226,9 +222,15 @@ pub async fn do_postgresql( let has_cached_con = mtex .as_ref() .is_some_and(|x| x.as_ref().is_some_and(|y| y.0 == database_string)); - let new_client = if has_cached_con { + + // tracing::error!("HAS CACHED CON: {}", has_cached_con); + let (new_client, mtex) = if has_cached_con { tracing::info!("Using cached connection"); - None + LAST_QUERY.store( + chrono::Utc::now().timestamp().try_into().unwrap_or(0), + std::sync::atomic::Ordering::Relaxed, + ); + (None, mtex) } else if sslmode == "require" { tracing::info!("Creating new connection"); let mut connector = TlsConnector::builder(); @@ -266,7 +268,7 @@ pub async fn do_postgresql( tracing::error!("connection error: {}", e); } }); - Some((client, handle)) + (Some((client, handle)), None) } else { tracing::info!("Creating new connection"); let (client, connection) = tokio::time::timeout( @@ -284,9 +286,13 @@ pub async fn do_postgresql( tracing::error!("connection error: {}", e); } }); - Some((client, handle)) + (Some((client, handle)), None) }; + let sig = parse_pgsql_sig(&query).map_err(|x| Error::ExecutionErr(x.to_string()))?; + + let (query, _) = &sanitize_and_interpolate_unsafe_sql_args(query, &sig.args, &pg_args)?; + let queries = parse_sql_blocks(query); let (client, handle) = if let Some((client, handle)) = new_client.as_ref() { @@ -296,7 +302,6 @@ pub async fn do_postgresql( (client, None) }; - let sig = parse_pgsql_sig(&query).map_err(|x| Error::ExecutionErr(x.to_string()))?; let param_idx_to_arg_and_value = sig .args .iter() @@ -348,7 +353,7 @@ pub async fn do_postgresql( let result = run_future_with_polling_update_job_poller( job.id, job.timeout, - db, + conn, mem_peak, canceled_by, result_f, @@ -359,61 +364,96 @@ pub async fn do_postgresql( ) .await?; + // drop the mtex to avoid holding the lock for too long, result has been returned + drop(mtex); + *mem_peak = size.load(Ordering::Relaxed) as i32; - RUNNING.store(false, std::sync::atomic::Ordering::Relaxed); - if let Some(handle) = handle { - if let Some(mut mtex) = mtex { - let abort_handler = handle.abort_handle(); + if !*CLOUD_HOSTED { + // tracing::error!("Found handle"); + if let Ok(mut mtex) = CONNECTION_CACHE.try_lock() { + if mtex.as_ref().is_none_or(|x| x.0 != database_string) { + // tracing::error!("Locked conn cached"); + let abort_handler = handle.abort_handle(); - if let Some(new_client) = new_client { - *mtex = Some((database_string, new_client.0)); - } - drop(mtex); - LAST_QUERY.store( - chrono::Utc::now().timestamp().try_into().unwrap_or(0), - std::sync::atomic::Ordering::Relaxed, - ); - - tokio::spawn(async move { - loop { - tokio::time::sleep(Duration::from_secs(5)).await; - let last_query = LAST_QUERY.load(std::sync::atomic::Ordering::Relaxed); - let now = chrono::Utc::now().timestamp().try_into().unwrap_or(0); - - //we cache connection for 5 minutes at most - if last_query + 60 * 5 < now - && !RUNNING.load(std::sync::atomic::Ordering::Relaxed) - { - tracing::info!("Closing cache connection due to inactivity"); - break; - } - let mtex = CONNECTION_CACHE.lock().await; - if mtex.is_none() { - // connection is not in the mutex anymore - break; - } else if let Some(mtex) = mtex.as_ref() { - if mtex.0.as_str() != &database_string_clone { - // connection is not the latest one - break; + let mut cache_new_con = false; + if let Some(new_client) = new_client { + cache_new_con = is_most_used_conn(&database_string).await; + if cache_new_con { + *mtex = Some((database_string, new_client.0)); + } else { + new_client.1.abort(); } + } else { + handle.abort(); } - tracing::debug!("Keeping cached connection alive due to activity") + if cache_new_con { + LAST_QUERY.store( + chrono::Utc::now().timestamp().try_into().unwrap_or(0), + std::sync::atomic::Ordering::Relaxed, + ); + tokio::spawn(async move { + loop { + tokio::time::sleep(Duration::from_secs(5)).await; + let last_query = + LAST_QUERY.load(std::sync::atomic::Ordering::Relaxed); + let now = chrono::Utc::now().timestamp().try_into().unwrap_or(0); + + //we cache connection for 5 minutes at most + if last_query + 60 * 1 < now { + // tracing::error!("Closing cache connection due to inactivity"); + tracing::info!( + "Closing cache pg executor connection due to inactivity" + ); + break; + } + let mtex = CONNECTION_CACHE.lock().await; + if mtex.is_none() { + // connection is not in the mutex anymore + break; + } else if let Some(mtex) = mtex.as_ref() { + if mtex.0.as_str() != &database_string_clone { + // connection is not the latest one + break; + } + } + + tracing::debug!( + "Keeping cached pg executor connection alive due to activity" + ) + } + let mut mtex = CONNECTION_CACHE.lock().await; + *mtex = None; + abort_handler.abort(); + }); + } + } else { + handle.abort(); } - let mut mtex = CONNECTION_CACHE.lock().await; - *mtex = None; - abort_handler.abort(); - }); + } else { + handle.abort(); + } } else { handle.abort(); } } - let raw_result = to_raw_value(&result); - *mem_peak = (raw_result.get().len() / 1000) as i32; + *mem_peak = (result.get().len() / 1000) as i32; // And then check that we got back the same string we sent over. - return Ok(raw_result); + return Ok(result); +} + +async fn is_most_used_conn(database_string: &str) -> bool { + let counter_map = CONNECTION_COUNTER.read().await; + let current_count = counter_map.get(database_string).copied().unwrap_or(0); + let max_count = counter_map.values().copied().max().unwrap_or(0); + current_count >= max_count +} + +async fn increment_connection_counter(database_string: &str) { + let mut counter_map = CONNECTION_COUNTER.write().await; + *counter_map.entry(database_string.to_string()).or_insert(0) += 1; } fn map_as_single_type( @@ -766,6 +806,7 @@ pub fn pg_cell_to_json_value( Type::BYTEA_ARRAY => get_array(row, column, column_i, |a: Vec| { Ok(JSONValue::String(format!("\\x{}", hex::encode(a)))) })?, + Type::VOID => JSONValue::Null, _ => get_basic(row, column, column_i, |a: String| Ok(JSONValue::String(a)))?, }) } diff --git a/backend/windmill-worker/src/php_executor.rs b/backend/windmill-worker/src/php_executor.rs index 4f579a54a8..ac50beb99f 100644 --- a/backend/windmill-worker/src/php_executor.rs +++ b/backend/windmill-worker/src/php_executor.rs @@ -7,9 +7,10 @@ use tokio::{fs::File, io::AsyncReadExt, process::Command}; use uuid::Uuid; use windmill_common::{ error::{self, to_anyhow, Result}, - jobs::QueuedJob, - worker::write_file, + worker::{write_file, Connection}, }; +use windmill_queue::MiniPulledJob; + use windmill_parser::Typ; use windmill_queue::{append_logs, CanceledBy}; @@ -19,8 +20,8 @@ use crate::{ read_result, start_child_process, OccupancyMetrics, }, handle_child::handle_child, - AuthedClientBackgroundTask, COMPOSER_CACHE_DIR, COMPOSER_PATH, DISABLE_NSJAIL, DISABLE_NUSER, - NSJAIL_PATH, PHP_PATH, + AuthedClient, COMPOSER_CACHE_DIR, COMPOSER_PATH, DISABLE_NSJAIL, DISABLE_NUSER, NSJAIL_PATH, + PHP_PATH, }; const NSJAIL_CONFIG_RUN_PHP_CONTENT: &str = include_str!("../nsjail/run.php.config.proto"); @@ -66,7 +67,7 @@ pub async fn composer_install( canceled_by: &mut Option, job_id: &Uuid, w_id: &str, - db: &sqlx::Pool, + conn: &Connection, job_dir: &str, worker_name: &str, requirements: String, @@ -93,7 +94,7 @@ pub async fn composer_install( handle_child( job_id, - db, + conn, mem_peak, canceled_by, child_process, @@ -104,6 +105,7 @@ pub async fn composer_install( None, false, &mut Some(occupancy_metrics), + None, ) .await?; @@ -136,9 +138,10 @@ pub async fn handle_php_job( requirements_o: Option<&String>, mem_peak: &mut i32, canceled_by: &mut Option, - job: &QueuedJob, - db: &sqlx::Pool, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + conn: &Connection, + client: &AuthedClient, + parent_runnable_path: Option, job_dir: &str, inner_content: &String, base_internal_url: &str, @@ -164,14 +167,14 @@ pub async fn handle_php_job( let autoload_line = if let Some(composer_json) = composer_json { let logs1 = "\n\n--- COMPOSER INSTALL ---\n".to_string(); - append_logs(&job.id, &job.workspace_id, logs1, db).await; + append_logs(&job.id, &job.workspace_id, logs1, conn).await; composer_install( mem_peak, canceled_by, &job.id, &job.workspace_id, - db, + conn, job_dir, worker_name, composer_json, @@ -186,7 +189,7 @@ pub async fn handle_php_job( let init_logs = "\n\n--- PHP CODE EXECUTION ---\n".to_string(); - append_logs(&job.id, job.workspace_id.to_string(), init_logs, db).await; + append_logs(&job.id, job.workspace_id.to_string(), init_logs, conn).await; let _ = write_file(job_dir, "main.php", inner_content)?; @@ -260,12 +263,13 @@ try {{ let reserved_variables_args_out_f = async { let args_and_out_f = async { - create_args_and_out_file(&client, job, job_dir, db).await?; + create_args_and_out_file(&client, job, job_dir, conn).await?; Ok(()) as Result<()> }; let reserved_variables_f = async { - let client = client.get_authed().await; - let vars = get_reserved_variables(job, &client.token, db).await?; + let vars = + get_reserved_variables(job, &client.token, conn, parent_runnable_path.clone()) + .await?; Ok(vars) as Result> }; let (_, reserved_variables) = tokio::try_join!(args_and_out_f, reserved_variables_f)?; @@ -326,7 +330,7 @@ try {{ handle_child( &job.id, - db, + conn, mem_peak, canceled_by, child, @@ -337,6 +341,7 @@ try {{ job.timeout, false, &mut Some(occupancy_metrics), + None, ) .await?; read_result(job_dir).await diff --git a/backend/windmill-worker/src/python_executor.rs b/backend/windmill-worker/src/python_executor.rs index d86c84dd0d..a776bbe7c7 100644 --- a/backend/windmill-worker/src/python_executor.rs +++ b/backend/windmill-worker/src/python_executor.rs @@ -10,7 +10,6 @@ use anyhow::anyhow; use itertools::Itertools; use regex::Regex; use serde_json::value::RawValue; -use sqlx::{Pool, Postgres}; use tokio::{ fs::{metadata, DirBuilder, File}, io::AsyncReadExt, @@ -26,21 +25,23 @@ use windmill_common::{ self, Error::{self}, }, - jobs::QueuedJob, utils::calculate_hash, - worker::{write_file, PythonAnnotations, WORKER_CONFIG}, - DB, + worker::{ + copy_dir_recursively, pad_string, write_file, Connection, PythonAnnotations, WORKER_CONFIG, + }, }; #[cfg(feature = "enterprise")] use windmill_common::variables::get_secret_value_as_admin; use std::env::var; -use windmill_queue::{append_logs, CanceledBy}; +use windmill_queue::{append_logs, CanceledBy, PrecomputedAgentInfo}; lazy_static::lazy_static! { - static ref PYTHON_PATH: String = - var("PYTHON_PATH").unwrap_or_else(|_| "/usr/local/bin/python3".to_string()); + static ref PYTHON_PATH: Option = var("PYTHON_PATH").ok().map(|v| { + tracing::warn!("PYTHON_PATH is set to {} and thus python will not be managed by uv and stay static regardless of annotation and instance settings. NOT RECOMMENDED", v); + v + }); static ref UV_PATH: String = var("UV_PATH").unwrap_or_else(|_| "/usr/local/bin/uv".to_string()); @@ -48,31 +49,19 @@ lazy_static::lazy_static! { static ref PY_CONCURRENT_DOWNLOADS: usize = var("PY_CONCURRENT_DOWNLOADS").ok().map(|flag| flag.parse().unwrap_or(20)).unwrap_or(20); - static ref FLOCK_PATH: String = - var("FLOCK_PATH").unwrap_or_else(|_| "/usr/bin/flock".to_string()); + static ref NON_ALPHANUM_CHAR: Regex = regex::Regex::new(r"[^0-9A-Za-z=.-]").unwrap(); static ref TRUSTED_HOST: Option = var("PY_TRUSTED_HOST").ok().or(var("PIP_TRUSTED_HOST").ok()); static ref INDEX_CERT: Option = var("PY_INDEX_CERT").ok().or(var("PIP_INDEX_CERT").ok()); static ref NATIVE_CERT: bool = var("PY_NATIVE_CERT").ok().or(var("UV_NATIVE_TLS").ok()).map(|flag| flag == "true").unwrap_or(false); - pub static ref USE_SYSTEM_PYTHON: bool = var("USE_SYSTEM_PYTHON") - .ok().map(|flag| flag == "true").unwrap_or(false); - - pub static ref USE_PIP_COMPILE: bool = var("USE_PIP_COMPILE") - .ok().map(|flag| flag == "true").unwrap_or(false); - - pub static ref USE_PIP_INSTALL: bool = var("USE_PIP_INSTALL") - .ok().map(|flag| flag == "true").unwrap_or(false); - static ref RELATIVE_IMPORT_REGEX: Regex = Regex::new(r#"(import|from)\s(((u|f)\.)|\.)"#).unwrap(); static ref EPHEMERAL_TOKEN_CMD: Option = var("EPHEMERAL_TOKEN_CMD").ok(); } const NSJAIL_CONFIG_DOWNLOAD_PY_CONTENT: &str = include_str!("../nsjail/download.py.config.proto"); -const NSJAIL_CONFIG_DOWNLOAD_PY_CONTENT_FALLBACK: &str = - include_str!("../nsjail/download.py.pip.config.proto"); const NSJAIL_CONFIG_RUN_PYTHON3_CONTENT: &str = include_str!("../nsjail/run.python3.config.proto"); const RELATIVE_PYTHON_LOADER: &str = include_str!("../loader.py"); @@ -88,9 +77,10 @@ use crate::{ start_child_process, OccupancyMetrics, }, handle_child::handle_child, - AuthedClientBackgroundTask, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, INSTANCE_PYTHON_VERSION, - LOCK_CACHE_DIR, NSJAIL_PATH, PATH_ENV, PIP_CACHE_DIR, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL, - PROXY_ENVS, PY_INSTALL_DIR, TZ_ENV, UV_CACHE_DIR, + worker_lockfiles::LOCKFILE_GENERATED_FROM_REQUIREMENTS_TXT, + worker_utils::ping_job_status, + AuthedClient, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, INSTANCE_PYTHON_VERSION, NSJAIL_PATH, + PATH_ENV, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL, PROXY_ENVS, PY_INSTALL_DIR, TZ_ENV, UV_CACHE_DIR, }; // To change latest stable version: @@ -107,19 +97,23 @@ pub enum PyVersion { } impl PyVersion { - pub async fn from_instance_version() -> Self { - match INSTANCE_PYTHON_VERSION.read().await.clone() { + pub async fn from_instance_version(job_id: &Uuid, w_id: &str, conn: &Connection) -> Self { + let mut err = None; + let pyv = match INSTANCE_PYTHON_VERSION.read().await.clone() { Some(v) => PyVersion::from_string_with_dots(&v).unwrap_or_else(|| { let v = PyVersion::default(); - tracing::error!( - "Cannot parse INSTANCE_PYTHON_VERSION ({:?}), fallback to latest_stable ({v:?})", - *INSTANCE_PYTHON_VERSION - ); + err = Some(format!("\nCannot parse INSTANCE_PYTHON_VERSION ({:?}), fallback to latest_stable ({v:?})", *INSTANCE_PYTHON_VERSION)); v }), // Use latest stable None => PyVersion::default(), + }; + + if let Some(msg) = err { + append_logs(job_id, w_id, &msg, conn).await; + tracing::error!(msg); } + pyv } /// e.g.: `/tmp/windmill/cache/python_3xy` pub fn to_cache_dir(&self) -> String { @@ -219,7 +213,7 @@ impl PyVersion { job_id: &Uuid, mem_peak: &mut i32, // canceled_by: &mut Option, - db: &Pool, + conn: &Connection, worker_name: &str, w_id: &str, occupancy_metrics: &mut Option<&mut OccupancyMetrics>, @@ -229,14 +223,23 @@ impl PyVersion { // } let res = self - .get_python_inner(job_id, mem_peak, db, worker_name, w_id, occupancy_metrics) + .get_python_inner(job_id, mem_peak, conn, worker_name, w_id, occupancy_metrics) .await; if let Err(ref e) = res { tracing::error!( - "worker_name: {worker_name}, w_id: {w_id}, job_id: {job_id}\n + "worker_name: {worker_name}, w_id: {w_id}, job_id: {job_id}\n Error while getting python from uv, falling back to system python: {e:?}" ); + append_logs( + job_id, + w_id, + format!( + "\nError while getting python from uv, falling back to system python: {e:?}" + ), + conn, + ) + .await; } res } @@ -245,7 +248,7 @@ impl PyVersion { job_id: &Uuid, mem_peak: &mut i32, // canceled_by: &mut Option, - db: &Pool, + conn: &Connection, worker_name: &str, w_id: &str, occupancy_metrics: &mut Option<&mut OccupancyMetrics>, @@ -256,7 +259,7 @@ impl PyVersion { if py_path.is_err() { // Install it if let Err(err) = self - .install_python(job_id, mem_peak, db, worker_name, w_id, occupancy_metrics) + .install_python(job_id, mem_peak, conn, worker_name, w_id, occupancy_metrics) .await { tracing::error!("Cannot install python: {err}"); @@ -282,13 +285,13 @@ impl PyVersion { job_id: &Uuid, mem_peak: &mut i32, // canceled_by: &mut Option, - db: &Pool, + conn: &Connection, worker_name: &str, w_id: &str, occupancy_metrics: &mut Option<&mut OccupancyMetrics>, ) -> error::Result<()> { let v = self.to_string_with_dot(); - append_logs(job_id, w_id, format!("\nINSTALLING PYTHON ({})", v), db).await; + append_logs(job_id, w_id, format!("\nINSTALLING PYTHON ({})", v), conn).await; // Create dirs for newly installed python // If we dont do this, NSJAIL will not be able to mount cache // For the default version directory created during startup (main.rs) @@ -311,6 +314,7 @@ impl PyVersion { .env_clear() .env("HOME", HOME_ENV.to_string()) .env("PATH", PATH_ENV.to_string()) + .envs(PROXY_ENVS.clone()) .args(["python", "install", v, "--python-preference=only-managed"]) // TODO: Do we need these? .envs([("UV_PYTHON_INSTALL_DIR", PY_INSTALL_DIR)]) @@ -325,15 +329,20 @@ impl PyVersion { .env( "TMP", std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), + ) + .env( + "LOCALAPPDATA", + std::env::var("LOCALAPPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Local", HOME_ENV.as_str())), ); } let child_process = start_child_process(child_cmd, "uv").await?; - append_logs(&job_id, &w_id, logs, db).await; + append_logs(&job_id, &w_id, logs, conn).await; handle_child( job_id, - db, + conn, mem_peak, &mut None, child_process, @@ -344,6 +353,7 @@ impl PyVersion { None, false, occupancy_metrics, + None, ) .await } @@ -356,6 +366,8 @@ impl PyVersion { let mut child_cmd = Command::new(uv_cmd); + child_cmd.env_clear(); + #[cfg(windows)] { child_cmd @@ -364,18 +376,23 @@ impl PyVersion { .env( "TMP", std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), + ) + .env( + "LOCALAPPDATA", + std::env::var("LOCALAPPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Local", HOME_ENV.as_str())), ); } let output = child_cmd // .current_dir(job_dir) - .env_clear() .env("HOME", HOME_ENV.to_string()) .env("PATH", PATH_ENV.to_string()) .args([ "python", "find", self.to_string_with_dot(), + "--system", "--python-preference=only-managed", ]) .envs([ @@ -444,15 +461,13 @@ pub async fn uv_pip_compile( mem_peak: &mut i32, canceled_by: &mut Option, job_dir: &str, - db: &Pool, + conn: &Connection, worker_name: &str, w_id: &str, occupancy_metrics: &mut Option<&mut OccupancyMetrics>, py_version: PyVersion, // Debug-only flag no_cache: bool, - // Fallback to pip-compile. Will be removed in future - mut no_uv: bool, ) -> error::Result { let mut logs = String::new(); logs.push_str(&format!("\nresolving dependencies...")); @@ -493,36 +508,27 @@ pub async fn uv_pip_compile( let requirements = format!("# py{}\n{}", py_version.to_string_no_dot(), requirements); #[cfg(feature = "enterprise")] - let requirements = replace_pip_secret(db, w_id, &requirements, worker_name, job_id).await?; + let requirements = replace_pip_secret(conn, w_id, &requirements, worker_name, job_id).await?; - let mut req_hash = format!("py-{}", calculate_hash(&requirements)); + let req_hash = format!("py-{}", calculate_hash(&requirements)); - if no_uv || *USE_PIP_COMPILE { - logs.push_str(&format!("\nFallback to pip-compile (Deprecated!)")); - // Set no_uv if not setted - no_uv = true; - // Make sure that if we put #no_uv (switch to pip-compile) to python code or used `USE_PIP_COMPILE=true` variable. - // Windmill will recalculate lockfile using pip-compile and dont take potentially broken lockfile (generated by uv) from cache (our db). - // It will recalculate lockfile even if inputs have not been changed. - req_hash.push_str("-no_uv"); - // Will be in format: - // py-000..000-no_uv - } if !no_cache { - if let Some(cached) = sqlx::query_scalar!( - "SELECT lockfile FROM pip_resolution_cache WHERE hash = $1", - // Python version is included in hash, - // hash will be the different for every python version - req_hash - ) - .fetch_optional(db) - .await? - { - logs.push_str(&format!( - "\nFound cached resolution: {req_hash}, on python version: {}", - py_version.to_string_with_dot() - )); - return Ok(cached); + if let Some(db) = conn.as_sql() { + if let Some(cached) = sqlx::query_scalar!( + "SELECT lockfile FROM pip_resolution_cache WHERE hash = $1", + // Python version is included in hash, + // hash will be the different for every python version + req_hash + ) + .fetch_optional(db) + .await? + { + logs.push_str(&format!( + "\nFound cached resolution: {req_hash}, on python version: {}", + py_version.to_string_with_dot() + )); + return Ok(cached); + } } } @@ -530,79 +536,10 @@ pub async fn uv_pip_compile( write_file(job_dir, file, &requirements)?; - // Fallback pip-compile. Will be removed in future - if no_uv { - tracing::debug!("Fallback to pip-compile"); - - let mut args = vec![ - "-q", - "--no-header", - file, - "--resolver=backtracking", - "--strip-extras", - ]; - let mut pip_args = vec![]; - let pip_extra_index_url = PIP_EXTRA_INDEX_URL - .read() - .await - .clone() - .map(handle_ephemeral_token); - if let Some(url) = pip_extra_index_url.as_ref() { - url.split(",").for_each(|url| { - args.extend(["--extra-index-url", url]); - pip_args.push(format!("--extra-index-url {}", url)); - }); - args.push("--no-emit-index-url"); - } - let pip_index_url = PIP_INDEX_URL - .read() - .await - .clone() - .map(handle_ephemeral_token); - if let Some(url) = pip_index_url.as_ref() { - args.extend(["--index-url", url, "--no-emit-index-url"]); - pip_args.push(format!("--index-url {}", url)); - } - if let Some(host) = TRUSTED_HOST.as_ref() { - args.extend(["--trusted-host", host]); - } - if let Some(cert_path) = INDEX_CERT.as_ref() { - args.extend(["--cert", cert_path]); - } - let pip_args_str = pip_args.join(" "); - if pip_args.len() > 0 { - args.extend(["--pip-args", &pip_args_str]); - } - tracing::debug!("pip-compile args: {:?}", args); - - let mut child_cmd = Command::new("pip-compile"); - child_cmd - .current_dir(job_dir) - .args(args) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); - let child_process = start_child_process(child_cmd, "pip-compile").await?; - append_logs(&job_id, &w_id, logs, db).await; - handle_child( - job_id, - db, - mem_peak, - canceled_by, - child_process, - false, - worker_name, - &w_id, - "pip-compile", - None, - false, - occupancy_metrics, - ) - .await - .map_err(|e| Error::ExecutionErr(format!("Lock file generation failed: {e:?}")))?; - } else { + { // Make sure we have python runtime installed py_version - .get_python(job_id, mem_peak, db, worker_name, w_id, occupancy_metrics) + .get_python(job_id, mem_peak, conn, worker_name, w_id, occupancy_metrics) .await?; let mut args = vec![ @@ -686,6 +623,11 @@ pub async fn uv_pip_compile( child_cmd .env("SystemRoot", SYSTEM_ROOT.as_str()) .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()) + .env( + "LOCALAPPDATA", + std::env::var("LOCALAPPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Local", HOME_ENV.as_str())), + ) .env( "TMP", std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), @@ -693,10 +635,10 @@ pub async fn uv_pip_compile( } let child_process = start_child_process(child_cmd, uv_cmd).await?; - append_logs(&job_id, &w_id, logs, db).await; + append_logs(&job_id, &w_id, logs, conn).await; handle_child( job_id, - db, + conn, mem_peak, canceled_by, child_process, @@ -708,6 +650,7 @@ pub async fn uv_pip_compile( None, false, occupancy_metrics, + None, ) .await .map_err(|e| { @@ -732,11 +675,13 @@ pub async fn uv_pip_compile( .collect::>() .join("\n") ); - sqlx::query!( + if let Some(db) = conn.as_sql() { + sqlx::query!( "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('3 days')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2", req_hash, lockfile ).fetch_optional(db).await?; + } Ok(lockfile) } @@ -771,8 +716,8 @@ pub async fn uv_pip_compile( async fn postinstall( additional_python_paths: &mut Vec, job_dir: &str, - job: &QueuedJob, - db: &sqlx::Pool, + job: &MiniPulledJob, + conn: &Connection, ) -> windmill_common::error::Result<()> { // It is guranteed that additional_python_paths only contains paths within windmill/cache/ // All other paths you would usually expect in PYTHONPATH are NOT included. These are added in downstream @@ -833,7 +778,7 @@ async fn postinstall( &job.id, &job.workspace_id, "\n\nCopying some packages from cache to job_dir...\n".to_string(), - db, + conn, ) .await; // Remove PATHs we just moved @@ -844,28 +789,35 @@ async fn postinstall( Ok(()) } -fn copy_dir_recursively(src: &Path, dst: &Path) -> windmill_common::error::Result<()> { - if !dst.exists() { - fs::create_dir_all(dst)?; - } - - tracing::debug!("Copying recursively from {:?} to {:?}", src, dst); - - for entry in fs::read_dir(src)? { - let entry = entry?; - let src_path = entry.path(); - let dst_path = dst.join(entry.file_name()); - - if src_path.is_dir() && !src_path.is_symlink() { - copy_dir_recursively(&src_path, &dst_path)?; - } else { - fs::copy(&src_path, &dst_path)?; - } - } - - tracing::debug!("Finished copying recursively from {:?} to {:?}", src, dst); - - Ok(()) +async fn get_python_path( + py_version: PyVersion, + worker_name: &str, + job_id: &Uuid, + w_id: &str, + mem_peak: &mut i32, + conn: &Connection, + occupancy_metrics: &mut Option<&mut OccupancyMetrics>, +) -> windmill_common::error::Result { + let python_path = if let Some(python_path) = PYTHON_PATH.clone() { + python_path + } else if let Some(python_path) = py_version + .get_python( + &job_id, + mem_peak, + conn, + worker_name, + w_id, + occupancy_metrics, + ) + .await? + { + python_path + } else { + return Err(Error::ExecutionErr(format!( + "uv could not manage python path. Please manage it manually by setting PYTHON_PATH environment variable to your python binary path" + ))); + }; + Ok(python_path) } #[tracing::instrument(level = "trace", skip_all)] @@ -874,19 +826,21 @@ pub async fn handle_python_job( job_dir: &str, worker_dir: &str, worker_name: &str, - job: &QueuedJob, + job: &MiniPulledJob, mem_peak: &mut i32, canceled_by: &mut Option, - db: &sqlx::Pool, - client: &AuthedClientBackgroundTask, + conn: &Connection, + client: &AuthedClient, + parent_runnable_path: Option, inner_content: &String, shared_mount: &str, base_internal_url: &str, envs: HashMap, new_args: &mut Option>>, occupancy_metrics: &mut OccupancyMetrics, + precomputed_agent_info: Option, ) -> windmill_common::error::Result> { - let script_path = crate::common::use_flow_root_path(job.script_path()); + let script_path = crate::common::use_flow_root_path(job.runnable_path()); let (py_version, mut additional_python_paths) = handle_python_deps( job_dir, @@ -895,51 +849,37 @@ pub async fn handle_python_job( &job.workspace_id, &script_path, &job.id, - db, + conn, worker_name, worker_dir, mem_peak, canceled_by, &mut Some(occupancy_metrics), + precomputed_agent_info, ) .await?; - let PythonAnnotations { no_uv, no_postinstall, .. } = PythonAnnotations::parse(inner_content); + let PythonAnnotations { no_postinstall, .. } = PythonAnnotations::parse(inner_content); tracing::debug!("Finished handling python dependencies"); - let python_path = if no_uv { - PYTHON_PATH.clone() - } else if let Some(python_path) = py_version - .get_python( - &job.id, - mem_peak, - db, - worker_name, - &job.workspace_id, - &mut Some(occupancy_metrics), - ) - .await? - { - python_path - } else { - PYTHON_PATH.clone() - }; + let python_path = get_python_path( + py_version, + worker_name, + &job.id, + &job.workspace_id, + mem_peak, + conn, + &mut Some(occupancy_metrics), + ) + .await?; if !no_postinstall { - if let Err(e) = postinstall(&mut additional_python_paths, job_dir, job, db).await { + if let Err(e) = postinstall(&mut additional_python_paths, job_dir, job, conn).await { tracing::error!("Postinstall stage has failed. Reason: {e}"); } tracing::debug!("Finished deps postinstall stage"); } - if no_uv { - append_logs( - &job.id, - &job.workspace_id, - format!("\n\n--- SYSTEM PYTHON (Fallback) CODE EXECUTION ---\n",), - db, - ) - .await; - } else { + { append_logs( &job.id, &job.workspace_id, @@ -947,7 +887,7 @@ pub async fn handle_python_job( "\n\n--- PYTHON ({}) CODE EXECUTION ---\n", py_version.to_string_with_dot() ), - db, + conn, ) .await; } @@ -964,7 +904,7 @@ pub async fn handle_python_job( pre_spread, ) = prepare_wrapper( job_dir, - job.is_flow_step, + job.is_flow_step(), job.preprocessed, job.script_entrypoint_override.as_deref(), inner_content, @@ -976,7 +916,7 @@ pub async fn handle_python_job( let apply_preprocessor = pre_spread.is_some(); - create_args_and_out_file(&client, job, job_dir, db).await?; + create_args_and_out_file(&client, job, job_dir, conn).await?; tracing::debug!("Finished preparing wrapper"); let preprocessor = if let Some(pre_spread) = pre_spread { @@ -1078,8 +1018,8 @@ except BaseException as e: tracing::debug!("Finished writing wrapper"); - let client = client.get_authed().await; - let mut reserved_variables = get_reserved_variables(job, &client.token, db).await?; + let mut reserved_variables = + get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?; // Add /tmp/windmill/cache/python_xyz/global-site-packages to PYTHONPATH. // Usefull if certain wheels needs to be preinstalled before execution. @@ -1192,6 +1132,11 @@ mount {{ { python_cmd.env("SystemRoot", SYSTEM_ROOT.as_str()); python_cmd.env("USERPROFILE", crate::USERPROFILE_ENV.as_str()); + python_cmd.env( + "LOCALAPPDATA", + std::env::var("LOCALAPPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Local", HOME_ENV.as_str())), + ); } start_child_process(python_cmd, &python_path).await? @@ -1199,7 +1144,7 @@ mount {{ handle_child( &job.id, - db, + conn, mem_peak, canceled_by, child, @@ -1210,6 +1155,7 @@ mount {{ job.timeout, false, &mut Some(occupancy_metrics), + None, ) .await?; @@ -1296,12 +1242,14 @@ async fn prepare_wrapper( let sig = windmill_parser_py::parse_python_signature( inner_content, main_override.map(ToString::to_string), + false, )?; let pre_sig = if apply_preprocessor { Some(windmill_parser_py::parse_python_signature( inner_content, Some("preprocessor".to_string()), + false, )?) } else { None @@ -1420,43 +1368,47 @@ async fn prepare_wrapper( #[cfg(feature = "enterprise")] async fn replace_pip_secret( - db: &DB, + conn: &Connection, w_id: &str, req: &str, worker_name: &str, job_id: &Uuid, ) -> error::Result { - if PIP_SECRET_VARIABLE.is_match(req) { - let mut joined = "".to_string(); - for req in req.lines() { - let nreq = if PIP_SECRET_VARIABLE.is_match(req) { - let capture = PIP_SECRET_VARIABLE.captures(req); - let variable = capture.unwrap().get(1).unwrap().as_str(); - if !variable.contains("/PIP_SECRET_") { - return Err(error::Error::internal_err(format!( + if let Some(db) = conn.as_sql() { + if PIP_SECRET_VARIABLE.is_match(req) { + let mut joined = "".to_string(); + for req in req.lines() { + let nreq = if PIP_SECRET_VARIABLE.is_match(req) { + let capture = PIP_SECRET_VARIABLE.captures(req); + let variable = capture.unwrap().get(1).unwrap().as_str(); + if !variable.contains("/PIP_SECRET_") { + return Err(error::Error::internal_err(format!( "invalid secret variable in pip requirements, (last part of path ma): {}", req ))); - } - let secret = get_secret_value_as_admin(db, w_id, variable).await?; - tracing::info!( - worker = %worker_name, - job_id = %job_id, - workspace_id = %w_id, - "found secret variable in pip requirements: {}", - req - ); - PIP_SECRET_VARIABLE - .replace(req, secret.as_str()) - .to_string() - } else { - req.to_string() - }; - joined.push_str(&nreq); - joined.push_str("\n"); - } + } + let secret = get_secret_value_as_admin(db, w_id, variable).await?; + tracing::info!( + worker = %worker_name, + job_id = %job_id, + workspace_id = %w_id, + "found secret variable in pip requirements: {}", + req + ); + PIP_SECRET_VARIABLE + .replace(req, secret.as_str()) + .to_string() + } else { + req.to_string() + }; + joined.push_str(&nreq); + joined.push_str("\n"); + } - Ok(joined) + Ok(joined) + } else { + Ok(req.to_string()) + } } else { Ok(req.to_string()) } @@ -1469,12 +1421,13 @@ async fn handle_python_deps( w_id: &str, script_path: &str, job_id: &Uuid, - db: &DB, + conn: &Connection, worker_name: &str, worker_dir: &str, mem_peak: &mut i32, canceled_by: &mut Option, occupancy_metrics: &mut Option<&mut OccupancyMetrics>, + precomputed_agent_info: Option, ) -> error::Result<(PyVersion, Vec)> { create_dependencies_dir(job_dir).await; @@ -1487,26 +1440,39 @@ async fn handle_python_deps( .clone(); let mut requirements; + let compilation_error_hint; let mut annotated_pyv = None; let mut annotated_pyv_numeric = None; let is_deployed = requirements_o.is_some(); - let instance_pyv = PyVersion::from_instance_version().await; + let instance_pyv = PyVersion::from_instance_version(job_id, w_id, conn).await; let annotations = windmill_common::worker::PythonAnnotations::parse(inner_content); let requirements = match requirements_o { Some(r) => r, None => { let mut already_visited = vec![]; - requirements = windmill_parser_py_imports::parse_python_imports( - inner_content, - w_id, - script_path, - db, - &mut already_visited, - &mut annotated_pyv_numeric, - ) - .await? - .join("\n"); + (requirements, compilation_error_hint) = match conn { + Connection::Sql(db) => { + let (r, h) = windmill_parser_py_imports::parse_python_imports( + inner_content, + w_id, + script_path, + db, + &mut already_visited, + &mut annotated_pyv_numeric, + ) + .await?; + + (r.join("\n"), h) + } + Connection::Http(_) => match precomputed_agent_info { + Some(PrecomputedAgentInfo::Python { py_version, requirements }) => { + annotated_pyv_numeric = py_version; + (requirements.clone().unwrap_or_else(|| "".to_string()), None) + } + _ => ("".to_string(), None), + }, + }; annotated_pyv = annotated_pyv_numeric.and_then(|v| PyVersion::from_numeric(v)); @@ -1517,32 +1483,26 @@ async fn handle_python_deps( mem_peak, canceled_by, job_dir, - db, + conn, worker_name, w_id, occupancy_metrics, annotated_pyv.unwrap_or(instance_pyv), annotations.no_cache, - annotations.no_uv || annotations.no_uv_compile, ) .await .map_err(|e| { - Error::ExecutionErr(format!("pip compile failed: {}", e.to_string())) + Error::ExecutionErr(format!( + "pip compile failed: {}{}", + e.to_string(), + compilation_error_hint.unwrap_or_default() + )) })?; } &requirements } }; - let requirements_lines: Vec<&str> = if requirements.len() > 0 { - requirements - .split("\n") - .filter(|x| !x.starts_with("--") && !x.trim().is_empty()) - .collect() - } else { - vec![] - }; - /* For deployed scripts we want to find out version in following order: 1. Assigned version (written in lockfile) @@ -1553,20 +1513,9 @@ async fn handle_python_deps( 2. Instance version 3. Latest Stable */ + let requirements_lines = split_requirements(requirements.as_str()); let final_version = if is_deployed { - // If script is deployed we can try to parse first line to get assigned version - if let Some(v) = requirements_lines - .get(0) - .and_then(|line| PyVersion::parse_version(line)) - { - // We have valid assigned version, we use it - v - } else { - // If there is no assigned version in lockfile we automatically fallback to 3.11 - // In this case we have dependencies, but no associated python version - // This is the case for old deployed scripts - PyVersion::Py311 - } + get_pyv_from_requirements_lines(&requirements_lines) } else { // This is not deployed script, meaning we test run it (Preview) annotated_pyv.unwrap_or(instance_pyv) @@ -1579,13 +1528,12 @@ async fn handle_python_deps( w_id, mem_peak, canceled_by, - db, + conn, worker_name, job_dir, worker_dir, occupancy_metrics, final_version, - annotations.no_uv || annotations.no_uv_install, ) .await?; additional_python_paths.append(&mut venv_path); @@ -1609,7 +1557,6 @@ async fn spawn_uv_install( (pip_extra_index_url, pip_index_url): (Option, Option), // If none, it is system python py_path: Option, - no_uv_install: bool, worker_dir: &str, ) -> Result { if !*DISABLE_NSJAIL { @@ -1651,17 +1598,14 @@ async fn spawn_uv_install( let _ = write_file( job_dir, &nsjail_proto, - &(if no_uv_install { - NSJAIL_CONFIG_DOWNLOAD_PY_CONTENT_FALLBACK - } else { - NSJAIL_CONFIG_DOWNLOAD_PY_CONTENT - }) - .replace("{WORKER_DIR}", worker_dir) - .replace("{PY_INSTALL_DIR}", &PY_INSTALL_DIR) - .replace("{TARGET_DIR}", &venv_p) - .replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string()), + NSJAIL_CONFIG_DOWNLOAD_PY_CONTENT + .replace("{WORKER_DIR}", worker_dir) + .replace("{PY_INSTALL_DIR}", &PY_INSTALL_DIR) + .replace("{TARGET_DIR}", &venv_p) + .replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string()) + .as_str(), )?; - + let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str()); nsjail_cmd .current_dir(job_dir) @@ -1673,72 +1617,47 @@ async fn spawn_uv_install( .stderr(Stdio::piped()); start_child_process(nsjail_cmd, NSJAIL_PATH.as_str()).await } else { - let fssafe_req = NON_ALPHANUM_CHAR.replace_all(&req, "_").to_string(); #[cfg(unix)] - let req = if no_uv_install { - format!("'{}'", req) - } else { - req.to_owned() - }; + let req = req.to_owned(); #[cfg(windows)] let req = format!("{}", req); - let mut command_args = if no_uv_install { - vec![ - PYTHON_PATH.as_str(), - "-m", - "pip", - "install", - &req, - "-I", - "--no-deps", - "--no-color", - "--isolated", - "--no-warn-conflicts", - "--disable-pip-version-check", - "-t", - venv_p, - ] - } else { - vec![ - UV_PATH.as_str(), - "pip", - "install", - &req, - "--no-deps", - "--no-color", - // Prevent uv from discovering configuration files. - "--no-config", - "--link-mode=copy", - "--system", - // Prefer main index over extra - // https://docs.astral.sh/uv/pip/compatibility/#packages-that-exist-on-multiple-indexes - // TODO: Use env variable that can be toggled from UI - "--index-strategy", - "unsafe-best-match", - "--target", - venv_p, - "--no-cache", - // If we invoke uv pip install, then we want to overwrite existing data - "--reinstall", - ] - }; + let mut command_args = vec![ + UV_PATH.as_str(), + "pip", + "install", + &req, + "--no-deps", + "--no-color", + // Prevent uv from discovering configuration files. + "--no-config", + "--link-mode=copy", + "--system", + // Prefer main index over extra + // https://docs.astral.sh/uv/pip/compatibility/#packages-that-exist-on-multiple-indexes + // TODO: Use env variable that can be toggled from UI + "--index-strategy", + "unsafe-best-match", + "--target", + venv_p, + "--no-cache", + // If we invoke uv pip install, then we want to overwrite existing data + "--reinstall", + ]; - if !no_uv_install { - if let Some(py_path) = py_path.as_ref() { - command_args.extend([ - "-p", - py_path.as_str(), - "--python-preference", - "only-managed", // - ]); - } else { - command_args.extend([ - "--python-preference", - "only-system", // - ]); - } + if let Some(py_path) = py_path.as_ref() { + command_args.extend([ + "-p", + py_path.as_str(), + "--python-preference", + "only-managed", // + ]); + } else { + command_args.extend([ + "--python-preference", + "only-system", // + ]); } if let Some(url) = pip_extra_index_url.as_ref() { @@ -1772,42 +1691,19 @@ async fn spawn_uv_install( #[cfg(unix)] { - if no_uv_install { - let mut flock_cmd = Command::new(FLOCK_PATH.as_str()); - flock_cmd - .env_clear() - .envs(PROXY_ENVS.clone()) - .envs(envs) - .args([ - "-x", - &format!( - "{}/{}-{}.lock", - LOCK_CACHE_DIR, - if no_uv_install { "pip" } else { "py311" }, - fssafe_req - ), - "--command", - &command_args.join(" "), - ]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); - start_child_process(flock_cmd, FLOCK_PATH.as_str()).await - } else { - let mut cmd = Command::new(command_args[0]); - cmd.env_clear() - .envs(PROXY_ENVS.clone()) - .envs(envs) - .args(&command_args[1..]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); - start_child_process(cmd, UV_PATH.as_str()).await - } + let mut cmd = Command::new(command_args[0]); + cmd.env_clear() + .envs(PROXY_ENVS.clone()) + .envs(envs) + .args(&command_args[1..]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + start_child_process(cmd, UV_PATH.as_str()).await } #[cfg(windows)] { - let installer_path = if no_uv_install { command_args[0] } else { "uv" }; - let mut cmd: Command = Command::new(&installer_path); + let mut cmd: Command = Command::new("uv"); cmd.env_clear() .envs(envs) .envs(PROXY_ENVS.clone()) @@ -1817,27 +1713,19 @@ async fn spawn_uv_install( "TMP", std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), ) + .env( + "LOCALAPPDATA", + std::env::var("LOCALAPPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Local", HOME_ENV.as_str())), + ) .args(&command_args[1..]) .stdout(Stdio::piped()) .stderr(Stdio::piped()); - start_child_process(cmd, installer_path).await + start_child_process(cmd, "uv").await } } } -/// length = 5 -/// value = "foo" -/// output = "foo " -/// 12345 -fn pad_string(value: &str, total_length: usize) -> String { - if value.len() >= total_length { - value.to_string() // Return the original string if it's already long enough - } else { - let padding_needed = total_length - value.len(); - format!("{value}{}", " ".repeat(padding_needed)) // Pad with spaces - } -} - /// uv pip install, include cached or pull from S3 pub async fn handle_python_reqs( requirements: Vec<&str>, @@ -1845,14 +1733,12 @@ pub async fn handle_python_reqs( w_id: &str, mem_peak: &mut i32, _canceled_by: &mut Option, - db: &sqlx::Pool, + conn: &Connection, _worker_name: &str, job_dir: &str, worker_dir: &str, _occupancy_metrics: &mut Option<&mut OccupancyMetrics>, py_version: PyVersion, - // TODO: Remove (Deprecated) - mut no_uv_install: bool, ) -> error::Result> { let worker_dir = worker_dir.to_string(); @@ -1870,7 +1756,7 @@ pub async fn handle_python_reqs( counter_arc: Arc>, total_to_install: usize, instant: std::time::Instant, - db: Pool, + conn: &Connection, ) { #[cfg(not(all(feature = "enterprise", feature = "parquet", unix)))] { @@ -1899,24 +1785,15 @@ pub async fn handle_python_reqs( if s3_push { " > (S3) " } else { "" }, instant.elapsed().as_millis(), ), - db, + conn, ) .await; // Drop lock, so next print success can fire } - no_uv_install |= *USE_PIP_INSTALL; - if no_uv_install { - append_logs(&job_id, w_id, "\nFallback to pip (Deprecated!)\n", db).await; - tracing::warn!("Fallback to pip"); - } // Parallelism level (N) - let parallel_limit = if no_uv_install { - 1 - } else { - // Semaphore will panic if value less then 1 - PY_CONCURRENT_DOWNLOADS.clamp(1, 30) - }; + let parallel_limit = // Semaphore will panic if value less then 1 + PY_CONCURRENT_DOWNLOADS.clamp(1, 30); tracing::info!( workspace_id = %w_id, @@ -1951,11 +1828,7 @@ pub async fn handle_python_reqs( if req.starts_with('#') || req.starts_with('-') || req.trim().is_empty() { continue; } - let py_prefix = if no_uv_install { - PIP_CACHE_DIR - } else { - &py_version.to_cache_dir() - }; + let py_prefix = &py_version.to_cache_dir(); let venv_p = format!( "{py_prefix}/{}", @@ -1974,7 +1847,7 @@ pub async fn handle_python_reqs( &job_id, w_id, format!("\nenv deps from local cache: {}\n", in_cache.join(", ")), - db, + conn, ) .await; } @@ -1988,7 +1861,7 @@ pub async fn handle_python_reqs( let (_done_tx, mut done_rx) = tokio::sync::mpsc::channel::<()>(1); let job_id_2 = job_id.clone(); - let db_2 = db.clone(); + let conn_2 = conn.clone(); let w_id_2 = w_id.to_string(); // Wheels to install @@ -2038,9 +1911,12 @@ pub async fn handle_python_reqs( *mem_peak_lock }; + // Notify server that we are still alive // Detect if job has been canceled - let canceled = sqlx::query_scalar!( + let canceled = match conn_2 { + Connection::Sql(ref db) => { + sqlx::query_scalar!( "UPDATE v2_job_runtime r SET memory_peak = $1, ping = now() @@ -2049,17 +1925,25 @@ pub async fn handle_python_reqs( RETURNING canceled_by IS NOT NULL AS \"canceled!\"", mem_peak_actual, job_id_2 - ) - .fetch_optional(&db_2) - .await - .unwrap_or_else(|e| { - tracing::error!(%e, "error updating job {job_id_2}: {e:#}"); - Some(false) - }) - .unwrap_or_else(|| { - // if the job is not in queue, it can only be in the completed_job so it is already complete - false - }); + ) + .fetch_optional(db) + .await + .unwrap_or_else(|e| { + tracing::error!(%e, "error updating job {job_id_2}: {e:#}"); + Some(false) + }) + .unwrap_or_else(|| { + // if the job is not in queue, it can only be in the completed_job so it is already complete + false + }) + } + Connection::Http(_) => { + if let Err(e) = ping_job_status(&conn_2, &job_id_2, Some(mem_peak_actual), None).await { + tracing::error!(%e, "error pinging job {job_id_2}: {e:#}"); + } + false + } + }; if canceled { @@ -2095,13 +1979,7 @@ pub async fn handle_python_reqs( let mut req_tl = 0; if total_to_install > 0 { let mut logs = String::new(); - // Do we use UV? - if no_uv_install { - logs.push_str("\n\n--- PIP INSTALL ---\n"); - } else { - logs.push_str("\n\n--- UV PIP INSTALL ---\n"); - } - + logs.push_str("\n\n--- UV PIP INSTALL ---\n"); logs.push_str("\nTo be installed: \n\n"); for (req, _) in &req_with_penv { if req.len() > req_tl { @@ -2122,7 +2000,7 @@ pub async fn handle_python_reqs( parallel_limit )); } - append_logs(&job_id, w_id, logs, db).await; + append_logs(&job_id, w_id, logs, conn).await; } let semaphore = Arc::new(Semaphore::new(parallel_limit)); @@ -2133,13 +2011,16 @@ pub async fn handle_python_reqs( let is_not_pro = !matches!(get_license_plan().await, LicensePlan::Pro); let total_time = std::time::Instant::now(); - let py_path = if no_uv_install { - None - } else { - py_version - .get_python(job_id, mem_peak, db, _worker_name, w_id, _occupancy_metrics) - .await? - }; + let py_path = py_version + .get_python( + job_id, + mem_peak, + conn, + _worker_name, + w_id, + _occupancy_metrics, + ) + .await?; let has_work = req_with_penv.len() > 0; for ((i, (req, venv_p)), mut kill_rx) in @@ -2162,7 +2043,7 @@ pub async fn handle_python_reqs( "started setup python dependencies" ); - let db = db.clone(); + let conn = conn.clone(); let job_id = job_id.clone(); let job_dir = job_dir.to_owned(); let w_id = w_id.to_owned(); @@ -2194,7 +2075,7 @@ pub async fn handle_python_reqs( tokio::select! { // Cancel was called on the job _ = kill_rx.recv() => return Err(anyhow::anyhow!("S3 pull was canceled")), - pull = pull_from_tar(os, venv_p.clone(), py_version.to_cache_dir_top_level(), no_uv_install) => { + pull = pull_from_tar(os, venv_p.clone(), py_version.to_cache_dir_top_level(), None, false) => { if let Err(e) = pull { tracing::info!( workspace_id = %w_id, @@ -2211,7 +2092,7 @@ pub async fn handle_python_reqs( counter_arc, total_to_install, start, - db + &conn ).await; pids.lock().await.get_mut(i).and_then(|e| e.take()); @@ -2240,7 +2121,6 @@ pub async fn handle_python_reqs( &job_dir, pip_indexes, py_path, - no_uv_install, &worker_dir ).await { Ok(r) => r, @@ -2251,7 +2131,7 @@ pub async fn handle_python_reqs( format!( "\nError while spawning proccess:\n{e}", ), - db, + &conn, ) .await; pids.lock().await.get_mut(i).and_then(|e| e.take()); @@ -2302,7 +2182,7 @@ pub async fn handle_python_reqs( "\nError while installing {}:\n{stderr_buf}", &req ), - db, + &conn, ) .await; pids.lock().await.get_mut(i).and_then(|e| e.take()); @@ -2339,14 +2219,14 @@ pub async fn handle_python_reqs( counter_arc, total_to_install, start, - db, // + &conn, // ) .await; #[cfg(all(feature = "enterprise", feature = "parquet", unix))] if s3_push { if let Some(os) = OBJECT_STORE_CACHE_SETTINGS.read().await.clone() { - tokio::spawn(build_tar_and_push(os, venv_p.clone(), py_version.to_cache_dir_top_level(), no_uv_install)); + tokio::spawn(build_tar_and_push(os, venv_p.clone(), py_version.to_cache_dir_top_level(), None, false)); } } @@ -2393,7 +2273,13 @@ pub async fn handle_python_reqs( if has_work { let total_time = total_time.elapsed().as_millis(); - append_logs(&job_id, w_id, format!("\nenv set in {}ms", total_time), db).await; + append_logs( + &job_id, + w_id, + format!("\nenv set in {}ms", total_time), + conn, + ) + .await; } *mem_peak = *mem_peak_thread_safe.lock().await; @@ -2409,6 +2295,37 @@ pub async fn handle_python_reqs( }; } +fn split_requirements(requirements: &str) -> Vec<&str> { + requirements + .split("\n") + .filter(|x| !x.trim_start().starts_with("--") && !x.trim().is_empty()) + .collect() +} +/// Check requirements/lockfile to figure out python version assigned to it. +fn get_pyv_from_requirements_lines(requirements_lines: &[&str]) -> PyVersion { + // If script is deployed we can try to parse first line to get assigned version + + let index = if requirements_lines.get(0).map_or(false, |line| { + line.starts_with(LOCKFILE_GENERATED_FROM_REQUIREMENTS_TXT) + }) { + 1 + } else { + 0 + }; + if let Some(v) = requirements_lines + .get(index) + .and_then(|line| PyVersion::parse_version(*line)) + { + // We have valid assigned version, we use it + v + } else { + // If there is no assigned version in lockfile we automatically fallback to 3.11 + // In this case we have dependencies, but no associated python version + // This is the case for old deployed scripts + PyVersion::Py311 + } +} + #[cfg(feature = "enterprise")] use crate::JobCompletedSender; #[cfg(feature = "enterprise")] @@ -2416,6 +2333,8 @@ use crate::{common::build_envs_map, dedicated_worker::handle_dedicated_process}; #[cfg(feature = "enterprise")] use windmill_common::variables; +use windmill_queue::MiniPulledJob; + #[cfg(feature = "enterprise")] pub async fn start_worker( requirements_o: Option<&String>, @@ -2429,13 +2348,13 @@ pub async fn start_worker( script_path: &str, token: &str, job_completed_tx: JobCompletedSender, - jobs_rx: tokio::sync::mpsc::Receiver>, + jobs_rx: tokio::sync::mpsc::Receiver>, killpill_rx: tokio::sync::broadcast::Receiver<()>, ) -> error::Result<()> { let mut mem_peak: i32 = 0; let mut canceled_by: Option = None; let context = variables::get_reserved_variables( - db, + &Connection::Sql(db.clone()), w_id, &token, "dedicated_worker@windmill.dev", @@ -2462,12 +2381,13 @@ pub async fn start_worker( w_id, script_path, &Uuid::nil(), - db, + &Connection::Sql(db.clone()), worker_name, job_dir, &mut mem_peak, &mut canceled_by, &mut None, + None, ) .await?; @@ -2550,7 +2470,7 @@ for line in sys.stdin: } let reserved_variables = windmill_common::variables::get_reserved_variables( - db, + &Connection::Sql(db.clone()), w_id, token, "dedicated_worker", @@ -2578,8 +2498,26 @@ for line in sys.stdin: base_internal_url.to_string(), ); proc_envs.insert("BASE_URL".to_string(), base_internal_url.to_string()); + + let py_version = if let Some(requirements) = requirements_o { + get_pyv_from_requirements_lines(&split_requirements(requirements.as_str())) + } else { + tracing::warn!(workspace_id = %w_id, "lockfile is empty for dedicated worker, thus python version cannot be inferred. Fallback to 3.11"); + PyVersion::Py311 + }; + + let python_path = get_python_path( + py_version, + worker_name, + &Uuid::nil(), + w_id, + &mut mem_peak, + &Connection::Sql(db.clone()), + &mut None, + ) + .await?; handle_dedicated_process( - &*PYTHON_PATH, + &python_path, job_dir, context_envs, envs, diff --git a/backend/windmill-worker/src/result_processor.rs b/backend/windmill-worker/src/result_processor.rs index dc99d1aae8..e1b2a763f6 100644 --- a/backend/windmill-worker/src/result_processor.rs +++ b/backend/windmill-worker/src/result_processor.rs @@ -2,7 +2,7 @@ use opentelemetry::trace::FutureExt; use serde::Serialize; -use sqlx::{types::Json, Pool, Postgres}; +use sqlx::types::Json; use std::{ collections::HashMap, sync::{ @@ -19,39 +19,107 @@ use uuid::Uuid; use windmill_common::{ add_time, error::{self, Error}, - jobs::{JobKind, QueuedJob}, + jobs::JobKind, utils::WarnAfterExt, - worker::{to_raw_value, WORKER_GROUP}, - DB, + worker::{to_raw_value, Connection, WORKER_GROUP}, + KillpillSender, DB, }; #[cfg(feature = "benchmark")] use windmill_common::bench::{BenchmarkInfo, BenchmarkIter}; -use windmill_queue::{append_logs, get_queued_job, CanceledBy, WrappedError}; +use windmill_queue::{ + append_logs, get_queued_job, CanceledBy, JobCompleted, MiniPulledJob, WrappedError, +}; use serde_json::{json, value::RawValue}; -use tokio::{ - sync::{ - self, - mpsc::{Receiver, Sender}, - }, - task::JoinHandle, -}; +use tokio::{sync::broadcast, task::JoinHandle}; use windmill_queue::{add_completed_job, add_completed_job_error}; use crate::{ bash_executor::ANSI_ESCAPE_RE, - common::{read_result, save_in_cache}, + common::{error_to_value, read_result, save_in_cache}, + otel_ee::add_root_flow_job_to_otlp, worker_flow::update_flow_status_after_job_completion, - AuthedClient, JobCompleted, JobCompletedSender, SameWorkerSender, SendResult, INIT_SCRIPT_TAG, + AuthedClient, JobCompletedSender, SameWorkerSender, SendResult, INIT_SCRIPT_TAG, }; +async fn process_jc( + jc: JobCompleted, + worker_name: &str, + base_internal_url: &str, + db: &DB, + worker_dir: &str, + same_worker_tx: &SameWorkerSender, + job_completed_sender: &JobCompletedSender, + #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, +) { + let success: bool = jc.success; + + let span = tracing::span!( + tracing::Level::INFO, + "job_postprocessing", + job_id = %jc.job.id, root_job = field::Empty, workspace_id = %jc.job.workspace_id, worker = %worker_name,tag = %jc.job.tag, + // hostname = %hostname, + language = field::Empty, + script_path = field::Empty, + flow_step_id = field::Empty, + parent_job = field::Empty, + otel.name = field::Empty + ); + let rj = if let Some(root_job) = jc.job.flow_innermost_root_job { + root_job + } else { + jc.job.id + }; + windmill_common::otel_ee::set_span_parent(&span, &rj); + + if let Some(lg) = jc.job.script_lang.as_ref() { + span.record("language", lg.as_str()); + } + if let Some(step_id) = jc.job.flow_step_id.as_ref() { + span.record( + "otel.name", + format!("job_postprocessing {}", step_id).as_str(), + ); + span.record("flow_step_id", step_id.as_str()); + } else { + span.record("otel.name", "job postprocessing"); + } + if let Some(parent_job) = jc.job.parent_job.as_ref() { + span.record("parent_job", parent_job.to_string().as_str()); + } + if let Some(script_path) = jc.job.runnable_path.as_ref() { + span.record("script_path", script_path.as_str()); + } + if let Some(root_job) = jc.job.flow_innermost_root_job.as_ref() { + span.record("root_job", root_job.to_string().as_str()); + } + + let root_job = handle_receive_completed_job( + jc, + &base_internal_url, + &db, + &worker_dir, + &same_worker_tx, + &worker_name, + job_completed_sender.clone(), + #[cfg(feature = "benchmark")] + bench, + ) + .instrument(span) + .await; + + if let Some(root_job) = root_job { + add_root_flow_job_to_otlp(&root_job, success); + } +} + pub fn start_background_processor( - mut job_completed_rx: Receiver, - job_completed_sender: Sender, + job_completed_rx: flume::Receiver, + job_completed_sender: JobCompletedSender, same_worker_queue_size: Arc, job_completed_processor_is_done: Arc, base_internal_url: String, @@ -59,7 +127,8 @@ pub fn start_background_processor( worker_dir: String, same_worker_tx: SameWorkerSender, worker_name: String, - killpill_tx: sync::broadcast::Sender<()>, + mut killpill_rx: broadcast::Receiver<()>, + killpill_tx: KillpillSender, is_dedicated_worker: bool, ) -> JoinHandle<()> { tokio::spawn(async move { @@ -68,89 +137,56 @@ pub fn start_background_processor( #[cfg(feature = "benchmark")] let mut infos = BenchmarkInfo::new(); + enum JobCompletedRx { + JobCompleted(SendResult), + Killpill, + } //if we have been killed, we want to drain the queue of jobs while let Some(sr) = { if has_been_killed && same_worker_queue_size.load(Ordering::SeqCst) == 0 { - job_completed_rx.try_recv().ok() + job_completed_rx + .try_recv() + .ok() + .map(JobCompletedRx::JobCompleted) } else { - job_completed_rx.recv().await + tokio::select! { + result = job_completed_rx.recv_async() => { + result.ok().map(JobCompletedRx::JobCompleted) + } + _ = killpill_rx.recv() => { + Some(JobCompletedRx::Killpill) + } + } } } { #[cfg(feature = "benchmark")] let mut bench = BenchmarkIter::new(); match sr { - SendResult::JobCompleted(jc) => { + JobCompletedRx::JobCompleted(SendResult::JobCompleted(jc)) => { let is_init_script_and_failure = !jc.success && jc.job.tag.as_str() == INIT_SCRIPT_TAG; let is_dependency_job = matches!( - jc.job.job_kind, + jc.job.kind, JobKind::Dependencies | JobKind::FlowDependencies ); - let success = jc.success; - - let span = tracing::span!( - tracing::Level::INFO, - "job_postprocessing", - job_id = %jc.job.id, root_job = field::Empty, workspace_id = %jc.job.workspace_id, worker = %worker_name,tag = %jc.job.tag, - // hostname = %hostname, - language = field::Empty, - script_path = field::Empty, - flow_step_id = field::Empty, - parent_job = field::Empty, - otel.name = field::Empty - ); - let rj = if let Some(root_job) = jc.job.root_job { - root_job - } else { - jc.job.id - }; - windmill_common::otel_ee::set_span_parent(&span, &rj); - - if let Some(lg) = jc.job.language.as_ref() { - span.record("language", lg.as_str()); - } - if let Some(step_id) = jc.job.flow_step_id.as_ref() { - span.record( - "otel.name", - format!("job_postprocessing {}", step_id).as_str(), - ); - span.record("flow_step_id", step_id.as_str()); - } else { - span.record("otel.name", "job postprocessing"); - } - if let Some(parent_job) = jc.job.parent_job.as_ref() { - span.record("parent_job", parent_job.to_string().as_str()); - } - if let Some(script_path) = jc.job.script_path.as_ref() { - span.record("script_path", script_path.as_str()); - } - if let Some(root_job) = jc.job.root_job.as_ref() { - span.record("root_job", root_job.to_string().as_str()); - } - - let root_job = handle_receive_completed_job( + process_jc( jc, + &worker_name, &base_internal_url, &db, &worker_dir, &same_worker_tx, - &worker_name, - job_completed_sender.clone(), + &job_completed_sender, #[cfg(feature = "benchmark")] &mut bench, ) - .instrument(span) .await; - if let Some(root_job) = root_job { - windmill_common::otel_ee::add_root_flow_job_to_otlp(&root_job, success); - } - if is_init_script_and_failure { tracing::error!("init script errored, exiting"); - killpill_tx.send(()).unwrap_or_default(); + killpill_tx.send(); break; } if is_dependency_job && is_dedicated_worker { @@ -162,7 +198,7 @@ pub fn start_background_processor( .execute(&db) .await .expect("update config to trigger restart of all dedicated workers at that config"); - killpill_tx.send(()).unwrap_or_default(); + killpill_tx.send(); } add_time!(bench, "job completed processed"); @@ -171,7 +207,7 @@ pub fn start_background_processor( infos.add_iter(bench, true); } } - SendResult::UpdateFlow { + JobCompletedRx::JobCompleted(SendResult::UpdateFlow { flow, w_id, success, @@ -179,7 +215,7 @@ pub fn start_background_processor( worker_dir, stop_early_override, token, - } => { + }) => { // let r; tracing::info!(parent_flow = %flow, "updating flow status"); if let Err(e) = update_flow_status_after_job_completion( @@ -209,7 +245,7 @@ pub fn start_background_processor( tracing::error!("Error updating flow status after job completion for {flow} on {worker_name}: {e:#}"); } } - SendResult::Kill => { + JobCompletedRx::Killpill => { has_been_killed = true; } } @@ -230,14 +266,14 @@ pub fn start_background_processor( async fn send_job_completed( job_completed_tx: JobCompletedSender, - job: Arc, + job: Arc, result: Arc>, result_columns: Option>, mem_peak: i32, canceled_by: Option, success: bool, cached_res_path: Option, - token: String, + token: &str, duration: Option, ) { let jc = JobCompleted { @@ -248,41 +284,43 @@ async fn send_job_completed( canceled_by, success, cached_res_path, - token, + token: token.to_string(), duration, }; job_completed_tx - .send(jc) + .send_job(jc) .with_context(windmill_common::otel_ee::otel_ctx()) .await .expect("send job completed") } pub async fn process_result( - job: Arc, + job: Arc, result: error::Result>>, job_dir: &str, job_completed_tx: JobCompletedSender, mem_peak: i32, canceled_by: Option, cached_res_path: Option, - token: String, + token: &str, column_order: Option>, new_args: Option>>, - db: &DB, + conn: &Connection, duration: Option, ) -> error::Result { match result { Ok(r) => { // Update script args to preprocessed args - if let Some(preprocessed_args) = new_args { - sqlx::query!( - "UPDATE v2_job SET args = $1, preprocessed = TRUE WHERE id = $2", - Json(preprocessed_args) as Json>>, - job.id - ) - .execute(db) - .await?; + if let Connection::Sql(db) = conn { + if let Some(preprocessed_args) = new_args { + sqlx::query!( + "UPDATE v2_job SET args = $1, preprocessed = TRUE WHERE id = $2", + Json(preprocessed_args) as Json>>, + job.id + ) + .execute(db) + .await?; + } } send_job_completed( @@ -309,22 +347,34 @@ pub async fn process_result( if res.as_ref().is_some_and(|x| !x.get().is_empty()) { res.unwrap() } else { - let last_10_log_lines = sqlx::query_scalar!( + match conn { + Connection::Sql(db) => { + let last_10_log_lines = sqlx::query_scalar!( "SELECT right(logs, 600) FROM job_logs WHERE job_id = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1", &job.id, &job.workspace_id ).fetch_one(db).await.ok().flatten().unwrap_or("".to_string()); - let log_lines = last_10_log_lines - .split("CODE EXECUTION ---") - .last() - .unwrap_or(&last_10_log_lines); + let log_lines = last_10_log_lines + .split("CODE EXECUTION ---") + .last() + .unwrap_or(&last_10_log_lines); - extract_error_value(&program, log_lines, i, job.flow_step_id.clone()) + extract_error_value( + &program, + log_lines, + i, + job.flow_step_id.clone(), + ) + } + Connection::Http(_) => { + to_raw_value(&"See logs for more details".to_string()) + } + } } } err @ _ => to_raw_value(&SerializedError { - message: format!("error during execution of the script:\n{err:#}",), + message: format!("execution error:\n{err:#}",), name: "ExecutionErr".to_string(), step_id: job.flow_step_id.clone(), exit_code: None, @@ -357,9 +407,9 @@ pub async fn handle_receive_completed_job( worker_dir: &str, same_worker_tx: &SameWorkerSender, worker_name: &str, - job_completed_tx: Sender, + job_completed_tx: JobCompletedSender, #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, -) -> Option> { +) -> Option> { let token = jc.token.clone(); let workspace = jc.job.workspace_id.clone(); let client = AuthedClient { @@ -424,16 +474,16 @@ pub async fn process_completed_job( worker_dir: &str, same_worker_tx: SameWorkerSender, worker_name: &str, - job_completed_tx: Sender, + job_completed_tx: JobCompletedSender, #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, -) -> error::Result>> { +) -> error::Result>> { if success { // println!("bef completed job{:?}", SystemTime::now()); if let Some(cached_path) = cached_res_path { save_in_cache(db, client, &job, cached_path, result.clone()).await; } - let is_flow_step = job.is_flow_step; + let is_flow_step = job.is_flow_step(); let parent_job = job.parent_job.clone(); let job_id = job.id.clone(); let workspace_id = job.workspace_id.clone(); @@ -457,6 +507,8 @@ pub async fn process_completed_job( })?; } + add_time!(bench, "pre add_completed_job"); + add_completed_job( db, &job, @@ -514,7 +566,7 @@ pub async fn process_completed_job( None, ) .await?; - if job.is_flow_step { + if job.is_flow_step() { if let Some(parent_job) = job.parent_job { tracing::error!(parent_flow = %parent_job, subflow = %job.id, "process completed job error, updating flow status"); let r = update_flow_status_after_job_completion( @@ -545,9 +597,9 @@ pub async fn process_completed_job( #[tracing::instrument(name = "job_error", level = "info", skip_all, fields(job_id = %job.id))] pub async fn handle_job_error( - db: &Pool, + db: &DB, client: &AuthedClient, - job: &QueuedJob, + job: &MiniPulledJob, mem_peak: i32, canceled_by: Option, err: Error, @@ -555,20 +607,17 @@ pub async fn handle_job_error( same_worker_tx: SameWorkerSender, worker_dir: &str, worker_name: &str, - job_completed_tx: Sender, + job_completed_tx: JobCompletedSender, #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, ) { - let err = match err { - Error::JsonErr(err) => err, - _ => json!({"message": err.to_string(), "name": "InternalErr"}), - }; + let err = error_to_value(err); let update_job_future = || async { append_logs( &job.id, &job.workspace_id, format!("Unexpected error during job execution:\n{err:#?}"), - db, + &db.into(), ) .await; add_completed_job_error( @@ -584,7 +633,7 @@ pub async fn handle_job_error( .await }; - let update_job_future = if job.is_flow_step || job.is_flow() { + let update_job_future = if job.is_flow_step() || job.is_flow() { let (flow, job_status_to_update) = if let Some(parent_job_id) = job.parent_job { if let Err(e) = update_job_future().await { tracing::error!( @@ -628,12 +677,12 @@ pub async fn handle_job_error( &parent_job.id, &job.workspace_id, format!("Unexpected error during flow job error handling:\n{err}"), - db, + &db.into(), ) .await; let _ = add_completed_job_error( db, - &parent_job, + &MiniPulledJob::from(&parent_job), mem_peak, canceled_by.clone(), e, @@ -653,7 +702,6 @@ pub async fn handle_job_error( if let Some(f) = update_job_future { let _ = f().await; } - tracing::error!(job_id = %job.id, "error handling job: {err:?} {} {} {}", job.id, job.workspace_id, job.created_by); } #[derive(Debug, Serialize)] diff --git a/backend/windmill-worker/src/rust_executor.rs b/backend/windmill-worker/src/rust_executor.rs index 639adc709a..9f0b783471 100644 --- a/backend/windmill-worker/src/rust_executor.rs +++ b/backend/windmill-worker/src/rust_executor.rs @@ -7,10 +7,10 @@ use itertools::Itertools; use tokio::{fs::File, io::AsyncReadExt, process::Command}; use windmill_common::{ error::{self, Error}, - jobs::QueuedJob, utils::calculate_hash, - worker::{save_cache, write_file}, + worker::{save_cache, write_file, Connection}, }; +use windmill_queue::MiniPulledJob; use windmill_queue::{append_logs, CanceledBy}; use crate::{ @@ -19,8 +19,8 @@ use crate::{ read_result, start_child_process, OccupancyMetrics, }, handle_child::handle_child, - AuthedClientBackgroundTask, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV, - PROXY_ENVS, RUST_CACHE_DIR, TZ_ENV, + AuthedClient, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, + RUST_CACHE_DIR, TZ_ENV, }; #[cfg(windows)] @@ -127,7 +127,7 @@ pub async fn generate_cargo_lockfile( mem_peak: &mut i32, canceled_by: &mut Option, job_dir: &str, - db: &sqlx::Pool, + conn: &Connection, worker_name: &str, w_id: &str, occupancy_metrics: &mut OccupancyMetrics, @@ -153,7 +153,7 @@ pub async fn generate_cargo_lockfile( let gen_lockfile_process = start_child_process(gen_lockfile_cmd, CARGO_PATH.as_str()).await?; handle_child( job_id, - db, + conn, mem_peak, canceled_by, gen_lockfile_process, @@ -164,6 +164,7 @@ pub async fn generate_cargo_lockfile( None, false, &mut Some(occupancy_metrics), + None, ) .await?; @@ -179,7 +180,7 @@ pub async fn build_rust_crate( mem_peak: &mut i32, canceled_by: &mut Option, job_dir: &str, - db: &sqlx::Pool, + conn: &Connection, worker_name: &str, w_id: &str, base_internal_url: &str, @@ -215,7 +216,7 @@ pub async fn build_rust_crate( let build_rust_process = start_child_process(build_rust_cmd, CARGO_PATH.as_str()).await?; handle_child( job_id, - db, + conn, mem_peak, canceled_by, build_rust_process, @@ -226,9 +227,10 @@ pub async fn build_rust_crate( None, false, &mut Some(occupancy_metrics), + None, ) .await?; - append_logs(job_id, w_id, "\n\n", db).await; + append_logs(job_id, w_id, "\n\n", conn).await; tokio::fs::copy( &format!("{job_dir}/target/release/main"), @@ -245,6 +247,7 @@ pub async fn build_rust_crate( &bin_path, &format!("{RUST_OBJECT_STORE_PREFIX}{hash}"), &format!("{job_dir}/main"), + false, ) .await { @@ -275,9 +278,10 @@ pub fn compute_rust_hash(code: &str, requirements_o: Option<&String>) -> String pub async fn handle_rust_job( mem_peak: &mut i32, canceled_by: &mut Option, - job: &QueuedJob, - db: &sqlx::Pool, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + conn: &Connection, + client: &AuthedClient, + parent_runnable_path: Option, inner_content: &str, job_dir: &str, requirements_o: Option<&String>, @@ -293,7 +297,8 @@ pub async fn handle_rust_job( let bin_path = format!("{}/{hash}", RUST_CACHE_DIR); let remote_path = format!("{RUST_OBJECT_STORE_PREFIX}{hash}"); - let (cache, cache_logs) = windmill_common::worker::load_cache(&bin_path, &remote_path).await; + let (cache, cache_logs) = + windmill_common::worker::load_cache(&bin_path, &remote_path, false).await; let cache_logs = if cache { let target = format!("{job_dir}/main"); @@ -309,11 +314,11 @@ pub async fn handle_rust_job( )) })?; - create_args_and_out_file(client, job, job_dir, db).await?; + create_args_and_out_file(client, job, job_dir, conn).await?; cache_logs } else { let logs1 = format!("{cache_logs}\n\n--- CARGO BUILD ---\n"); - append_logs(&job.id, &job.workspace_id, logs1, db).await; + append_logs(&job.id, &job.workspace_id, logs1, conn).await; gen_cargo_crate(inner_content, job_dir)?; @@ -323,14 +328,14 @@ pub async fn handle_rust_job( } } - create_args_and_out_file(client, job, job_dir, db).await?; + create_args_and_out_file(client, job, job_dir, conn).await?; build_rust_crate( &job.id, mem_peak, canceled_by, job_dir, - db, + conn, worker_name, &job.workspace_id, base_internal_url, @@ -341,10 +346,10 @@ pub async fn handle_rust_job( }; let logs2 = format!("{cache_logs}\n\n--- RUST CODE EXECUTION ---\n"); - append_logs(&job.id, &job.workspace_id, logs2, db).await; + append_logs(&job.id, &job.workspace_id, logs2, conn).await; - let client = &client.get_authed().await; - let reserved_variables = get_reserved_variables(job, &client.token, db).await?; + let reserved_variables = + get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?; let child = if !*DISABLE_NSJAIL { let _ = write_file( @@ -353,6 +358,7 @@ pub async fn handle_rust_job( &NSJAIL_CONFIG_RUN_RUST_CONTENT .replace("{JOB_DIR}", job_dir) .replace("{CACHE_DIR}", RUST_CACHE_DIR) + .replace("{CACHE_HASH}", &hash) .replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string()) .replace("{SHARED_MOUNT}", shared_mount), )?; @@ -394,7 +400,7 @@ pub async fn handle_rust_job( }; handle_child( &job.id, - db, + conn, mem_peak, canceled_by, child, @@ -405,6 +411,7 @@ pub async fn handle_rust_job( job.timeout, false, &mut Some(occupancy_metrics), + None, ) .await?; read_result(job_dir).await diff --git a/backend/windmill-worker/src/sanitized_sql_params.rs b/backend/windmill-worker/src/sanitized_sql_params.rs new file mode 100644 index 0000000000..465b67658f --- /dev/null +++ b/backend/windmill-worker/src/sanitized_sql_params.rs @@ -0,0 +1,100 @@ +use anyhow::anyhow; +use std::collections::HashMap; + +use serde_json::Value; +use windmill_common::error; +use windmill_parser::Arg; +use windmill_parser_sql::{SANITIZED_ENUM_STR, SANITIZED_RAW_STRING_STR}; + +/// Identifier must be a continuous ASCII alphanumeric word, not starting with +/// a number, that can contain underscores +fn sanitize_identifier(arg: &Arg, input: &str) -> Result<(), error::Error> { + if input.is_empty() { + return Err(error::Error::BadRequest(format!( + "Interpolated argument `{}` cannot be empty", + arg.name + ))); + } + if input + .chars() + .next() + .map(|c| c.is_ascii_alphabetic()) + .unwrap_or(false) + && input.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') + { + Ok(()) + } else { + Err(error::Error::BadRequest(format!("Interpolated argument `{}` contained forbidden characters. Received `{}` but should only contain alphanumerical characters and `_`.", arg.name, input))) + } +} + +pub fn sanitize_and_interpolate_unsafe_sql_args( + code: &str, + args: &Vec, + args_map: &HashMap, +) -> Result<(String, Vec), error::Error> { + let mut ret = code.to_string(); + let mut args_to_skip = vec![]; + + for arg in args { + if let Some(typ) = &arg.otyp { + let pattern = format!("%%{}%%", arg.name); + match typ.as_str() { + SANITIZED_ENUM_STR => { + let replace = + args_map + .get(&arg.name) + .and_then(|rv| rv.as_str()) + .ok_or(anyhow!( + "Sanitized enum `{}` needs to receive a string", + arg.name + ))?; + let windmill_parser::Typ::Str(Some(variants)) = &arg.typ else { + return Err(error::Error::ArgumentErr(format!( + "Wrong type of argument for sanitized enum `{}`", + arg.name + ))); + }; + if variants.iter().all(|v| v != replace) { + return Err(error::Error::ArgumentErr(format!( + "Sanitized enum argument `{}` expected one of `[{}]` but received `{}`", + arg.name, + variants + .iter() + .map(|s| format!("{s}")) + .collect::>() + .join(","), + replace, + ))); + } + + sanitize_identifier(&arg, replace)?; + ret = ret.replace(&pattern, replace); + args_to_skip.push(arg.name.to_string()); + } + SANITIZED_RAW_STRING_STR => { + let replace = + args_map + .get(&arg.name) + .and_then(|rv| rv.as_str()) + .ok_or(anyhow!( + "Sanitized raw string `{}` needs to receive a string", + arg.name + ))?; + let windmill_parser::Typ::Str(_) = &arg.typ else { + return Err(error::Error::ArgumentErr(format!( + "Wrong type of argument for sanitized raw string `{}`", + arg.name + ))); + }; + sanitize_identifier(&arg, replace)?; + ret = ret.replace(&pattern, &replace); + args_to_skip.push(arg.name.to_string()); + } + _ => continue, + } + } + } + + Ok((ret, args_to_skip)) +} diff --git a/backend/windmill-worker/src/schema.rs b/backend/windmill-worker/src/schema.rs new file mode 100644 index 0000000000..f5cce5cd90 --- /dev/null +++ b/backend/windmill-worker/src/schema.rs @@ -0,0 +1,94 @@ +use std::collections::HashMap; +use windmill_common::schema::{SchemaValidationRule, SchemaValidator}; +use windmill_parser::{MainArgSignature, Typ}; + + +fn make_rules_for_arg_typ(typ: &Typ) -> Vec { + let mut rules = vec![]; + + match typ { + Typ::Str(enum_variants) => { + rules.push(SchemaValidationRule::IsString); + + if let Some(enum_variants) = enum_variants { + rules.push(SchemaValidationRule::StrictEnum( + enum_variants + .iter() + .map(|v| serde_json::Value::String(v.to_string())) + .collect(), + )); + } + } + Typ::Int => { + rules.push(SchemaValidationRule::IsInteger); + } + Typ::Float => { + rules.push(SchemaValidationRule::IsNumber); + } + Typ::Bool => { + rules.push(SchemaValidationRule::IsBool); + } + Typ::List(typ) => { + rules.push(SchemaValidationRule::IsArray(make_rules_for_arg_typ(typ))); + } + Typ::Bytes => { + rules.push(SchemaValidationRule::IsString); + rules.push(SchemaValidationRule::IsBytes); + } + Typ::Datetime => { + rules.push(SchemaValidationRule::IsString); + rules.push(SchemaValidationRule::IsDatetime); + } + Typ::Email => { + rules.push(SchemaValidationRule::IsString); + rules.push(SchemaValidationRule::IsEmail); + } + Typ::Sql => { + rules.push(SchemaValidationRule::IsString); + } + Typ::Object(props) => { + let mut obj_rules = vec![]; + + for prop in props { + obj_rules.push((prop.key.to_string(), make_rules_for_arg_typ(&prop.typ))); + } + + rules.push(SchemaValidationRule::IsObject(obj_rules)) + } + Typ::OneOf(variants) => { + let mut rules_map = HashMap::new(); + + for variant in variants { + let mut obj_rules = vec![]; + + for prop in &variant.properties { + obj_rules.push((prop.key.to_string(), make_rules_for_arg_typ(&prop.typ))); + } + rules_map.insert(variant.label.to_string(), vec![SchemaValidationRule::IsObject(obj_rules)]); + } + + rules.push(SchemaValidationRule::IsOneOf(rules_map)) + } + Typ::Resource(_) => (), + Typ::DynSelect(_) => (), + Typ::Unknown => (), + } + + rules +} + +pub fn schema_validator_from_main_arg_sig(sig: &MainArgSignature) -> SchemaValidator { + let mut rules = vec![]; + let mut required = vec![]; + + for arg in &sig.args { + if !arg.has_default { + required.push(arg.name.to_string()); + } + + rules.push((arg.name.to_string(), make_rules_for_arg_typ(&arg.typ))); + } + + SchemaValidator { required, rules } +} + diff --git a/backend/windmill-worker/src/snowflake_executor.rs b/backend/windmill-worker/src/snowflake_executor.rs index 3315df6bc1..a4aba420c0 100644 --- a/backend/windmill-worker/src/snowflake_executor.rs +++ b/backend/windmill-worker/src/snowflake_executor.rs @@ -9,17 +9,18 @@ use serde_json::{json, value::RawValue, Value}; use sha2::{Digest, Sha256}; use std::collections::HashMap; use windmill_common::error::to_anyhow; +use windmill_common::worker::Connection; -use windmill_common::jobs::QueuedJob; use windmill_common::{error::Error, worker::to_raw_value}; use windmill_parser_sql::{parse_db_resource, parse_snowflake_sig, parse_sql_blocks}; -use windmill_queue::{CanceledBy, HTTP_CLIENT}; +use windmill_queue::{CanceledBy, MiniPulledJob, HTTP_CLIENT}; use serde::{Deserialize, Serialize}; use crate::common::{build_http_client, resolve_job_timeout, OccupancyMetrics}; use crate::handle_child::run_future_with_polling_update_job_poller; -use crate::{common::build_args_values, AuthedClientBackgroundTask}; +use crate::sanitized_sql_params::sanitize_and_interpolate_unsafe_sql_args; +use crate::{common::build_args_values, AuthedClient}; #[derive(Serialize)] struct Claims { @@ -124,15 +125,21 @@ fn do_snowflake_inner<'a>( skip_collect: bool, http_client: &'a Client, ) -> windmill_common::error::Result>>> { - body.insert("statement".to_string(), json!(query)); - - let mut bindings = serde_json::Map::new(); let sig = parse_snowflake_sig(&query) .map_err(|x| Error::ExecutionErr(x.to_string()))? .args; + let (query, args_to_skip) = &sanitize_and_interpolate_unsafe_sql_args(query, &sig, &job_args)?; + + body.insert("statement".to_string(), json!(query)); + + let mut bindings = serde_json::Map::new(); + let mut i = 1; for arg in &sig { + if args_to_skip.contains(&arg.name) { + continue; + } let arg_t = arg.otyp.clone().unwrap_or_else(|| "string".to_string()); let arg_v = job_args.get(&arg.name).cloned().unwrap_or(json!("")); let snowflake_v = convert_typ_val(arg_t, arg_v); @@ -240,25 +247,23 @@ fn do_snowflake_inner<'a>( } pub async fn do_snowflake( - job: &QueuedJob, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + client: &AuthedClient, query: &str, - db: &sqlx::Pool, + conn: &Connection, mem_peak: &mut i32, canceled_by: &mut Option, worker_name: &str, column_order: &mut Option>, occupancy_metrics: &mut OccupancyMetrics, ) -> windmill_common::error::Result> { - let snowflake_args = build_args_values(job, client, db).await?; + let snowflake_args = build_args_values(job, client, conn).await?; let inline_db_res_path = parse_db_resource(&query); let db_arg = if let Some(inline_db_res_path) = inline_db_res_path { Some( client - .get_authed() - .await .get_resource_value_interpolated::( &inline_db_res_path, Some(job.id.to_string()), @@ -358,7 +363,7 @@ pub async fn do_snowflake( json!(database.database.unwrap().to_uppercase()), ); } - let timeout = resolve_job_timeout(&db, &job.workspace_id, job.id, job.timeout) + let timeout = resolve_job_timeout(&conn, &job.workspace_id, job.id, job.timeout) .await .0 .as_secs(); @@ -367,7 +372,7 @@ pub async fn do_snowflake( let queries = parse_sql_blocks(query); let (timeout_duration, _, _) = - resolve_job_timeout(&db, &job.workspace_id, job.id, job.timeout).await; + resolve_job_timeout(&conn, &job.workspace_id, job.id, job.timeout).await; let http_client = build_http_client(timeout_duration)?; @@ -420,7 +425,7 @@ pub async fn do_snowflake( let r = run_future_with_polling_update_job_poller( job.id, job.timeout, - db, + conn, mem_peak, canceled_by, result_f.map_err(to_anyhow), diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index 31c076de9d..7199504beb 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -9,16 +9,20 @@ // #[cfg(feature = "otel")] // use opentelemetry::{global, KeyValue}; +use anyhow::anyhow; +use futures::TryFutureExt; use windmill_common::{ + agent_workers::DECODED_AGENT_TOKEN, apps::AppScriptId, - auth::{fetch_authed_from_permissioned_as, JWTAuthClaims, JobPerms, JWT_SECRET}, - cache::{ScriptData, ScriptMetadata}, + cache::{future::FutureCachedExt, ScriptData, ScriptMetadata}, + schema::{should_validate_schema, SchemaValidator}, scripts::PREVIEW_IS_TAR_CODEBASE_HASH, utils::WarnAfterExt, worker::{ - get_memory, get_vcpus, get_windmill_memory_usage, get_worker_memory_usage, write_file, - ROOT_CACHE_DIR, TMP_DIR, + write_file, Connection, HttpClient, MAX_TIMEOUT, ROOT_CACHE_DIR, ROOT_CACHE_NOMOUNT_DIR, + TMP_DIR, }, + KillpillSender, }; #[cfg(feature = "enterprise")] @@ -37,7 +41,7 @@ use windmill_common::METRICS_ENABLED; use reqwest::Response; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use sqlx::{types::Json, Pool, Postgres}; +use sqlx::types::Json; use std::{ collections::HashMap, fs::DirBuilder, @@ -47,6 +51,7 @@ use std::{ }, time::Duration, }; +use windmill_parser::MainArgSignature; use uuid::Uuid; @@ -54,17 +59,17 @@ use windmill_common::{ cache::{self, RawData}, error::{self, to_anyhow, Error}, flows::FlowNodeId, - jobs::{JobKind, QueuedJob}, + jobs::JobKind, scripts::{get_full_hub_script_by_path, ScriptHash, ScriptLang, PREVIEW_IS_CODEBASE_HASH}, - users::SUPERADMIN_SECRET_EMAIL, utils::StripPath, - worker::{update_ping, CLOUD_HOSTED, NO_LOGS, WORKER_CONFIG, WORKER_GROUP}, + worker::{CLOUD_HOSTED, NO_LOGS, WORKER_CONFIG, WORKER_GROUP}, DB, IS_READY, }; use windmill_queue::{ - append_logs, canceled_job_to_result, empty_result, pull, push, CanceledBy, PulledJob, PushArgs, - PushIsolationLevel, HTTP_CLIENT, + append_logs, canceled_job_to_result, empty_result, get_same_worker_job, pull, push_init_job, + CanceledBy, JobAndPerms, JobCompleted, MiniPulledJob, PrecomputedAgentInfo, PulledJob, + SameWorkerPayload, HTTP_CLIENT, }; #[cfg(feature = "prometheus")] @@ -80,7 +85,8 @@ use tokio::fs::symlink_file as symlink; use tokio::{ sync::{ - mpsc::{self, Sender}, + broadcast, + mpsc::{self, Receiver, Sender}, RwLock, }, task::JoinHandle, @@ -90,10 +96,11 @@ use tokio::{ use rand::Rng; use crate::{ + agent_workers::queue_init_job, bash_executor::{handle_bash_job, handle_powershell_job}, bun_executor::handle_bun_job, common::{ - build_args_map, cached_result_path, get_cached_resource_value_if_valid, + build_args_map, cached_result_path, error_to_value, get_cached_resource_value_if_valid, get_reserved_variables, update_worker_ping_for_failed_init_script, OccupancyMetrics, }, csharp_executor::handle_csharp_job, @@ -106,15 +113,23 @@ use crate::{ js_eval::{eval_fetch_timeout, transpile_ts}, pg_executor::do_postgresql, result_processor::{process_result, start_background_processor}, - worker_flow::{handle_flow, update_flow_status_in_progress}, + schema::schema_validator_from_main_arg_sig, + worker_flow::handle_flow, worker_lockfiles::{ handle_app_dependency_job, handle_dependency_job, handle_flow_dependency_job, }, + worker_utils::{insert_ping, queue_vacuum, update_worker_ping_full}, }; #[cfg(feature = "rust")] use crate::rust_executor::handle_rust_job; +#[cfg(feature = "nu")] +use crate::nu_executor::{handle_nu_job, JobHandlerInput as JobHandlerInputNu}; + +#[cfg(feature = "java")] +use crate::java_executor::{handle_java_job, JobHandlerInput as JobHandlerInputJava}; + #[cfg(feature = "php")] use crate::php_executor::handle_php_job; @@ -130,9 +145,6 @@ use crate::mysql_executor::do_mysql; #[cfg(feature = "oracledb")] use crate::oracledb_executor::do_oracledb; -use backon::ConstantBuilder; -use backon::{BackoffBuilder, Retryable}; - #[cfg(feature = "enterprise")] use crate::dedicated_worker::create_dedicated_worker_map; @@ -150,124 +162,6 @@ use windmill_common::bench::{benchmark_init, BenchmarkInfo, BenchmarkIter}; use windmill_common::add_time; -pub async fn create_token_for_owner_in_bg( - db: &Pool, - job: &QueuedJob, -) -> Arc> { - let rw_lock = Arc::new(RwLock::new(String::new())); - // skipping test runs - if job.workspace_id != "" { - let mut locked = rw_lock.clone().write_owned().await; - let db = db.clone(); - let w_id = job.workspace_id.clone(); - let owner = job.permissioned_as.clone(); - let email = job.email.clone(); - let job_id = job.id.clone(); - - let label = if job.permissioned_as != format!("u/{}", job.created_by) - && job.permissioned_as != job.created_by - { - format!("ephemeral-script-end-user-{}", job.created_by) - } else { - "ephemeral-script".to_string() - }; - tokio::spawn(async move { - let token = create_token_for_owner( - &db.clone(), - &w_id, - &owner, - &label, - *SCRIPT_TOKEN_EXPIRY, - &email, - &job_id, - ) - .warn_after_seconds(5) - .await - .expect("could not create job token"); - *locked = token; - }); - }; - return rw_lock; -} - -#[tracing::instrument(level = "trace", skip_all)] -pub async fn create_token_for_owner( - db: &Pool, - w_id: &str, - owner: &str, - label: &str, - expires_in: u64, - email: &str, - job_id: &Uuid, -) -> error::Result { - // TODO: Bad implementation. We should not have access to this DB here. - if let Some(token) = JOB_TOKEN.as_ref() { - return Ok(token.clone()); - } - - let jwt_secret = JWT_SECRET.read().await; - - if jwt_secret.is_empty() { - return Err(Error::internal_err("No JWT secret found".to_string())); - } - - let job_authed = match sqlx::query_as!( - JobPerms, - "SELECT * FROM job_perms WHERE job_id = $1 AND workspace_id = $2", - job_id, - w_id - ) - .fetch_optional(db) - .await - { - Ok(Some(jp)) => jp.into(), - _ => { - tracing::warn!("Could not get permissions for job {job_id} from job_perms table, getting permissions directly..."); - fetch_authed_from_permissioned_as(owner.to_string(), email.to_string(), w_id, db) - .await - .map_err(|e| { - Error::internal_err(format!( - "Could not get permissions directly for job {job_id}: {e:#}" - )) - })? - } - }; - - let payload = JWTAuthClaims { - email: job_authed.email, - username: job_authed.username, - is_admin: job_authed.is_admin, - is_operator: job_authed.is_operator, - groups: job_authed.groups, - folders: job_authed.folders, - label: Some(label.to_string()), - workspace_id: w_id.to_string(), - exp: (chrono::Utc::now() + chrono::Duration::seconds(expires_in as i64)).timestamp() - as usize, - job_id: Some(job_id.to_string()), - scopes: None, - }; - - let token = jsonwebtoken::encode( - &jsonwebtoken::Header::new(jsonwebtoken::Algorithm::HS256), - &payload, - &jsonwebtoken::EncodingKey::from_secret(jwt_secret.as_bytes()), - ) - .map_err(|err| { - Error::internal_err(format!( - "Could not encode JWT token for job {job_id}: {:?}", - err - )) - })?; - Ok(format!("jwt_{}", token)) -} - -pub const ROOT_CACHE_NOMOUNT_DIR: &str = concatcp!(TMP_DIR, "/cache_nomount/"); - -pub const LOCK_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "lock"); -// Used as fallback now -pub const PIP_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "pip"); - pub const PY310_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "python_310"); pub const PY311_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "python_311"); pub const PY312_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "python_312"); @@ -278,20 +172,28 @@ pub const TAR_PY311_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "tar/python_311" pub const TAR_PY312_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "tar/python_312"); pub const TAR_PY313_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "tar/python_313"); +pub const TAR_JAVA_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "tar/java"); + pub const UV_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "uv"); pub const PY_INSTALL_DIR: &str = concatcp!(ROOT_CACHE_DIR, "py_runtime"); pub const TAR_PYBASE_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "tar"); -pub const TAR_PIP_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "tar/pip"); pub const DENO_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "deno"); pub const DENO_CACHE_DIR_DEPS: &str = concatcp!(ROOT_CACHE_DIR, "deno/deps"); pub const DENO_CACHE_DIR_NPM: &str = concatcp!(ROOT_CACHE_DIR, "deno/npm"); pub const GO_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "go"); pub const RUST_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "rust"); +pub const NU_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "nu"); pub const CSHARP_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "csharp"); + +// JAVA +pub const JAVA_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "java"); +pub const COURSIER_CACHE_DIR: &str = concatcp!(JAVA_CACHE_DIR, "/coursier-cache"); +pub const JAVA_REPOSITORY_DIR: &str = concatcp!(JAVA_CACHE_DIR, "/repository"); +// for related places search: ADD_NEW_LANG pub const BUN_CACHE_DIR: &str = concatcp!(ROOT_CACHE_NOMOUNT_DIR, "bun"); pub const BUN_BUNDLE_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "bun"); -pub const BUN_DEPSTAR_CACHE_DIR: &str = concatcp!(ROOT_CACHE_NOMOUNT_DIR, "buntar"); +pub const BUN_CODEBASE_BUNDLE_CACHE_DIR: &str = concatcp!(ROOT_CACHE_NOMOUNT_DIR, "script_bundle"); pub const GO_BIN_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "gobin"); pub const POWERSHELL_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "powershell"); @@ -301,10 +203,7 @@ const NUM_SECS_PING: u64 = 5; const NUM_SECS_READINGS: u64 = 60; const INCLUDE_DEPS_PY_SH_CONTENT: &str = include_str!("../nsjail/download_deps.py.sh"); -const INCLUDE_DEPS_PY_SH_CONTENT_FALLBACK: &str = include_str!("../nsjail/download_deps.py.pip.sh"); -pub const DEFAULT_CLOUD_TIMEOUT: u64 = 900; -pub const DEFAULT_SELFHOSTED_TIMEOUT: u64 = 604800; // 7 days pub const DEFAULT_SLEEP_QUEUE: u64 = 50; // only 1 native job so that we don't have to worry about concurrency issues on non dedicated native jobs workers @@ -312,8 +211,8 @@ pub const DEFAULT_NATIVE_JOBS: usize = 1; const VACUUM_PERIOD: u32 = 50000; -#[cfg(any(target_os = "linux"))] -const DROP_CACHE_PERIOD: u32 = 1000; +// #[cfg(any(target_os = "linux"))] +// const DROP_CACHE_PERIOD: u32 = 1000; pub const MAX_BUFFERED_DEDICATED_JOBS: usize = 3; @@ -345,16 +244,20 @@ const DOTNET_DEFAULT_PATH: &str = "/usr/bin/dotnet"; lazy_static::lazy_static! { - pub static ref JOB_TOKEN: Option = std::env::var("JOB_TOKEN").ok(); - pub static ref SLEEP_QUEUE: u64 = std::env::var("SLEEP_QUEUE") .ok() - .and_then(|x| x.parse::().ok()) - .unwrap_or(DEFAULT_SLEEP_QUEUE * std::env::var("NUM_WORKERS") - .ok() - .map(|x| x.parse().ok()) - .flatten() - .unwrap_or(2) / 2); + .and_then(|x| x.parse::().ok()) + .unwrap_or_else(|| { + if std::env::var("MODE").unwrap_or_default() == "agent" { + 1000 + } else { + DEFAULT_SLEEP_QUEUE * std::env::var("NUM_WORKERS") + .ok() + .map(|x| x.parse().ok()) + .flatten() + .unwrap_or(2) / 2 + } + }); pub static ref DISABLE_NUSER: bool = std::env::var("DISABLE_NUSER") @@ -381,6 +284,8 @@ lazy_static::lazy_static! { let mut proxy_env = Vec::new(); if let Some(no_proxy) = NO_PROXY.as_ref() { proxy_env.push(("NO_PROXY", no_proxy.to_string())); + } else if HTTPS_PROXY.is_some() || HTTP_PROXY.is_some() { + proxy_env.push(("NO_PROXY", "localhost,127.0.0.1".to_string())); } if let Some(http_proxy) = HTTP_PROXY.as_ref() { proxy_env.push(("HTTP_PROXY", http_proxy.to_string())); @@ -401,6 +306,7 @@ lazy_static::lazy_static! { pub static ref NSJAIL_PATH: String = std::env::var("NSJAIL_PATH").unwrap_or_else(|_| "nsjail".to_string()); pub static ref PATH_ENV: String = std::env::var("PATH").unwrap_or_else(|_| String::new()); pub static ref HOME_ENV: String = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string()); + pub static ref GIT_PATH: String = std::env::var("GIT_PATH").unwrap_or_else(|_| "/usr/bin/git".to_string()); pub static ref NODE_PATH: Option = std::env::var("NODE_PATH").ok(); @@ -413,16 +319,18 @@ lazy_static::lazy_static! { pub static ref NPM_CONFIG_REGISTRY: Arc>> = Arc::new(RwLock::new(None)); pub static ref BUNFIG_INSTALL_SCOPES: Arc>> = Arc::new(RwLock::new(None)); pub static ref NUGET_CONFIG: Arc>> = Arc::new(RwLock::new(None)); + pub static ref MAVEN_REPOS: Arc>> = Arc::new(RwLock::new(None)); + pub static ref NO_DEFAULT_MAVEN: AtomicBool = AtomicBool::new(std::env::var("NO_DEFAULT_MAVEN") + .ok() + .and_then(|x| x.parse::().ok()) + .unwrap_or(false)); pub static ref PIP_EXTRA_INDEX_URL: Arc>> = Arc::new(RwLock::new(None)); pub static ref PIP_INDEX_URL: Arc>> = Arc::new(RwLock::new(None)); pub static ref INSTANCE_PYTHON_VERSION: Arc>> = Arc::new(RwLock::new(None)); pub static ref JOB_DEFAULT_TIMEOUT: Arc>> = Arc::new(RwLock::new(None)); - static ref MAX_TIMEOUT: u64 = std::env::var("TIMEOUT") - .ok() - .and_then(|x| x.parse::().ok()) - .unwrap_or_else(|| if *CLOUD_HOSTED { DEFAULT_CLOUD_TIMEOUT } else { DEFAULT_SELFHOSTED_TIMEOUT }); + pub static ref MAX_WAIT_FOR_SIGINT: u64 = std::env::var("MAX_WAIT_FOR_SIGINT") .ok() @@ -436,10 +344,6 @@ lazy_static::lazy_static! { pub static ref MAX_TIMEOUT_DURATION: Duration = Duration::from_secs(*MAX_TIMEOUT); - pub static ref SCRIPT_TOKEN_EXPIRY: u64 = std::env::var("SCRIPT_TOKEN_EXPIRY") - .ok() - .and_then(|x| x.parse::().ok()) - .unwrap_or(*MAX_TIMEOUT); pub static ref GLOBAL_CACHE_INTERVAL: u64 = std::env::var("GLOBAL_CACHE_INTERVAL") .ok() @@ -470,25 +374,6 @@ pub const MAX_RESULT_SIZE: usize = 1024 * 1024 * 2; // 2MB pub const INIT_SCRIPT_TAG: &str = "init_script"; -pub struct AuthedClientBackgroundTask { - pub base_internal_url: String, - pub workspace: String, - pub token: Arc>, -} - -impl AuthedClientBackgroundTask { - pub async fn get_authed(&self) -> AuthedClient { - return AuthedClient { - base_internal_url: self.base_internal_url.clone(), - workspace: self.workspace.clone(), - token: self.get_token().await, - force_client: None, - }; - } - pub async fn get_token(&self) -> String { - return self.token.read().await.clone(); - } -} #[derive(Clone)] pub struct AuthedClient { pub base_internal_url: String, @@ -637,24 +522,96 @@ impl AuthedClient { } } -#[allow(dead_code)] -#[derive(Clone)] -pub struct JobCompletedSender(Sender); - #[derive(Clone)] pub struct SameWorkerSender(pub Sender, pub Arc); -pub struct SameWorkerPayload { - pub job_id: Uuid, - pub recoverable: bool, +#[allow(dead_code)] +#[derive(Clone)] +pub enum JobCompletedSender { + Sql(flume::Sender, broadcast::Sender<()>), + Http(HttpClient), + NeverUsed, } impl JobCompletedSender { - pub async fn send( - &self, - jc: JobCompleted, - ) -> Result<(), tokio::sync::mpsc::error::SendError> { - self.0.send(SendResult::JobCompleted(jc)).await + pub fn new( + conn: &Connection, + buffer_size: usize, + ) -> ( + Self, + Option<(flume::Receiver, broadcast::Receiver<()>)>, + ) { + match conn { + Connection::Sql(_) => { + let (sender, receiver) = flume::bounded::(buffer_size); + let (killpill_tx, killpill_rx) = broadcast::channel::<()>(buffer_size); + ( + Self::Sql(sender, killpill_tx), + Some((receiver, killpill_rx)), + ) + } + Connection::Http(client) => (Self::Http(client.clone()), None), + } + } + pub fn new_never_used() -> (Self, Option>) { + (Self::NeverUsed, None) + } + + pub async fn send_job(&self, jc: JobCompleted) -> anyhow::Result<()> { + match self { + Self::Sql(sender, _) => sender + .send_async(SendResult::JobCompleted(jc)) + .await + .map_err(|_e| { + anyhow::anyhow!("Failed to send job completed to background processor") + }), + Self::Http(client) => { + crate::agent_workers::send_result(client, jc).await?; + Ok(()) + } + Self::NeverUsed => { + tracing::error!( + "Sending job completed to NeverUsed JobCompletedSender, this should not happen" + ); + Ok(()) + } + } + } + + pub async fn send(&self, send_result: SendResult) -> Result<(), flume::SendError> { + match self { + Self::Sql(sender, _) => sender.send_async(send_result).await, + Self::Http(_) => { + tracing::error!("Sending job completed to http client, this should not happen"); + Ok(()) + } + Self::NeverUsed => { + tracing::error!( + "Sending job completed to NeverUsed JobCompletedSender, this should not happen" + ); + Ok(()) + } + } + } + + pub async fn kill(&self) -> Result<(), broadcast::error::SendError<()>> { + match self { + Self::Sql(_, killpill_tx) => { + tracing::info!("Sending killpill to bg processors"); + killpill_tx.send(())?; + Ok(()) + } + Self::Http(_) => { + tracing::error!("Sending kill to http client, this should not happen"); + Ok(()) + } + Self::NeverUsed => { + tracing::error!( + "Sending kill to NeverUsed JobCompletedSender, this should not happen" + ); + Ok(()) + } + } } } @@ -683,11 +640,11 @@ pub async fn drop_cache() { Ok(mut file) => { // Write '3' to the file to drop caches if let Err(e) = tokio::io::AsyncWriteExt::write_all(&mut file, b"3").await { - tracing::warn!("Failed to write to /proc/sys/vm/drop_caches (expected to not work in not in privileged mode, only required to forcefully drop the cache to avoid spurrious oom killer): {}", e); + tracing::warn!("Failed to write to /proc/sys/vm/drop_caches (expected to work only in privileged mode, only required to forcefully drop the cache to avoid spurrious oom killer): {}", e); } } Err(e) => { - tracing::warn!("Failed to open /proc/sys/vm/drop_caches (expected to not work in not in privileged mode, only required to forcefully drop the cache to avoid spurrious oom killer):: {}", e); + tracing::warn!("Failed to open /proc/sys/vm/drop_caches (expected to work only in privileged mode, only required to forcefully drop the cache to avoid spurrious oom killer):: {}", e); } } } @@ -697,17 +654,17 @@ const OUTSTANDING_WAIT_TIME_THRESHOLD_MS: i64 = 1000; async fn insert_wait_time( job_id: Uuid, root_job_id: Option, - db: &Pool, + db: &DB, wait_time: i64, ) -> sqlx::error::Result<()> { sqlx::query!( - "INSERT INTO outstanding_wait_time(job_id, self_wait_time_ms) VALUES ($1, $2) - ON CONFLICT (job_id) DO UPDATE SET self_wait_time_ms = EXCLUDED.self_wait_time_ms", - job_id, - wait_time - ) - .execute(db) - .await?; + "INSERT INTO outstanding_wait_time(job_id, self_wait_time_ms) VALUES ($1, $2) + ON CONFLICT (job_id) DO UPDATE SET self_wait_time_ms = EXCLUDED.self_wait_time_ms", + job_id, + wait_time + ) + .execute(db) + .await?; if let Some(root_id) = root_job_id { // TODO: queued_job.root_job is not guaranteed to be the true root job (e.g. parallel flow @@ -718,16 +675,16 @@ async fn insert_wait_time( COALESCE(outstanding_wait_time.aggregate_wait_time_ms, 0) + EXCLUDED.aggregate_wait_time_ms", root_id, wait_time - ) - .execute(db) - .await?; + ) + .execute(db) + .await?; } Ok(()) } fn add_outstanding_wait_time( - queued_job: &QueuedJob, - db: &Pool, + conn: &Connection, + queued_job: &MiniPulledJob, waiting_threshold: i64, ) -> () { let wait_time; @@ -743,32 +700,30 @@ fn add_outstanding_wait_time( } let job_id = queued_job.id; - let root_job_id = queued_job.root_job; - let db = db.clone(); + let root_job_id = queued_job.flow_innermost_root_job; + let conn = conn.clone(); - tokio::spawn(async move { + if let Some(db) = conn.as_sql() { + let db = db.clone(); + tokio::spawn(async move { match insert_wait_time(job_id, root_job_id, &db, wait_time).await { Ok(()) => tracing::warn!("job {job_id} waited for an executor for a significant amount of time. Recording value wait_time={}ms", wait_time), Err(e) => tracing::error!("Failed to insert outstanding wait time: {}", e), } - }.in_current_span()); + }.in_current_span()); + } } -// struct WorkerMtrics { -// job_ -// } - pub async fn run_worker( - db: &Pool, + conn: &Connection, hostname: &str, worker_name: String, i_worker: u64, _num_workers: u32, ip: &str, mut killpill_rx: tokio::sync::broadcast::Receiver<()>, - killpill_tx: tokio::sync::broadcast::Sender<()>, + killpill_tx: KillpillSender, base_internal_url: &str, - agent_mode: bool, ) { #[cfg(not(feature = "enterprise"))] if !*DISABLE_NSJAIL { @@ -785,16 +740,16 @@ pub async fn run_worker( #[cfg(feature = "python")] { - let (db, worker_name, hostname, worker_dir) = ( - db.clone(), + let (conn, worker_name, hostname, worker_dir) = ( + conn.clone(), worker_name.clone(), hostname.to_owned(), worker_dir.clone(), ); tokio::spawn(async move { - if let Err(e) = PyVersion::from_instance_version() + if let Err(e) = PyVersion::from_instance_version(&Uuid::nil(), "", &conn) .await - .get_python(&Uuid::nil(), &mut 0, &db, &worker_name, "", &mut None) + .get_python(&Uuid::nil(), &mut 0, &conn, &worker_name, "", &mut None) .await { tracing::error!( @@ -805,7 +760,7 @@ pub async fn run_worker( ); } if let Err(e) = PyVersion::Py311 - .get_python(&Uuid::nil(), &mut 0, &db, &worker_name, "", &mut None) + .get_python(&Uuid::nil(), &mut 0, &conn, &worker_name, "", &mut None) .await { tracing::error!( @@ -834,18 +789,13 @@ pub async fn run_worker( "download_deps.py.sh", INCLUDE_DEPS_PY_SH_CONTENT, ); - - // TODO: Remove (Deprecated) - let _ = write_file( - &worker_dir, - "download_deps.py.pip.sh", - INCLUDE_DEPS_PY_SH_CONTENT_FALLBACK, - ); } let mut last_ping = Instant::now() - Duration::from_secs(NUM_SECS_PING + 1); - update_ping(hostname, &worker_name, ip, db).await; + insert_ping(hostname, &worker_name, ip, conn) + .await + .expect("initial ping could be sent"); #[cfg(feature = "prometheus")] let uptime_metric = if METRICS_ENABLED.load(Ordering::Relaxed) { @@ -1054,7 +1004,11 @@ pub async fn run_worker( .unwrap(); #[cfg(feature = "benchmark")] - benchmark_init(benchmark_jobs, &db).await; + { + if let Some(db) = conn.as_sql() { + benchmark_init(benchmark_jobs, db).await; + } + } #[cfg(feature = "prometheus")] if let Some(ws) = WORKER_STARTED.as_ref() { @@ -1063,27 +1017,32 @@ pub async fn run_worker( let (same_worker_tx, mut same_worker_rx) = mpsc::channel::(5); - let (job_completed_tx, job_completed_rx) = mpsc::channel::(3); - - let job_completed_tx = JobCompletedSender(job_completed_tx); + let (job_completed_tx, job_completed_rx) = JobCompletedSender::new(&conn, 3); let same_worker_queue_size = Arc::new(AtomicU16::new(0)); let same_worker_tx = SameWorkerSender(same_worker_tx, same_worker_queue_size.clone()); - let job_completed_processor_is_done = Arc::new(AtomicBool::new(false)); + let job_completed_processor_is_done = + Arc::new(AtomicBool::new(matches!(conn, Connection::Http(_)))); - let send_result = start_background_processor( - job_completed_rx, - job_completed_tx.0.clone(), - same_worker_queue_size.clone(), - job_completed_processor_is_done.clone(), - base_internal_url.to_string(), - db.clone(), - worker_dir.clone(), - same_worker_tx.clone(), - worker_name.clone(), - killpill_tx.clone(), - is_dedicated_worker, - ); + let send_result = match (conn, job_completed_rx) { + (Connection::Sql(db), Some((job_completed_rx, bg_killpill_rx))) => { + Some(start_background_processor( + job_completed_rx, + job_completed_tx.clone(), + same_worker_queue_size.clone(), + job_completed_processor_is_done.clone(), + base_internal_url.to_string(), + db.clone(), + worker_dir.clone(), + same_worker_tx.clone(), + worker_name.clone(), + bg_killpill_rx, + killpill_tx.clone(), + is_dedicated_worker, + )) + } + _ => None, + }; let mut last_executed_job: Option = None; @@ -1096,12 +1055,20 @@ pub async fn run_worker( let vacuum_shift = rand::rng().random_range(0..VACUUM_PERIOD); IS_READY.store(true, Ordering::Relaxed); - tracing::info!( - worker = %worker_name, hostname = %hostname, - "listening for jobs, WORKER_GROUP: {}, config: {:?}", - *WORKER_GROUP, - WORKER_CONFIG.read().await - ); + if let Some(token) = DECODED_AGENT_TOKEN.as_ref() { + tracing::info!( + worker = %worker_name, hostname = %hostname, + "listening for jobs, agent mode, tags: {:?}", + token.tags + ); + } else { + tracing::info!( + worker = %worker_name, hostname = %hostname, + "listening for jobs, WORKER_GROUP: {}, config: {:?}", + *WORKER_GROUP, + WORKER_CONFIG.read().await + ); + } // (dedi_path, dedicated_worker_tx, dedicated_worker_handle) // Option>>, @@ -1109,30 +1076,35 @@ pub async fn run_worker( #[cfg(feature = "enterprise")] let (dedicated_workers, is_flow_worker, dedicated_handles): ( - HashMap>>, + HashMap>>, bool, Vec>, - ) = create_dedicated_worker_map( - &killpill_tx, - &killpill_rx, - db, - &worker_dir, - base_internal_url, - &worker_name, - &job_completed_tx, - ) - .await; + ) = match conn { + Connection::Sql(pool) => { + create_dedicated_worker_map( + &killpill_tx, + &killpill_rx, + pool, + &worker_dir, + base_internal_url, + &worker_name, + &job_completed_tx, + ) + .await + } + Connection::Http(_) => (HashMap::new(), false, vec![]), + }; #[cfg(not(feature = "enterprise"))] let (dedicated_workers, is_flow_worker, dedicated_handles): ( - HashMap>>, + HashMap>>, bool, Vec>, ) = (HashMap::new(), false, vec![]); if i_worker == 1 { - if let Err(e) = queue_init_bash_maybe(db, same_worker_tx.clone(), &worker_name).await { - killpill_tx.send(()).unwrap_or_default(); + if let Err(e) = queue_init_bash_maybe(conn, same_worker_tx.clone(), &worker_name).await { + killpill_tx.send(); tracing::error!(worker = %worker_name, hostname = %hostname, "Error queuing init bash script for worker {worker_name}: {e:#}"); return; } @@ -1158,7 +1130,7 @@ pub async fn run_worker( }; let mut suspend_first_success = false; let mut last_reading = Instant::now() - Duration::from_secs(NUM_SECS_READINGS + 1); - let mut last_30jobs_suspended: Vec = vec![false; 30]; + let mut last_30jobs_suspended = 0; let mut last_suspend_first = Instant::now(); let mut killed_but_draining_same_worker_jobs = false; @@ -1168,11 +1140,12 @@ pub async fn run_worker( { if let Ok(_) = killpill_rx.try_recv() { tracing::info!(worker = %worker_name, hostname = %hostname, "killpill received on worker waiting for valid key"); - job_completed_tx - .0 - .send(SendResult::Kill) - .await - .expect("send kill to job completed tx"); + if send_result.is_some() { + job_completed_tx + .kill() + .await + .expect("send kill to job completed tx"); + } break; } let valid_key = *LICENSE_KEY_VALID.read().await; @@ -1209,106 +1182,70 @@ pub async fn run_worker( } if last_ping.elapsed().as_secs() > NUM_SECS_PING { - let tags = WORKER_CONFIG.read().await.worker_tags.clone(); - - let memory_usage = get_worker_memory_usage(); - let wm_memory_usage = get_windmill_memory_usage(); - - let (vcpus, memory) = if *REFRESH_CGROUP_READINGS - && last_reading.elapsed().as_secs() > NUM_SECS_READINGS - { - last_reading = Instant::now(); - (get_vcpus(), get_memory()) - } else { - (None, None) - }; - - let (occupancy_rate, occupancy_rate_15s, occupancy_rate_5m, occupancy_rate_30m) = - occupancy_metrics.update_occupancy_metrics(); - - if let Err(e) = (|| sqlx::query!( - "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2, - occupancy_rate = $3, memory_usage = $4, wm_memory_usage = $5, vcpus = COALESCE($7, vcpus), - memory = COALESCE($8, memory), occupancy_rate_15s = $9, occupancy_rate_5m = $10, occupancy_rate_30m = $11 WHERE worker = $6", + let read_cgroups = + *REFRESH_CGROUP_READINGS && last_reading.elapsed().as_secs() > NUM_SECS_READINGS; + update_worker_ping_full( + &conn, + read_cgroups, jobs_executed, - tags.as_slice(), - occupancy_rate, - memory_usage, - wm_memory_usage, &worker_name, - vcpus, - memory, - occupancy_rate_15s, - occupancy_rate_5m, - occupancy_rate_30m - ).execute(db)).retry( - ConstantBuilder::default() - .with_delay(std::time::Duration::from_secs(2)) - .with_max_times(10) - .build(), + &hostname, + &mut occupancy_metrics, + &killpill_tx, ) - .notify(|err, dur| { - tracing::error!( - worker = %worker_name, hostname = %hostname, - "retrying updating worker ping in {dur:#?}, err: {err:#?}" - ); - }) - .sleep(tokio::time::sleep) - .await { - tracing::error!( - worker = %worker_name, hostname = %hostname, - "failed to update worker ping, exiting: {}", e); - killpill_tx.send(()).unwrap_or_default(); - } - tracing::info!( - worker = %worker_name, hostname = %hostname, - "ping update, memory: container={}MB, windmill={}MB", - memory_usage.unwrap_or_default() / (1024 * 1024), - wm_memory_usage.unwrap_or_default() / (1024 * 1024) - ); + .await; + if read_cgroups { + last_reading = Instant::now(); + } last_ping = Instant::now(); } if (jobs_executed as u32 + vacuum_shift) % VACUUM_PERIOD == 0 { - let db2 = db.clone(); - let current_span = tracing::Span::current(); - let worker_name = worker_name.clone(); - let hostname = hostname.to_string(); - tokio::task::spawn( - (async move { - tracing::info!(worker = %worker_name, hostname = %hostname, "vacuuming queue"); - if let Err(e) = sqlx::query!("VACUUM (skip_locked) v2_job_queue, v2_job_runtime, v2_job_status") - .execute(&db2) - .await - { - tracing::error!(worker = %worker_name, hostname = %hostname, "failed to vacuum queue: {}", e); - } - tracing::info!(worker = %worker_name, hostname = %hostname, "vacuumed queue"); - }) - .instrument(current_span), - ); + queue_vacuum(&conn, &worker_name, &hostname).await; jobs_executed += 1; } - #[cfg(any(target_os = "linux"))] - if (jobs_executed as u32 + 1) % DROP_CACHE_PERIOD == 0 { - drop_cache().await; - jobs_executed += 1; - } + // #[cfg(any(target_os = "linux"))] + // if (jobs_executed as u32 + 1) % DROP_CACHE_PERIOD == 0 { + // drop_cache().await; + // jobs_executed += 1; + // } #[cfg(feature = "benchmark")] if benchmark_jobs > 0 && infos.iters == benchmark_jobs as u64 { tracing::info!("benchmark finished, exiting"); job_completed_tx - .0 - .send(SendResult::Kill) + .kill() .await .expect("send kill to job completed tx"); break; } else { tracing::info!("benchmark not finished, still pulling jobs {}", infos.iters); } + enum NextJob { + Sql(PulledJob), + Http(JobAndPerms), + } + + impl NextJob { + pub fn job(self) -> MiniPulledJob { + match self { + NextJob::Sql(job) => job.job, + NextJob::Http(job) => job.job, + } + } + } + + impl std::ops::Deref for NextJob { + type Target = MiniPulledJob; + fn deref(&self) -> &Self::Target { + match self { + NextJob::Sql(job) => &job.job, + NextJob::Http(job) => &job.job, + } + } + } let next_job = { // println!("2: {:?}", instant.elapsed()); @@ -1324,38 +1261,42 @@ pub async fn run_worker( "received {} from same worker channel", same_worker_job.job_id ); - let r = sqlx::query_as::<_, PulledJob>( - "WITH ping AS ( - UPDATE v2_job_runtime SET ping = NOW() WHERE id = $1 RETURNING id - ) SELECT * FROM v2_as_queue WHERE id = (SELECT id FROM ping)", - ) - .bind(same_worker_job.job_id) - .fetch_optional(db) - .await - .map_err(|_| { - Error::internal_err("Impossible to fetch same_worker job".to_string()) - }); - if r.is_err() && !same_worker_job.recoverable { - tracing::error!( - worker = %worker_name, hostname = %hostname, - "failed to fetch same_worker job on a non recoverable job, exiting" - ); - job_completed_tx - .0 - .send(SendResult::Kill) + + match &conn { + Connection::Sql(db) => { + let job = get_same_worker_job(db, &same_worker_job).await; + // tracing::error!("r: {:?}", r); + if job.is_err() && !same_worker_job.recoverable { + tracing::error!( + worker = %worker_name, hostname = %hostname, + "failed to fetch same_worker job on a non recoverable job, exiting: {job:?}", + ); + job_completed_tx + .kill() + .await + .expect("send kill to job completed tx"); + break; + } else { + job.map(|x| x.map(NextJob::Sql)) + } + } + Connection::Http(client) => client + .post( + &format!( + "/api/agent_workers/same_worker_job/{}", + same_worker_job.job_id + ), + &same_worker_job, + ) .await - .expect("send kill to job completed tx"); - break; - } else { - r + .map_err(|e| error::Error::InternalErr(e.to_string())) + .map(|x: Option| x.map(|y| NextJob::Http(y))), } } else if let Ok(_) = killpill_rx.try_recv() { if !killed_but_draining_same_worker_jobs { - tracing::info!(worker = %worker_name, hostname = %hostname, "received killpill for worker {}, jobs are not pulled anymore except same_worker jobs", i_worker); killed_but_draining_same_worker_jobs = true; job_completed_tx - .0 - .send(SendResult::Kill) + .kill() .await .expect("send kill to job completed tx"); } @@ -1370,75 +1311,91 @@ pub async fn run_worker( continue; } } else { - let pull_time = Instant::now(); - let likelihood_of_suspend = - (1.0 + last_30jobs_suspended.iter().filter(|&&x| x).count() as f64) / 31.0; - let suspend_first = suspend_first_success - || rand::random::() < likelihood_of_suspend - || last_suspend_first.elapsed().as_secs_f64() > 5.0; + match &conn { + Connection::Sql(db) => { + let pull_time = Instant::now(); + let likelihood_of_suspend = last_30jobs_suspended as f64 / 30.0; + let suspend_first = suspend_first_success + || rand::random::() < likelihood_of_suspend + || last_suspend_first.elapsed().as_secs_f64() > 5.0; - if suspend_first { - last_suspend_first = Instant::now(); + if suspend_first { + last_suspend_first = Instant::now(); + } + + let job = pull( + &db, + suspend_first, + &worker_name, + None, + #[cfg(feature = "benchmark")] + &mut bench, + ) + .await; + + add_time!(bench, "job pulled from DB"); + let duration_pull_s = pull_time.elapsed().as_secs_f64(); + let err_pull = job.is_ok(); + // let empty = job.as_ref().is_ok_and(|x| x.is_none()); + + if duration_pull_s > 0.5 { + let empty = job.as_ref().is_ok_and(|x| x.job.is_none()); + tracing::warn!(worker = %worker_name, hostname = %hostname, "pull took more than 0.5s ({duration_pull_s}), this is a sign that the database is VERY undersized for this load. empty: {empty}, err: {err_pull}"); + #[cfg(feature = "prometheus")] + if empty { + if let Some(wp) = worker_pull_over_500_counter_empty.as_ref() { + wp.inc(); + } + } else if let Some(wp) = worker_pull_over_500_counter.as_ref() { + wp.inc(); + } + } else if duration_pull_s > 0.1 { + let empty = job.as_ref().is_ok_and(|x| x.job.is_none()); + tracing::warn!(worker = %worker_name, hostname = %hostname, "pull took more than 0.1s ({duration_pull_s}) this is a sign that the database is undersized for this load. empty: {empty}, err: {err_pull}"); + #[cfg(feature = "prometheus")] + if empty { + if let Some(wp) = worker_pull_over_100_counter_empty.as_ref() { + wp.inc(); + } + } else if let Some(wp) = worker_pull_over_100_counter.as_ref() { + wp.inc(); + } + } + + if let Ok(j) = job.as_ref() { + let suspend_success = j.suspended; + if suspend_first { + if last_30jobs_suspended < 30 { + last_30jobs_suspended += 1; + } + } else { + last_30jobs_suspended -= 1; + } + suspend_first_success = suspend_first && suspend_success; + #[cfg(feature = "prometheus")] + if j.job.is_some() { + if let Some(wp) = worker_pull_duration_counter.as_ref() { + wp.inc_by(duration_pull_s); + } + if let Some(wp) = worker_pull_duration.as_ref() { + wp.observe(duration_pull_s); + } + } else { + if let Some(wp) = worker_pull_duration_counter_empty.as_ref() { + wp.inc_by(duration_pull_s); + } + if let Some(wp) = worker_pull_duration_empty.as_ref() { + wp.observe(duration_pull_s); + } + } + } + job.map(|x| x.job.map(NextJob::Sql)) + } + Connection::Http(client) => crate::agent_workers::pull_job(&client) + .await + .map_err(|e| error::Error::InternalErr(e.to_string())) + .map(|x| x.map(|y| NextJob::Http(y))), } - - let job = pull(&db, suspend_first).await; - - add_time!(bench, "job pulled from DB"); - let duration_pull_s = pull_time.elapsed().as_secs_f64(); - let err_pull = job.is_ok(); - // let empty = job.as_ref().is_ok_and(|x| x.is_none()); - - if !agent_mode && duration_pull_s > 0.5 { - let empty = job.as_ref().is_ok_and(|x| x.0.is_none()); - tracing::warn!(worker = %worker_name, hostname = %hostname, "pull took more than 0.5s ({duration_pull_s}), this is a sign that the database is VERY undersized for this load. empty: {empty}, err: {err_pull}"); - #[cfg(feature = "prometheus")] - if empty { - if let Some(wp) = worker_pull_over_500_counter_empty.as_ref() { - wp.inc(); - } - } else if let Some(wp) = worker_pull_over_500_counter.as_ref() { - wp.inc(); - } - } else if !agent_mode && duration_pull_s > 0.1 { - let empty = job.as_ref().is_ok_and(|x| x.0.is_none()); - tracing::warn!(worker = %worker_name, hostname = %hostname, "pull took more than 0.1s ({duration_pull_s}) this is a sign that the database is undersized for this load. empty: {empty}, err: {err_pull}"); - #[cfg(feature = "prometheus")] - if empty { - if let Some(wp) = worker_pull_over_100_counter_empty.as_ref() { - wp.inc(); - } - } else if let Some(wp) = worker_pull_over_100_counter.as_ref() { - wp.inc(); - } - } - - if let Ok(j) = job.as_ref() { - let suspend_success = j.1; - if suspend_first { - last_30jobs_suspended.push(suspend_success); - if last_30jobs_suspended.len() > 30 { - last_30jobs_suspended.remove(0); - } - } - suspend_first_success = suspend_first && suspend_success; - #[cfg(feature = "prometheus")] - if j.0.is_some() { - if let Some(wp) = worker_pull_duration_counter.as_ref() { - wp.inc_by(duration_pull_s); - } - if let Some(wp) = worker_pull_duration.as_ref() { - wp.observe(duration_pull_s); - } - } else { - if let Some(wp) = worker_pull_duration_counter_empty.as_ref() { - wp.inc_by(duration_pull_s); - } - if let Some(wp) = worker_pull_duration_empty.as_ref() { - wp.observe(duration_pull_s); - } - } - } - job.map(|x| x.0) } }; @@ -1457,16 +1414,17 @@ pub async fn run_worker( tracing::debug!(worker = %worker_name, hostname = %hostname, "started handling of job {}", job.id); - if matches!(job.job_kind, JobKind::Script | JobKind::Preview) { + if matches!(job.kind, JobKind::Script | JobKind::Preview) { if !dedicated_workers.is_empty() { let key_o = if is_flow_worker { job.flow_step_id.as_ref().map(|x| x.to_string()) } else { - job.script_path.as_ref().map(|x| x.to_string()) + job.runnable_path.as_ref().map(|x| x.to_string()) }; if let Some(key) = key_o { if let Some(dedicated_worker_tx) = dedicated_workers.get(&key) { - if let Err(e) = dedicated_worker_tx.send(Arc::new(job.job)).await { + if let Err(e) = dedicated_worker_tx.send(Arc::new(job.job())).await + { tracing::info!("failed to send jobs to dedicated workers. Likely dedicated worker has been shut down. This is normal: {e:?}"); } @@ -1481,11 +1439,11 @@ pub async fn run_worker( } } } - if matches!(job.job_kind, JobKind::Noop) { + if matches!(job.kind, JobKind::Noop) { add_time!(bench, "send job completed START"); job_completed_tx - .send(JobCompleted { - job: Arc::new(job.job), + .send_job(JobCompleted { + job: Arc::new(job.job()), success: true, result: Arc::new(empty_result()), result_columns: None, @@ -1499,8 +1457,7 @@ pub async fn run_worker( .expect("send job completed END"); add_time!(bench, "sent job completed"); } else { - let token = create_token_for_owner_in_bg(&db, &job).await; - add_outstanding_wait_time(&job, db, OUTSTANDING_WAIT_TIME_THRESHOLD_MS); + add_outstanding_wait_time(&conn, &job, OUTSTANDING_WAIT_TIME_THRESHOLD_MS); #[cfg(feature = "prometheus")] register_metric( @@ -1547,14 +1504,14 @@ pub async fn run_worker( .await; let job_root = job - .root_job + .flow_innermost_root_job .map(|x| x.to_string()) .unwrap_or_else(|| "none".to_string()); if job.id == Uuid::nil() { tracing::info!("running warmup job"); } else { - tracing::info!(workspace_id = %job.workspace_id, job_id = %job.id, root_id = %job_root, "fetched job {}, root job: {}", job.id, job_root); + tracing::info!(workspace_id = %job.workspace_id, job_id = %job.id, root_id = %job_root, "fetched job {} (root job: {}, scheduled for: {})", job.id, job_root, job.scheduled_for); } // Here we can't remove the job id, but maybe with the // fields macro we can make a job id that only appears when // the job is defined? @@ -1568,7 +1525,7 @@ pub async fn run_worker( let same_worker = job.same_worker; - let folder = if job.language == Some(ScriptLang::Go) { + let folder = if job.script_lang == Some(ScriptLang::Go) { DirBuilder::new() .recursive(true) .create(&format!("{job_dir}/go")) @@ -1600,17 +1557,34 @@ pub async fn run_worker( .expect("could not create shared dir"); } - let authed_client = AuthedClientBackgroundTask { - base_internal_url: base_internal_url.to_string(), - token, - workspace: job.workspace_id.to_string(), - }; - #[cfg(feature = "prometheus")] let tag = job.tag.clone(); let is_init_script: bool = job.tag.as_str() == INIT_SCRIPT_TAG; - let PulledJob { job, raw_code, raw_lock, raw_flow } = job; + let JobAndPerms { + job, + raw_code, + raw_lock, + raw_flow, + parent_runnable_path, + token, + precomputed_agent_info: precomputed_bundle, + } = match (job, &conn) { + (NextJob::Sql(job), Connection::Sql(db)) => job.get_job_and_perms(db).await, + (NextJob::Sql(_), Connection::Http(_)) => { + panic!("sql job on http connection") + } + (NextJob::Http(job), _) => job, + }; + + // let token = create_token(&db, &job, job_perms).await; + let authed_client = AuthedClient { + base_internal_url: base_internal_url.to_string(), + token, + workspace: job.workspace_id.to_string(), + force_client: None, + }; + let arc_job = Arc::new(job); add_time!(bench, "handle_queued_job START"); @@ -1619,12 +1593,12 @@ pub async fn run_worker( language = field::Empty, script_path = field::Empty, flow_step_id = field::Empty, parent_job = field::Empty, otel.name = field::Empty); - let rj = if let Some(root_job) = arc_job.root_job { + let rj = if let Some(root_job) = arc_job.flow_innermost_root_job { root_job } else { arc_job.id }; - if let Some(lg) = arc_job.language.as_ref() { + if let Some(lg) = arc_job.script_lang.as_ref() { span.record("language", lg.as_str()); } if let Some(step_id) = arc_job.flow_step_id.as_ref() { @@ -1636,10 +1610,10 @@ pub async fn run_worker( if let Some(parent_job) = arc_job.parent_job.as_ref() { span.record("parent_job", parent_job.to_string().as_str()); } - if let Some(script_path) = arc_job.script_path.as_ref() { + if let Some(script_path) = arc_job.runnable_path.as_ref() { span.record("script_path", script_path.as_str()); } - if let Some(root_job) = arc_job.root_job.as_ref() { + if let Some(root_job) = arc_job.flow_innermost_root_job.as_ref() { span.record("root_job", root_job.to_string().as_str()); } @@ -1651,7 +1625,8 @@ pub async fn run_worker( raw_code, raw_lock, raw_flow, - db, + parent_runnable_path, + conn, &authed_client, &hostname, &worker_name, @@ -1662,6 +1637,7 @@ pub async fn run_worker( job_completed_tx.clone(), &mut occupancy_metrics, &mut killpill_rx2, + precomputed_bundle, #[cfg(feature = "benchmark")] &mut bench, ) @@ -1669,26 +1645,50 @@ pub async fn run_worker( .await { Err(err) => { - handle_job_error( - db, - &authed_client.get_authed().await, - arc_job.as_ref(), - 0, - None, - err, - false, - same_worker_tx.clone(), - &worker_dir, - &worker_name, - (&job_completed_tx.0).clone(), - #[cfg(feature = "benchmark")] - &mut bench, - ) - .await; + match conn { + Connection::Sql(db) => { + handle_job_error( + db, + &authed_client, + arc_job.as_ref(), + 0, + None, + err, + false, + same_worker_tx.clone(), + &worker_dir, + &worker_name, + job_completed_tx.clone(), + #[cfg(feature = "benchmark")] + &mut bench, + ) + .await; + } + Connection::Http(_) => { + job_completed_tx + .send_job(JobCompleted { + job: arc_job.clone(), + result: Arc::new( + windmill_common::worker::to_raw_value( + &error_to_value(err), + ), + ), + result_columns: None, + mem_peak: 0, + canceled_by: None, + success: false, + cached_res_path: None, + token: authed_client.token.clone(), + duration: None, + }) + .await + .expect("send job completed"); + } + } if is_init_script { tracing::error!("init script job failed (in handler), exiting"); update_worker_ping_for_failed_init_script( - db, + conn, &worker_name, arc_job.id, ) @@ -1698,8 +1698,12 @@ pub async fn run_worker( } Ok(false) if is_init_script => { tracing::error!("init script job failed, exiting"); - update_worker_ping_for_failed_init_script(db, &worker_name, arc_job.id) - .await; + update_worker_ping_for_failed_init_script( + conn, + &worker_name, + arc_job.id, + ) + .await; break; } _ => {} @@ -1804,58 +1808,42 @@ pub async fn run_worker( drop(job_completed_tx); tracing::info!(worker = %worker_name, hostname = %hostname, "waiting for job_completed_processor to finish processing remaining jobs"); - if let Err(e) = send_result.await { - tracing::error!("error in awaiting send_result process: {e:?}") + if let Some(send_result) = send_result { + if let Err(e) = send_result.await { + tracing::error!("error in awaiting send_result process: {e:?}") + } } tracing::info!(worker = %worker_name, hostname = %hostname, "worker {} exited", worker_name); tracing::info!(worker = %worker_name, hostname = %hostname, "number of jobs executed: {}", jobs_executed); } async fn queue_init_bash_maybe<'c>( - db: &Pool, + conn: &Connection, same_worker_tx: SameWorkerSender, worker_name: &str, -) -> error::Result { - if let Some(content) = WORKER_CONFIG.read().await.init_bash.clone() { - let tx = PushIsolationLevel::IsolatedRoot(db.clone()); - let ehm = HashMap::new(); - let (uuid, inner_tx) = push( - &db, - tx, - "admins", - windmill_common::jobs::JobPayload::Code(windmill_common::jobs::RawCode { - hash: None, - content: content.clone(), - path: Some(format!("init_script_{worker_name}")), - language: ScriptLang::Bash, - lock: None, - custom_concurrency_key: None, - concurrent_limit: None, - concurrency_time_window_s: None, - cache_ttl: None, - dedicated_worker: None, - }), - PushArgs::from(&ehm), - worker_name, - "worker@windmill.dev", - SUPERADMIN_SECRET_EMAIL.to_string(), - None, - None, - None, - None, - None, - false, - true, - None, - true, - Some("init_script".to_string()), - None, - None, - None, - None, - ) - .await?; - inner_tx.commit().await?; +) -> anyhow::Result { + let uuid_content = match conn { + Connection::Sql(db) => { + if let Some(content) = WORKER_CONFIG.read().await.init_bash.clone() { + Some(( + push_init_job(db, content.clone(), worker_name).await?, + content, + )) + } else { + None + } + } + Connection::Http(client) => { + let init_script = std::env::var("INIT_SCRIPT"); + if init_script.is_ok() { + let content = init_script.unwrap(); + Some((queue_init_job(client, &content).await?, content)) + } else { + None + } + } + }; + if let Some((uuid, content)) = uuid_content { same_worker_tx .send(SameWorkerPayload { job_id: uuid, recoverable: false }) .await @@ -1878,34 +1866,20 @@ pub enum SendResult { stop_early_override: Option, token: String, }, - Kill, -} - -#[derive(Debug, Clone)] -pub struct JobCompleted { - pub job: Arc, - pub result: Arc>, - pub result_columns: Option>, - pub mem_peak: i32, - pub success: bool, - pub cached_res_path: Option, - pub token: String, - pub canceled_by: Option, - pub duration: Option, } async fn do_nativets( - job: &QueuedJob, - client: &AuthedClientBackgroundTask, + job: &MiniPulledJob, + client: &AuthedClient, env_code: String, code: String, - db: &Pool, + conn: &Connection, mem_peak: &mut i32, canceled_by: &mut Option, worker_name: &str, occupancy_metrics: &mut OccupancyMetrics, ) -> windmill_common::error::Result> { - let args = build_args_map(job, client, db).await?.map(Json); + let args = build_args_map(job, client, conn).await?.map(Json); let job_args = if args.is_some() { args.as_ref() } else { @@ -1917,9 +1891,10 @@ async fn do_nativets( code.clone(), transpile_ts(code)?, job_args, + None, job.id, job.timeout, - db, + conn, mem_peak, canceled_by, worker_name, @@ -1937,12 +1912,13 @@ pub struct PreviousResult<'a> { } async fn handle_queued_job( - job: Arc, + job: Arc, raw_code: Option, raw_lock: Option, raw_flow: Option>>, - db: &DB, - client: &AuthedClientBackgroundTask, + parent_runnable_path: Option, + conn: &Connection, + client: &AuthedClient, hostname: &str, worker_name: &str, worker_dir: &str, @@ -1952,11 +1928,12 @@ async fn handle_queued_job( job_completed_tx: JobCompletedSender, occupancy_metrics: &mut OccupancyMetrics, killpill_rx: &mut tokio::sync::broadcast::Receiver<()>, + precomputed_agent_info: Option, #[cfg(feature = "benchmark")] _bench: &mut BenchmarkIter, ) -> windmill_common::error::Result { // Extract the active span from the context - if job.canceled { + if job.canceled_by.is_some() { return Err(Error::JsonErr(canceled_job_to_result(&job))); } if let Some(e) = &job.pre_run_error { @@ -1964,150 +1941,146 @@ async fn handle_queued_job( } #[cfg(any(not(feature = "enterprise"), feature = "sqlx"))] - if job.parent_job.is_none() && job.created_by.starts_with("email-") { - let daily_count = sqlx::query!( + match conn { + Connection::Sql(db) => { + if job.parent_job.is_none() && job.created_by.starts_with("email-") { + let daily_count = sqlx::query!( "SELECT value FROM metrics WHERE id = 'email_trigger_usage' AND created_at > NOW() - INTERVAL '1 day' ORDER BY created_at DESC LIMIT 1" ).fetch_optional(db) .warn_after_seconds(5) .await?.map(|x| serde_json::from_value::(x.value).unwrap_or(1)); - if let Some(count) = daily_count { - if count >= 100 { - return Err(error::Error::QuotaExceeded(format!( - "Email trigger usage limit of 100 per day has been reached." - ))); - } else { - sqlx::query!( + if let Some(count) = daily_count { + if count >= 100 { + return Err(error::Error::QuotaExceeded(format!( + "Email trigger usage limit of 100 per day has been reached." + ))); + } else { + sqlx::query!( "UPDATE metrics SET value = $1 WHERE id = 'email_trigger_usage' AND created_at > NOW() - INTERVAL '1 day'", serde_json::json!(count + 1) ) .execute(db) .warn_after_seconds(5) .await?; - } - } else { - sqlx::query!( + } + } else { + sqlx::query!( "INSERT INTO metrics (id, value) VALUES ('email_trigger_usage', to_jsonb(1))" ) - .execute(db) - .warn_after_seconds(5) - .await?; + .execute(db) + .warn_after_seconds(5) + .await?; + } + } + } + Connection::Http(_) => { + return Err(Error::internal_err(format!( + "Could not check email trigger usage for job with agent worker {}", + job.id + ))) } } - if job.is_flow_step { - let _ = update_flow_status_in_progress( - db, - &job.workspace_id, - job.parent_job - .ok_or_else(|| Error::internal_err(format!("expected parent job")))?, - job.id, - ) - .warn_after_seconds(5) - .await?; - } else if let Some(parent_job) = job.parent_job { - let _ = sqlx::query_scalar!( - "UPDATE v2_job_status SET - workflow_as_code_status = jsonb_set( - jsonb_set( - COALESCE(workflow_as_code_status, '{}'::jsonb), - array[$1], - COALESCE(workflow_as_code_status->$1, '{}'::jsonb) - ), - array[$1, 'started_at'], - to_jsonb(now()::text) - ) - WHERE id = $2", - &job.id.to_string(), - parent_job - ) - .execute(db) - .warn_after_seconds(5) - .await - .inspect_err(|e| { - tracing::error!( - "Could not update parent job `started_at` in workflow as code status: {}", - e - ) - }); + // no need to mark job as started if http conn, it's done by the server when pulled + if let Connection::Sql(db) = conn { + job.mark_as_started_if_step(db).await?; } let started = Instant::now(); // Pre-fetch preview jobs raw values if necessary. // The `raw_*` values passed to this function are the original raw values from `queue` tables, // they are kept for backward compatibility as they have been moved to the `job` table. - let preview_data = match (job.job_kind, job.script_hash) { + let preview_data = match (job.kind, job.runnable_id) { ( JobKind::Preview | JobKind::Dependencies | JobKind::FlowPreview | JobKind::Flow | JobKind::FlowDependencies, - None, - ) => Some(cache::job::fetch_preview(db, &job.id, raw_lock, raw_code, raw_flow).await?), + x, + ) => match x.map(|x| x.0) { + None | Some(PREVIEW_IS_CODEBASE_HASH) | Some(PREVIEW_IS_TAR_CODEBASE_HASH) => { + Some(cache::job::fetch_preview(conn, &job.id, raw_lock, raw_code, raw_flow).await?) + } + _ => None, + }, _ => None, }; + let cached_res_path = if job.cache_ttl.is_some() { - Some(cached_result_path(db, &client.get_authed().await, &job, preview_data.as_ref()).await) + match conn { + Connection::Sql(db) => { + Some(cached_result_path(db, &client, &job, preview_data.as_ref()).await) + } + Connection::Http(_) => None, + } } else { None }; - if let Some(cached_res_path) = cached_res_path.as_ref() { - let authed_client = client.get_authed().await; + if let Some(db) = conn.as_sql() { + if let Some(cached_res_path) = cached_res_path.as_ref() { + let cached_result_maybe = get_cached_resource_value_if_valid( + db, + &client, + &job.id, + &job.workspace_id, + &cached_res_path, + ) + .warn_after_seconds(5) + .await; + if let Some(result) = cached_result_maybe { + { + let logs = "Job skipped because args & path found in cache and not expired" + .to_string(); + append_logs(&job.id, &job.workspace_id, logs, conn).await; + } + job_completed_tx + .send_job(JobCompleted { + job, + result, + result_columns: None, + mem_peak: 0, + canceled_by: None, + success: true, + cached_res_path: None, + token: client.token.clone(), + duration: None, + }) + .await + .expect("send job completed"); - let cached_result_maybe = get_cached_resource_value_if_valid( - db, - &authed_client, - &job.id, - &job.workspace_id, - &cached_res_path, - ) - .warn_after_seconds(5) - .await; - if let Some(result) = cached_result_maybe { - { - let logs = - "Job skipped because args & path found in cache and not expired".to_string(); - append_logs(&job.id, &job.workspace_id, logs, db).await; + return Ok(true); } - job_completed_tx - .send(JobCompleted { - job, - result, - result_columns: None, - mem_peak: 0, - canceled_by: None, - success: true, - cached_res_path: None, - token: authed_client.token, - duration: None, - }) - .await - .expect("send job completed"); - - return Ok(true); - } - }; - if job.is_flow() { - let flow_data = match preview_data { - Some(RawData::Flow(data)) => data, - // Not a preview: fetch from the cache or the database. - _ => cache::job::fetch_flow(db, job.job_kind, job.script_hash).await?, }; - handle_flow( - job, - &flow_data, - db, - &client.get_authed().await, - None, - same_worker_tx, - worker_dir, - job_completed_tx.0.clone(), - ) - .warn_after_seconds(10) - .await?; - Ok(true) + } + if job.is_flow() { + if let Some(db) = conn.as_sql() { + let flow_data = match preview_data { + Some(RawData::Flow(data)) => data, + // Not a preview: fetch from the cache or the database. + _ => cache::job::fetch_flow(db, job.kind, job.runnable_id).await?, + }; + handle_flow( + job, + &flow_data, + db, + &client, + None, + same_worker_tx, + worker_dir, + job_completed_tx.clone(), + worker_name, + ) + .warn_after_seconds(10) + .await?; + Ok(true) + } else { + return Err(Error::internal_err( + "Could not handle flow job with agent worker".to_string(), + )); + } } else { let mut logs = "".to_string(); let mut mem_peak: i32 = 0; @@ -2134,7 +2107,7 @@ async fn handle_queued_job( #[cfg(not(feature = "enterprise"))] if job.concurrent_limit.is_some() { logs.push_str("---\n"); - logs.push_str("WARNING: This job has concurrency limits enabled. Concurrency limits are going to become an Enterprise Edition feature in the near future.\n"); + logs.push_str("WARNING: This job has concurrency limits enabled. Concurrency limits are an EE feature and the setting is ignored.\n"); logs.push_str("---\n"); } @@ -2143,15 +2116,60 @@ async fn handle_queued_job( "handling job {}", job.id ); - append_logs(&job.id, &job.workspace_id, logs, db).await; + append_logs(&job.id, &job.workspace_id, logs, conn).await; let mut column_order: Option> = None; let mut new_args: Option>> = None; - let result = match job.job_kind { - JobKind::Dependencies => { - handle_dependency_job( + let result = match job.kind { + JobKind::Dependencies => match conn { + Connection::Sql(db) => { + handle_dependency_job( + &job, + preview_data.as_ref(), + &mut mem_peak, + &mut canceled_by, + job_dir, + db, + worker_name, + worker_dir, + base_internal_url, + &client.token, + occupancy_metrics, + ) + .await + } + Connection::Http(_) => { + return Err(Error::internal_err( + "Could not handle dependency job with agent worker".to_string(), + )); + } + }, + JobKind::FlowDependencies => match conn { + Connection::Sql(db) => { + handle_flow_dependency_job( + &job, + preview_data.as_ref(), + &mut mem_peak, + &mut canceled_by, + job_dir, + db, + worker_name, + worker_dir, + base_internal_url, + &client.token, + occupancy_metrics, + ) + .await + } + Connection::Http(_) => { + return Err(Error::internal_err( + "Could not handle flow dependency job with agent worker".to_string(), + )); + } + }, + JobKind::AppDependencies => match conn { + Connection::Sql(db) => handle_app_dependency_job( &job, - preview_data.as_ref(), &mut mem_peak, &mut canceled_by, job_dir, @@ -2159,41 +2177,17 @@ async fn handle_queued_job( worker_name, worker_dir, base_internal_url, - &client.get_token().await, + &client.token, occupancy_metrics, ) .await - } - JobKind::FlowDependencies => { - handle_flow_dependency_job( - &job, - preview_data.as_ref(), - &mut mem_peak, - &mut canceled_by, - job_dir, - db, - worker_name, - worker_dir, - base_internal_url, - &client.get_token().await, - occupancy_metrics, - ) - .await - } - JobKind::AppDependencies => handle_app_dependency_job( - &job, - &mut mem_peak, - &mut canceled_by, - job_dir, - db, - worker_name, - worker_dir, - base_internal_url, - &client.get_token().await, - occupancy_metrics, - ) - .await - .map(|()| serde_json::from_str("{}").unwrap()), + .map(|()| serde_json::from_str("{}").unwrap()), + Connection::Http(_) => { + return Err(Error::internal_err( + "Could not handle app dependency job with agent worker".to_string(), + )); + } + }, JobKind::Identity => Ok(job .args .as_ref() @@ -2210,8 +2204,9 @@ async fn handle_queued_job( let r = handle_code_execution_job( job.as_ref(), preview_data, - db, + conn, client, + parent_runnable_path, job_dir, worker_dir, &mut mem_peak, @@ -2222,6 +2217,7 @@ async fn handle_queued_job( &mut new_args, occupancy_metrics, killpill_rx, + precomputed_agent_info, ) .await; occupancy_metrics.total_duration_of_running_jobs += @@ -2250,10 +2246,10 @@ async fn handle_queued_job( mem_peak, canceled_by, cached_res_path, - client.get_token().await, + &client.token, column_order, new_args, - db, + conn, Some(started.elapsed().as_millis() as i64), ) .await @@ -2292,6 +2288,7 @@ pub struct ContentReqLangEnvs { pub language: Option, pub envs: Option>, pub codebase: Option, + pub schema: Option, } pub async fn get_hub_script_content_and_requirements( @@ -2310,15 +2307,16 @@ pub async fn get_hub_script_content_and_requirements( language: Some(script.language), envs: None, codebase: None, + schema: Some(script.schema.get().to_string()), }) } pub async fn get_script_content_by_hash( script_hash: &ScriptHash, _w_id: &str, - db: &DB, + conn: &Connection, ) -> error::Result { - let (data, metadata) = cache::script::fetch(db, *script_hash).await?; + let (data, metadata) = cache::script::fetch(conn, *script_hash).await?; Ok(ContentReqLangEnvs { content: data.code.clone(), lockfile: data.lock.clone(), @@ -2329,15 +2327,105 @@ pub async fn get_script_content_by_hash( Some(x) if x.ends_with(".tar") => Some(format!("{}.tar", script_hash)), Some(_) => Some(script_hash.to_string()), }, + schema: None, }) } +async fn try_validate_schema( + job: &MiniPulledJob, + conn: &Connection, + schema_validator: Option<&SchemaValidator>, + code: &str, + language: Option<&ScriptLang>, + schema: Option<&String>, +) -> Result<(), Error> { + if let Some(args) = job.args.as_ref() { + if let Some(sv) = schema_validator { + sv.validate(args)?; + } else { + let validators_cache = cache::anon!({ (u8, ScriptHash) => Arc> } in "schemavalidators" <= 1000); + + let sv_fut = async move { + if language.map(|l| should_validate_schema(code, l)).unwrap_or(false) { + if let Some(schema) = schema { + Ok(Some(SchemaValidator::from_schema(schema)?)) + } else { + if let Some(sig) = parse_sig_of_lang( + code, + language, + job.script_entrypoint_override.clone(), + )? { + Ok(Some(schema_validator_from_main_arg_sig(&sig))) + } else { + Err(anyhow!("Job was expected to validate the arguments schema, but no schema was provided and couldn't be inferred from the script for language `{language:?}`. Try removing schema validation for this job").into()) + } + } + } else { Ok(None) } + } + .map_ok(Arc::new); + + let sub_key: u8 = match job.kind { + JobKind::Script => 0, + JobKind::FlowScript => 1, + JobKind::AppScript => 2, + JobKind::Script_Hub => 3, + JobKind::Preview => 4, + JobKind::DeploymentCallback => 5, + JobKind::SingleScriptFlow => 6, + JobKind::Dependencies => 7, + JobKind::Flow => 8, + JobKind::FlowPreview => 9, + JobKind::Identity => 10, + JobKind::FlowDependencies => 11, + JobKind::AppDependencies => 12, + JobKind::Noop => 13, + JobKind::FlowNode => 14, + }; + + let sv = match job.runnable_id { + Some(hash) if job.kind != JobKind::Preview && job.kind != JobKind::FlowPreview => { + sv_fut.cached(validators_cache, (sub_key, hash)).await? + } + _ => sv_fut.await?, + }; + + if sv.is_some() && job.kind == JobKind::Preview { + append_logs( + &job.id, + &job.workspace_id, + "\n--- ARGS VALIDATION ---\nScript contains `schema_validation` annotation, running schema validation for the script arguments...\n", + conn, + ) + .await; + } + + sv.as_ref() + .as_ref() + .map(|sv| sv.validate(args)) + .transpose()?; + + if sv.is_some() { + append_logs( + &job.id, + &job.workspace_id, + "Script arguments were validated!\n\n", + conn, + ) + .await; + } + } + } + + Ok(()) +} + #[tracing::instrument(level = "trace", skip_all)] async fn handle_code_execution_job( - job: &QueuedJob, + job: &MiniPulledJob, preview: Option>, - db: &sqlx::Pool, - client: &AuthedClientBackgroundTask, + conn: &Connection, + client: &AuthedClient, + parent_runnable_path: Option, job_dir: &str, #[allow(unused_variables)] worker_dir: &str, mem_peak: &mut i32, @@ -2348,9 +2436,10 @@ async fn handle_code_execution_job( new_args: &mut Option>>, occupancy_metrics: &mut OccupancyMetrics, killpill_rx: &mut tokio::sync::broadcast::Receiver<()>, + precomputed_agent_info: Option, ) -> error::Result> { let script_hash = || { - job.script_hash + job.runnable_id .ok_or_else(|| Error::internal_err("expected script hash")) }; let (arc_data, arc_metadata, data, metadata): ( @@ -2359,80 +2448,126 @@ async fn handle_code_execution_job( ScriptData, ScriptMetadata, ); - let (ScriptData { code, lock }, ScriptMetadata { language, envs, codebase }) = match job - .job_kind - { + let ( + ScriptData { code, lock }, + ScriptMetadata { language, envs, codebase, schema_validator, schema }, + ) = match job.kind { JobKind::Preview => { - let codebase = match job.script_hash.map(|x| x.0) { + let codebase = match job.runnable_id.map(|x| x.0) { Some(PREVIEW_IS_CODEBASE_HASH) => Some(job.id.to_string()), Some(PREVIEW_IS_TAR_CODEBASE_HASH) => Some(format!("{}.tar", job.id)), _ => None, }; - arc_data = - preview.ok_or_else(|| Error::internal_err("expected preview".to_string()))?; - metadata = ScriptMetadata { language: job.language, codebase, envs: None }; - (arc_data.as_ref(), &metadata) + if codebase.is_none() && job.runnable_id.is_some() { + (arc_data, arc_metadata) = + cache::script::fetch(conn, job.runnable_id.unwrap()).await?; + (arc_data.as_ref(), arc_metadata.as_ref()) + } else { + arc_data = + preview.ok_or_else(|| Error::internal_err("expected preview".to_string()))?; + metadata = ScriptMetadata { + language: job.script_lang, + codebase, + envs: None, + schema: None, + schema_validator: None, + }; + (arc_data.as_ref(), &metadata) + } } JobKind::Script_Hub => { - let ContentReqLangEnvs { content, lockfile, language, envs, codebase } = - get_hub_script_content_and_requirements(job.script_path.as_ref(), Some(db)).await?; + let ContentReqLangEnvs { content, lockfile, language, envs, codebase, schema } = + get_hub_script_content_and_requirements(job.runnable_path.as_ref(), conn.as_sql()) + .await?; + data = ScriptData { code: content, lock: lockfile }; - metadata = ScriptMetadata { language, envs, codebase }; + metadata = ScriptMetadata { language, envs, codebase, schema, schema_validator: None }; (&data, &metadata) } JobKind::Script => { - (arc_data, arc_metadata) = cache::script::fetch(db, script_hash()?).await?; + (arc_data, arc_metadata) = cache::script::fetch(conn, script_hash()?).await?; (arc_data.as_ref(), arc_metadata.as_ref()) } JobKind::FlowScript => { - arc_data = cache::flow::fetch_script(db, FlowNodeId(script_hash()?.0)).await?; - metadata = ScriptMetadata { language: job.language, envs: None, codebase: None }; + arc_data = cache::flow::fetch_script(conn, FlowNodeId(script_hash()?.0)).await?; + metadata = ScriptMetadata { + language: job.script_lang, + envs: None, + codebase: None, + schema: None, + schema_validator: None, + }; (arc_data.as_ref(), &metadata) } JobKind::AppScript => { - arc_data = cache::app::fetch_script(db, AppScriptId(script_hash()?.0)).await?; - metadata = ScriptMetadata { language: job.language, envs: None, codebase: None }; + arc_data = cache::app::fetch_script(conn, AppScriptId(script_hash()?.0)).await?; + metadata = ScriptMetadata { + language: job.script_lang, + envs: None, + codebase: None, + schema: None, + schema_validator: None, + }; (arc_data.as_ref(), &metadata) } - JobKind::DeploymentCallback => { - let script_path = job - .script_path - .as_ref() - .ok_or_else(|| Error::internal_err("expected script path".to_string()))?; - if script_path.starts_with("hub/") { - let ContentReqLangEnvs { content, lockfile, language, envs, codebase } = - get_hub_script_content_and_requirements(Some(script_path), Some(db)).await?; - data = ScriptData { code: content, lock: lockfile }; - metadata = ScriptMetadata { language, envs, codebase }; - (&data, &metadata) - } else { - let hash = sqlx::query_scalar!( - "SELECT hash FROM script WHERE path = $1 AND workspace_id = $2 AND + JobKind::DeploymentCallback => match conn { + Connection::Sql(db) => { + let script_path = job + .runnable_path + .as_ref() + .ok_or_else(|| Error::internal_err("expected script path".to_string()))?; + if script_path.starts_with("hub/") { + let ContentReqLangEnvs { content, lockfile, language, envs, codebase, schema } = + get_hub_script_content_and_requirements(Some(script_path), conn.as_sql()) + .await?; + data = ScriptData { code: content, lock: lockfile }; + metadata = + ScriptMetadata { language, envs, codebase, schema, schema_validator: None }; + (&data, &metadata) + } else { + let hash = sqlx::query_scalar!( + "SELECT hash FROM script WHERE path = $1 AND workspace_id = $2 AND deleted = false AND lock IS not NULL AND lock_error_logs IS NULL", - script_path, - &job.workspace_id - ) - .fetch_optional(db) - .await? - .ok_or_else(|| Error::internal_err("expected script hash".to_string()))?; + script_path, + &job.workspace_id + ) + .fetch_optional(db) + .await? + .ok_or_else(|| Error::internal_err("expected script hash".to_string()))?; - (arc_data, arc_metadata) = cache::script::fetch(db, ScriptHash(hash)).await?; - (arc_data.as_ref(), arc_metadata.as_ref()) + (arc_data, arc_metadata) = cache::script::fetch(conn, ScriptHash(hash)).await?; + (arc_data.as_ref(), arc_metadata.as_ref()) + } } - } + Connection::Http(_) => { + return Err(Error::internal_err( + "Could not handle deployment callback with agent worker".to_string(), + )); + } + }, _ => unreachable!( "handle_code_execution_job should never be reachable with a non-code execution job" ), }; - let language = *language; + try_validate_schema( + job, + conn, + schema_validator.as_ref(), + code, + language.as_ref(), + schema.as_ref(), + ) + .await?; + + let language = language.clone(); if language == Some(ScriptLang::Postgresql) { return do_postgresql( job, &client, &code, - db, + conn, mem_peak, canceled_by, worker_name, @@ -2451,7 +2586,7 @@ async fn handle_code_execution_job( job, &client, &code, - db, + conn, mem_peak, canceled_by, worker_name, @@ -2481,7 +2616,7 @@ async fn handle_code_execution_job( job, &client, &code, - db, + conn, mem_peak, canceled_by, worker_name, @@ -2504,7 +2639,7 @@ async fn handle_code_execution_job( job, &client, &code, - db, + conn, mem_peak, canceled_by, worker_name, @@ -2535,11 +2670,12 @@ async fn handle_code_execution_job( job, &client, &code, - db, + conn, mem_peak, canceled_by, worker_name, occupancy_metrics, + job_dir, ) .await; } @@ -2565,7 +2701,7 @@ async fn handle_code_execution_job( job, &client, &code, - db, + conn, mem_peak, canceled_by, worker_name, @@ -2579,7 +2715,7 @@ async fn handle_code_execution_job( job, &client, &code, - db, + conn, mem_peak, canceled_by, worker_name, @@ -2591,11 +2727,12 @@ async fn handle_code_execution_job( &job.id, &job.workspace_id, "\n--- FETCH TS EXECUTION ---\n", - db, + conn, ) .await; - let reserved_variables = get_reserved_variables(job, &client.get_token().await, db).await?; + let reserved_variables = + get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?; let env_code = format!( "const process = {{ env: {{}} }};\nconst BASE_URL = '{base_internal_url}';\nconst BASE_INTERNAL_URL = '{base_internal_url}';\nprocess.env['BASE_URL'] = BASE_URL;process.env['BASE_INTERNAL_URL'] = BASE_INTERNAL_URL;\n{}", @@ -2610,7 +2747,7 @@ async fn handle_code_execution_job( &client, env_code, code.clone(), - db, + conn, mem_peak, canceled_by, worker_name, @@ -2621,7 +2758,7 @@ async fn handle_code_execution_job( } let lang_str = job - .language + .script_lang .as_ref() .map(|x| format!("{x:?}")) .unwrap_or_else(|| "NO_LANG".to_string()); @@ -2633,8 +2770,8 @@ async fn handle_code_execution_job( job.id ); - let shared_mount = if job.same_worker && job.language != Some(ScriptLang::Deno) { - let folder = if job.language == Some(ScriptLang::Go) { + let shared_mount = if job.same_worker && job.script_lang != Some(ScriptLang::Deno) { + let folder = if job.script_lang == Some(ScriptLang::Go) { "/go" } else { "" @@ -2678,14 +2815,16 @@ mount {{ job, mem_peak, canceled_by, - db, + conn, client, + parent_runnable_path, &code, &shared_mount, base_internal_url, envs, new_args, occupancy_metrics, + precomputed_agent_info, ) .await } @@ -2695,8 +2834,9 @@ mount {{ mem_peak, canceled_by, job, - db, + conn, client, + parent_runnable_path, job_dir, &code, base_internal_url, @@ -2714,8 +2854,9 @@ mount {{ mem_peak, canceled_by, job, - db, + conn, client, + parent_runnable_path, job_dir, &code, base_internal_url, @@ -2724,6 +2865,7 @@ mount {{ &shared_mount, new_args, occupancy_metrics, + precomputed_agent_info, ) .await } @@ -2732,8 +2874,9 @@ mount {{ mem_peak, canceled_by, job, - db, + conn, client, + parent_runnable_path, &code, job_dir, lock.as_ref(), @@ -2750,8 +2893,9 @@ mount {{ mem_peak, canceled_by, job, - db, + conn, client, + parent_runnable_path, &code, job_dir, &shared_mount, @@ -2768,8 +2912,9 @@ mount {{ mem_peak, canceled_by, job, - db, + conn, client, + parent_runnable_path, &code, job_dir, &shared_mount, @@ -2792,8 +2937,9 @@ mount {{ mem_peak, canceled_by, job, - db, + conn, client, + parent_runnable_path, job_dir, &code, base_internal_url, @@ -2815,8 +2961,9 @@ mount {{ mem_peak, canceled_by, job, - db, + conn, client, + parent_runnable_path, &code, job_dir, lock.as_ref(), @@ -2843,8 +2990,9 @@ mount {{ job, mem_peak, canceled_by, - db, + conn, client, + parent_runnable_path, &code, &shared_mount, base_internal_url, @@ -2858,8 +3006,9 @@ mount {{ mem_peak, canceled_by, job, - db, + conn, client, + parent_runnable_path, &code, job_dir, lock.as_ref(), @@ -2871,6 +3020,57 @@ mount {{ ) .await } + Some(ScriptLang::Nu) => { + #[cfg(not(feature = "nu"))] + return Err( + anyhow::anyhow!("Nu is not available because the feature is not enabled").into(), + ); + + #[cfg(feature = "nu")] + handle_nu_job(JobHandlerInputNu { + mem_peak, + canceled_by, + job, + conn, + client, + parent_runnable_path, + inner_content: &code, + job_dir, + requirements_o: lock.as_ref(), + shared_mount: &shared_mount, + base_internal_url, + worker_name, + envs, + occupancy_metrics, + }) + .await + } + Some(ScriptLang::Java) => { + #[cfg(not(feature = "java"))] + return Err(anyhow::anyhow!( + "Java is not available because the feature is not enabled" + ) + .into()); + + #[cfg(feature = "java")] + handle_java_job(JobHandlerInputJava { + mem_peak, + canceled_by, + job, + conn, + client, + parent_runnable_path, + inner_content: &code, + job_dir, + requirements_o: lock.as_ref(), + shared_mount: &shared_mount, + base_internal_url, + worker_name, + envs, + occupancy_metrics, + }) + .await + } _ => panic!("unreachable, language is not supported: {language:#?}"), }; tracing::info!( @@ -2884,3 +3084,67 @@ mount {{ result } + +fn parse_sig_of_lang( + code: &str, + language: Option<&ScriptLang>, + main_override: Option, +) -> Result> { + Ok(if let Some(lang) = language { + match lang { + ScriptLang::Nativets | ScriptLang::Deno | ScriptLang::Bun | ScriptLang::Bunnative => { + Some(windmill_parser_ts::parse_deno_signature( + code, + true, + false, + main_override, + )?) + } + #[cfg(feature = "python")] + ScriptLang::Python3 => Some(windmill_parser_py::parse_python_signature( + code, + main_override, + false, + )?), + #[cfg(not(feature = "python"))] + ScriptLang::Python3 => None, + ScriptLang::Go => Some(windmill_parser_go::parse_go_sig(code)?), + ScriptLang::Bash => Some(windmill_parser_bash::parse_bash_sig(code)?), + ScriptLang::Powershell => Some(windmill_parser_bash::parse_powershell_sig(code)?), + ScriptLang::Postgresql => Some(windmill_parser_sql::parse_pgsql_sig(code)?), + ScriptLang::Mysql => Some(windmill_parser_sql::parse_mysql_sig(code)?), + ScriptLang::Bigquery => Some(windmill_parser_sql::parse_bigquery_sig(code)?), + ScriptLang::Snowflake => Some(windmill_parser_sql::parse_snowflake_sig(code)?), + ScriptLang::Graphql => None, + ScriptLang::Mssql => Some(windmill_parser_sql::parse_mssql_sig(code)?), + ScriptLang::OracleDB => Some(windmill_parser_sql::parse_oracledb_sig(code)?), + #[cfg(feature = "php")] + ScriptLang::Php => Some(windmill_parser_php::parse_php_signature( + code, + main_override, + )?), + #[cfg(not(feature = "php"))] + ScriptLang::Php => None, + #[cfg(feature = "rust")] + ScriptLang::Rust => Some(windmill_parser_rust::parse_rust_signature(code)?), + #[cfg(not(feature = "rust"))] + ScriptLang::Rust => None, + ScriptLang::Ansible => Some(windmill_parser_yaml::parse_ansible_sig(code)?), + #[cfg(feature = "csharp")] + ScriptLang::CSharp => Some(windmill_parser_csharp::parse_csharp_signature(code)?), + #[cfg(not(feature = "csharp"))] + ScriptLang::CSharp => None, + #[cfg(feature = "nu")] + ScriptLang::Nu => Some(windmill_parser_nu::parse_nu_signature(code)?), + #[cfg(not(feature = "nu"))] + ScriptLang::Nu => None, + #[cfg(feature = "java")] + ScriptLang::Java => Some(windmill_parser_java::parse_java_signature(code)?), + #[cfg(not(feature = "java"))] + ScriptLang::Java => None, + // for related places search: ADD_NEW_LANG + } + } else { + None + }) +} diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index 97ffc30719..fe5ffcdeca 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -14,8 +14,7 @@ use std::time::Duration; use crate::common::{cached_result_path, save_in_cache}; use crate::js_eval::{eval_timeout, IdContext}; use crate::{ - AuthedClient, PreviousResult, SameWorkerPayload, SameWorkerSender, SendResult, JOB_TOKEN, - KEEP_JOB_DIR, + AuthedClient, JobCompletedSender, PreviousResult, SameWorkerSender, SendResult, KEEP_JOB_DIR, }; use anyhow::Context; use futures::TryFutureExt; @@ -25,7 +24,6 @@ use serde_json::value::RawValue; use serde_json::{json, Value}; use sqlx::types::Json; use sqlx::{FromRow, Postgres, Transaction}; -use tokio::sync::mpsc::Sender; use tracing::instrument; use uuid::Uuid; use windmill_common::add_time; @@ -37,10 +35,10 @@ use windmill_common::db::Authed; use windmill_common::flow_status::{ ApprovalConditions, FlowStatusModuleWParent, Iterator as FlowIterator, JobResult, }; -use windmill_common::flows::{add_virtual_items_if_necessary, Branch, FlowNodeId}; +use windmill_common::flows::{add_virtual_items_if_necessary, Branch, FlowNodeId, StopAfterIf}; use windmill_common::jobs::{ script_hash_to_tag_and_limits, script_path_to_payload, JobKind, JobPayload, OnBehalfOf, - QueuedJob, RawCode, ENTRYPOINT_OVERRIDE, + RawCode, ENTRYPOINT_OVERRIDE, }; use windmill_common::scripts::ScriptHash; use windmill_common::users::username_to_permissioned_as; @@ -54,10 +52,12 @@ use windmill_common::{ }, flows::{FlowModule, FlowModuleValue, FlowValue, InputTransform, Retry, Suspend}, }; +use windmill_queue::flow_status::Step; use windmill_queue::schedule::get_schedule_opt; use windmill_queue::{ - add_completed_job, add_completed_job_error, append_logs, handle_maybe_scheduled_job, - CanceledBy, PushArgs, PushIsolationLevel, WrappedError, + add_completed_job, add_completed_job_error, append_logs, get_mini_pulled_job, + handle_maybe_scheduled_job, CanceledBy, MiniPulledJob, PushArgs, PushIsolationLevel, + SameWorkerPayload, WrappedError, }; type DB = sqlx::Pool; @@ -80,12 +80,11 @@ pub async fn update_flow_status_after_job_completion( worker_dir: &str, stop_early_override: Option, worker_name: &str, - job_completed_tx: Sender, + job_completed_tx: JobCompletedSender, #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, -) -> error::Result>> { +) -> error::Result>> { // this is manual tailrecursion because async_recursion blows up the stack potentially_crash_for_testing(); - let mut rec = RecUpdateFlowStatusAfterJobCompletion { flow, job_id_for_status: job_id_for_status.clone(), @@ -166,7 +165,7 @@ pub async fn update_flow_status_after_job_completion( pub enum UpdateFlowStatusAfterJobCompletion { Rec(RecUpdateFlowStatusAfterJobCompletion), - Done(Arc), + Done(Arc), NotDone, NonLastParallelBranch, } @@ -184,6 +183,30 @@ struct RecoveryObject { recover: Option, } +fn get_stop_after_if_data( + stop_early: bool, + stop_after_if: Option<&StopAfterIf>, +) -> (bool, Option) { + if let Some(stop_after_if) = stop_after_if { + let err_msg = stop_early + .then(|| { + let err_msg = stop_after_if.error_message.as_ref().and_then(|message| { + let err_start_msg = "Flow early stop"; + let s = if message.is_empty() { + format!("{}: {}", err_start_msg, &stop_after_if.expr) + } else { + format!("{}: {}", err_start_msg, message) + }; + Some(s) + }); + err_msg + }) + .flatten(); + return (stop_after_if.skip_if_stopped, err_msg); + } + return (false, None); +} + // #[instrument(level = "trace", skip_all)] pub async fn update_flow_status_after_job_completion_internal( db: &DB, @@ -199,7 +222,7 @@ pub async fn update_flow_status_after_job_completion_internal( stop_early_override: Option, skip_error_handler: bool, worker_name: &str, - job_completed_tx: Sender, + job_completed_tx: JobCompletedSender, #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, ) -> error::Result { add_time!(bench, "update flow status internal START"); @@ -208,6 +231,7 @@ pub async fn update_flow_status_after_job_completion_internal( flow_job, flow_data, stop_early, + stop_early_err_msg, skip_if_stop_early, nresult, is_failure_step, @@ -216,34 +240,34 @@ pub async fn update_flow_status_after_job_completion_internal( // tracing::debug!("UPDATE FLOW STATUS: {flow:?} {success} {result:?} {w_id} {depth}"); let (job_kind, script_hash, old_status, raw_flow) = sqlx::query!( - "SELECT - job_kind AS \"job_kind!: JobKind\", - script_hash AS \"script_hash: ScriptHash\", - flow_status AS \"flow_status!: Json>\", - raw_flow AS \"raw_flow: Json>\" - FROM v2_as_queue WHERE id = $1 AND workspace_id = $2 LIMIT 1", - flow, - w_id - ) - .fetch_one(db) - .await - .map_err(|e| { - Error::internal_err(format!( - "fetching flow status {flow} while reporting {success} {result:?}: {e:#}" - )) - }) - .and_then(|record| { - Ok(( - record.job_kind, - record.script_hash, - serde_json::from_str::(record.flow_status.0.get()).map_err(|e| { - Error::internal_err(format!( - "requiring current module to be parsable as FlowStatus: {e:?}" - )) - })?, - record.raw_flow, - )) - })?; + "SELECT + kind AS \"job_kind!: JobKind\", + runnable_id AS \"script_hash: ScriptHash\", + flow_status AS \"flow_status!: Json>\", + raw_flow AS \"raw_flow: Json>\" + FROM v2_job INNER JOIN v2_job_status ON v2_job.id = v2_job_status.id WHERE v2_job.id = $1 AND v2_job.workspace_id = $2 LIMIT 1", + flow, + w_id + ) + .fetch_one(db) + .await + .map_err(|e| { + Error::internal_err(format!( + "fetching flow status {flow} while reporting {success} {result:?}: {e:#}" + )) + }) + .and_then(|record| { + Ok(( + record.job_kind, + record.script_hash, + serde_json::from_str::(record.flow_status.0.get()).map_err(|e| { + Error::internal_err(format!( + "requiring current module to be parsable as FlowStatus: {e:?}" + )) + })?, + record.raw_flow, + )) + })?; let flow_data = cache::job::fetch_flow(db, job_kind, script_hash) .or_else(|_| cache::job::fetch_preview_flow(db, &flow, raw_flow)) @@ -299,7 +323,7 @@ pub async fn update_flow_status_after_job_completion_internal( let is_failure_step = old_status.step >= old_status.modules.len() as i32 && old_status.modules.len() > 0; - let (mut stop_early, mut skip_if_stop_early, continue_on_error) = + let (mut stop_early, mut stop_early_err_msg, mut skip_if_stop_early, continue_on_error) = if let Some(se) = stop_early_override { //do not stop early if module is a flow step let step = match module_step { @@ -315,7 +339,6 @@ pub async fn update_flow_status_after_job_completion_internal( } current_module - .as_ref() .map(|module| { serde_json::from_str::(module.value.get()) .map(|v| v.r#type == "flow") @@ -327,19 +350,19 @@ pub async fn update_flow_status_after_job_completion_internal( }; if is_flow { - (false, false, false) + (false, None, false, false) } else { - (true, se, false) + (true, None, se, false) } } else if is_failure_step || matches!(module_step, Step::PreprocessorStep) { - (false, false, false) - } else if let Some(current_module) = current_module.as_ref() { + (false, None, false, false) + } else if let Some(current_module) = current_module { let stop_early = success && !is_branch_all - && if let Some(ref expr) = current_module + && if let Some(expr) = current_module .stop_after_if .as_ref() - .map(|x| x.expr.clone()) + .map(|x| x.expr.as_str()) { let all_iters = match &module_status { @@ -352,9 +375,9 @@ pub async fn update_flow_status_after_job_completion_internal( }; let args = sqlx::query_scalar!( "SELECT - args AS \"args: Json>>\" - FROM v2_job - WHERE id = $1", + args AS \"args: Json>>\" + FROM v2_job + WHERE id = $1", flow ) .fetch_one(db) @@ -376,42 +399,53 @@ pub async fn update_flow_status_after_job_completion_internal( } else { false }; + let (skip_if_stopped, stop_early_err_msg) = + get_stop_after_if_data(stop_early, current_module.stop_after_if.as_ref()); ( stop_early, - current_module - .stop_after_if - .as_ref() - .map(|x| x.skip_if_stopped) - .unwrap_or(false), + stop_early_err_msg.filter(|_| !(is_loop || is_branch_all)), + skip_if_stopped, current_module.continue_on_error.unwrap_or(false), ) } else { - (false, false, false) + (false, None, false, false) }; - let skip_branch_failure = match module_status { + let skip_seq_branch_failure = match module_status { FlowStatusModule::InProgress { branchall: Some(BranchAllStatus { branch, .. }), - parallel, + parallel: false, .. - } => compute_skip_branchall_failure( - job_id_for_status, - *branch, - *parallel, - db, - current_module, - ) - .await? - .unwrap_or(false), + } => { + compute_skip_branchall_failure(branch.to_owned(), false, current_module, &None) + .await? + } _ => false, }; if matches!(module_step, Step::PreprocessorStep) { sqlx::query!( - "UPDATE v2_job SET - args = (SELECT result FROM v2_job_completed WHERE id = $1), - preprocessed = TRUE - WHERE id = $2", + "WITH job_result AS ( + SELECT result + FROM v2_job_completed + WHERE id = $1 + ) + UPDATE v2_job + SET args = COALESCE( + CASE + WHEN job_result.result IS NULL THEN NULL + WHEN jsonb_typeof(job_result.result) = 'object' + THEN job_result.result + WHEN jsonb_typeof(job_result.result) = 'null' + THEN NULL + ELSE jsonb_build_object('value', job_result.result) + END, + '{}'::jsonb + ), + preprocessed = TRUE + FROM job_result + WHERE v2_job.id = $2; + ", job_id_for_status, flow ) @@ -446,46 +480,46 @@ pub async fn update_flow_status_after_job_completion_internal( }; let nindex = if let Some(position) = position { - sqlx::query_scalar!( - "UPDATE v2_job_status SET - flow_status = JSONB_SET( - JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4), - ARRAY['modules', $1::TEXT, 'iterator', 'index'], - ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb - ) - WHERE id = $2 - RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", - old_status.step, - flow, - position as i32, - json!(success) - ) - } else { - sqlx::query_scalar!( - "UPDATE v2_job_status SET - flow_status = JSONB_SET( - flow_status, - ARRAY['modules', $1::TEXT, 'iterator', 'index'], - ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb - ) - WHERE id = $2 - RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", - old_status.step, - flow - ) - } - .fetch_one(&mut *tx) - .await.map_err(|e| { - Error::internal_err(format!( - "error while fetching iterator index: {e:#}" - )) - })? - .ok_or_else(|| Error::internal_err(format!("requiring an index in InProgress")))?; + sqlx::query_scalar!( + "UPDATE v2_job_status SET + flow_status = JSONB_SET( + JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4), + ARRAY['modules', $1::TEXT, 'iterator', 'index'], + ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb + ) + WHERE id = $2 + RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", + old_status.step, + flow, + position as i32, + json!(success) + ) + } else { + sqlx::query_scalar!( + "UPDATE v2_job_status SET + flow_status = JSONB_SET( + flow_status, + ARRAY['modules', $1::TEXT, 'iterator', 'index'], + ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb + ) + WHERE id = $2 + RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", + old_status.step, + flow + ) + } + .fetch_one(&mut *tx) + .await.map_err(|e| { + Error::internal_err(format!( + "error while fetching iterator index: {e:#}" + )) + })? + .ok_or_else(|| Error::internal_err(format!("requiring an index in InProgress")))?; tracing::info!( - "parallel iteration {job_id_for_status} of flow {flow} update nindex: {nindex} len: {len}", - nindex = nindex, - len = itered.len() - ); + "parallel iteration {job_id_for_status} of flow {flow} update nindex: {nindex} len: {len}", + nindex = nindex, + len = itered.len() + ); (nindex, itered.len() as i32) } (_, Some(BranchAllStatus { len, .. })) => { @@ -496,42 +530,42 @@ pub async fn update_flow_status_after_job_completion_internal( }; let nindex = if let Some(position) = position { - sqlx::query_scalar!( - "UPDATE v2_job_status SET - flow_status = JSONB_SET( - JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4), - ARRAY['modules', $1::TEXT, 'branchall', 'branch'], - ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb - ) - WHERE id = $2 - RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", - old_status.step, - flow, - position as i32, - json!(success) - ) - } else { - sqlx::query_scalar!( - "UPDATE v2_job_status SET - flow_status = JSONB_SET( - flow_status, - ARRAY['modules', $1::TEXT, 'branchall', 'branch'], - ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb - ) - WHERE id = $2 - RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", - old_status.step, - flow - ) - } - .fetch_one(&mut *tx) - .await - .map_err(|e| { - Error::internal_err(format!( - "error while fetching branchall index: {e:#}" - )) - })? - .ok_or_else(|| Error::internal_err(format!("requiring an index in InProgress")))?; + sqlx::query_scalar!( + "UPDATE v2_job_status SET + flow_status = JSONB_SET( + JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4), + ARRAY['modules', $1::TEXT, 'branchall', 'branch'], + ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb + ) + WHERE id = $2 + RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", + old_status.step, + flow, + position as i32, + json!(success) + ) + } else { + sqlx::query_scalar!( + "UPDATE v2_job_status SET + flow_status = JSONB_SET( + flow_status, + ARRAY['modules', $1::TEXT, 'branchall', 'branch'], + ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb + ) + WHERE id = $2 + RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", + old_status.step, + flow + ) + } + .fetch_one(&mut *tx) + .await + .map_err(|e| { + Error::internal_err(format!( + "error while fetching branchall index: {e:#}" + )) + })? + .ok_or_else(|| Error::internal_err(format!("requiring an index in InProgress")))?; (nindex, *len as i32) } _ => Err(Error::internal_err(format!( @@ -554,50 +588,51 @@ pub async fn update_flow_status_after_job_completion_internal( } let new_status = if skip_loop_failures - || sqlx::query_scalar!( - "SELECT success AS \"success!\" FROM v2_as_completed_job WHERE id = ANY($1)", - jobs.as_slice() - ) - .fetch_all(&mut *tx) - .await - .map_err(|e| { - Error::internal_err(format!( - "error while fetching sucess from completed_jobs: {e:#}" - )) - })? - .into_iter() - .all(|x| x) - { - success = true; - FlowStatusModule::Success { - id: module_status.id(), - job: job_id_for_status.clone(), - flow_jobs: Some(jobs.clone()), - flow_jobs_success: flow_jobs_success.clone(), - branch_chosen: None, - approvers: vec![], - failed_retries: vec![], - skipped: false, - } - } else { - success = false; - FlowStatusModule::Failure { - id: module_status.id(), - job: job_id_for_status.clone(), - flow_jobs: Some(jobs.clone()), - flow_jobs_success: flow_jobs_success.clone(), - branch_chosen: None, - failed_retries: vec![], - } - }; + || sqlx::query_scalar!( + "SELECT status = 'success' OR status = 'skipped' AS \"success!\" FROM v2_job_completed WHERE id = ANY($1)", + jobs.as_slice() + ) + .fetch_all(&mut *tx) + .await + .map_err(|e| { + Error::internal_err(format!( + "error while fetching sucess from completed_jobs: {e:#}" + )) + })? + .into_iter() + .all(|x| x) + { + success = true; + FlowStatusModule::Success { + id: module_status.id(), + job: job_id_for_status.clone(), + flow_jobs: Some(jobs.clone()), + flow_jobs_success: flow_jobs_success.clone(), + branch_chosen: None, + approvers: vec![], + failed_retries: vec![], + skipped: false, + } + } else { + success = false; + FlowStatusModule::Failure { + id: module_status.id(), + job: job_id_for_status.clone(), + flow_jobs: Some(jobs.clone()), + flow_jobs_success: flow_jobs_success.clone(), + branch_chosen: None, + failed_retries: vec![], + } + }; let r = sqlx::query_scalar!( - "DELETE FROM parallel_monitor_lock WHERE parent_flow_id = $1 RETURNING last_ping", - flow, - ).fetch_optional(db).await.map_err(|e| { - Error::internal_err(format!( - "error while deleting parallel_monitor_lock: {e:#}" - )) - })?; + "DELETE FROM parallel_monitor_lock WHERE parent_flow_id = $1 RETURNING last_ping", + flow, + ).fetch_optional(db).await.map_err(|e| { + Error::internal_err(format!( + "error while deleting parallel_monitor_lock: {e:#}" + )) + })?; + if r.is_some() { tracing::info!( "parallel flow has removed lock on its parent, last ping was {:?}", @@ -615,10 +650,10 @@ pub async fn update_flow_status_after_job_completion_internal( if parallelism.is_some() { sqlx::query!( "UPDATE v2_job_queue q SET suspend = 0 - FROM v2_job j, v2_job_status f - WHERE parent_job = $1 - AND f.id = j.id AND q.id = j.id - AND suspend = $2 AND (f.flow_status->'step')::int = 0", + FROM v2_job j, v2_job_status f + WHERE parent_job = $1 + AND f.id = j.id AND q.id = j.id + AND suspend = $2 AND (f.flow_status->'step')::int = 0", flow, nindex ) @@ -632,12 +667,12 @@ pub async fn update_flow_status_after_job_completion_internal( } let r = sqlx::query_scalar!( - "DELETE FROM parallel_monitor_lock WHERE parent_flow_id = $1 and job_id = $2 RETURNING last_ping", - flow, - job_id_for_status - ).fetch_optional(db).await.map_err(|e| { - Error::internal_err(format!("error while removing parallel_monitor_lock: {e:#}")) - })?; + "DELETE FROM parallel_monitor_lock WHERE parent_flow_id = $1 and job_id = $2 RETURNING last_ping", + flow, + job_id_for_status + ).fetch_optional(db).await.map_err(|e| { + Error::internal_err(format!("error while removing parallel_monitor_lock: {e:#}")) + })?; if r.is_some() { tracing::info!( "parallel flow has removed lock on its parent, last ping was {:?}", @@ -678,7 +713,7 @@ pub async fn update_flow_status_after_job_completion_internal( flow_jobs_success, flow_jobs, .. - } if branch.to_owned() < len - 1 && (success || skip_branch_failure) => { + } if branch.to_owned() < len - 1 && (success || skip_seq_branch_failure) => { if let Some(jobs) = flow_jobs { set_success_in_flow_job_success( flow_jobs_success, @@ -718,7 +753,9 @@ pub async fn update_flow_status_after_job_completion_internal( } } } - if success || (flow_jobs.is_some() && (skip_loop_failures || skip_branch_failure)) { + if success + || (flow_jobs.is_some() && (skip_loop_failures || skip_seq_branch_failure)) + { let is_skipped = if current_module.as_ref().is_some_and(|m| m.skip_if.is_some()) { sqlx::query_scalar!( @@ -775,11 +812,22 @@ pub async fn update_flow_status_after_job_completion_internal( } }; + let skip_parallel_branchall_failure = match (module_status, new_status.as_ref()) { + ( + FlowStatusModule::InProgress { branchall: Some(_), parallel: true, .. }, + Some(FlowStatusModule::Success { flow_jobs_success, .. }), + ) => compute_skip_branchall_failure(0, true, current_module, flow_jobs_success).await?, + ( + FlowStatusModule::InProgress { branchall: Some(_), parallel: true, .. }, + Some(FlowStatusModule::Failure { flow_jobs_success, .. }), + ) => compute_skip_branchall_failure(0, true, current_module, flow_jobs_success).await?, + _ => false, + }; let step_counter = if inc_step_counter { sqlx::query!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['step'], $1) - WHERE id = $2", + SET flow_status = JSONB_SET(flow_status, ARRAY['step'], $1) + WHERE id = $2", json!(old_status.step + 1), flow ) @@ -793,6 +841,13 @@ pub async fn update_flow_status_after_job_completion_internal( old_status.step }; + // tracing::error!( + // "step_counter: {:?} {} {inc_step_counter} {flow}", + // step_counter, + // old_status.step, + // ); + // panic!("stop"); + /* is_last_step is true when the step_counter (the next step index) is an invalid index */ let is_last_step = usize::try_from(step_counter) .map(|i| !(..old_status.modules.len()).contains(&i)) @@ -801,20 +856,20 @@ pub async fn update_flow_status_after_job_completion_internal( if let Some(new_status) = new_status.as_ref() { if is_failure_step { let parent_module = sqlx::query_scalar!( - "SELECT flow_status->'failure_module'->>'parent_module' FROM v2_job_status WHERE id = $1", - flow - ) - .fetch_one(&mut *tx) - .await.map_err(|e| { - Error::internal_err(format!( - "error while fetching failure module: {e:#}" - )) - })?; + "SELECT flow_status->'failure_module'->>'parent_module' FROM v2_job_status WHERE id = $1", + flow + ) + .fetch_one(&mut *tx) + .await.map_err(|e| { + Error::internal_err(format!( + "error while fetching failure module: {e:#}" + )) + })?; sqlx::query!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['failure_module'], $1) - WHERE id = $2", + SET flow_status = JSONB_SET(flow_status, ARRAY['failure_module'], $1) + WHERE id = $2", json!(FlowStatusModuleWParent { parent_module, module_status: new_status.clone() @@ -831,8 +886,8 @@ pub async fn update_flow_status_after_job_completion_internal( } else if matches!(module_step, Step::PreprocessorStep) { sqlx::query!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1) - WHERE id = $2", + SET flow_status = JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1) + WHERE id = $2", json!(new_status), flow ) @@ -846,8 +901,8 @@ pub async fn update_flow_status_after_job_completion_internal( } else { sqlx::query!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2) - WHERE id = $3", + SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2) + WHERE id = $3", old_status.step.to_string(), json!(new_status), flow @@ -860,40 +915,44 @@ pub async fn update_flow_status_after_job_completion_internal( if let Some(job_result) = new_status.job_result() { sqlx::query!( - "UPDATE v2_job_status - SET flow_leaf_jobs = JSONB_SET(coalesce(flow_leaf_jobs, '{}'::jsonb), ARRAY[$1::TEXT], $2) - WHERE COALESCE((SELECT flow_innermost_root_job FROM v2_job WHERE id = $3), $3) = id", - new_status.id(), - json!(job_result), - flow - ) - .execute(&mut *tx) - .await.map_err(|e| { - Error::internal_err(format!( - "error while setting leaf jobs: {e:#}" - )) - })?; + "UPDATE v2_job_status + SET flow_leaf_jobs = JSONB_SET(coalesce(flow_leaf_jobs, '{}'::jsonb), ARRAY[$1::TEXT], $2) + WHERE COALESCE((SELECT flow_innermost_root_job FROM v2_job WHERE id = $3), $3) = id", + new_status.id(), + json!(job_result), + flow + ) + .execute(&mut *tx) + .await.map_err(|e| { + Error::internal_err(format!( + "error while setting leaf jobs: {e:#}" + )) + })?; } } } - let nresult = match &new_status { - Some(FlowStatusModule::Success { flow_jobs: Some(jobs), .. }) - | Some(FlowStatusModule::Failure { flow_jobs: Some(jobs), .. }) => { - Arc::new(retrieve_flow_jobs_results(db, w_id, jobs).await?) + let mut nresult = if let Some(stop_early_err_msg) = stop_early_err_msg.as_ref() { + Arc::new(to_raw_value(stop_early_err_msg)) + } else { + match &new_status { + Some(FlowStatusModule::Success { flow_jobs: Some(jobs), .. }) + | Some(FlowStatusModule::Failure { flow_jobs: Some(jobs), .. }) => { + Arc::new(retrieve_flow_jobs_results(db, w_id, jobs).await?) + } + _ => result.clone(), } - _ => result.clone(), }; match &new_status { Some(FlowStatusModule::Success { .. }) if is_loop || is_branch_all => { - if let Some(ref expr) = current_module + if let Some(stop_after_all_iters_if) = current_module .as_ref() - .and_then(|m| m.stop_after_all_iters_if.as_ref().map(|x| x.expr.clone())) + .and_then(|m| m.stop_after_all_iters_if.as_ref()) { let args = sqlx::query_scalar!( "SELECT args AS \"args: Json>>\" - FROM v2_job WHERE id = $1", + FROM v2_job WHERE id = $1", flow ) .fetch_one(db) @@ -903,7 +962,7 @@ pub async fn update_flow_status_after_job_completion_internal( })?; let should_stop = compute_bool_from_expr( - &expr, + &stop_after_all_iters_if.expr, Marc::new(args.unwrap_or_default().0), nresult.clone(), None, @@ -915,15 +974,14 @@ pub async fn update_flow_status_after_job_completion_internal( .await?; if should_stop { - stop_early = should_stop; - skip_if_stop_early = current_module - .as_ref() - .and_then(|m| { - m.stop_after_all_iters_if - .as_ref() - .map(|x| x.skip_if_stopped) - }) - .unwrap_or(false); + stop_early = true; + let (skip_if_stopped, err_msg_internal) = + get_stop_after_if_data(should_stop, Some(stop_after_all_iters_if)); + skip_if_stop_early = skip_if_stopped; + if err_msg_internal.is_some() { + stop_early_err_msg = err_msg_internal; + nresult = Arc::new(to_raw_value(&stop_early_err_msg)); + } } } } @@ -935,8 +993,8 @@ pub async fn update_flow_status_after_job_completion_internal( { sqlx::query!( "UPDATE v2_job_status - SET flow_status = flow_status - 'retry' - WHERE id = $1", + SET flow_status = flow_status - 'retry' + WHERE id = $1", flow ) .execute(&mut *tx) @@ -944,29 +1002,28 @@ pub async fn update_flow_status_after_job_completion_internal( .context("remove flow status retry")?; } - let flow_job = sqlx::query_as::<_, QueuedJob>( - "SELECT * FROM v2_as_queue WHERE id = $1 AND workspace_id = $2", - ) - .bind(flow) - .bind(w_id) - .fetch_optional(&mut *tx) - .await - .map_err(Into::::into)? - .ok_or_else(|| Error::internal_err(format!("requiring flow to be in the queue")))?; + let flow_job = get_mini_pulled_job(&mut *tx, &flow) + .await? + .ok_or_else(|| Error::internal_err(format!("requiring flow to be in the queue")))?; tx.commit().await?; let job_root = flow_job - .root_job + .flow_innermost_root_job .map(|x| x.to_string()) .unwrap_or_else(|| "none".to_string()); tracing::info!(id = %flow_job.id, root_id = %job_root, "update flow status"); let should_continue_flow = match success { _ if stop_early => false, - _ if flow_job.canceled => false, + _ if flow_job.is_canceled() => false, true => !is_last_step, false if unrecoverable => false, - false if skip_branch_failure || skip_loop_failures || continue_on_error => { + false + if skip_seq_branch_failure + || skip_parallel_branchall_failure + || skip_loop_failures + || continue_on_error => + { !is_last_step } false @@ -1010,6 +1067,7 @@ pub async fn update_flow_status_after_job_completion_internal( flow_job, flow_data, stop_early, + stop_early_err_msg, skip_if_stop_early, nresult, is_failure_step, @@ -1021,7 +1079,7 @@ pub async fn update_flow_status_after_job_completion_internal( let done = if !should_continue_flow { { - let logs = if flow_job.canceled { + let logs = if flow_job.is_canceled() { "Flow job canceled\n".to_string() } else if stop_early { format!("Flow job stopped early because of a stop early predicate returning true\n") @@ -1030,16 +1088,16 @@ pub async fn update_flow_status_after_job_completion_internal( } else { "Flow job completed with error\n".to_string() }; - append_logs(&flow_job.id, w_id, logs, db).await; + append_logs(&flow_job.id, w_id, logs, &db.into()).await; } #[cfg(feature = "enterprise")] if flow_job.parent_job.is_none() { // run the cleanup step only when the root job is complete if !_cleanup_module.flow_jobs_to_clean.is_empty() { tracing::debug!( - "Cleaning up jobs arguments, result and logs as they were marked as delete_after_use {:?}", - _cleanup_module.flow_jobs_to_clean - ); + "Cleaning up jobs arguments, result and logs as they were marked as delete_after_use {:?}", + _cleanup_module.flow_jobs_to_clean + ); sqlx::query!( "UPDATE v2_job SET args = '{}'::jsonb WHERE id = ANY($1)", &_cleanup_module.flow_jobs_to_clean, @@ -1060,7 +1118,7 @@ pub async fn update_flow_status_after_job_completion_internal( })?; } } - if flow_job.canceled { + if flow_job.is_canceled() { add_completed_job_error( db, &flow_job, @@ -1088,14 +1146,15 @@ pub async fn update_flow_status_after_job_completion_internal( } let success = success && (!is_failure_step || result_has_recover_true(nresult.clone())) - && !skip_error_handler; + && !skip_error_handler + && stop_early_err_msg.is_none(); add_time!(bench, "flow status update 1"); if success { add_completed_job( db, &flow_job, - success, + true, stop_early && skip_if_stop_early, Json(&nresult), None, @@ -1109,7 +1168,7 @@ pub async fn update_flow_status_after_job_completion_internal( add_completed_job( db, &flow_job, - success, + false, stop_early && skip_if_stop_early, Json( &serde_json::from_str::(nresult.get()).unwrap_or_else( @@ -1137,6 +1196,7 @@ pub async fn update_flow_status_after_job_completion_internal( same_worker_tx.clone(), worker_dir, job_completed_tx, + worker_name, ) .warn_after_seconds(10) .await @@ -1147,7 +1207,7 @@ pub async fn update_flow_status_after_job_completion_internal( &flow_job.id, w_id, format!("Unexpected error during flow chaining:\n{:#?}", e), - db, + &db.into(), ) .await; let _ = add_completed_job_error(db, &flow_job, 0, None, e, worker_name, true, None) @@ -1163,7 +1223,7 @@ pub async fn update_flow_status_after_job_completion_internal( let _ = tokio::fs::remove_dir_all(format!("{worker_dir}/{}", flow_job.id)).await; } - if flow_job.is_flow_step { + if flow_job.is_flow_step() { if let Some(parent_job) = flow_job.parent_job { tracing::info!(subflow_id = %flow_job.id, parent_id = %parent_job, "subflow is finished, updating parent flow status"); @@ -1207,12 +1267,12 @@ async fn set_success_in_flow_job_success<'c>( if let Some(position) = position { sqlx::query!( "UPDATE v2_job_status SET - flow_status = JSONB_SET( - flow_status, - ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], - $4 - ) - WHERE id = $2", + flow_status = JSONB_SET( + flow_status, + ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], + $4 + ) + WHERE id = $2", old_status.step as i32, flow, position as i32, @@ -1235,8 +1295,8 @@ async fn retrieve_flow_jobs_results( ) -> error::Result> { let results = sqlx::query!( "SELECT result, id - FROM v2_job_completed - WHERE id = ANY($1) AND workspace_id = $2", + FROM v2_job_completed + WHERE id = ANY($1) AND workspace_id = $2", job_uuids.as_slice(), w_id ) @@ -1254,47 +1314,48 @@ async fn retrieve_flow_jobs_results( .ok_or_else(|| Error::internal_err(format!("missing job result for {}", j))) }) .collect::, _>>()?; - tracing::debug!("Retrieved results for flow jobs {:?}", results); Ok(to_raw_value(&results)) } async fn compute_skip_branchall_failure<'c>( - job: &Uuid, branch: usize, parallel: bool, - db: &DB, flow_module: Option<&FlowModule>, -) -> Result, Error> { - let branch = if parallel { - sqlx::query_scalar!("SELECT runnable_path FROM v2_job WHERE id = $1", job) - .fetch_one(db) - .await - .map_err(|e| { - Error::internal_err(format!("error during retrieval of branchall index: {e:#}")) - })? - .map(|p| { - BRANCHALL_INDEX_RE - .captures(&p) - .map(|x| x.get(1).unwrap().as_str().parse::().ok()) - .flatten() - .ok_or(Error::internal_err(format!( - "could not parse branchall index from path: {p}" - ))) - }) - .ok_or_else(|| { - Error::internal_err(format!("no branchall script path found for job {job}")) - })?? - } else { - branch as i32 - }; - Ok(flow_module + successes: &Option>>, +) -> windmill_common::error::Result { + let branches = flow_module .and_then(|x| x.get_branches_skip_failures().ok()) - .and_then(|x| { + .map(|x| { x.branches - .get(branch as usize) - .map(|x| x.skip_failure.unwrap_or(false)) - })) + .iter() + .map(|b| b.skip_failure.unwrap_or(false)) + .collect::>() + }); + if parallel { + if let Some(successes) = successes { + for (i, success) in successes.iter().enumerate() { + if branches + .as_ref() + .and_then(|x| x.get(i)) + .unwrap_or(&false) + .to_owned() + { + continue; + } + if !(success.unwrap_or(false)) { + return Ok(false); + } + } + Ok(true) + } else { + Ok(false) + } + } else { + Ok(branches + .and_then(|x| x.get(branch as usize).map(|b| b.to_owned())) + .unwrap_or(false)) + } } // async fn retrieve_cleanup_module<'c>(flow_uuid: Uuid, db: &DB) -> Result { @@ -1367,102 +1428,6 @@ async fn compute_bool_from_expr( } } -pub async fn update_flow_status_in_progress( - db: &DB, - _w_id: &str, - flow: Uuid, - job_in_progress: Uuid, -) -> error::Result { - let step = get_step_of_flow_status(db, flow).await?; - match step { - Step::Step(step) => { - sqlx::query!( - "UPDATE v2_job_status SET - flow_status = jsonb_set( - jsonb_set(flow_status, ARRAY['modules', $3::INTEGER::TEXT, 'job'], to_jsonb($1::UUID::TEXT)), - ARRAY['modules', $3::INTEGER::TEXT, 'type'], - to_jsonb('InProgress'::text) - ) - WHERE id = $2", - job_in_progress, - flow, - step as i32 - ) - .execute(db) - .await?; - } - Step::PreprocessorStep => { - sqlx::query!( - "UPDATE v2_job_status SET - flow_status = jsonb_set( - jsonb_set(flow_status, ARRAY['preprocessor_module', 'job'], to_jsonb($1::UUID::TEXT)), - ARRAY['preprocessor_module', 'type'], - to_jsonb('InProgress'::text) - ) - WHERE id = $2", - job_in_progress, - flow - ) - .execute(db) - .await?; - } - Step::FailureStep => { - sqlx::query!( - "UPDATE v2_job_status SET - flow_status = jsonb_set( - jsonb_set(flow_status, ARRAY['failure_module', 'job'], to_jsonb($1::UUID::TEXT)), - ARRAY['failure_module', 'type'], - to_jsonb('InProgress'::text) - ) - WHERE id = $2", - job_in_progress, - flow - ) - .execute(db) - .await?; - } - } - - Ok(step) -} - -#[derive(Debug, Copy, Clone)] -pub enum Step { - Step(usize), - PreprocessorStep, - FailureStep, -} - -impl Step { - fn from_i32_and_len(step: i32, len: usize) -> Self { - if step < 0 { - Step::PreprocessorStep - } else if (step as usize) < len { - Step::Step(step as usize) - } else { - Step::FailureStep - } - } -} - -#[instrument(level = "trace", skip_all)] -pub async fn get_step_of_flow_status(db: &DB, id: Uuid) -> error::Result { - let r = sqlx::query!( - "SELECT (flow_status->'step')::integer as step, jsonb_array_length(flow_status->'modules') as len - FROM v2_job_status WHERE id = $1", - id - ) - .fetch_one(db) - .await - .map_err(|e| Error::internal_err(format!("fetching step flow status: {e:#}")))?; - - if let Some(step) = r.step { - Ok(Step::from_i32_and_len(step, r.len.unwrap_or(0) as usize)) - } else { - Err(Error::internal_err("step is null".to_string())) - } -} - /// resumes should be in order of timestamp ascending, so that more recent are at the end #[instrument(level = "trace", skip_all)] async fn transform_input( @@ -1520,27 +1485,29 @@ async fn transform_input( #[instrument(level = "trace", skip_all)] pub async fn handle_flow( - flow_job: Arc, + flow_job: Arc, flow_data: &cache::FlowData, db: &sqlx::Pool, client: &AuthedClient, last_result: Option>>, same_worker_tx: SameWorkerSender, worker_dir: &str, - job_completed_tx: Sender, + job_completed_tx: JobCompletedSender, + worker_name: &str, ) -> anyhow::Result<()> { let flow = flow_data.value(); let status = flow_job .parse_flow_status() .with_context(|| "Unable to parse flow status")?; - if !flow_job.is_flow_step + let schedule_path = flow_job.schedule_path(); + if !flow_job.is_flow_step() && status.retry.fail_count == 0 - && flow_job.schedule_path.is_some() - && flow_job.script_path.is_some() + && schedule_path.is_some() + && flow_job.runnable_path.is_some() && status.step == 0 { - let schedule_path = flow_job.schedule_path.as_ref().unwrap(); + let schedule_path = schedule_path.as_ref().unwrap(); let schedule = get_schedule_opt(db, &flow_job.workspace_id, schedule_path) .warn_after_seconds(5) @@ -1551,7 +1518,7 @@ pub async fn handle_flow( db, &flow_job, &schedule, - flow_job.script_path.as_ref().unwrap(), + flow_job.runnable_path.as_ref().unwrap(), &flow_job.workspace_id, ) .warn_after_seconds(5) @@ -1570,20 +1537,24 @@ pub async fn handle_flow( ); } } + let mut rec = Some(PushNextFlowJobRec { flow_job: flow_job, status: status }); + while let Some(nrec) = rec { + rec = push_next_flow_job( + nrec.flow_job, + nrec.status, + flow, + db, + client, + last_result.clone(), + same_worker_tx.clone(), + worker_dir, + job_completed_tx.clone(), + worker_name, + ) + .warn_after_seconds(10) + .await?; + } - push_next_flow_job( - flow_job, - status, - flow, - db, - client, - last_result, - same_worker_tx, - worker_dir, - job_completed_tx, - ) - .warn_after_seconds(10) - .await?; Ok(()) } @@ -1626,10 +1597,15 @@ fn potentially_crash_for_testing() { lazy_static::lazy_static! { pub static ref EHM: HashMap> = HashMap::new(); } + +struct PushNextFlowJobRec { + flow_job: Arc, + status: FlowStatus, +} // #[async_recursion] // #[instrument(level = "trace", skip_all)] async fn push_next_flow_job( - flow_job: Arc, + flow_job: Arc, mut status: FlowStatus, flow: &FlowValue, db: &sqlx::Pool, @@ -1637,10 +1613,11 @@ async fn push_next_flow_job( last_job_result: Option>>, same_worker_tx: SameWorkerSender, worker_dir: &str, - job_completed_tx: Sender, -) -> error::Result<()> { + job_completed_tx: JobCompletedSender, + worker_name: &str, +) -> error::Result> { let job_root = flow_job - .root_job + .flow_innermost_root_job .map(|x| x.to_string()) .unwrap_or_else(|| "none".to_string()); tracing::info!(id = %flow_job.id, root_id = %job_root, "pushing next flow job"); @@ -1660,7 +1637,7 @@ async fn push_next_flow_job( Step::FailureStep => status.failure_module.module_status.clone(), }; - let fj: mappable_rc::Marc = flow_job.clone().into(); + let fj: mappable_rc::Marc = flow_job.clone().into(); let arc_flow_job_args: Marc>> = Marc::map(fj, |x| { if let Some(args) = &x.args { &args.0 @@ -1693,25 +1670,39 @@ async fn push_next_flow_job( )) })?; - return Ok(()); + return Ok(None); } if matches!(step, Step::Step(0)) { - if !flow_job.is_flow_step && flow_job.schedule_path.is_some() { + if !flow_job.is_flow_step() && flow_job.schedule_path().is_some() { + let schedule_path = flow_job.schedule_path(); let no_flow_overlap = sqlx::query_scalar!( "SELECT no_flow_overlap FROM schedule WHERE path = $1 AND workspace_id = $2", - flow_job.schedule_path.as_ref().unwrap(), + schedule_path.as_ref().unwrap(), flow_job.workspace_id.as_str() ) .fetch_one(db) .await?; if no_flow_overlap { let overlapping = sqlx::query_scalar!( - "SELECT id AS \"id!\" FROM v2_as_queue WHERE schedule_path = $1 AND workspace_id = $2 AND id != $3 AND running = true", - flow_job.schedule_path.as_ref().unwrap(), - flow_job.workspace_id.as_str(), - flow_job.id - ).fetch_all(db).await?; + // Query plan: + // - use of the `ix_v2_job_root_by_path` index; hence the `parent_job IS NULL` + // clause. + // - select from `v2_job` first, then join with `v2_job_queue` to avoid a full + // table scan on `running = true`. + "SELECT id + FROM v2_job j JOIN v2_job_queue USING (id) + WHERE j.workspace_id = $2 AND trigger_kind = 'schedule' AND trigger = $1 AND runnable_path = $4 + AND parent_job IS NULL + AND j.id != $3 + AND running = true", + schedule_path.as_ref().unwrap(), + flow_job.workspace_id.as_str(), + flow_job.id, + flow_job.runnable_path() + ) + .fetch_all(db) + .await?; if overlapping.len() > 0 { let overlapping_str = overlapping .iter() @@ -1719,26 +1710,26 @@ async fn push_next_flow_job( .collect::>() .join(", "); job_completed_tx - .send(SendResult::UpdateFlow { - flow: flow_job.id, - success: true, - result: serde_json::from_str( - &format!("\"not allowed to overlap with {overlapping_str}, scheduling next iteration\""), - ) - .unwrap(), - stop_early_override: Some(true), - w_id: flow_job.workspace_id.clone(), - worker_dir: worker_dir.to_string(), - token: client.token.clone(), - }) - .await - .map_err(|e| { - Error::internal_err(format!( - "error sending update flow message to job completed channel: {e:#}" - )) - })?; + .send(SendResult::UpdateFlow { + flow: flow_job.id, + success: true, + result: serde_json::from_str( + &format!("\"not allowed to overlap with {overlapping_str}, scheduling next iteration\""), + ) + .unwrap(), + stop_early_override: Some(true), + w_id: flow_job.workspace_id.clone(), + worker_dir: worker_dir.to_string(), + token: client.token.clone(), + }) + .await + .map_err(|e| { + Error::internal_err(format!( + "error sending update flow message to job completed channel: {e:#}" + )) + })?; - return Ok(()); + return Ok(None); } } } @@ -1775,7 +1766,7 @@ async fn push_next_flow_job( )) })?; - return Ok(()); + return Ok(None); } } } @@ -1828,13 +1819,13 @@ async fn push_next_flow_job( .context("lock flow in queue")?; let resumes = sqlx::query_as::<_, ResumeRow>( - "SELECT value, approver, resume_id, approved FROM resume_job WHERE job = $1 ORDER BY created_at ASC", - ) - .bind(last) - .fetch_all(&mut *tx) - .await? - .into_iter() - .collect::>(); + "SELECT value, approver, resume_id, approved FROM resume_job WHERE job = $1 ORDER BY created_at ASC", + ) + .bind(last) + .fetch_all(&mut *tx) + .await? + .into_iter() + .collect::>(); resume_messages.extend(resumes.iter().map(|r| to_raw_value(&r.value))); approvers.extend(resumes.iter().map(|r| { @@ -1864,22 +1855,22 @@ async fn push_next_flow_job( .insert("previous_result".to_string(), arc_last_job_result.clone()); let eval_result = serde_json::from_str::>( - eval_timeout( - expr.to_string(), - context, - Some(arc_flow_job_args.clone()), - None, - None, - None - ) - .await - .map_err(|e| { - Error::ExecutionErr(format!( - "Error during isolated evaluation of expression `{expr}`:\n{e:#}" - )) - })? - .get(), - ); + eval_timeout( + expr.to_string(), + context, + Some(arc_flow_job_args.clone()), + None, + None, + None + ) + .await + .map_err(|e| { + Error::ExecutionErr(format!( + "Error during isolated evaluation of expression `{expr}`:\n{e:#}" + )) + })? + .get(), + ); if eval_result.is_ok() { user_groups_required = eval_result.ok().unwrap_or(Vec::new()) } else { @@ -1898,8 +1889,8 @@ async fn push_next_flow_job( }; sqlx::query!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['approval_conditions'], $1) - WHERE id = $2", + SET flow_status = JSONB_SET(flow_status, ARRAY['approval_conditions'], $1) + WHERE id = $2", json!(approval_conditions), flow_job.id ) @@ -1923,7 +1914,7 @@ async fn push_next_flow_job( .permissioned_as .trim_start_matches("u/") .to_string(), - email: flow_job.email.clone(), + email: flow_job.permissioned_as_email.clone(), username_override: None, }; @@ -1937,42 +1928,42 @@ async fn push_next_flow_job( resume_messages.push(to_raw_value(&js)); audit_log( - &mut *tx, - &audit_author, - "jobs.suspend_resume", - ActionKind::Update, - &flow_job.workspace_id, - Some(&serde_json::json!({"approved": false, "job_id": flow_job.id, "details": "Suspend timed out without approval but can continue".to_string()}).to_string()), - None, - ) - .await?; + &mut *tx, + &audit_author, + "jobs.suspend_resume", + ActionKind::Update, + &flow_job.workspace_id, + Some(&serde_json::json!({"approved": false, "job_id": flow_job.id, "details": "Suspend timed out without approval but can continue".to_string()}).to_string()), + None, + ) + .await?; } sqlx::query!( - "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'approvers'], $2) - WHERE id = $3", - (status.step - 1).to_string(), - json!(resumes - .into_iter() - .map(|r| Approval { - resume_id: r.resume_id as u16, - approver: r - .approver.clone() - .unwrap_or_else(|| "unknown".to_string()) - }) - .collect::>() - ), - flow_job.id - ) - .execute(&mut *tx) - .await?; + "UPDATE v2_job_status + SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'approvers'], $2) + WHERE id = $3", + (status.step - 1).to_string(), + json!(resumes + .into_iter() + .map(|r| Approval { + resume_id: r.resume_id as u16, + approver: r + .approver.clone() + .unwrap_or_else(|| "unknown".to_string()) + }) + .collect::>() + ), + flow_job.id + ) + .execute(&mut *tx) + .await?; // Remove the approval conditions from the flow status sqlx::query!( "UPDATE v2_job_status - SET flow_status = flow_status - 'approval_conditions' - WHERE id = $1", + SET flow_status = flow_status - 'approval_conditions' + WHERE id = $1", flow_job.id ) .execute(&mut *tx) @@ -1989,14 +1980,14 @@ async fn push_next_flow_job( { sqlx::query!( "WITH suspend AS ( - UPDATE v2_job_queue SET suspend = $2, suspend_until = now() + $3 - WHERE id = $4 - RETURNING id - ) UPDATE v2_job_status SET flow_status = JSONB_SET( - flow_status, - ARRAY['modules', flow_status->>'step'::TEXT], - $1 - ) WHERE id = (SELECT id FROM suspend)", + UPDATE v2_job_queue SET suspend = $2, suspend_until = now() + $3 + WHERE id = $4 + RETURNING id + ) UPDATE v2_job_status SET flow_status = JSONB_SET( + flow_status, + ARRAY['modules', flow_status->>'step'::TEXT], + $1 + ) WHERE id = (SELECT id FROM suspend)", json!(FlowStatusModule::WaitingForEvents { id: status_module.id(), count: required_events, @@ -2013,29 +2004,28 @@ async fn push_next_flow_job( sqlx::query!( "UPDATE v2_job_runtime SET ping = NULL - WHERE id = $1 AND ping = $2", + WHERE id = $1", flow_job.id, - flow_job.last_ping ) .execute(&mut *tx) .await?; tx.commit().await?; - return Ok(()); + return Ok(None); /* cancelled or we're WaitingForEvents but we don't have enough messages (timed out) */ } else { if is_disapproved.is_none() { audit_log( - &mut *tx, - &audit_author, - "jobs.suspend_resume", - ActionKind::Update, - &flow_job.workspace_id, - Some(&serde_json::json!({"approved": false, "job_id": flow_job.id, "details": "Suspend timed out without approval and is cancelled".to_string()}).to_string()), - None, - ) - .await?; + &mut *tx, + &audit_author, + "jobs.suspend_resume", + ActionKind::Update, + &flow_job.workspace_id, + Some(&serde_json::json!({"approved": false, "job_id": flow_job.id, "details": "Suspend timed out without approval and is cancelled".to_string()}).to_string()), + None, + ) + .await?; } tx.commit().await?; @@ -2059,7 +2049,13 @@ async fn push_next_flow_job( let result: Value = json!({ "error": {"message": logs, "name": error_name}}); - append_logs(&flow_job.id, &flow_job.workspace_id, logs.clone(), db).await; + append_logs( + &flow_job.id, + &flow_job.workspace_id, + logs.clone(), + &db.into(), + ) + .await; job_completed_tx .send(SendResult::UpdateFlow { @@ -2078,7 +2074,7 @@ async fn push_next_flow_job( )) })?; - return Ok(()); + return Ok(None); } } } @@ -2131,22 +2127,22 @@ async fn push_next_flow_job( context.insert("previous_result".to_string(), arc_last_job_result.clone()); serde_json::from_str( - eval_timeout( - expr.to_string(), - context, - Some(arc_flow_job_args.clone()), - None, - None, - None, - ) - .await - .map_err(|e| { - Error::ExecutionErr(format!( - "Error during isolated evaluation of expression `{expr}`:\n{e:#}" - )) - })? - .get(), - ) + eval_timeout( + expr.to_string(), + context, + Some(arc_flow_job_args.clone()), + None, + None, + None, + ) + .await + .map_err(|e| { + Error::ExecutionErr(format!( + "Error during isolated evaluation of expression `{expr}`:\n{e:#}" + )) + })? + .get(), + ) } }; match json_value.and_then(|x| serde_json::from_str::(x.get())) { @@ -2195,18 +2191,18 @@ async fn push_next_flow_job( scheduled_for_o = Some(from_now(retry_in)); status.retry.failed_jobs.push(job.clone()); sqlx::query!( - "UPDATE v2_job_status - SET flow_status = JSONB_SET(JSONB_SET(flow_status, ARRAY['retry'], $1), ARRAY['modules', $3::TEXT, 'failed_retries'], $4) - WHERE id = $2", - json!(RetryStatus { fail_count, ..status.retry.clone() }), - flow_job.id, - status.step.to_string(), - json!(status.retry.failed_jobs) - ) - .execute(db) - .warn_after_seconds(2) - .await - .context("update flow retry")?; + "UPDATE v2_job_status + SET flow_status = JSONB_SET(JSONB_SET(flow_status, ARRAY['retry'], $1), ARRAY['modules', $3::TEXT, 'failed_retries'], $4) + WHERE id = $2", + json!(RetryStatus { fail_count, ..status.retry.clone() }), + flow_job.id, + status.step.to_string(), + json!(status.retry.failed_jobs) + ) + .execute(db) + .warn_after_seconds(2) + .await + .context("update flow retry")?; status_module = FlowStatusModule::WaitingForPriorSteps { id: status_module.id() }; // we get the args from the last failed job @@ -2233,8 +2229,8 @@ async fn push_next_flow_job( if module.retry.as_ref().is_some_and(|x| x.has_attempts()) { sqlx::query!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['retry'], $1) - WHERE id = $2", + SET flow_status = JSONB_SET(flow_status, ARRAY['retry'], $1) + WHERE id = $2", json!(RetryStatus { fail_count: 0, failed_jobs: vec![] }), flow_job.id ) @@ -2292,7 +2288,7 @@ async fn push_next_flow_job( } else if let Some(id) = get_args_from_id { let args = sqlx::query_scalar!( "SELECT args AS \"args: Json>>\" - FROM v2_job WHERE id = $1 AND workspace_id = $2", + FROM v2_job WHERE id = $1 AND workspace_id = $2", id, &flow_job.workspace_id ) @@ -2377,41 +2373,52 @@ async fn push_next_flow_job( let (job_payloads, next_status) = match next_flow_transform { NextFlowTransform::Continue(job_payload, next_state) => (job_payload, next_state), - NextFlowTransform::EmptyInnerFlows => { - sqlx::query!( + NextFlowTransform::EmptyInnerFlows { branch_chosen } => { + let raw_status = sqlx::query_scalar!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2) - WHERE id = $3", + SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2) + WHERE id = $3 + RETURNING flow_status AS \"flow_status: Json>\"", status.step.to_string(), json!(FlowStatusModule::Success { id: status_module.id(), job: Uuid::nil(), flow_jobs: Some(vec![]), flow_jobs_success: Some(vec![]), - branch_chosen: None, + branch_chosen: branch_chosen, approvers: vec![], failed_retries: vec![], skipped: false, }), flow_job.id ) - .execute(db) - .await?; - // flow is reprocessed by the worker in a state where the module has completed successfully. - // The next steps are pull -> handle flow -> push next flow job -> update flow status since module status is success - same_worker_tx - .send(SameWorkerPayload { job_id: flow_job.id, recoverable: true }) - .await - .expect("send to same worker"); - return Ok(()); + .fetch_optional(db) + .await? + .flatten(); + + let status = raw_status + .as_ref() + .and_then(|v| serde_json::from_str::((**v).get()).ok()); + + if let Some(status) = status { + // // flow is reprocessed by the worker in a state where the module has completed successfully. + return Ok(Some(PushNextFlowJobRec { + flow_job: flow_job, + status: status, + })); + } else { + return Err(Error::BadRequest( + "impossible to parse new flow status after applying innr flows".to_string(), + )); + } } }; // Also check `flow_job.same_worker` for [`JobKind::Flow`] jobs as it's no // more reflected to the flow value on push. let job_same_worker = flow_job.same_worker - && matches!(flow_job.job_kind, JobKind::Flow) - && flow_job.script_hash.is_some(); + && matches!(flow_job.kind, JobKind::Flow) + && flow_job.runnable_id.is_some(); let continue_on_same_worker = (flow.same_worker || job_same_worker) && module.suspend.is_none() && module.sleep.is_none(); @@ -2581,26 +2588,29 @@ async fn push_next_flow_job( } { None } else { - flow_job.root_job.or_else(|| Some(flow_job.id)) + flow_job + .flow_innermost_root_job + .or_else(|| Some(flow_job.id)) }; // forward root job permissions to the new job - let job_perms: Option = if JOB_TOKEN.is_none() { - if let Some(root_job) = &flow_job.root_job.or_else(|| Some(flow_job.id)) { + let job_perms: Option = { + if let Some(root_job) = &flow_job + .flow_innermost_root_job + .or_else(|| Some(flow_job.id)) + { sqlx::query_as!( - JobPerms, - "SELECT * FROM job_perms WHERE job_id = $1 AND workspace_id = $2", - root_job, - flow_job.workspace_id, - ) - .fetch_optional(&mut *tx) - .await? - .map(|x| x.into()) + JobPerms, + "SELECT email, username, is_admin, is_operator, groups, folders FROM job_perms WHERE job_id = $1 AND workspace_id = $2", + root_job, + flow_job.workspace_id, + ) + .fetch_optional(&mut *tx) + .await? + .map(|x| x.into()) } else { None } - } else { - None }; tracing::debug!(id = %flow_job.id, root_id = %job_root, "computed perms for job {i} of {len}"); @@ -2616,7 +2626,10 @@ async fn push_next_flow_job( { (&on_behalf_of.email, on_behalf_of.permissioned_as.clone()) } else { - (&flow_job.email, flow_job.permissioned_as.to_owned()) + ( + &flow_job.permissioned_as_email, + flow_job.permissioned_as.to_owned(), + ) }; let tx2 = PushIsolationLevel::Transaction(tx); let (uuid, mut inner_tx) = push( @@ -2629,7 +2642,7 @@ async fn push_next_flow_job( email, permissioned_as, scheduled_for_o, - flow_job.schedule_path.clone(), + flow_job.schedule_path(), Some(flow_job.id), root_job, None, @@ -2646,6 +2659,16 @@ async fn push_next_flow_job( .warn_after_seconds(2) .await?; + if continue_on_same_worker { + let _ = sqlx::query!( + "UPDATE v2_job_queue SET worker = $2 WHERE id = $1", + uuid, + worker_name + ) + .execute(&mut *inner_tx) + .await; + } + tracing::debug!(id = %flow_job.id, root_id = %job_root, "pushed next flow job: {uuid}"); if value_with_parallel.type_ == "forloopflow" { @@ -2655,10 +2678,10 @@ async fn push_next_flow_job( if i as u16 >= p { sqlx::query!( "UPDATE v2_job_queue SET - suspend = $1, - suspend_until = now() + interval '14 day', - running = true - WHERE id = $2", + suspend = $1, + suspend_until = now() + interval '14 day', + running = true + WHERE id = $2", (i as u16 - p + 1) as i32, uuid, ) @@ -2675,14 +2698,14 @@ async fn push_next_flow_job( })?; sqlx::query!( - "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['cleanup_module', 'flow_jobs_to_clean'], COALESCE(flow_status->'cleanup_module'->'flow_jobs_to_clean', '[]'::jsonb) || $1) - WHERE id = $2", - uuid_singleton_json, - root_job.unwrap_or(flow_job.id) - ) - .execute(&mut *inner_tx) - .await?; + "UPDATE v2_job_status + SET flow_status = JSONB_SET(flow_status, ARRAY['cleanup_module', 'flow_jobs_to_clean'], COALESCE(flow_status->'cleanup_module'->'flow_jobs_to_clean', '[]'::jsonb) || $1) + WHERE id = $2", + uuid_singleton_json, + root_job.unwrap_or(flow_job.id) + ) + .execute(&mut *inner_tx) + .await?; } tx = inner_tx; @@ -2701,7 +2724,7 @@ async fn push_next_flow_job( for uuid in &uuids { sqlx::query!( "INSERT INTO parallel_monitor_lock (parent_flow_id, job_id) - VALUES ($1, $2)", + VALUES ($1, $2)", flow_job.id, uuid ) @@ -2805,12 +2828,12 @@ async fn push_next_flow_job( Step::FailureStep => { sqlx::query!( "UPDATE v2_job_status SET - flow_status = JSONB_SET( - JSONB_SET(flow_status, ARRAY['failure_module'], $1), - ARRAY['step'], - $2 - ) - WHERE id = $3", + flow_status = JSONB_SET( + JSONB_SET(flow_status, ARRAY['failure_module'], $1), + ARRAY['step'], + $2 + ) + WHERE id = $3", json!(FlowStatusModuleWParent { parent_module: Some(current_id.clone()), module_status: new_status @@ -2824,12 +2847,12 @@ async fn push_next_flow_job( Step::PreprocessorStep => { sqlx::query!( "UPDATE v2_job_status SET - flow_status = JSONB_SET( - JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1), - ARRAY['step'], - $2 - ) - WHERE id = $3", + flow_status = JSONB_SET( + JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1), + ARRAY['step'], + $2 + ) + WHERE id = $3", json!(new_status), json!(-1), flow_job.id @@ -2840,12 +2863,12 @@ async fn push_next_flow_job( Step::Step(i) => { sqlx::query!( "UPDATE v2_job_status SET - flow_status = JSONB_SET( - JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2), - ARRAY['step'], - $3 - ) - WHERE id = $4", + flow_status = JSONB_SET( + JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2), + ARRAY['step'], + $3 + ) + WHERE id = $4", i as i32, json!(new_status), json!(i), @@ -2865,21 +2888,23 @@ async fn push_next_flow_job( .execute(&mut *tx) .await?; + if continue_on_same_worker { + if !is_one_uuid { + return Err(Error::BadRequest( + "Cannot continue on same worker with multiple jobs, parallel cannot be used in conjunction with same_worker".to_string(), + )); + } + } tx.commit().warn_after_seconds(3).await?; tracing::info!(id = %flow_job.id, root_id = %job_root, "all next flow jobs pushed: {uuids:?}"); if continue_on_same_worker { - if !is_one_uuid { - return Err(Error::BadRequest( - "Cannot continue on same worker with multiple jobs, parallel cannot be used in conjunction with same_worker".to_string(), - )); - } same_worker_tx .send(SameWorkerPayload { job_id: first_uuid, recoverable: true }) .await .map_err(to_anyhow)?; } - return Ok(()); + return Ok(None); } // async fn jump_to_next_step( @@ -3001,7 +3026,7 @@ enum ContinuePayload { } enum NextFlowTransform { - EmptyInnerFlows, + EmptyInnerFlows { branch_chosen: Option }, Continue(ContinuePayload, NextStatus), } @@ -3055,22 +3080,22 @@ fn payload_from_modules<'a>( }) } -fn get_path(flow_job: &QueuedJob, status: &FlowStatus, module: &FlowModule) -> String { +fn get_path(flow_job: &MiniPulledJob, status: &FlowStatus, module: &FlowModule) -> String { if status .preprocessor_module .as_ref() .is_some_and(|x| x.id() == module.id) { - format!("{}/preprocessor", flow_job.script_path()) + format!("{}/preprocessor", flow_job.runnable_path()) } else { - format!("{}/step-{}", flow_job.script_path(), status.step) + format!("{}/{}", flow_job.runnable_path(), module.id) } } async fn compute_next_flow_transform( arc_flow_job_args: Marc>>, arc_last_job_result: Arc>, - flow_job: &QueuedJob, + flow_job: &MiniPulledJob, flow: &FlowValue, by_id: Option, db: &DB, @@ -3235,7 +3260,7 @@ async fn compute_next_flow_transform( /* forloop modules are expected set `iter: { value: Value, index: usize }` as job arguments */ FlowModuleValue::ForloopFlow { modules, modules_node, iterator, parallel, .. } => { // if it's a simple single step flow, we will collapse it as an optimization and need to pass flow_input as an arg - let is_simple = !matches!(flow_job.job_kind, JobKind::FlowPreview) + let is_simple = !matches!(flow_job.kind, JobKind::FlowPreview) && !parallel && is_simple_modules(&modules, flow.failure_module.as_ref()); @@ -3267,7 +3292,9 @@ async fn compute_next_flow_transform( .await?; match next_loop_status { - ForLoopStatus::EmptyIterator => Ok(NextFlowTransform::EmptyInnerFlows), + ForLoopStatus::EmptyIterator => { + Ok(NextFlowTransform::EmptyInnerFlows { branch_chosen: None }) + } ForLoopStatus::NextIteration(ns) => { next_loop_iteration( flow, @@ -3304,7 +3331,7 @@ async fn compute_next_flow_transform( flow.failure_module.as_ref(), flow.same_worker, || format!("{}-{i}", status.step), - || format!("{}/forloop-{i}", flow_job.script_path()), + || format!("{}/forloop-{i}", flow_job.runnable_path()), true, ) else { return None; @@ -3319,7 +3346,7 @@ async fn compute_next_flow_transform( }) .collect::>(); if payloads.is_empty() { - return Ok(NextFlowTransform::EmptyInnerFlows); + return Ok(NextFlowTransform::EmptyInnerFlows { branch_chosen: None }); } Ok(NextFlowTransform::Continue( ContinuePayload::ParallelJobs(payloads), @@ -3375,12 +3402,12 @@ async fn compute_next_flow_transform( )))?, }; - let (modules, modules_node) = match branch { - BranchChosen::Default => (default, default_node), + let (modules, modules_node, branch_idx) = match branch { + BranchChosen::Default => (default, default_node, 0), BranchChosen::Branch { branch } => branches .into_iter() .nth(branch) - .map(|Branch { modules, modules_node, .. }| (modules, modules_node)) + .map(|Branch { modules, modules_node, .. }| (modules, modules_node, branch + 1)) .ok_or_else(|| { Error::BadRequest(format!( "Unrecognized branch for BranchOne {status_module:?}" @@ -3394,10 +3421,10 @@ async fn compute_next_flow_transform( flow.failure_module.as_ref(), flow.same_worker, || status.step.to_string(), - || format!("{}/branchone-{}", flow_job.script_path(), status.step), + || format!("{}/branchone-{}", flow_job.runnable_path(), branch_idx), true, ) else { - return Ok(NextFlowTransform::EmptyInnerFlows); + return Ok(NextFlowTransform::EmptyInnerFlows { branch_chosen: Some(branch) }); }; Ok(NextFlowTransform::Continue( @@ -3417,7 +3444,7 @@ async fn compute_next_flow_transform( | FlowStatusModule::WaitingForEvents { .. } | FlowStatusModule::WaitingForExecutor { .. } => { if branches.is_empty() { - return Ok(NextFlowTransform::EmptyInnerFlows); + return Ok(NextFlowTransform::EmptyInnerFlows { branch_chosen: None }); } else if parallel { let len = branches.len(); let payloads: Vec = branches @@ -3430,7 +3457,7 @@ async fn compute_next_flow_transform( flow.failure_module.as_ref(), flow.same_worker, || format!("{}-{i}", status.step), - || format!("{}/branchall-{}", flow_job.script_path(), i), + || format!("{}/branchall-{}", flow_job.runnable_path(), i), false, ) else { return None; @@ -3445,7 +3472,7 @@ async fn compute_next_flow_transform( }) .collect::>(); if payloads.is_empty() { - return Ok(NextFlowTransform::EmptyInnerFlows); + return Ok(NextFlowTransform::EmptyInnerFlows { branch_chosen: None }); } return Ok(NextFlowTransform::Continue( ContinuePayload::ParallelJobs(payloads), @@ -3497,13 +3524,15 @@ async fn compute_next_flow_transform( || { format!( "{}/branchall-{}", - flow_job.script_path(), + flow_job.runnable_path(), branch_status.branch ) }, false, ) else { - return Ok(NextFlowTransform::EmptyInnerFlows); + return Ok(NextFlowTransform::EmptyInnerFlows { + branch_chosen: Some(BranchChosen::Default), + }); }; Ok(NextFlowTransform::Continue( @@ -3530,13 +3559,13 @@ async fn next_loop_iteration( ns: ForloopNextIteration, modules: Vec, modules_node: Option, - flow_job: &QueuedJob, + flow_job: &MiniPulledJob, is_simple: bool, db: &sqlx::Pool, module: &FlowModule, delete_after_use: bool, ) -> Result { - let inner_path = || format!("{}/loop-{}", flow_job.script_path(), ns.index); + let inner_path = || format!("{}/loop-{}", flow_job.runnable_path(), ns.index); if is_simple { let mut value = modules[0].get_value()?; let simple_input_transforms = match &mut value { @@ -3565,7 +3594,7 @@ async fn next_loop_iteration( inner_path, true, ) else { - return Ok(NextFlowTransform::EmptyInnerFlows); + return Ok(NextFlowTransform::EmptyInnerFlows { branch_chosen: None }); }; Ok(NextFlowTransform::Continue( @@ -3601,7 +3630,7 @@ pub(super) fn is_simple_modules( async fn next_forloop_status( status_module: &FlowStatusModule, by_id: Option, - flow_job: &QueuedJob, + flow_job: &MiniPulledJob, previous_id: &str, status: &FlowStatus, iterator: &InputTransform, @@ -3714,11 +3743,11 @@ async fn next_forloop_status( itered.clone() }; let (index, next) = index - .checked_add(1) - .and_then(|i| itered_new.get(i).map(|next| (i, next))) - .with_context(|| { - format!("Could not find iteration number {index} restarting inside the for-loop flow. It's possible the itered-array has changed and this value isn't available anymore.") - })?; + .checked_add(1) + .and_then(|i| itered_new.get(i).map(|next| (i, next))) + .with_context(|| { + format!("Could not find iteration number {index} restarting inside the for-loop flow. It's possible the itered-array has changed and this value isn't available anymore.") + })?; ForLoopStatus::NextIteration(ForloopNextIteration { index, @@ -3740,7 +3769,7 @@ async fn next_forloop_status( async fn payload_from_simple_module( value: FlowModuleValue, db: &sqlx::Pool, - flow_job: &QueuedJob, + flow_job: &MiniPulledJob, module: &FlowModule, inner_path: String, ) -> Result { @@ -3861,7 +3890,7 @@ async fn script_to_payload( script_hash: Option, script_path: String, db: &sqlx::Pool, - flow_job: &QueuedJob, + flow_job: &MiniPulledJob, module: &FlowModule, tag_override: Option, ) -> Result { @@ -3935,7 +3964,7 @@ async fn script_to_payload( } async fn get_transform_context( - flow_job: &QueuedJob, + flow_job: &MiniPulledJob, previous_id: &str, status: &FlowStatus, ) -> error::Result { @@ -4018,7 +4047,7 @@ async fn get_previous_job_result( Some(FlowStatusModule::Success { job, .. }) => Ok(Some( sqlx::query_scalar!( "SELECT result AS \"result!: Json>\" - FROM v2_job_completed WHERE id = $1 AND workspace_id = $2", + FROM v2_job_completed WHERE id = $1 AND workspace_id = $2", job, w_id ) diff --git a/backend/windmill-worker/src/worker_lockfiles.rs b/backend/windmill-worker/src/worker_lockfiles.rs index 3888d8eb63..82ad8cc77b 100644 --- a/backend/windmill-worker/src/worker_lockfiles.rs +++ b/backend/windmill-worker/src/worker_lockfiles.rs @@ -1,6 +1,10 @@ +use std::borrow::Cow; use std::collections::HashMap; +use std::fs::{create_dir_all, remove_dir_all}; use std::path::{Component, Path, PathBuf}; +#[cfg(feature = "python")] +use crate::ansible_executor::{get_git_repos_lock, AnsibleDependencyLocks}; use async_recursion::async_recursion; use serde_json::value::RawValue; use serde_json::{json, Value}; @@ -15,14 +19,15 @@ use windmill_common::jobs::JobPayload; use windmill_common::scripts::ScriptHash; #[cfg(feature = "python")] use windmill_common::worker::PythonAnnotations; -use windmill_common::worker::{to_raw_value, to_raw_value_owned, write_file}; +use windmill_common::worker::{to_raw_value, to_raw_value_owned, write_file, Connection}; +#[cfg(feature = "python")] +use windmill_parser_yaml::AnsibleRequirements; use windmill_common::{ apps::AppScriptId, cache::{self, RawData}, error::{self, to_anyhow}, flows::{add_virtual_items_if_necessary, FlowValue}, - jobs::QueuedJob, scripts::ScriptLang, DB, }; @@ -30,17 +35,19 @@ use windmill_git_sync::{handle_deployment_metadata, DeployedObject}; #[cfg(feature = "python")] use windmill_parser_py_imports::parse_relative_imports; use windmill_parser_ts::parse_expr_for_imports; -use windmill_queue::{append_logs, CanceledBy, PushIsolationLevel}; +use windmill_queue::{append_logs, CanceledBy, MiniPulledJob, PushIsolationLevel}; use crate::common::OccupancyMetrics; use crate::csharp_executor::generate_nuget_lockfile; +#[cfg(feature = "java")] +use crate::java_executor::resolve; + #[cfg(feature = "php")] use crate::php_executor::{composer_install, parse_php_imports}; #[cfg(feature = "python")] use crate::python_executor::{ - create_dependencies_dir, handle_python_reqs, uv_pip_compile, PyVersion, USE_PIP_COMPILE, - USE_PIP_INSTALL, + create_dependencies_dir, handle_python_reqs, uv_pip_compile, PyVersion, }; #[cfg(feature = "rust")] use crate::rust_executor::generate_cargo_lockfile; @@ -58,17 +65,17 @@ pub async fn update_script_dependency_map( relative_imports: Vec, ) -> error::Result<()> { let importer_kind = "script"; + + let mut tx = db.begin().await?; + tx = clear_dependency_parent_path(parent_path, script_path, w_id, importer_kind, tx).await?; + + tx = clear_dependency_map_for_item(script_path, w_id, importer_kind, tx, &None).await?; + if !relative_imports.is_empty() { let mut logs = "".to_string(); logs.push_str("\n--- RELATIVE IMPORTS ---\n\n"); logs.push_str(&relative_imports.join("\n")); - let mut tx = db.begin().await?; - tx = - clear_dependency_parent_path(parent_path, script_path, w_id, importer_kind, tx).await?; - - tx = clear_dependency_map_for_item(script_path, w_id, importer_kind, tx, &None).await?; - tx = add_relative_imports_to_dependency_map( script_path, w_id, @@ -79,9 +86,10 @@ pub async fn update_script_dependency_map( None, ) .await?; - tx.commit().await?; - append_logs(job_id, w_id, logs, db).await; + append_logs(job_id, w_id, logs, &db.into()).await; } + tx.commit().await?; + Ok(()) } @@ -178,7 +186,7 @@ fn try_normalize(path: &Path) -> Option { Some(ret) } -fn parse_bun_relative_imports(raw_code: &str, script_path: &str) -> error::Result> { +fn parse_ts_relative_imports(raw_code: &str, script_path: &str) -> error::Result> { let mut relative_imports = vec![]; let r = parse_expr_for_imports(raw_code)?; for import in r { @@ -210,27 +218,27 @@ pub fn extract_relative_imports( match language { #[cfg(feature = "python")] Some(ScriptLang::Python3) => parse_relative_imports(&raw_code, script_path).ok(), - Some(ScriptLang::Bun) | Some(ScriptLang::Bunnative) => { - parse_bun_relative_imports(&raw_code, script_path).ok() + Some(ScriptLang::Bun) | Some(ScriptLang::Bunnative) | Some(ScriptLang::Deno) => { + parse_ts_relative_imports(&raw_code, script_path).ok() } _ => None, } } #[tracing::instrument(level = "trace", skip_all)] pub async fn handle_dependency_job( - job: &QueuedJob, + job: &MiniPulledJob, preview_data: Option<&RawData>, mem_peak: &mut i32, canceled_by: &mut Option, job_dir: &str, - db: &sqlx::Pool, + db: &DB, worker_name: &str, worker_dir: &str, base_internal_url: &str, token: &str, occupancy_metrics: &mut OccupancyMetrics, ) -> error::Result> { - let script_path = job.script_path(); + let script_path = job.runnable_path(); let raw_deps = job .args .as_ref() @@ -240,7 +248,7 @@ pub async fn handle_dependency_job( }) .unwrap_or(false); let npm_mode = if job - .language + .script_lang .as_ref() .map(|v| v == &ScriptLang::Bun) .unwrap_or(false) @@ -261,16 +269,41 @@ pub async fn handle_dependency_job( // `JobKind::Dependencies` job store either: // - A saved script `hash` in the `script_hash` column. // - Preview raw lock and code in the `queue` or `job` table. - let script_data = match job.script_hash { - Some(hash) => &cache::script::fetch(db, hash).await?.0, + let script_data = &match job.runnable_id { + Some(hash) => match cache::script::fetch(&Connection::from(db.clone()), hash).await { + Ok(d) => Cow::Owned(d.0), + Err(e) => { + let logs2 = sqlx::query_scalar!( + "SELECT logs FROM job_logs WHERE job_id = $1 AND workspace_id = $2", + &job.id, + &job.workspace_id + ) + .fetch_optional(db) + .await? + .flatten() + .unwrap_or_else(|| "no logs".to_string()); + sqlx::query!( + "UPDATE script SET lock_error_logs = $1 WHERE hash = $2 AND workspace_id = $3", + &format!("{logs2}\n{e}"), + &job.runnable_id.unwrap_or(ScriptHash(0)).0, + &job.workspace_id + ) + .execute(db) + .await?; + return Err(Error::ExecutionErr(format!( + "Error creating schema validator: {e}" + ))); + } + }, _ => match preview_data { - Some(RawData::Script(data)) => data, + Some(RawData::Script(data)) => Cow::Borrowed(data), _ => return Err(Error::internal_err("expected script hash")), }, }; + let content = capture_dependency_job( &job.id, - job.language.as_ref().map(|v| Ok(v)).unwrap_or_else(|| { + job.script_lang.as_ref().map(|v| Ok(v)).unwrap_or_else(|| { Err(Error::internal_err( "Job Language required for dependency jobs".to_owned(), )) @@ -294,14 +327,14 @@ pub async fn handle_dependency_job( match content { Ok(content) => { - if job.script_hash.is_none() { + if job.runnable_id.is_none() { // it a one-off raw script dependency job, no need to update the db return Ok(to_raw_value_owned( json!({ "status": "Successful lock file generation", "lock": content }), )); } - let hash = job.script_hash.unwrap_or(ScriptHash(0)); + let hash = job.runnable_id.unwrap_or(ScriptHash(0)); let w_id = &job.workspace_id; sqlx::query!( "UPDATE script SET lock = $1 WHERE hash = $2 AND workspace_id = $3", @@ -319,7 +352,7 @@ pub async fn handle_dependency_job( get_deployment_msg_and_parent_path_from_args(job.args.clone()); if let Err(e) = handle_deployment_metadata( - &job.email, + &job.permissioned_as_email, &job.created_by, &db, &w_id, @@ -336,44 +369,22 @@ pub async fn handle_dependency_job( tracing::error!(%e, "error handling deployment metadata"); } - let relative_imports = - extract_relative_imports(&script_data.code, script_path, &job.language); - if let Some(relative_imports) = relative_imports { - update_script_dependency_map( - &job.id, - db, - w_id, - &parent_path, - script_path, - relative_imports, - ) - .await?; - let already_visited = job - .args - .as_ref() - .map(|x| { - x.get("already_visited") - .map(|v| serde_json::from_str::>(v.get()).ok()) - .flatten() - }) - .flatten() - .unwrap_or_default(); - if let Err(e) = trigger_dependents_to_recompute_dependencies( - w_id, - script_path, - deployment_message, - parent_path, - &job.email, - &job.created_by, - &job.permissioned_as, - db, - already_visited, - ) - .await - { - tracing::error!(%e, "error triggering dependents to recompute dependencies"); - } - } + process_relative_imports( + db, + Some(job.id), + job.args.as_ref(), + &job.workspace_id, + script_path, + parent_path, + deployment_message, + &script_data.code, + &job.script_lang, + &job.permissioned_as_email, + &job.created_by, + &job.permissioned_as, + None, + ) + .await?; Ok(to_raw_value_owned( json!({ "status": "Successful lock file generation", "lock": content }), @@ -392,15 +403,100 @@ pub async fn handle_dependency_job( sqlx::query!( "UPDATE script SET lock_error_logs = $1 WHERE hash = $2 AND workspace_id = $3", &format!("{logs2}\n{error}"), - &job.script_hash.unwrap_or(ScriptHash(0)).0, + &job.runnable_id.unwrap_or(ScriptHash(0)).0, &job.workspace_id ) .execute(db) .await?; - Err(Error::ExecutionErr(format!("Error locking file: {error}")))? + Err(Error::ExecutionErr(format!( + "Error locking file: {error}\n\nlogs:\n{}", + remove_ansi_codes(&logs2) + )))? } } } +fn remove_ansi_codes(s: &str) -> String { + lazy_static::lazy_static! { + static ref ANSI_REGEX: regex::Regex = regex::Regex::new(r"\x1b\[[0-9;]*[a-zA-Z]").unwrap(); + } + ANSI_REGEX.replace_all(s, "").to_string() +} + +pub async fn process_relative_imports( + db: &sqlx::Pool, + job_id: Option, + args: Option<&Json>>>, + w_id: &str, + script_path: &str, + parent_path: Option, + deployment_message: Option, + code: &str, + script_lang: &Option, + permissioned_as_email: &str, + created_by: &str, + permissioned_as: &str, + lock: Option, +) -> error::Result<()> { + let relative_imports = extract_relative_imports(&code, script_path, script_lang); + if let Some(relative_imports) = relative_imports { + if (script_lang.is_some_and(|v| v == ScriptLang::Bun) + && lock + .as_ref() + .is_some_and(|v| v.contains("generatedFromPackageJson"))) + || (script_lang.is_some_and(|v| v == ScriptLang::Python3) + && lock + .as_ref() + .is_some_and(|v| v.starts_with(LOCKFILE_GENERATED_FROM_REQUIREMENTS_TXT))) + { + // if the lock file is generated from a package.json/requirements.txt, we need to clear the dependency map + // because we do not want to have dependencies be recomputed automatically. Empty relative imports passed + // to update_script_dependency_map will clear the dependency map. + update_script_dependency_map( + &job_id.unwrap_or_else(|| Uuid::nil()), + db, + w_id, + &parent_path, + script_path, + vec![], + ) + .await?; + } else { + update_script_dependency_map( + &job_id.unwrap_or_else(|| Uuid::nil()), + db, + w_id, + &parent_path, + script_path, + relative_imports, + ) + .await?; + } + let already_visited = args + .map(|x| { + x.get("already_visited") + .map(|v| serde_json::from_str::>(v.get()).ok()) + .flatten() + }) + .flatten() + .unwrap_or_default(); + if let Err(e) = trigger_dependents_to_recompute_dependencies( + w_id, + script_path, + deployment_message, + parent_path, + permissioned_as_email, + created_by, + permissioned_as, + db, + already_visited, + ) + .await + { + tracing::error!(%e, "error triggering dependents to recompute dependencies"); + } + } + Ok(()) +} async fn trigger_dependents_to_recompute_dependencies( w_id: &str, @@ -538,7 +634,7 @@ async fn trigger_dependents_to_recompute_dependencies( } pub async fn handle_flow_dependency_job( - job: &QueuedJob, + job: &MiniPulledJob, preview_data: Option<&RawData>, mem_peak: &mut i32, canceled_by: &mut Option, @@ -550,7 +646,7 @@ pub async fn handle_flow_dependency_job( token: &str, occupancy_metrics: &mut OccupancyMetrics, ) -> error::Result> { - let job_path = job.script_path.clone().ok_or_else(|| { + let job_path = job.runnable_path.clone().ok_or_else(|| { error::Error::internal_err( "Cannot resolve flow dependencies for flow without path".to_string(), ) @@ -571,7 +667,7 @@ pub async fn handle_flow_dependency_job( None } else { Some( - job.script_hash + job.runnable_id .clone() .ok_or_else(|| { Error::internal_err( @@ -598,7 +694,7 @@ pub async fn handle_flow_dependency_job( // `JobKind::FlowDependencies` job store either: // - A saved flow version `id` in the `script_hash` column. // - Preview raw flow in the `queue` or `job` table. - let mut flow = match job.script_hash { + let mut flow = match job.runnable_id { Some(ScriptHash(id)) => cache::flow::fetch_version(db, id).await?, _ => match preview_data { Some(RawData::Flow(data)) => data.clone(), @@ -612,8 +708,18 @@ pub async fn handle_flow_dependency_job( tx = clear_dependency_parent_path(&parent_path, &job_path, &job.workspace_id, "flow", tx) .await?; + if !skip_flow_update { + sqlx::query!( + "DELETE FROM workspace_runnable_dependencies WHERE flow_path = $1 AND workspace_id = $2", + job_path, + job.workspace_id + ) + .execute(&mut *tx) + .await?; + } let modified_ids; - (flow.modules, tx, modified_ids) = lock_modules( + let errors; + (flow.modules, tx, modified_ids, errors) = lock_modules( flow.modules, job, mem_peak, @@ -628,8 +734,46 @@ pub async fn handle_flow_dependency_job( token, &nodes_to_relock, occupancy_metrics, + skip_flow_update, ) .await?; + if !errors.is_empty() { + let error_message = errors + .iter() + .map(|e| format!("{}: {}", e.id, e.error)) + .collect::>() + .join("\n"); + let logs2 = sqlx::query_scalar!( + "SELECT logs FROM job_logs WHERE job_id = $1 AND workspace_id = $2", + &job.id, + &job.workspace_id + ) + .fetch_optional(db) + .await? + .flatten() + .unwrap_or_else(|| "no logs".to_string()); + sqlx::query!( + "UPDATE flow SET lock_error_logs = $1 WHERE path = $2 AND workspace_id = $3", + &format!("{logs2}\n{error_message}"), + &job.runnable_path(), + &job.workspace_id + ) + .execute(db) + .await?; + return Err(Error::ExecutionErr(format!( + "Error locking flow modules:\n{}\n\nlogs:\n{}", + error_message, + remove_ansi_codes(&logs2) + ))); + } else { + sqlx::query!( + "UPDATE flow SET lock_error_logs = NULL WHERE path = $1 AND workspace_id = $2", + &job.runnable_path(), + &job.workspace_id + ) + .execute(db) + .await?; + } let new_flow_value = Json(serde_json::value::to_raw_value(&flow).map_err(to_anyhow)?); // Re-check cancellation to ensure we don't accidentally override a flow. @@ -693,7 +837,7 @@ pub async fn handle_flow_dependency_job( tx.commit().await?; if let Err(e) = handle_deployment_metadata( - &job.email, + &job.permissioned_as_email, &job.created_by, &db, &job.workspace_id, @@ -739,9 +883,14 @@ fn get_deployment_msg_and_parent_path_from_args( (deployment_message, parent_path) } +struct LockModuleError { + id: String, + error: Error, +} + async fn lock_modules<'c>( modules: Vec, - job: &QueuedJob, + job: &MiniPulledJob, mem_peak: &mut i32, canceled_by: &mut Option, job_dir: &str, @@ -754,15 +903,19 @@ async fn lock_modules<'c>( token: &str, locks_to_reload: &Option>, occupancy_metrics: &mut OccupancyMetrics, + skip_flow_update: bool, // (modules to replace old seq (even unmmodified ones), new transaction, modified ids) ) ) -> Result<( Vec, sqlx::Transaction<'c, sqlx::Postgres>, Vec, + Vec, )> { let mut new_flow_modules = Vec::new(); let mut modified_ids = Vec::new(); + let mut errors = Vec::new(); for mut e in modules.into_iter() { + let id = e.id.clone(); let mut nmodified_ids = Vec::new(); let FlowModuleValue::RawScript { lock, @@ -787,7 +940,7 @@ async fn lock_modules<'c>( parallelism, } => { let nmodules; - (nmodules, tx, nmodified_ids) = Box::pin(lock_modules( + (nmodules, tx, modified_ids, errors) = Box::pin(lock_modules( modules, job, mem_peak, @@ -802,6 +955,7 @@ async fn lock_modules<'c>( token, locks_to_reload, occupancy_metrics, + skip_flow_update, )) .await?; e.value = FlowModuleValue::ForloopFlow { @@ -820,7 +974,8 @@ async fn lock_modules<'c>( for mut b in branches { let nmodules; let inner_modified_ids; - (nmodules, tx, inner_modified_ids) = Box::pin(lock_modules( + let inner_errors; + (nmodules, tx, inner_modified_ids, inner_errors) = Box::pin(lock_modules( b.modules, job, mem_peak, @@ -835,9 +990,11 @@ async fn lock_modules<'c>( token, locks_to_reload, occupancy_metrics, + skip_flow_update, )) .await?; nmodified_ids.extend(inner_modified_ids); + errors.extend(inner_errors); b.modules = nmodules; nbranches.push(b) } @@ -845,7 +1002,7 @@ async fn lock_modules<'c>( } FlowModuleValue::WhileloopFlow { modules, modules_node, skip_failures } => { let nmodules; - (nmodules, tx, nmodified_ids) = Box::pin(lock_modules( + (nmodules, tx, nmodified_ids, errors) = Box::pin(lock_modules( modules, job, mem_peak, @@ -860,6 +1017,7 @@ async fn lock_modules<'c>( token, locks_to_reload, occupancy_metrics, + skip_flow_update, )) .await?; e.value = FlowModuleValue::WhileloopFlow { @@ -875,8 +1033,8 @@ async fn lock_modules<'c>( for mut b in branches { let nmodules; let inner_modified_ids; - - (nmodules, tx, inner_modified_ids) = Box::pin(lock_modules( + let inner_errors; + (nmodules, tx, inner_modified_ids, inner_errors) = Box::pin(lock_modules( b.modules, job, mem_peak, @@ -891,14 +1049,17 @@ async fn lock_modules<'c>( token, locks_to_reload, occupancy_metrics, + skip_flow_update, )) .await?; nmodified_ids.extend(inner_modified_ids); + errors.extend(inner_errors); b.modules = nmodules; nbranches.push(b) } let ndefault; - (ndefault, tx, nmodified_ids) = Box::pin(lock_modules( + let ninner_errors; + (ndefault, tx, nmodified_ids, ninner_errors) = Box::pin(lock_modules( default, job, mem_peak, @@ -913,8 +1074,10 @@ async fn lock_modules<'c>( token, locks_to_reload, occupancy_metrics, + skip_flow_update, )) .await?; + errors.extend(ninner_errors); e.value = FlowModuleValue::BranchOne { branches: nbranches, default: ndefault, @@ -922,6 +1085,29 @@ async fn lock_modules<'c>( } .into(); } + FlowModuleValue::Script { path, hash, .. } + if !path.starts_with("hub/") && !skip_flow_update => + { + sqlx::query!( + "INSERT INTO workspace_runnable_dependencies (flow_path, runnable_path, script_hash, runnable_is_flow, workspace_id) VALUES ($1, $2, $3, FALSE, $4) ON CONFLICT DO NOTHING", + job_path, + path, + hash.map(|h| h.0), + job.workspace_id + ) + .execute(&mut *tx) + .await?; + } + FlowModuleValue::Flow { path, .. } if !skip_flow_update => { + sqlx::query!( + "INSERT INTO workspace_runnable_dependencies (flow_path, runnable_path, runnable_is_flow, workspace_id) VALUES ($1, $2, TRUE, $3) ON CONFLICT DO NOTHING", + job_path, + path, + job.workspace_id, + ) + .execute(&mut *tx) + .await?; + } _ => (), }; modified_ids.extend(nmodified_ids); @@ -946,6 +1132,12 @@ async fn lock_modules<'c>( modified_ids.push(e.id.clone()); + remove_dir_all(job_dir).map_err(|e| { + Error::ExecutionErr(format!("Error removing job dir for flow step lock: {e}")) + })?; + create_dir_all(job_dir).map_err(|e| { + Error::ExecutionErr(format!("Error creating job dir for flow step lock: {e}")) + })?; let new_lock = capture_dependency_job( &job.id, &language, @@ -999,7 +1191,7 @@ async fn lock_modules<'c>( Some(e.id.clone()), ) .await?; - append_logs(&job.id, &job.workspace_id, logs, db).await; + append_logs(&job.id, &job.workspace_id, logs, &db.into()).await; } if language == ScriptLang::Bun || language == ScriptLang::Bunnative { @@ -1014,12 +1206,7 @@ async fn lock_modules<'c>( } Err(error) => { // TODO: Record flow raw script error lock logs - tracing::warn!( - path = path, - language = ?language, - error = ?error, - "Failed to generate flow lock for raw script" - ); + errors.push(LockModuleError { id, error }); None } }; @@ -1038,7 +1225,8 @@ async fn lock_modules<'c>( new_flow_modules.push(e); continue; } - Ok((new_flow_modules, tx, modified_ids)) + + Ok((new_flow_modules, tx, modified_ids, errors)) } async fn insert_flow_node<'c>( @@ -1130,7 +1318,6 @@ async fn insert_flow_modules<'c>( same_worker, )) .await?; - add_virtual_items_if_necessary(modules); if modules.is_empty() || crate::worker_flow::is_simple_modules(modules, failure_module) { return Ok(tx); } @@ -1259,6 +1446,7 @@ async fn reduce_flow<'c>( } module.value = to_raw_value(&val); } + add_virtual_items_if_necessary(&mut *modules); Ok(tx) } @@ -1319,7 +1507,7 @@ fn skip_creating_new_lock(language: &ScriptLang, content: &str) -> bool { #[async_recursion] async fn lock_modules_app( value: Value, - job: &QueuedJob, + job: &MiniPulledJob, mem_peak: &mut i32, canceled_by: &mut Option, job_dir: &str, @@ -1333,6 +1521,22 @@ async fn lock_modules_app( ) -> Result { match value { Value::Object(mut m) => { + if let (Some(Value::String(ref run_type)), Some(path), Some("runnableByPath")) = ( + m.get("runType"), + m.get("path").and_then(|s| s.as_str()), + m.get("type").and_then(|s| s.as_str()), + ) { + // No script_hash because apps don't supports script version locks yet + sqlx::query!( + "INSERT INTO workspace_runnable_dependencies (app_path, runnable_path, runnable_is_flow, workspace_id) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING", + job_path, + path, + run_type == "flow", + job.workspace_id + ) + .execute(db) + .await?; + } if m.contains_key("inlineScript") { let v = m.get_mut("inlineScript").unwrap(); if let Some(v) = v.as_object_mut() { @@ -1371,7 +1575,7 @@ async fn lock_modules_app( worker_dir, base_internal_url, token, - &format!("{}/app", job.script_path()), + &format!("{}/app", job.runnable_path()), false, None, occupancy_metrics, @@ -1379,7 +1583,7 @@ async fn lock_modules_app( .await; match new_lock { Ok(new_lock) => { - append_logs(&job.id, &job.workspace_id, logs, db).await; + append_logs(&job.id, &job.workspace_id, logs, &db.into()).await; let anns = windmill_common::worker::TypeScriptAnnotations::parse( &content, @@ -1467,7 +1671,7 @@ async fn lock_modules_app( } pub async fn handle_app_dependency_job( - job: &QueuedJob, + job: &MiniPulledJob, mem_peak: &mut i32, canceled_by: &mut Option, job_dir: &str, @@ -1478,17 +1682,26 @@ pub async fn handle_app_dependency_job( token: &str, occupancy_metrics: &mut OccupancyMetrics, ) -> error::Result<()> { - let job_path = job.script_path.clone().ok_or_else(|| { + let job_path = job.runnable_path.clone().ok_or_else(|| { error::Error::internal_err( "Cannot resolve app dependencies for app without path".to_string(), ) })?; let id = job - .script_hash + .runnable_id .clone() .ok_or_else(|| Error::internal_err("App Dependency requires script hash".to_owned()))? .0; + + sqlx::query!( + "DELETE FROM workspace_runnable_dependencies WHERE app_path = $1 AND workspace_id = $2", + job_path, + job.workspace_id + ) + .execute(db) + .await?; + let record = sqlx::query!("SELECT app_id, value FROM app_version WHERE id = $1", id) .fetch_optional(db) .await? @@ -1549,7 +1762,7 @@ pub async fn handle_app_dependency_job( get_deployment_msg_and_parent_path_from_args(job.args.clone()); if let Err(e) = handle_deployment_metadata( - &job.email, + &job.permissioned_as_email, &job.created_by, &db, &job.workspace_id, @@ -1588,6 +1801,90 @@ pub async fn handle_app_dependency_job( } } +// async fn upload_raw_app( +// app_value: &RawAppValue, +// job: &QueuedJob, +// mem_peak: &mut i32, +// canceled_by: &mut Option, +// job_dir: &str, +// db: &sqlx::Pool, +// worker_name: &str, +// occupancy_metrics: &mut Option<&mut OccupancyMetrics>, +// version: i64, +// ) -> Result<()> { +// let mut entrypoint = "index.ts"; +// for file in app_value.files.iter() { +// if file.0 == "/index.tsx" { +// entrypoint = "index.tsx"; +// } else if file.0 == "/index.js" { +// entrypoint = "index.js"; +// } +// write_file(&job_dir, file.0, &file.1)?; +// } +// let common_bun_proc_envs: HashMap = get_common_bun_proc_envs(None).await; + +// install_bun_lockfile( +// mem_peak, +// canceled_by, +// &job.id, +// &job.workspace_id, +// Some(db), +// job_dir, +// worker_name, +// common_bun_proc_envs, +// false, +// occupancy_metrics, +// ) +// .await?; +// let mut cmd = tokio::process::Command::new("esbuild"); +// let mut args = "--bundle --minify --outdir=dist/" +// .split(' ') +// .collect::>(); +// args.push(entrypoint); +// cmd.current_dir(job_dir) +// .env_clear() +// .args(args) +// .stdout(Stdio::piped()) +// .stderr(Stdio::piped()); +// let child = start_child_process(cmd, "esbuild").await?; + +// crate::handle_child::handle_child( +// &job.id, +// db, +// mem_peak, +// canceled_by, +// child, +// false, +// worker_name, +// &job.workspace_id, +// "esbuild", +// Some(30), +// false, +// occupancy_metrics, +// ) +// .await?; +// let output_dir = format!("{}/dist", job_dir); +// let target_dir = format!("/home/rfiszel/wmill/{}/{}", job.workspace_id, version); + +// tokio::fs::create_dir_all(&target_dir).await?; + +// tracing::info!("Copying files from {} to {}", output_dir, target_dir); + +// let index_ts = format!("{}/index.js", output_dir); +// let index_css = format!("{}/index.css", output_dir); + +// if tokio::fs::metadata(&index_ts).await.is_ok() { +// tokio::fs::copy(&index_ts, format!("{}/index.js", target_dir)).await?; +// } + +// if tokio::fs::metadata(&index_css).await.is_ok() { +// tokio::fs::copy(&index_css, format!("{}/index.css", target_dir)).await?; +// } +// // let file_path = format!("/home/rfiszel/wmill/{}/{}", job.workspace_id, version); + +// Ok(()) +// } + #[cfg(feature = "python")] async fn python_dep( reqs: String, @@ -1602,8 +1899,6 @@ async fn python_dep( occupancy_metrics: &mut Option<&mut OccupancyMetrics>, annotated_pyv_numeric: Option, annotations: PythonAnnotations, - no_uv_compile: bool, - no_uv_install: bool, ) -> std::result::Result { create_dependencies_dir(job_dir).await; @@ -1620,7 +1915,7 @@ async fn python_dep( let final_version = annotated_pyv_numeric .and_then(|pyv| PyVersion::from_numeric(pyv)) - .unwrap_or(PyVersion::from_instance_version().await); + .unwrap_or(PyVersion::from_instance_version(job_id, w_id, &db.into()).await); let req: std::result::Result = uv_pip_compile( job_id, @@ -1628,13 +1923,12 @@ async fn python_dep( mem_peak, canceled_by, job_dir, - db, + &db.into(), worker_name, w_id, occupancy_metrics, final_version, annotations.no_cache, - no_uv_compile, ) .await; // install the dependencies to pre-fill the cache @@ -1645,13 +1939,12 @@ async fn python_dep( w_id, mem_peak, canceled_by, - db, + &Connection::Sql(db.clone()), worker_name, job_dir, worker_dir, occupancy_metrics, final_version, - no_uv_install, ) .await; @@ -1665,6 +1958,126 @@ async fn python_dep( req } +#[cfg(feature = "python")] +async fn ansible_dep( + reqs: AnsibleRequirements, + job_id: &Uuid, + mem_peak: &mut i32, + canceled_by: &mut Option, + job_dir: &str, + db: &sqlx::Pool, + worker_name: &str, + w_id: &str, + worker_dir: &str, + occupancy_metrics: &mut OccupancyMetrics, + token: &str, + base_internal_url: &str, +) -> std::result::Result { + use windmill_parser_yaml::add_versions_to_requirements_yaml; + + use crate::{ + ansible_executor::{ + create_ansible_cfg, get_collection_locks, get_git_ssh_cmd, get_role_locks, + install_galaxy_collections, + }, + AuthedClient, + }; + + let python_lockfile = python_dep( + reqs.python_reqs.join("\n").to_string(), + job_id, + mem_peak, + canceled_by, + job_dir, + db, + worker_name, + w_id, + worker_dir, + &mut Some(occupancy_metrics), + None, + PythonAnnotations::default(), + ) + .await?; + + let conn = &Connection::Sql(db.clone()); + + let authed_client = AuthedClient { + base_internal_url: base_internal_url.to_string(), + token: token.to_string(), + workspace: w_id.to_string(), + force_client: None, + }; + + let git_ssh_cmd = get_git_ssh_cmd(&reqs, job_dir, &authed_client).await?; + + let git_repos = get_git_repos_lock( + &reqs.git_repos, + job_dir, + job_id, + worker_name, + conn, + mem_peak, + canceled_by, + w_id, + occupancy_metrics, + &git_ssh_cmd, + ) + .await?; + + let ansible_lockfile; + + create_ansible_cfg(Some(&reqs), job_dir, false)?; + + if let Some(collections) = reqs.roles_and_collections.as_ref() { + install_galaxy_collections( + collections, + job_dir, + job_id, + worker_name, + w_id, + mem_peak, + canceled_by, + conn, + occupancy_metrics, + &git_ssh_cmd, + ) + .await?; + + let (collection_versions, logs1) = get_collection_locks(job_dir).await?; + + let (role_versions, logs2) = if collections.contains("roles:") { + get_role_locks(job_dir).await? + } else { + (HashMap::new(), String::new()) + }; + + let (reqs_yaml, logs3) = + add_versions_to_requirements_yaml(&collections, &role_versions, &collection_versions)?; + + let logs = format!("\n{logs1}\n{logs2}\n{logs3}\n"); + + append_logs(job_id, w_id, &logs, conn).await; + + ansible_lockfile = AnsibleDependencyLocks { + python_lockfile, + git_repos, + collections_and_roles: reqs_yaml, + collections_and_roles_logs: logs, + }; + } else { + ansible_lockfile = AnsibleDependencyLocks { + python_lockfile, + git_repos, + collections_and_roles: String::new(), + collections_and_roles_logs: String::new(), + }; + } + + serde_json::to_string(&ansible_lockfile).map_err(|e| e.into()) +} + +pub const LOCKFILE_GENERATED_FROM_REQUIREMENTS_TXT: &str = "# from requirements.txt"; + async fn capture_dependency_job( job_id: &Uuid, job_language: &ScriptLang, @@ -1714,23 +2127,9 @@ async fn capture_dependency_job( &mut annotated_pyv_numeric, ) .await? + .0 .join("\n") }; - let PythonAnnotations { no_uv, no_uv_install, no_uv_compile, .. } = anns; - if no_uv || no_uv_install || no_uv_compile || *USE_PIP_COMPILE || *USE_PIP_INSTALL { - if let Err(e) = sqlx::query!( - r#" - INSERT INTO metrics (id, value) - VALUES ('no_uv_usage_py', $1) - "#, - serde_json::to_value("").map_err(to_anyhow)? - ) - .execute(db) - .await - { - tracing::error!("Error inserting no_uv_usage_py to db: {:?}", e); - } - } python_dep( reqs, @@ -1745,10 +2144,15 @@ async fn capture_dependency_job( &mut Some(occupancy_metrics), annotated_pyv_numeric, anns, - no_uv_compile | no_uv, - no_uv_install | no_uv, ) .await + .map(|res| { + if raw_deps { + format!("{}\n{}", LOCKFILE_GENERATED_FROM_REQUIREMENTS_TXT, res) + } else { + res + } + }) } } ScriptLang::Ansible => { @@ -1765,25 +2169,9 @@ async fn capture_dependency_job( )); } let (_logs, reqs, _) = windmill_parser_yaml::parse_ansible_reqs(job_raw_code)?; - let reqs = reqs.map(|r| r.python_reqs.join("\n")).unwrap_or_default(); - if *USE_PIP_COMPILE || *USE_PIP_INSTALL { - if let Err(e) = sqlx::query!( - r#" - INSERT INTO metrics (id, value) - VALUES ('no_uv_usage_ansible', $1) - "#, - serde_json::to_value("").map_err(to_anyhow)? - ) - .execute(db) - .await - { - tracing::error!("Error inserting no_uv_usage_ansible to db: {:?}", e); - }; - } - - python_dep( - reqs, + ansible_dep( + reqs.unwrap_or_default(), job_id, mem_peak, canceled_by, @@ -1792,11 +2180,9 @@ async fn capture_dependency_job( worker_name, w_id, worker_dir, - &mut Some(occupancy_metrics), - None, - PythonAnnotations::default(), - false, - false, + occupancy_metrics, + token, + base_internal_url, ) .await } @@ -1813,7 +2199,7 @@ async fn capture_dependency_job( mem_peak, canceled_by, job_dir, - db, + &db.into(), false, false, false, @@ -1835,7 +2221,7 @@ async fn capture_dependency_job( mem_peak, canceled_by, job_dir, - Some(db), + Some(&db.into()), w_id, worker_name, base_internal_url, @@ -1855,7 +2241,7 @@ async fn capture_dependency_job( canceled_by, job_id, w_id, - Some(db), + Some(&db.into()), token, script_path, job_dir, @@ -1878,7 +2264,7 @@ async fn capture_dependency_job( script_path, job_id, w_id, - Some(db.clone()), + Some(&db), &job_dir, base_internal_url, worker_name, @@ -1915,7 +2301,7 @@ async fn capture_dependency_job( canceled_by, job_id, w_id, - db, + &Connection::Sql(db.clone()), job_dir, worker_name, reqs, @@ -1944,7 +2330,7 @@ async fn capture_dependency_job( mem_peak, canceled_by, job_dir, - db, + &Connection::Sql(db.clone()), worker_name, w_id, occupancy_metrics, @@ -1967,22 +2353,31 @@ async fn capture_dependency_job( mem_peak, canceled_by, job_dir, - db, + &Connection::Sql(db.clone()), worker_name, w_id, occupancy_metrics, ) .await } - ScriptLang::Postgresql => Ok("".to_owned()), - ScriptLang::Mysql => Ok("".to_owned()), - ScriptLang::Bigquery => Ok("".to_owned()), - ScriptLang::Snowflake => Ok("".to_owned()), - ScriptLang::Mssql => Ok("".to_owned()), - ScriptLang::Graphql => Ok("".to_owned()), - ScriptLang::OracleDB => Ok("".to_owned()), - ScriptLang::Bash => Ok("".to_owned()), - ScriptLang::Powershell => Ok("".to_owned()), - ScriptLang::Nativets => Ok("".to_owned()), + #[cfg(feature = "java")] + ScriptLang::Java => { + if raw_deps { + return Err(Error::ExecutionErr( + "Raw dependencies not supported for Java".to_string(), + )); + } + + resolve( + job_id, + job_raw_code, + job_dir, + &Connection::Sql(db.clone()), + w_id, + ) + .await + } + // for related places search: ADD_NEW_LANG + _ => Ok("".to_owned()), } } diff --git a/backend/windmill-worker/src/worker_utils.rs b/backend/windmill-worker/src/worker_utils.rs new file mode 100644 index 0000000000..4c83448236 --- /dev/null +++ b/backend/windmill-worker/src/worker_utils.rs @@ -0,0 +1,322 @@ +use backon::{BackoffBuilder, ConstantBuilder, Retryable}; +use tracing::Instrument; +use uuid::Uuid; +use windmill_common::{ + agent_workers::{PingJobStatus, PingJobStatusResponse}, + worker::{ + get_memory, get_vcpus, get_windmill_memory_usage, get_worker_memory_usage, + insert_ping_query, update_job_ping_query, update_worker_ping_from_job_query, + update_worker_ping_main_loop_query, Connection, Ping, PingType, WORKER_CONFIG, + WORKER_GROUP, + }, + KillpillSender, +}; + +use crate::{ + agent_workers::UPDATE_PING_URL, + common::{OccupancyMetrics, OccupancyResult}, +}; + +pub(crate) async fn update_worker_ping_full( + conn: &Connection, + read_cgroups: bool, + jobs_executed: i32, + worker_name: &str, + hostname: &str, + occupancy_metrics: &mut OccupancyMetrics, + killpill_tx: &KillpillSender, +) { + let tags = WORKER_CONFIG.read().await.worker_tags.clone(); + + let memory_usage = get_worker_memory_usage(); + let wm_memory_usage = get_windmill_memory_usage(); + + let (vcpus, memory) = if read_cgroups { + (get_vcpus(), get_memory()) + } else { + (None, None) + }; + + let OccupancyResult { + occupancy_rate, + occupancy_rate_15s, + occupancy_rate_5m, + occupancy_rate_30m, + } = occupancy_metrics.update_occupancy_metrics(); + + if let Err(e) = (|| { + update_worker_ping_full_inner( + conn, + jobs_executed, + &worker_name, + &tags, + memory_usage, + wm_memory_usage, + vcpus, + memory, + occupancy_rate, + occupancy_rate_15s, + occupancy_rate_5m, + occupancy_rate_30m, + ) + }) + .retry( + ConstantBuilder::default() + .with_delay(std::time::Duration::from_secs(2)) + .with_max_times(10) + .build(), + ) + .notify(|err, dur| { + tracing::error!( + worker = %worker_name, hostname = %hostname, + "retrying updating worker ping in {dur:#?}, err: {err:#?}" + ); + }) + .sleep(tokio::time::sleep) + .await + { + tracing::error!( + worker = %worker_name, hostname = %hostname, + "failed to update worker ping, exiting: {}", e); + killpill_tx.send(); + } + tracing::info!( + worker = %worker_name, hostname = %hostname, + "ping update, memory: container={}MB, windmill={}MB", + memory_usage.unwrap_or_default() / (1024 * 1024), + wm_memory_usage.unwrap_or_default() / (1024 * 1024) + ); +} + +async fn update_worker_ping_full_inner( + conn: &Connection, + jobs_executed: i32, + worker_name: &str, + tags: &[String], + memory_usage: Option, + wm_memory_usage: Option, + vcpus: Option, + memory: Option, + occupancy_rate: f32, + occupancy_rate_15s: Option, + occupancy_rate_5m: Option, + occupancy_rate_30m: Option, +) -> anyhow::Result<()> { + match conn { + Connection::Sql(db) => { + update_worker_ping_main_loop_query( + worker_name, + tags, + vcpus, + memory, + Some(jobs_executed), + Some(occupancy_rate), + memory_usage, + wm_memory_usage, + occupancy_rate_15s, + occupancy_rate_5m, + occupancy_rate_30m, + db, + ) + .await?; + } + Connection::Http(client) => { + client + .post::<_, ()>( + UPDATE_PING_URL, + &Ping { + last_job_executed: None, + last_job_workspace_id: None, + worker_instance: None, + ip: None, + tags: Some(tags.to_vec()), + dw: None, + jobs_executed: Some(jobs_executed), + occupancy_rate: Some(occupancy_rate), + occupancy_rate_15s: Some(occupancy_rate_15s.unwrap_or(0.0)), + occupancy_rate_5m: Some(occupancy_rate_5m.unwrap_or(0.0)), + occupancy_rate_30m: Some(occupancy_rate_30m.unwrap_or(0.0)), + version: None, + vcpus: vcpus, + memory: memory, + memory_usage: get_worker_memory_usage(), + wm_memory_usage: get_windmill_memory_usage(), + ping_type: PingType::MainLoop, + }, + ) + .await?; + } + } + Ok(()) +} + +pub async fn insert_ping( + worker_instance: &str, + worker_name: &str, + ip: &str, + db: &Connection, +) -> anyhow::Result<()> { + let (tags, dw) = { + let wc = WORKER_CONFIG.read().await.clone(); + ( + wc.worker_tags, + wc.dedicated_worker + .as_ref() + .map(|x| format!("{}:{}", x.workspace_id, x.path)), + ) + }; + + let vcpus = get_vcpus(); + let memory = get_memory(); + + match db { + Connection::Sql(db) => { + insert_ping_query( + worker_instance, + worker_name, + WORKER_GROUP.as_str(), + ip, + tags.as_slice(), + dw, + windmill_common::utils::GIT_VERSION, + vcpus, + memory, + db, + ) + .await?; + } + Connection::Http(client) => { + client + .post::<_, ()>( + UPDATE_PING_URL, + &Ping { + last_job_executed: None, + last_job_workspace_id: None, + worker_instance: Some(worker_instance.to_string()), + ip: Some(ip.to_string()), + tags: Some(tags.to_vec()), + dw: dw, + jobs_executed: None, + occupancy_rate: None, + occupancy_rate_15s: None, + occupancy_rate_5m: None, + occupancy_rate_30m: None, + version: Some(windmill_common::utils::GIT_VERSION.to_string()), + vcpus: vcpus, + memory: memory, + memory_usage: get_worker_memory_usage(), + wm_memory_usage: get_windmill_memory_usage(), + ping_type: PingType::Initial, + }, + ) + .await?; + } + } + Ok(()) +} + +pub async fn update_worker_ping_from_job( + conn: &Connection, + job_id: &Uuid, + w_id: &str, + worker_name: &str, + memory_usage: Option, + wm_memory_usage: Option, + occupancy: Option, +) -> anyhow::Result<()> { + let occupancy_rate = occupancy.as_ref().map(|x| x.occupancy_rate); + let occupancy_rate_15s = occupancy.as_ref().and_then(|x| x.occupancy_rate_15s); + let occupancy_rate_5m = occupancy.as_ref().and_then(|x| x.occupancy_rate_5m); + let occupancy_rate_30m = occupancy.as_ref().and_then(|x| x.occupancy_rate_30m); + match conn.clone() { + Connection::Sql(ref db) => { + update_worker_ping_from_job_query( + job_id, + w_id, + worker_name, + memory_usage, + wm_memory_usage, + occupancy_rate, + occupancy_rate_15s, + occupancy_rate_5m, + occupancy_rate_30m, + db, + ) + .await?; + } + Connection::Http(client) => { + client + .post::( + UPDATE_PING_URL, + &Ping { + last_job_executed: Some(job_id.clone()), + last_job_workspace_id: Some(w_id.to_string()), + ping_type: PingType::Job, + worker_instance: None, + ip: None, + tags: None, + dw: None, + version: None, + vcpus: None, + memory: None, + memory_usage: memory_usage, + wm_memory_usage: wm_memory_usage, + jobs_executed: None, + occupancy_rate: occupancy_rate, + occupancy_rate_15s: occupancy_rate_15s, + occupancy_rate_5m: occupancy_rate_5m, + occupancy_rate_30m: occupancy_rate_30m, + }, + ) + .await?; + } + } + Ok(()) +} + +pub async fn ping_job_status( + conn: &Connection, + job_id: &Uuid, + mem_peak: Option, + current_mem: Option, +) -> anyhow::Result { + match conn { + Connection::Sql(ref db) => update_job_ping_query(job_id, db, mem_peak).await, + Connection::Http(client) => { + client + .post( + &format!("/api/agent_workers/ping_job_status/{}", job_id), + &PingJobStatus { mem_peak, current_mem }, + ) + .await + } + } +} + +pub(crate) async fn queue_vacuum(conn: &Connection, worker_name: &str, hostname: &str) { + match conn { + Connection::Sql(db) => { + let db2 = db.clone(); + let current_span = tracing::Span::current(); + let worker_name = worker_name.to_string(); + let hostname = hostname.to_string(); + tokio::task::spawn( + (async move { + tracing::info!(worker = %worker_name, hostname = %hostname, "vacuuming queue"); + if let Err(e) = sqlx::query!("VACUUM v2_job_queue, v2_job_runtime, v2_job_status") + .execute(&db2) + .await + { + tracing::error!(worker = %worker_name, hostname = %hostname, "failed to vacuum queue: {}", e); + } + tracing::info!(worker = %worker_name, hostname = %hostname, "vacuumed queue"); + }) + .instrument(current_span), + ); + } + Connection::Http(_) => { + // do nothing in http mode + () + } + } +} diff --git a/benchmarks/bench.sh b/benchmarks/bench.sh new file mode 100644 index 0000000000..5e2837c2c6 --- /dev/null +++ b/benchmarks/bench.sh @@ -0,0 +1,43 @@ +curl -fsSL https://get.docker.com | sh +sudo usermod -aG docker $USER +newgrp docker + + +mkdir pg_logs +chmod 777 pg_logs + +sudo docker run --network=host -e POSTGRES_PASSWORD=changeme -e POSTGRES_USER=postgres -e POSTGRES_DB=windmill -e POSTGRES_INITDB_ARGS="-c log_duration=on -c log_statement=all -c log_min_duration_statement=0 -c shared_buffers=2GB -c work_mem=32MB -c effective_cache_size=4GB -c shared_preload_libraries=auto_explain -c auto_explain.log_min_duration=5 -c auto_explain.log_analyze=on -c auto_explain.log_timing=on -c auto_explain.log_buffers=on -c auto_explain.log_verbose=on \ + -c log_statement=all \ + -c log_min_duration_statement=0 \ + -c shared_buffers=2GB \ + -c work_mem=32MB \ + -c effective_cache_size=4GB \ + -c shared_preload_libraries=auto_explain \ + -c auto_explain.log_min_duration=5 \ + -c auto_explain.log_analyze=on \ + -c auto_explain.log_timing=on \ + -c auto_explain.log_buffers=on \ + -c auto_explain.log_verbose=on \ + -c auto_explain.log_nested_statements=on \ + -c logging_collector=on \ + -c log_directory='/var/log/postgresql' \ + -c log_filename='postgresql.log'" \ + -v ~/pg_logs:/var/log/postgresql \ + postgres + + +docker run -it --network=host -e DATABASE_URL=postgres://postgres:changeme@localhost/windmill ghcr.io/windmill-labs/windmill:main + + +curl -fsSL https://deno.land/install.sh | sh + +cat < suite.json +[ + { + "kind": "noop", + "jobs": 90000 + } +] +EOF + +deno run --unstable -A -r https://raw.githubusercontent.com/windmill-labs/windmill/main/benchmarks/benchmark_suite.ts -c suite.json diff --git a/benchmarks/benchmark_oneoff.ts b/benchmarks/benchmark_oneoff.ts index 41321b3ec6..6685eae1c5 100644 --- a/benchmarks/benchmark_oneoff.ts +++ b/benchmarks/benchmark_oneoff.ts @@ -26,7 +26,7 @@ async function verifyOutputs(uuids: string[], workspace: string) { incorrectResults++; } if (job.result !== uuid) { - console.log(`Job ${uuid} did not output the correct value`); + console.log(`Job ${uuid} did not output the correct value: ${JSON.stringify(job)}`); incorrectResults++; } } catch (_) { @@ -37,6 +37,7 @@ async function verifyOutputs(uuids: string[], workspace: string) { console.log(`Incorrect results: ${incorrectResults}`); } +export const NON_TEST_TAGS = ["deno", "python", "go", "bash", "dedicated", "bun", "nativets", "flow"] export async function main({ host, email, @@ -96,11 +97,11 @@ export async function main({ windmill.setClient(final_token, host); const enc = (s: string) => new TextEncoder().encode(s); - async function getQueueCount() { + async function getQueueCount(tags?: string[]) { return ( await ( await fetch( - config.server + "/api/w/" + config.workspace_id + "/jobs/queue/count", + config.server + "/api/w/" + config.workspace_id + "/jobs/queue/count" + (tags && tags.length > 0 ? "?tags=" + tags.join(",") : ""), { headers: { ["Authorization"]: "Bearer " + config.token } } ) ).json() @@ -132,11 +133,11 @@ export async function main({ } let pastJobs = 0; - async function getCompletedJobsCount(): Promise { + async function getCompletedJobsCount(tags?: string[]): Promise { const completedJobs = ( await ( await fetch( - host + "/api/w/" + config.workspace_id + "/jobs/completed/count", + host + "/api/w/" + config.workspace_id + "/jobs/completed/count" + (tags && tags.length > 0 ? "?tags=" + tags.join(",") : ""), { headers: { ["Authorization"]: "Bearer " + config.token } } ) ).json() @@ -152,7 +153,6 @@ export async function main({ await createBenchScript(kind, workspace); } - pastJobs = await getCompletedJobsCount(); const jobsSent = jobs; console.log(`Bulk creating ${jobsSent} jobs`); @@ -201,18 +201,51 @@ export async function main({ kind: "rawscript", rawscript: { language: api.RawScript.language.BASH, - content: "# let's bloat that bash script, 3.. 2.. 1.. BOOM\n".repeat(25000) + "echo \"$WM_FLOW_JOB_ID\"\n", + content: "# let's bloat that bash script, 3.. 2.. 1.. BOOM\n".repeat(100) + "echo \"$WM_FLOW_JOB_ID\"\n", }, }); } else { throw new Error("Unknown script pattern " + kind); } - const response = await fetch( - config.server + + let testOtherTag = false; + if (testOtherTag) { + const otherTagTodo = 2000000; + + let parsed = JSON.parse(body); + parsed.tag = "test"; + let nbody = JSON.stringify(parsed); + let response2 = await fetch( + config.server + "/api/w/" + config.workspace_id + - `/jobs/add_batch_jobs/${jobsSent}`, + `/jobs/add_batch_jobs/${otherTagTodo}`, + { + method: "POST", + headers: { + ["Authorization"]: "Bearer " + config.token, + "Content-Type": "application/json", + }, + body: nbody, + } + ); + if (!response2.ok) { + throw new Error( + "Failed to create jobs: " + + response2.statusText + + " " + + (await response2.text()) + ); + } + } + + pastJobs = await getCompletedJobsCount(NON_TEST_TAGS); + + const response = await fetch( + config.server + + "/api/w/" + + config.workspace_id + + `/jobs/add_batch_jobs/${jobsSent}`, { method: "POST", headers: { @@ -222,20 +255,24 @@ export async function main({ body, } ); + + + + + if (!response.ok) { throw new Error( "Failed to create jobs: " + - response.statusText + - " " + - (await response.text()) + response.statusText + + " " + + (await response.text()) ); } const uuids = await response.json(); const end_create = Date.now(); const create_duration = end_create - start_create; console.log( - `Jobs successfully added to the queue in ${ - create_duration / 1000 + `Jobs successfully added to the queue in ${create_duration / 1000 }s. Windmill will start pulling them\n` ); let start = Date.now(); @@ -248,14 +285,14 @@ export async function main({ while (completedJobs < jobsSent) { const loopStart = Date.now(); if (!didStart) { - const actual_queue = await getQueueCount(); + const actual_queue = await getQueueCount(NON_TEST_TAGS); if (actual_queue < jobsSent) { start = Date.now(); didStart = true; } } else { const elapsed = start ? Date.now() - start : 0; - completedJobs = await getCompletedJobsCount(); + completedJobs = await getCompletedJobsCount(NON_TEST_TAGS); if (nStepsFlow > 0) { completedJobs = Math.floor(completedJobs / (nStepsFlow + 1)); } @@ -263,9 +300,9 @@ export async function main({ const instThr = lastElapsed > 0 ? ( - ((completedJobs - lastCompletedJobs) / (elapsed - lastElapsed)) * - 1000 - ).toFixed(2) + ((completedJobs - lastCompletedJobs) / (elapsed - lastElapsed)) * + 1000 + ).toFixed(2) : 0; lastElapsed = elapsed; @@ -275,8 +312,7 @@ export async function main({ enc( `elapsed: ${(elapsed / 1000).toFixed( 2 - )} | jobs executed: ${completedJobs}/${jobsSent} (thr: inst ${instThr} - avg ${avgThr}) | remaining: ${ - jobsSent - completedJobs + )} | jobs executed: ${completedJobs}/${jobsSent} (thr: inst ${instThr} - avg ${avgThr}) | remaining: ${jobsSent - completedJobs } \r` ) ); @@ -294,7 +330,7 @@ export async function main({ console.log(`avg. throughput (jobs/time): ${jobsSent / total_duration_sec}`); console.log("completed jobs", completedJobs); - console.log("queue length:", await getQueueCount()); + console.log("queue length:", await getQueueCount(NON_TEST_TAGS)); if ( !noVerify && diff --git a/benchmarks/benchmark_suite.ts b/benchmarks/benchmark_suite.ts index e03148ce99..f4840dda05 100644 --- a/benchmarks/benchmark_suite.ts +++ b/benchmarks/benchmark_suite.ts @@ -39,6 +39,7 @@ async function main({ workspace, configPath, workers, + factor }: { host: string; email?: string; @@ -47,6 +48,7 @@ async function main({ workspace: string; configPath: string; workers: number; + factor?: number; }) { async function getConfig(configPath: string): Promise { if (configPath.startsWith("http")) { @@ -77,7 +79,7 @@ async function main({ token, workspace, kind: benchmark.kind, - jobs: benchmark.jobs, + jobs: benchmark.jobs * (factor ?? 1), }); if (benchmark.noSave) { @@ -153,6 +155,9 @@ await new Command() "Number of workers that are used to run the benchmarks (only affect graph title)", { default: 1 } ) + .option("--factor ", "Factor to multiply the number of jobs by.", { + default: 1, + }) .action(main) .command( "upgrade", diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 880999a48a..cc627194ca 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts"; import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts"; import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts"; -export const VERSION = "v1.458.1"; +export const VERSION = "v1.488.0"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ @@ -25,7 +25,7 @@ async function waitForDeployment(workspace: string, hash: string) { if (resp.lock !== null) { return; } - } catch (err) {} + } catch (err) { } await sleep(0.5); } throw new Error("Script did not deploy in time"); @@ -246,7 +246,7 @@ export const getFlowPayload = (flowPattern: string): api.FlowPreview => { input_transforms: {}, language: api.RawScript.language.BASH, type: "rawscript", - content: "# let's bloat that bash script, 3.. 2.. 1.. BOOM\n".repeat(25000) + "echo \"$WM_FLOW_JOB_ID\"\n", + content: "# let's bloat that bash script, 3.. 2.. 1.. BOOM\n".repeat(100) + `if [[ -z $\{WM_FLOW_JOB_ID+x\} ]]; then\necho "not set"\nelif [[ -z "$WM_FLOW_JOB_ID" ]]; then\necho "empty"\nelse\necho "$WM_FLOW_JOB_ID"\nfi`, }, } ], diff --git a/cli/.gitignore b/cli/.gitignore index 409ee189ef..d0b1f2c514 100644 --- a/cli/.gitignore +++ b/cli/.gitignore @@ -1 +1,2 @@ npm/ +gen/ \ No newline at end of file diff --git a/cli/bootstrap/script_bootstrap.ts b/cli/bootstrap/script_bootstrap.ts index 79ca7d92b7..2ac67bdb83 100644 --- a/cli/bootstrap/script_bootstrap.ts +++ b/cli/bootstrap/script_bootstrap.ts @@ -94,6 +94,11 @@ function main() { } } `, + nu: ` +def main [] { + print "Hello World" +} + `, rust: `fn main() -> Result<(), String> { println!("Hello World"); @@ -114,4 +119,12 @@ inventory: debug: msg: "Hello, world!" `, + java: ` +public class Main { + public static void main() { + System.out.println("Hello World"); + } +} +`, +// for related places search: ADD_NEW_LANG }; diff --git a/cli/codebase.ts b/cli/codebase.ts index 844a51f52d..8af46ff284 100644 --- a/cli/codebase.ts +++ b/cli/codebase.ts @@ -2,25 +2,31 @@ import { Codebase, SyncOptions } from "./conf.ts"; import { log } from "./deps.ts"; import { digestDir } from "./utils.ts"; -export type SyncCodebase = Codebase & { digest: string }; -export async function listSyncCodebases( +export type SyncCodebase = Codebase & { getDigest: () => Promise }; +export function listSyncCodebases( options: SyncOptions -): Promise { +): SyncCodebase[] { const res: SyncCodebase[] = []; const nb_codebase = options?.codebases?.length ?? 0; if (nb_codebase > 0) { - log.info(`Found ${nb_codebase} codebases:`); + log.info(`Found ${nb_codebase} codebases: ${options?.codebases?.map((c) => c.relative_path).join(", ")}`); } for (const codebase of options?.codebases ?? []) { - let digest = await digestDir( - codebase.relative_path, - JSON.stringify(codebase) - ); - if (Array.isArray(codebase.assets) && codebase.assets.length > 0) { - digest += ".tar"; - } - log.info(`Codebase ${codebase.relative_path}, digest: ${digest}`); - res.push({ ...codebase, digest }); + let _digest: string | undefined = undefined; + const getDigest: () => Promise = async () => { + if (_digest == undefined) { + _digest = await digestDir( + codebase.relative_path, + JSON.stringify(codebase) + ); + if (Array.isArray(codebase.assets) && codebase.assets.length > 0) { + _digest += ".tar"; + } + log.info(`Codebase ${codebase.relative_path}, digest: ${_digest}`); + } + return _digest; + }; + res.push({ ...codebase, getDigest }); } return res; diff --git a/cli/conf.ts b/cli/conf.ts index 2bb3a69b9b..e2a4c1cdbf 100644 --- a/cli/conf.ts +++ b/cli/conf.ts @@ -4,14 +4,17 @@ export interface SyncOptions { stateful?: boolean; raw?: boolean; yes?: boolean; + dryRun?: boolean; skipPull?: boolean; failConflicts?: boolean; plainSecrets?: boolean; json?: boolean; skipVariables?: boolean; skipResources?: boolean; + skipResourceTypes?: boolean; skipSecrets?: boolean; includeSchedules?: boolean; + includeTriggers?: boolean; includeUsers?: boolean; includeGroups?: boolean; includeSettings?: boolean; @@ -60,5 +63,5 @@ export async function mergeConfigWithConfigFile( opts: T ): Promise { const configFile = await readConfigFile(); - return Object.assign(configFile, opts); + return Object.assign(configFile ?? {}, opts); } diff --git a/cli/deno.json b/cli/deno.json index 6276c18bbc..fae7e8f7dd 100644 --- a/cli/deno.json +++ b/cli/deno.json @@ -15,4 +15,4 @@ "@std/yaml": "jsr:@std/yaml@^1.0.5", "@types/diff": "npm:@types/diff@^5.2.2" } -} +} \ No newline at end of file diff --git a/cli/deno.lock b/cli/deno.lock index 4d4111261c..a0a4c6ce2e 100644 --- a/cli/deno.lock +++ b/cli/deno.lock @@ -1,2319 +1,2196 @@ { - "version": "3", - "packages": { - "specifiers": { - "jsr:@david/code-block-writer@^13.0.2": "jsr:@david/code-block-writer@13.0.2", - "jsr:@deno/cache-dir@^0.10.3": "jsr:@deno/cache-dir@0.10.3", - "jsr:@deno/dnt@0.41.3": "jsr:@deno/dnt@0.41.3", - "jsr:@deno/dnt@^0.41.3": "jsr:@deno/dnt@0.41.3", - "jsr:@std/assert@1.0.0-rc.2": "jsr:@std/assert@1.0.0-rc.2", - "jsr:@std/assert@^0.223.0": "jsr:@std/assert@0.223.0", - "jsr:@std/assert@^0.226.0": "jsr:@std/assert@0.226.0", - "jsr:@std/bytes@^0.223.0": "jsr:@std/bytes@0.223.0", - "jsr:@std/bytes@^1.0.2": "jsr:@std/bytes@1.0.2", - "jsr:@std/cli@1.0.0-rc.2": "jsr:@std/cli@1.0.0-rc.2", - "jsr:@std/encoding": "jsr:@std/encoding@1.0.4", - "jsr:@std/encoding@1.0.0-rc.2": "jsr:@std/encoding@1.0.0-rc.2", - "jsr:@std/encoding@^1.0.4": "jsr:@std/encoding@1.0.4", - "jsr:@std/fmt@1": "jsr:@std/fmt@1.0.2", - "jsr:@std/fmt@^0.223": "jsr:@std/fmt@0.223.0", - "jsr:@std/fmt@^1.0.2": "jsr:@std/fmt@1.0.2", - "jsr:@std/fmt@~0.225.4": "jsr:@std/fmt@0.225.6", - "jsr:@std/fs": "jsr:@std/fs@1.0.3", - "jsr:@std/fs@1": "jsr:@std/fs@1.0.3", - "jsr:@std/fs@^0.223": "jsr:@std/fs@0.223.0", - "jsr:@std/fs@^0.229.3": "jsr:@std/fs@0.229.3", - "jsr:@std/fs@^1.0.3": "jsr:@std/fs@1.0.3", - "jsr:@std/io": "jsr:@std/io@0.224.7", - "jsr:@std/io@^0.223": "jsr:@std/io@0.223.0", - "jsr:@std/io@^0.224.7": "jsr:@std/io@0.224.7", - "jsr:@std/io@~0.224.2": "jsr:@std/io@0.224.7", - "jsr:@std/log": "jsr:@std/log@0.224.7", - "jsr:@std/log@^0.224.7": "jsr:@std/log@0.224.7", - "jsr:@std/net": "jsr:@std/net@1.0.2", - "jsr:@std/net@^1.0.2": "jsr:@std/net@1.0.2", - "jsr:@std/path": "jsr:@std/path@1.0.4", - "jsr:@std/path@1": "jsr:@std/path@1.0.4", - "jsr:@std/path@1.0.0-rc.1": "jsr:@std/path@1.0.0-rc.1", - "jsr:@std/path@1.0.0-rc.2": "jsr:@std/path@1.0.0-rc.2", - "jsr:@std/path@^0.223": "jsr:@std/path@0.223.0", - "jsr:@std/path@^0.225.2": "jsr:@std/path@0.225.2", - "jsr:@std/path@^1.0.4": "jsr:@std/path@1.0.4", - "jsr:@std/streams@^1.0.4": "jsr:@std/streams@1.0.4", - "jsr:@std/text@1.0.0-rc.1": "jsr:@std/text@1.0.0-rc.1", - "jsr:@std/yaml": "jsr:@std/yaml@1.0.5", - "jsr:@std/yaml@^1.0.5": "jsr:@std/yaml@1.0.5", - "jsr:@ts-morph/bootstrap@^0.24.0": "jsr:@ts-morph/bootstrap@0.24.0", - "jsr:@ts-morph/common@^0.24.0": "jsr:@ts-morph/common@0.24.0", - "jsr:@windmill-labs/cliffy-ansi@1.0.0-rc.5": "jsr:@windmill-labs/cliffy-ansi@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-ansi@^1.0.0-rc.5": "jsr:@windmill-labs/cliffy-ansi@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-command@1.0.0-rc.5": "jsr:@windmill-labs/cliffy-command@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-command@^1.0.0-rc.5": "jsr:@windmill-labs/cliffy-command@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-flags@1.0.0-rc.5": "jsr:@windmill-labs/cliffy-flags@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-internal@1.0.0-rc.5": "jsr:@windmill-labs/cliffy-internal@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-keycode@1.0.0-rc.5": "jsr:@windmill-labs/cliffy-keycode@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-prompt@1.0.0-rc.5": "jsr:@windmill-labs/cliffy-prompt@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-prompt@1.0.0-rc.6": "jsr:@windmill-labs/cliffy-prompt@1.0.0-rc.6", - "jsr:@windmill-labs/cliffy-prompt@^1.0.0-rc.5": "jsr:@windmill-labs/cliffy-prompt@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-table@1.0.0-rc.5": "jsr:@windmill-labs/cliffy-table@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-table@^1.0.0-rc.5": "jsr:@windmill-labs/cliffy-table@1.0.0-rc.5", - "npm:@ayonli/jsext": "npm:@ayonli/jsext@0.9.58", - "npm:@oakserver/oak@12": "npm:@oakserver/oak@12.6.2", - "npm:@types/diff@^5.2.2": "npm:@types/diff@5.2.2", - "npm:@types/node": "npm:@types/node@18.16.19", - "npm:diff": "npm:diff@5.2.0", - "npm:es-main": "npm:es-main@1.3.0", - "npm:esbuild": "npm:esbuild@0.23.0", - "npm:eslint-plugin-import@^2.30.0": "npm:eslint-plugin-import@2.30.0_eslint@8.57.1", - "npm:eslint@^9.10.0": "npm:eslint@9.10.0", - "npm:express": "npm:express@4.19.2", - "npm:get-port": "npm:get-port@7.1.0", - "npm:get-port@7.1.0": "npm:get-port@7.1.0", - "npm:gitignore-parser": "npm:gitignore-parser@0.0.2", - "npm:jszip@3.7.1": "npm:jszip@3.7.1", - "npm:minimatch": "npm:minimatch@10.0.1", - "npm:open": "npm:open@10.1.0", - "npm:windmill-client@1.364.0": "npm:windmill-client@1.364.0", - "npm:ws": "npm:ws@8.18.0" + "version": "4", + "specifiers": { + "jsr:@david/code-block-writer@^13.0.2": "13.0.2", + "jsr:@deno/cache-dir@~0.10.3": "0.10.3", + "jsr:@deno/dnt@0.41.3": "0.41.3", + "jsr:@deno/dnt@~0.41.3": "0.41.3", + "jsr:@deno/graph@~0.73.1": "0.73.1", + "jsr:@std/assert@0.223": "0.223.0", + "jsr:@std/assert@0.226": "0.226.0", + "jsr:@std/assert@1.0.0-rc.2": "1.0.0-rc.2", + "jsr:@std/bytes@0.223": "0.223.0", + "jsr:@std/bytes@^1.0.2": "1.0.2", + "jsr:@std/cli@1.0.0-rc.2": "1.0.0-rc.2", + "jsr:@std/encoding@*": "1.0.4", + "jsr:@std/encoding@1.0.0-rc.2": "1.0.0-rc.2", + "jsr:@std/encoding@1.0.4": "1.0.4", + "jsr:@std/encoding@^1.0.4": "1.0.4", + "jsr:@std/fmt@0.223": "0.223.0", + "jsr:@std/fmt@1": "1.0.2", + "jsr:@std/fmt@^1.0.2": "1.0.2", + "jsr:@std/fmt@~0.225.4": "0.225.6", + "jsr:@std/fs@*": "1.0.3", + "jsr:@std/fs@0.223": "0.223.0", + "jsr:@std/fs@1": "1.0.3", + "jsr:@std/fs@^1.0.3": "1.0.3", + "jsr:@std/fs@~0.229.3": "0.229.3", + "jsr:@std/io@*": "0.224.7", + "jsr:@std/io@0.223": "0.223.0", + "jsr:@std/io@~0.224.2": "0.224.7", + "jsr:@std/io@~0.224.7": "0.224.7", + "jsr:@std/log@*": "0.224.7", + "jsr:@std/log@~0.224.7": "0.224.7", + "jsr:@std/net@*": "1.0.2", + "jsr:@std/net@^1.0.2": "1.0.2", + "jsr:@std/path@*": "1.0.4", + "jsr:@std/path@0.223": "0.223.0", + "jsr:@std/path@1": "1.0.4", + "jsr:@std/path@1.0.0-rc.1": "1.0.0-rc.1", + "jsr:@std/path@1.0.0-rc.2": "1.0.0-rc.2", + "jsr:@std/path@^1.0.4": "1.0.4", + "jsr:@std/path@~0.225.2": "0.225.2", + "jsr:@std/streams@^1.0.4": "1.0.4", + "jsr:@std/text@1.0.0-rc.1": "1.0.0-rc.1", + "jsr:@std/yaml@*": "1.0.5", + "jsr:@std/yaml@^1.0.5": "1.0.5", + "jsr:@ts-morph/bootstrap@0.24": "0.24.0", + "jsr:@ts-morph/common@0.24": "0.24.0", + "jsr:@windmill-labs/cliffy-ansi@1.0.0-rc.5": "1.0.0-rc.5", + "jsr:@windmill-labs/cliffy-ansi@^1.0.0-rc.5": "1.0.0-rc.5", + "jsr:@windmill-labs/cliffy-command@1.0.0-rc.5": "1.0.0-rc.5", + "jsr:@windmill-labs/cliffy-command@^1.0.0-rc.5": "1.0.0-rc.5", + "jsr:@windmill-labs/cliffy-flags@1.0.0-rc.5": "1.0.0-rc.5", + "jsr:@windmill-labs/cliffy-internal@1.0.0-rc.5": "1.0.0-rc.5", + "jsr:@windmill-labs/cliffy-keycode@1.0.0-rc.5": "1.0.0-rc.5", + "jsr:@windmill-labs/cliffy-prompt@1.0.0-rc.5": "1.0.0-rc.5", + "jsr:@windmill-labs/cliffy-prompt@1.0.0-rc.6": "1.0.0-rc.6", + "jsr:@windmill-labs/cliffy-prompt@^1.0.0-rc.5": "1.0.0-rc.6", + "jsr:@windmill-labs/cliffy-table@1.0.0-rc.5": "1.0.0-rc.5", + "jsr:@windmill-labs/cliffy-table@^1.0.0-rc.5": "1.0.0-rc.5", + "npm:@ayonli/jsext@*": "0.9.58", + "npm:@oakserver/oak@12": "12.6.2", + "npm:@types/diff@^5.2.2": "5.2.2", + "npm:@types/node@*": "18.16.19", + "npm:diff@*": "5.2.0", + "npm:es-main@*": "1.3.0", + "npm:esbuild@*": "0.23.0", + "npm:eslint-plugin-import@^2.30.0": "2.30.0_eslint@8.57.1", + "npm:eslint@^9.10.0": "9.10.0", + "npm:express@*": "4.19.2", + "npm:get-port@*": "7.1.0", + "npm:get-port@7.1.0": "7.1.0", + "npm:gitignore-parser@*": "0.0.2", + "npm:jszip@3.7.1": "3.7.1", + "npm:minimatch@*": "10.0.1", + "npm:open@*": "10.1.0", + "npm:windmill-client@1.364.0": "1.364.0", + "npm:windmill-parser-wasm-csharp@*": "1.437.1", + "npm:windmill-parser-wasm-go@*": "1.429.0", + "npm:windmill-parser-wasm-php@*": "1.429.0", + "npm:windmill-parser-wasm-py@*": "1.477.1", + "npm:windmill-parser-wasm-regex@*": "1.439.0", + "npm:windmill-parser-wasm-rust@*": "1.429.0", + "npm:windmill-parser-wasm-ts@*": "1.438.2", + "npm:windmill-parser-wasm-yaml@*": "1.429.0", + "npm:ws@*": "8.18.0" + }, + "jsr": { + "@david/code-block-writer@13.0.2": { + "integrity": "14dd3baaafa3a2dea8bf7dfbcddeccaa13e583da2d21d666c01dc6d681cd74ad" }, - "jsr": { - "@david/code-block-writer@13.0.2": { - "integrity": "14dd3baaafa3a2dea8bf7dfbcddeccaa13e583da2d21d666c01dc6d681cd74ad" - }, - "@deno/cache-dir@0.10.3": { - "integrity": "eb022f84ecc49c91d9d98131c6e6b118ff63a29e343624d058646b9d50404776", - "dependencies": [ - "jsr:@std/fmt@^0.223", - "jsr:@std/fs@^0.223", - "jsr:@std/io@^0.223", - "jsr:@std/path@^0.223" - ] - }, - "@deno/dnt@0.41.3": { - "integrity": "b2ef2c8a5111eef86cb5bfcae103d6a2938e8e649e2461634a7befb7fc59d6d2", - "dependencies": [ - "jsr:@david/code-block-writer@^13.0.2", - "jsr:@deno/cache-dir@^0.10.3", - "jsr:@std/fmt@1", - "jsr:@std/fs@1", - "jsr:@std/path@1", - "jsr:@ts-morph/bootstrap@^0.24.0" - ] - }, - "@std/assert@0.223.0": { - "integrity": "eb8d6d879d76e1cc431205bd346ed4d88dc051c6366365b1af47034b0670be24" - }, - "@std/assert@0.226.0": { - "integrity": "0dfb5f7c7723c18cec118e080fec76ce15b4c31154b15ad2bd74822603ef75b3" - }, - "@std/assert@1.0.0-rc.2": { - "integrity": "0484eab1d76b55fca1c3beaff485a274e67dd3b9f065edcbe70030dfc0b964d3" - }, - "@std/bytes@0.223.0": { - "integrity": "84b75052cd8680942c397c2631318772b295019098f40aac5c36cead4cba51a8" - }, - "@std/bytes@1.0.2": { - "integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57" - }, - "@std/cli@1.0.0-rc.2": { - "integrity": "97dfae82b9f0e189768ebfa7a5da53375955b94bad0a1804f8e3b73563b03787" - }, - "@std/encoding@1.0.0-rc.2": { - "integrity": "160d7674a20ebfbccdf610b3801fee91cf6e42d1c106dd46bbaf46e395cd35ef" - }, - "@std/encoding@1.0.4": { - "integrity": "2266cd516b32369e3dc5695717c96bf88343a1f761d6e6187a02a2bbe2af86ae" - }, - "@std/fmt@0.223.0": { - "integrity": "6deb37794127dfc7d7bded2586b9fc6f5d50e62a8134846608baf71ffc1a5208" - }, - "@std/fmt@0.225.6": { - "integrity": "aba6aea27f66813cecfd9484e074a9e9845782ab0685c030e453a8a70b37afc8" - }, - "@std/fmt@1.0.2": { - "integrity": "87e9dfcdd3ca7c066e0c3c657c1f987c82888eb8103a3a3baa62684ffeb0f7a7" - }, - "@std/fs@0.223.0": { - "integrity": "3b4b0550b2c524cbaaa5a9170c90e96cbb7354e837ad1bdaf15fc9df1ae9c31c" - }, - "@std/fs@0.229.3": { - "integrity": "783bca21f24da92e04c3893c9e79653227ab016c48e96b3078377ebd5222e6eb", - "dependencies": [ - "jsr:@std/path@1.0.0-rc.1" - ] - }, - "@std/fs@1.0.3": { - "integrity": "3cb839b1360b0a42d8b367c3093bfe4071798e6694fa44cf1963e04a8edba4fe", - "dependencies": [ - "jsr:@std/path@^1.0.4" - ] - }, - "@std/io@0.223.0": { - "integrity": "2d8c3c2ab3a515619b90da2c6ff5ea7b75a94383259ef4d02116b228393f84f1", - "dependencies": [ - "jsr:@std/assert@^0.223.0", - "jsr:@std/bytes@^0.223.0" - ] - }, - "@std/io@0.224.7": { - "integrity": "a70848793c44a7c100926571a8c9be68ba85487bfcd4d0540d86deabe1123dc9", - "dependencies": [ - "jsr:@std/bytes@^1.0.2" - ] - }, - "@std/log@0.224.7": { - "integrity": "021941e5cd16de60cb11599c9b36f892aea95987fe66c753922808da27909e18", - "dependencies": [ - "jsr:@std/fmt@^1.0.2", - "jsr:@std/fs@^1.0.3", - "jsr:@std/io@^0.224.7" - ] - }, - "@std/net@1.0.2": { - "integrity": "520c18ddb7f67d3830a1adfef03a155d496fe9683a9cb63bb823b5afb86484dc" - }, - "@std/path@0.223.0": { - "integrity": "593963402d7e6597f5a6e620931661053572c982fc014000459edc1f93cc3989", - "dependencies": [ - "jsr:@std/assert@^0.223.0" - ] - }, - "@std/path@0.225.2": { - "integrity": "0f2db41d36b50ef048dcb0399aac720a5348638dd3cb5bf80685bf2a745aa506", - "dependencies": [ - "jsr:@std/assert@^0.226.0" - ] - }, - "@std/path@1.0.0-rc.1": { - "integrity": "b8c00ae2f19106a6bb7cbf1ab9be52aa70de1605daeb2dbdc4f87a7cbaf10ff6" - }, - "@std/path@1.0.0-rc.2": { - "integrity": "39f20d37a44d1867abac8d91c169359ea6e942237a45a99ee1e091b32b921c7d" - }, - "@std/path@1.0.4": { - "integrity": "48dd5d8389bcfcd619338a01bdf862cb7799933390146a54ae59356a0acc7105" - }, - "@std/streams@1.0.4": { - "integrity": "a1a5b01c74ca1d2dcaacfe1d4bbb91392e765946d82a3471bd95539adc6da83a", - "dependencies": [ - "jsr:@std/bytes@^1.0.2" - ] - }, - "@std/text@1.0.0-rc.1": { - "integrity": "34c722203e87ee12792c8d4a0cd2ee0e001341cbce75b860fc21be19d62232b0" - }, - "@std/yaml@1.0.5": { - "integrity": "71ba3d334305ee2149391931508b2c293a8490f94a337eef3a09cade1a2a2742" - }, - "@ts-morph/bootstrap@0.24.0": { - "integrity": "a826a2ef7fa8a7c3f1042df2c034d20744d94da2ee32bf29275bcd4dffd3c060", - "dependencies": [ - "jsr:@ts-morph/common@^0.24.0" - ] - }, - "@ts-morph/common@0.24.0": { - "integrity": "12b625b8e562446ba658cdbe9ad77774b4bd96b992ae8bd34c60dbf24d06c1f3", - "dependencies": [ - "jsr:@std/fs@^0.229.3", - "jsr:@std/path@^0.225.2" - ] - }, - "@windmill-labs/cliffy-ansi@1.0.0-rc.5": { - "integrity": "1109cbcb0c415b57779352f708f5969b8c645f56bc555cbafc6ea5e0c6a360a4", - "dependencies": [ - "jsr:@std/encoding@1.0.0-rc.2", - "jsr:@std/fmt@~0.225.4", - "jsr:@std/io@~0.224.2", - "jsr:@windmill-labs/cliffy-internal@1.0.0-rc.5" - ] - }, - "@windmill-labs/cliffy-command@1.0.0-rc.5": { - "integrity": "3eaa9def5f5afa1028f4a60ee4d9065ccc5f194d032824365c6ebcb9f46db66e", - "dependencies": [ - "jsr:@std/fmt@~0.225.4", - "jsr:@std/text@1.0.0-rc.1", - "jsr:@windmill-labs/cliffy-flags@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-internal@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-table@1.0.0-rc.5" - ] - }, - "@windmill-labs/cliffy-flags@1.0.0-rc.5": { - "integrity": "0e4b5b53a02295f8bf27d93b3bcca5d5d001a0286aea17404e6ef6347f69363c", - "dependencies": [ - "jsr:@std/text@1.0.0-rc.1" - ] - }, - "@windmill-labs/cliffy-internal@1.0.0-rc.5": { - "integrity": "876b989ad2d1b739cc4a4f1386dbb80f819d2851ad583c1f486fc6ebe9beadf6" - }, - "@windmill-labs/cliffy-keycode@1.0.0-rc.5": { - "integrity": "2bc1b1af363528e38ed47bce525f417cab278b829a423353ffd589622f5a746e" - }, - "@windmill-labs/cliffy-prompt@1.0.0-rc.5": { - "integrity": "329a097911f219b15ea643ae83b6b360a11df7fc4cafdac1bf6869259475033a", - "dependencies": [ - "jsr:@std/assert@1.0.0-rc.2", - "jsr:@std/fmt@~0.225.4", - "jsr:@std/io@~0.224.2", - "jsr:@std/path@1.0.0-rc.2", - "jsr:@std/text@1.0.0-rc.1", - "jsr:@windmill-labs/cliffy-ansi@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-internal@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-keycode@1.0.0-rc.5" - ] - }, - "@windmill-labs/cliffy-prompt@1.0.0-rc.6": { - "integrity": "ffe09bee0e1e07bc12b2147be509d10b47eb6a36cbfea80fc206b4ae86693205", - "dependencies": [ - "jsr:@std/assert@1.0.0-rc.2", - "jsr:@std/fmt@~0.225.4", - "jsr:@std/io@~0.224.2", - "jsr:@std/path@1.0.0-rc.2", - "jsr:@std/text@1.0.0-rc.1", - "jsr:@windmill-labs/cliffy-ansi@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-internal@1.0.0-rc.5", - "jsr:@windmill-labs/cliffy-keycode@1.0.0-rc.5" - ] - }, - "@windmill-labs/cliffy-table@1.0.0-rc.5": { - "integrity": "5f26cb6ccbc2fbf1b1f79f9062d166e32e11d34398c0deac7e6f9d8378970546", - "dependencies": [ - "jsr:@std/cli@1.0.0-rc.2", - "jsr:@std/fmt@~0.225.4" - ] - } + "@deno/cache-dir@0.10.3": { + "integrity": "eb022f84ecc49c91d9d98131c6e6b118ff63a29e343624d058646b9d50404776", + "dependencies": [ + "jsr:@deno/graph", + "jsr:@std/fmt@0.223", + "jsr:@std/fs@0.223", + "jsr:@std/io@0.223", + "jsr:@std/path@0.223" + ] }, - "npm": { - "@ayonli/jsext@0.9.58": { - "integrity": "sha512-AwGf64K6VqGyYLFA6rgyuU6jBbwUNJctENpW1bZayvXcIprhdfs7cn7S+EXi0pnNLupT9ptODYkKneB3/YuWww==", - "dependencies": { - "iconv-lite": "iconv-lite@0.6.3", - "sudo-prompt": "sudo-prompt@9.2.1", - "ws": "ws@8.18.0" - } - }, - "@deno/shim-crypto@0.3.1": { - "integrity": "sha512-ed4pNnfur6UbASEgF34gVxR9p7Mc3qF+Ygbmjiil8ws5IhNFhPDFy5vE5hQAUA9JmVsSxXPcVLM5Rf8LOZqQ5Q==", - "dependencies": {} - }, - "@deno/shim-deno-test@0.5.0": { - "integrity": "sha512-4nMhecpGlPi0cSzT67L+Tm+GOJqvuk8gqHBziqcUQOarnuIax1z96/gJHCSIz2Z0zhxE6Rzwb3IZXPtFh51j+w==", - "dependencies": {} - }, - "@deno/shim-deno@0.17.0": { - "integrity": "sha512-+FzsP65eehAgTQdzt1izLEV17ePCZqHxDQqRDbpRc1yJVYtDI2MvbRq5DvOj90uRt6zKn9qtWpEueDqG1QORhQ==", - "dependencies": { - "@deno/shim-deno-test": "@deno/shim-deno-test@0.5.0", - "which": "which@4.0.0" - } - }, - "@esbuild/aix-ppc64@0.23.0": { - "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", - "dependencies": {} - }, - "@esbuild/android-arm64@0.23.0": { - "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", - "dependencies": {} - }, - "@esbuild/android-arm@0.23.0": { - "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", - "dependencies": {} - }, - "@esbuild/android-x64@0.23.0": { - "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", - "dependencies": {} - }, - "@esbuild/darwin-arm64@0.23.0": { - "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", - "dependencies": {} - }, - "@esbuild/darwin-x64@0.23.0": { - "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", - "dependencies": {} - }, - "@esbuild/freebsd-arm64@0.23.0": { - "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", - "dependencies": {} - }, - "@esbuild/freebsd-x64@0.23.0": { - "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", - "dependencies": {} - }, - "@esbuild/linux-arm64@0.23.0": { - "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", - "dependencies": {} - }, - "@esbuild/linux-arm@0.23.0": { - "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", - "dependencies": {} - }, - "@esbuild/linux-ia32@0.23.0": { - "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", - "dependencies": {} - }, - "@esbuild/linux-loong64@0.23.0": { - "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", - "dependencies": {} - }, - "@esbuild/linux-mips64el@0.23.0": { - "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", - "dependencies": {} - }, - "@esbuild/linux-ppc64@0.23.0": { - "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", - "dependencies": {} - }, - "@esbuild/linux-riscv64@0.23.0": { - "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", - "dependencies": {} - }, - "@esbuild/linux-s390x@0.23.0": { - "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", - "dependencies": {} - }, - "@esbuild/linux-x64@0.23.0": { - "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", - "dependencies": {} - }, - "@esbuild/netbsd-x64@0.23.0": { - "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", - "dependencies": {} - }, - "@esbuild/openbsd-arm64@0.23.0": { - "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", - "dependencies": {} - }, - "@esbuild/openbsd-x64@0.23.0": { - "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", - "dependencies": {} - }, - "@esbuild/sunos-x64@0.23.0": { - "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", - "dependencies": {} - }, - "@esbuild/win32-arm64@0.23.0": { - "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", - "dependencies": {} - }, - "@esbuild/win32-ia32@0.23.0": { - "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", - "dependencies": {} - }, - "@esbuild/win32-x64@0.23.0": { - "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", - "dependencies": {} - }, - "@eslint-community/eslint-utils@4.4.0_eslint@8.57.1": { - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dependencies": { - "eslint": "eslint@8.57.1", - "eslint-visitor-keys": "eslint-visitor-keys@3.4.3" - } - }, - "@eslint-community/eslint-utils@4.4.0_eslint@9.10.0": { - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dependencies": { - "eslint": "eslint@9.10.0", - "eslint-visitor-keys": "eslint-visitor-keys@3.4.3" - } - }, - "@eslint-community/regexpp@4.11.1": { - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", - "dependencies": {} - }, - "@eslint/config-array@0.18.0": { - "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", - "dependencies": { - "@eslint/object-schema": "@eslint/object-schema@2.1.4", - "debug": "debug@4.3.7", - "minimatch": "minimatch@3.1.2" - } - }, - "@eslint/eslintrc@2.1.4": { - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dependencies": { - "ajv": "ajv@6.12.6", - "debug": "debug@4.3.7", - "espree": "espree@9.6.1_acorn@8.12.1", - "globals": "globals@13.24.0", - "ignore": "ignore@5.3.2", - "import-fresh": "import-fresh@3.3.0", - "js-yaml": "js-yaml@4.1.0", - "minimatch": "minimatch@3.1.2", - "strip-json-comments": "strip-json-comments@3.1.1" - } - }, - "@eslint/eslintrc@3.1.0": { - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", - "dependencies": { - "ajv": "ajv@6.12.6", - "debug": "debug@4.3.7", - "espree": "espree@10.1.0_acorn@8.12.1", - "globals": "globals@14.0.0", - "ignore": "ignore@5.3.2", - "import-fresh": "import-fresh@3.3.0", - "js-yaml": "js-yaml@4.1.0", - "minimatch": "minimatch@3.1.2", - "strip-json-comments": "strip-json-comments@3.1.1" - } - }, - "@eslint/js@8.57.1": { - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "dependencies": {} - }, - "@eslint/js@9.10.0": { - "integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==", - "dependencies": {} - }, - "@eslint/object-schema@2.1.4": { - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", - "dependencies": {} - }, - "@eslint/plugin-kit@0.1.0": { - "integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==", - "dependencies": { - "levn": "levn@0.4.1" - } - }, - "@fastify/busboy@2.1.1": { - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "dependencies": {} - }, - "@humanwhocodes/config-array@0.13.0": { - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "dependencies": { - "@humanwhocodes/object-schema": "@humanwhocodes/object-schema@2.0.3", - "debug": "debug@4.3.7", - "minimatch": "minimatch@3.1.2" - } - }, - "@humanwhocodes/module-importer@1.0.1": { - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dependencies": {} - }, - "@humanwhocodes/object-schema@2.0.3": { - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "dependencies": {} - }, - "@humanwhocodes/retry@0.3.0": { - "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", - "dependencies": {} - }, - "@nodelib/fs.scandir@2.1.5": { - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "@nodelib/fs.stat@2.0.5", - "run-parallel": "run-parallel@1.2.0" - } - }, - "@nodelib/fs.stat@2.0.5": { - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dependencies": {} - }, - "@nodelib/fs.walk@1.2.8": { - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "@nodelib/fs.scandir@2.1.5", - "fastq": "fastq@1.17.1" - } - }, - "@oakserver/oak@12.6.2": { - "integrity": "sha512-q9LfyC9tWV68me0GEUuA66qbwH8ep0bBdq9V02fePlPmPVUBCAzQQkomyaI/L4Uur+YALVXgLoTaC2rviZ7I4w==", - "dependencies": { - "@deno/shim-crypto": "@deno/shim-crypto@0.3.1", - "@deno/shim-deno": "@deno/shim-deno@0.17.0", - "tslib": "tslib@2.3.1", - "undici": "undici@5.28.4" - } - }, - "@rtsao/scc@1.1.0": { - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dependencies": {} - }, - "@types/diff@5.2.2": { - "integrity": "sha512-qVqLpd49rmJA2nZzLVsmfS/aiiBpfVE95dHhPVwG0NmSBAt+riPxnj53wq2oBq5m4Q2RF1IWFEUpnZTgrQZfEQ==", - "dependencies": {} - }, - "@types/json5@0.0.29": { - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dependencies": {} - }, - "@types/node@18.16.19": { - "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==", - "dependencies": {} - }, - "@ungap/structured-clone@1.2.0": { - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dependencies": {} - }, - "accepts@1.3.8": { - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "mime-types@2.1.35", - "negotiator": "negotiator@0.6.3" - } - }, - "acorn-jsx@5.3.2_acorn@8.12.1": { - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dependencies": { - "acorn": "acorn@8.12.1" - } - }, - "acorn@8.12.1": { - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "dependencies": {} - }, - "ajv@6.12.6": { - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "fast-deep-equal@3.1.3", - "fast-json-stable-stringify": "fast-json-stable-stringify@2.1.0", - "json-schema-traverse": "json-schema-traverse@0.4.1", - "uri-js": "uri-js@4.4.1" - } - }, - "ansi-regex@5.0.1": { - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dependencies": {} - }, - "ansi-styles@4.3.0": { - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "color-convert@2.0.1" - } - }, - "argparse@2.0.1": { - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dependencies": {} - }, - "array-buffer-byte-length@1.0.1": { - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "is-array-buffer": "is-array-buffer@3.0.4" - } - }, - "array-flatten@1.1.1": { - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dependencies": {} - }, - "array-includes@3.1.8": { - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "es-abstract": "es-abstract@1.23.3", - "es-object-atoms": "es-object-atoms@1.0.0", - "get-intrinsic": "get-intrinsic@1.2.4", - "is-string": "is-string@1.0.7" - } - }, - "array.prototype.findlastindex@1.2.5": { - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "es-abstract": "es-abstract@1.23.3", - "es-errors": "es-errors@1.3.0", - "es-object-atoms": "es-object-atoms@1.0.0", - "es-shim-unscopables": "es-shim-unscopables@1.0.2" - } - }, - "array.prototype.flat@1.3.2": { - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "es-abstract": "es-abstract@1.23.3", - "es-shim-unscopables": "es-shim-unscopables@1.0.2" - } - }, - "array.prototype.flatmap@1.3.2": { - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "es-abstract": "es-abstract@1.23.3", - "es-shim-unscopables": "es-shim-unscopables@1.0.2" - } - }, - "arraybuffer.prototype.slice@1.0.3": { - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dependencies": { - "array-buffer-byte-length": "array-buffer-byte-length@1.0.1", - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "es-abstract": "es-abstract@1.23.3", - "es-errors": "es-errors@1.3.0", - "get-intrinsic": "get-intrinsic@1.2.4", - "is-array-buffer": "is-array-buffer@3.0.4", - "is-shared-array-buffer": "is-shared-array-buffer@1.0.3" - } - }, - "available-typed-arrays@1.0.7": { - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dependencies": { - "possible-typed-array-names": "possible-typed-array-names@1.0.0" - } - }, - "balanced-match@1.0.2": { - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dependencies": {} - }, - "body-parser@1.20.2": { - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "bytes@3.1.2", - "content-type": "content-type@1.0.5", - "debug": "debug@2.6.9", - "depd": "depd@2.0.0", - "destroy": "destroy@1.2.0", - "http-errors": "http-errors@2.0.0", - "iconv-lite": "iconv-lite@0.4.24", - "on-finished": "on-finished@2.4.1", - "qs": "qs@6.11.0", - "raw-body": "raw-body@2.5.2", - "type-is": "type-is@1.6.18", - "unpipe": "unpipe@1.0.0" - } - }, - "brace-expansion@1.1.11": { - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "balanced-match@1.0.2", - "concat-map": "concat-map@0.0.1" - } - }, - "brace-expansion@2.0.1": { - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "balanced-match@1.0.2" - } - }, - "bundle-name@4.1.0": { - "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", - "dependencies": { - "run-applescript": "run-applescript@7.0.0" - } - }, - "bytes@3.1.2": { - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dependencies": {} - }, - "call-bind@1.0.7": { - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "es-define-property@1.0.0", - "es-errors": "es-errors@1.3.0", - "function-bind": "function-bind@1.1.2", - "get-intrinsic": "get-intrinsic@1.2.4", - "set-function-length": "set-function-length@1.2.2" - } - }, - "callsites@3.1.0": { - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dependencies": {} - }, - "chalk@4.1.2": { - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "ansi-styles@4.3.0", - "supports-color": "supports-color@7.2.0" - } - }, - "color-convert@2.0.1": { - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "color-name@1.1.4" - } - }, - "color-name@1.1.4": { - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dependencies": {} - }, - "concat-map@0.0.1": { - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dependencies": {} - }, - "content-disposition@0.5.4": { - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "safe-buffer@5.2.1" - } - }, - "content-type@1.0.5": { - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dependencies": {} - }, - "cookie-signature@1.0.6": { - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dependencies": {} - }, - "cookie@0.6.0": { - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dependencies": {} - }, - "core-util-is@1.0.3": { - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dependencies": {} - }, - "cross-spawn@7.0.3": { - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "path-key@3.1.1", - "shebang-command": "shebang-command@2.0.0", - "which": "which@2.0.2" - } - }, - "data-view-buffer@1.0.1": { - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "es-errors": "es-errors@1.3.0", - "is-data-view": "is-data-view@1.0.1" - } - }, - "data-view-byte-length@1.0.1": { - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "es-errors": "es-errors@1.3.0", - "is-data-view": "is-data-view@1.0.1" - } - }, - "data-view-byte-offset@1.0.0": { - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "es-errors": "es-errors@1.3.0", - "is-data-view": "is-data-view@1.0.1" - } - }, - "debug@2.6.9": { - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "ms@2.0.0" - } - }, - "debug@3.2.7": { - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "ms@2.1.3" - } - }, - "debug@4.3.7": { - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dependencies": { - "ms": "ms@2.1.3" - } - }, - "deep-is@0.1.4": { - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dependencies": {} - }, - "default-browser-id@5.0.0": { - "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", - "dependencies": {} - }, - "default-browser@5.2.1": { - "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", - "dependencies": { - "bundle-name": "bundle-name@4.1.0", - "default-browser-id": "default-browser-id@5.0.0" - } - }, - "define-data-property@1.1.4": { - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "es-define-property@1.0.0", - "es-errors": "es-errors@1.3.0", - "gopd": "gopd@1.0.1" - } - }, - "define-lazy-prop@3.0.0": { - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dependencies": {} - }, - "define-properties@1.2.1": { - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dependencies": { - "define-data-property": "define-data-property@1.1.4", - "has-property-descriptors": "has-property-descriptors@1.0.2", - "object-keys": "object-keys@1.1.1" - } - }, - "depd@2.0.0": { - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dependencies": {} - }, - "destroy@1.2.0": { - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dependencies": {} - }, - "diff@5.2.0": { - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dependencies": {} - }, - "doctrine@2.1.0": { - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dependencies": { - "esutils": "esutils@2.0.3" - } - }, - "doctrine@3.0.0": { - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dependencies": { - "esutils": "esutils@2.0.3" - } - }, - "ee-first@1.1.1": { - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dependencies": {} - }, - "encodeurl@1.0.2": { - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dependencies": {} - }, - "es-abstract@1.23.3": { - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", - "dependencies": { - "array-buffer-byte-length": "array-buffer-byte-length@1.0.1", - "arraybuffer.prototype.slice": "arraybuffer.prototype.slice@1.0.3", - "available-typed-arrays": "available-typed-arrays@1.0.7", - "call-bind": "call-bind@1.0.7", - "data-view-buffer": "data-view-buffer@1.0.1", - "data-view-byte-length": "data-view-byte-length@1.0.1", - "data-view-byte-offset": "data-view-byte-offset@1.0.0", - "es-define-property": "es-define-property@1.0.0", - "es-errors": "es-errors@1.3.0", - "es-object-atoms": "es-object-atoms@1.0.0", - "es-set-tostringtag": "es-set-tostringtag@2.0.3", - "es-to-primitive": "es-to-primitive@1.2.1", - "function.prototype.name": "function.prototype.name@1.1.6", - "get-intrinsic": "get-intrinsic@1.2.4", - "get-symbol-description": "get-symbol-description@1.0.2", - "globalthis": "globalthis@1.0.4", - "gopd": "gopd@1.0.1", - "has-property-descriptors": "has-property-descriptors@1.0.2", - "has-proto": "has-proto@1.0.3", - "has-symbols": "has-symbols@1.0.3", - "hasown": "hasown@2.0.2", - "internal-slot": "internal-slot@1.0.7", - "is-array-buffer": "is-array-buffer@3.0.4", - "is-callable": "is-callable@1.2.7", - "is-data-view": "is-data-view@1.0.1", - "is-negative-zero": "is-negative-zero@2.0.3", - "is-regex": "is-regex@1.1.4", - "is-shared-array-buffer": "is-shared-array-buffer@1.0.3", - "is-string": "is-string@1.0.7", - "is-typed-array": "is-typed-array@1.1.13", - "is-weakref": "is-weakref@1.0.2", - "object-inspect": "object-inspect@1.13.2", - "object-keys": "object-keys@1.1.1", - "object.assign": "object.assign@4.1.5", - "regexp.prototype.flags": "regexp.prototype.flags@1.5.2", - "safe-array-concat": "safe-array-concat@1.1.2", - "safe-regex-test": "safe-regex-test@1.0.3", - "string.prototype.trim": "string.prototype.trim@1.2.9", - "string.prototype.trimend": "string.prototype.trimend@1.0.8", - "string.prototype.trimstart": "string.prototype.trimstart@1.0.8", - "typed-array-buffer": "typed-array-buffer@1.0.2", - "typed-array-byte-length": "typed-array-byte-length@1.0.1", - "typed-array-byte-offset": "typed-array-byte-offset@1.0.2", - "typed-array-length": "typed-array-length@1.0.6", - "unbox-primitive": "unbox-primitive@1.0.2", - "which-typed-array": "which-typed-array@1.1.15" - } - }, - "es-define-property@1.0.0": { - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "get-intrinsic@1.2.4" - } - }, - "es-errors@1.3.0": { - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dependencies": {} - }, - "es-main@1.3.0": { - "integrity": "sha512-AzORKdz1Zt97TzbYQnIrI3ZiibWpRXUfpo/w0xOJ20GpNYd2bd3MU9m31zS/aJ1TJl6JfLTok83Y8HjNunYT0A==", - "dependencies": {} - }, - "es-object-atoms@1.0.0": { - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dependencies": { - "es-errors": "es-errors@1.3.0" - } - }, - "es-set-tostringtag@2.0.3": { - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dependencies": { - "get-intrinsic": "get-intrinsic@1.2.4", - "has-tostringtag": "has-tostringtag@1.0.2", - "hasown": "hasown@2.0.2" - } - }, - "es-shim-unscopables@1.0.2": { - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dependencies": { - "hasown": "hasown@2.0.2" - } - }, - "es-to-primitive@1.2.1": { - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dependencies": { - "is-callable": "is-callable@1.2.7", - "is-date-object": "is-date-object@1.0.5", - "is-symbol": "is-symbol@1.0.4" - } - }, - "esbuild@0.23.0": { - "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", - "dependencies": { - "@esbuild/aix-ppc64": "@esbuild/aix-ppc64@0.23.0", - "@esbuild/android-arm": "@esbuild/android-arm@0.23.0", - "@esbuild/android-arm64": "@esbuild/android-arm64@0.23.0", - "@esbuild/android-x64": "@esbuild/android-x64@0.23.0", - "@esbuild/darwin-arm64": "@esbuild/darwin-arm64@0.23.0", - "@esbuild/darwin-x64": "@esbuild/darwin-x64@0.23.0", - "@esbuild/freebsd-arm64": "@esbuild/freebsd-arm64@0.23.0", - "@esbuild/freebsd-x64": "@esbuild/freebsd-x64@0.23.0", - "@esbuild/linux-arm": "@esbuild/linux-arm@0.23.0", - "@esbuild/linux-arm64": "@esbuild/linux-arm64@0.23.0", - "@esbuild/linux-ia32": "@esbuild/linux-ia32@0.23.0", - "@esbuild/linux-loong64": "@esbuild/linux-loong64@0.23.0", - "@esbuild/linux-mips64el": "@esbuild/linux-mips64el@0.23.0", - "@esbuild/linux-ppc64": "@esbuild/linux-ppc64@0.23.0", - "@esbuild/linux-riscv64": "@esbuild/linux-riscv64@0.23.0", - "@esbuild/linux-s390x": "@esbuild/linux-s390x@0.23.0", - "@esbuild/linux-x64": "@esbuild/linux-x64@0.23.0", - "@esbuild/netbsd-x64": "@esbuild/netbsd-x64@0.23.0", - "@esbuild/openbsd-arm64": "@esbuild/openbsd-arm64@0.23.0", - "@esbuild/openbsd-x64": "@esbuild/openbsd-x64@0.23.0", - "@esbuild/sunos-x64": "@esbuild/sunos-x64@0.23.0", - "@esbuild/win32-arm64": "@esbuild/win32-arm64@0.23.0", - "@esbuild/win32-ia32": "@esbuild/win32-ia32@0.23.0", - "@esbuild/win32-x64": "@esbuild/win32-x64@0.23.0" - } - }, - "escape-html@1.0.3": { - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dependencies": {} - }, - "escape-string-regexp@4.0.0": { - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dependencies": {} - }, - "eslint-import-resolver-node@0.3.9": { - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dependencies": { - "debug": "debug@3.2.7", - "is-core-module": "is-core-module@2.15.1", - "resolve": "resolve@1.22.8" - } - }, - "eslint-module-utils@2.11.0": { - "integrity": "sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==", - "dependencies": { - "debug": "debug@3.2.7" - } - }, - "eslint-plugin-import@2.30.0_eslint@8.57.1": { - "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", - "dependencies": { - "@rtsao/scc": "@rtsao/scc@1.1.0", - "array-includes": "array-includes@3.1.8", - "array.prototype.findlastindex": "array.prototype.findlastindex@1.2.5", - "array.prototype.flat": "array.prototype.flat@1.3.2", - "array.prototype.flatmap": "array.prototype.flatmap@1.3.2", - "debug": "debug@3.2.7", - "doctrine": "doctrine@2.1.0", - "eslint": "eslint@8.57.1", - "eslint-import-resolver-node": "eslint-import-resolver-node@0.3.9", - "eslint-module-utils": "eslint-module-utils@2.11.0", - "hasown": "hasown@2.0.2", - "is-core-module": "is-core-module@2.15.1", - "is-glob": "is-glob@4.0.3", - "minimatch": "minimatch@3.1.2", - "object.fromentries": "object.fromentries@2.0.8", - "object.groupby": "object.groupby@1.0.3", - "object.values": "object.values@1.2.0", - "semver": "semver@6.3.1", - "tsconfig-paths": "tsconfig-paths@3.15.0" - } - }, - "eslint-scope@7.2.2": { - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dependencies": { - "esrecurse": "esrecurse@4.3.0", - "estraverse": "estraverse@5.3.0" - } - }, - "eslint-scope@8.0.2": { - "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", - "dependencies": { - "esrecurse": "esrecurse@4.3.0", - "estraverse": "estraverse@5.3.0" - } - }, - "eslint-visitor-keys@3.4.3": { - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dependencies": {} - }, - "eslint-visitor-keys@4.0.0": { - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", - "dependencies": {} - }, - "eslint@8.57.1": { - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "dependencies": { - "@eslint-community/eslint-utils": "@eslint-community/eslint-utils@4.4.0_eslint@8.57.1", - "@eslint-community/regexpp": "@eslint-community/regexpp@4.11.1", - "@eslint/eslintrc": "@eslint/eslintrc@2.1.4", - "@eslint/js": "@eslint/js@8.57.1", - "@humanwhocodes/config-array": "@humanwhocodes/config-array@0.13.0", - "@humanwhocodes/module-importer": "@humanwhocodes/module-importer@1.0.1", - "@nodelib/fs.walk": "@nodelib/fs.walk@1.2.8", - "@ungap/structured-clone": "@ungap/structured-clone@1.2.0", - "ajv": "ajv@6.12.6", - "chalk": "chalk@4.1.2", - "cross-spawn": "cross-spawn@7.0.3", - "debug": "debug@4.3.7", - "doctrine": "doctrine@3.0.0", - "escape-string-regexp": "escape-string-regexp@4.0.0", - "eslint-scope": "eslint-scope@7.2.2", - "eslint-visitor-keys": "eslint-visitor-keys@3.4.3", - "espree": "espree@9.6.1_acorn@8.12.1", - "esquery": "esquery@1.6.0", - "esutils": "esutils@2.0.3", - "fast-deep-equal": "fast-deep-equal@3.1.3", - "file-entry-cache": "file-entry-cache@6.0.1", - "find-up": "find-up@5.0.0", - "glob-parent": "glob-parent@6.0.2", - "globals": "globals@13.24.0", - "graphemer": "graphemer@1.4.0", - "ignore": "ignore@5.3.2", - "imurmurhash": "imurmurhash@0.1.4", - "is-glob": "is-glob@4.0.3", - "is-path-inside": "is-path-inside@3.0.3", - "js-yaml": "js-yaml@4.1.0", - "json-stable-stringify-without-jsonify": "json-stable-stringify-without-jsonify@1.0.1", - "levn": "levn@0.4.1", - "lodash.merge": "lodash.merge@4.6.2", - "minimatch": "minimatch@3.1.2", - "natural-compare": "natural-compare@1.4.0", - "optionator": "optionator@0.9.4", - "strip-ansi": "strip-ansi@6.0.1", - "text-table": "text-table@0.2.0" - } - }, - "eslint@9.10.0": { - "integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==", - "dependencies": { - "@eslint-community/eslint-utils": "@eslint-community/eslint-utils@4.4.0_eslint@9.10.0", - "@eslint-community/regexpp": "@eslint-community/regexpp@4.11.1", - "@eslint/config-array": "@eslint/config-array@0.18.0", - "@eslint/eslintrc": "@eslint/eslintrc@3.1.0", - "@eslint/js": "@eslint/js@9.10.0", - "@eslint/plugin-kit": "@eslint/plugin-kit@0.1.0", - "@humanwhocodes/module-importer": "@humanwhocodes/module-importer@1.0.1", - "@humanwhocodes/retry": "@humanwhocodes/retry@0.3.0", - "@nodelib/fs.walk": "@nodelib/fs.walk@1.2.8", - "ajv": "ajv@6.12.6", - "chalk": "chalk@4.1.2", - "cross-spawn": "cross-spawn@7.0.3", - "debug": "debug@4.3.7", - "escape-string-regexp": "escape-string-regexp@4.0.0", - "eslint-scope": "eslint-scope@8.0.2", - "eslint-visitor-keys": "eslint-visitor-keys@4.0.0", - "espree": "espree@10.1.0_acorn@8.12.1", - "esquery": "esquery@1.6.0", - "esutils": "esutils@2.0.3", - "fast-deep-equal": "fast-deep-equal@3.1.3", - "file-entry-cache": "file-entry-cache@8.0.0", - "find-up": "find-up@5.0.0", - "glob-parent": "glob-parent@6.0.2", - "ignore": "ignore@5.3.2", - "imurmurhash": "imurmurhash@0.1.4", - "is-glob": "is-glob@4.0.3", - "is-path-inside": "is-path-inside@3.0.3", - "json-stable-stringify-without-jsonify": "json-stable-stringify-without-jsonify@1.0.1", - "lodash.merge": "lodash.merge@4.6.2", - "minimatch": "minimatch@3.1.2", - "natural-compare": "natural-compare@1.4.0", - "optionator": "optionator@0.9.4", - "strip-ansi": "strip-ansi@6.0.1", - "text-table": "text-table@0.2.0" - } - }, - "espree@10.1.0_acorn@8.12.1": { - "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", - "dependencies": { - "acorn": "acorn@8.12.1", - "acorn-jsx": "acorn-jsx@5.3.2_acorn@8.12.1", - "eslint-visitor-keys": "eslint-visitor-keys@4.0.0" - } - }, - "espree@9.6.1_acorn@8.12.1": { - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dependencies": { - "acorn": "acorn@8.12.1", - "acorn-jsx": "acorn-jsx@5.3.2_acorn@8.12.1", - "eslint-visitor-keys": "eslint-visitor-keys@3.4.3" - } - }, - "esquery@1.6.0": { - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dependencies": { - "estraverse": "estraverse@5.3.0" - } - }, - "esrecurse@4.3.0": { - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dependencies": { - "estraverse": "estraverse@5.3.0" - } - }, - "estraverse@5.3.0": { - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dependencies": {} - }, - "esutils@2.0.3": { - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dependencies": {} - }, - "etag@1.8.1": { - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dependencies": {} - }, - "express@4.19.2": { - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "dependencies": { - "accepts": "accepts@1.3.8", - "array-flatten": "array-flatten@1.1.1", - "body-parser": "body-parser@1.20.2", - "content-disposition": "content-disposition@0.5.4", - "content-type": "content-type@1.0.5", - "cookie": "cookie@0.6.0", - "cookie-signature": "cookie-signature@1.0.6", - "debug": "debug@2.6.9", - "depd": "depd@2.0.0", - "encodeurl": "encodeurl@1.0.2", - "escape-html": "escape-html@1.0.3", - "etag": "etag@1.8.1", - "finalhandler": "finalhandler@1.2.0", - "fresh": "fresh@0.5.2", - "http-errors": "http-errors@2.0.0", - "merge-descriptors": "merge-descriptors@1.0.1", - "methods": "methods@1.1.2", - "on-finished": "on-finished@2.4.1", - "parseurl": "parseurl@1.3.3", - "path-to-regexp": "path-to-regexp@0.1.7", - "proxy-addr": "proxy-addr@2.0.7", - "qs": "qs@6.11.0", - "range-parser": "range-parser@1.2.1", - "safe-buffer": "safe-buffer@5.2.1", - "send": "send@0.18.0", - "serve-static": "serve-static@1.15.0", - "setprototypeof": "setprototypeof@1.2.0", - "statuses": "statuses@2.0.1", - "type-is": "type-is@1.6.18", - "utils-merge": "utils-merge@1.0.1", - "vary": "vary@1.1.2" - } - }, - "fast-deep-equal@3.1.3": { - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dependencies": {} - }, - "fast-json-stable-stringify@2.1.0": { - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dependencies": {} - }, - "fast-levenshtein@2.0.6": { - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dependencies": {} - }, - "fastq@1.17.1": { - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dependencies": { - "reusify": "reusify@1.0.4" - } - }, - "file-entry-cache@6.0.1": { - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dependencies": { - "flat-cache": "flat-cache@3.2.0" - } - }, - "file-entry-cache@8.0.0": { - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dependencies": { - "flat-cache": "flat-cache@4.0.1" - } - }, - "finalhandler@1.2.0": { - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "debug@2.6.9", - "encodeurl": "encodeurl@1.0.2", - "escape-html": "escape-html@1.0.3", - "on-finished": "on-finished@2.4.1", - "parseurl": "parseurl@1.3.3", - "statuses": "statuses@2.0.1", - "unpipe": "unpipe@1.0.0" - } - }, - "find-up@5.0.0": { - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dependencies": { - "locate-path": "locate-path@6.0.0", - "path-exists": "path-exists@4.0.0" - } - }, - "flat-cache@3.2.0": { - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dependencies": { - "flatted": "flatted@3.3.1", - "keyv": "keyv@4.5.4", - "rimraf": "rimraf@3.0.2" - } - }, - "flat-cache@4.0.1": { - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dependencies": { - "flatted": "flatted@3.3.1", - "keyv": "keyv@4.5.4" - } - }, - "flatted@3.3.1": { - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dependencies": {} - }, - "for-each@0.3.3": { - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "is-callable@1.2.7" - } - }, - "forwarded@0.2.0": { - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dependencies": {} - }, - "fresh@0.5.2": { - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dependencies": {} - }, - "fs.realpath@1.0.0": { - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dependencies": {} - }, - "function-bind@1.1.2": { - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dependencies": {} - }, - "function.prototype.name@1.1.6": { - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "es-abstract": "es-abstract@1.23.3", - "functions-have-names": "functions-have-names@1.2.3" - } - }, - "functions-have-names@1.2.3": { - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dependencies": {} - }, - "get-intrinsic@1.2.4": { - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "es-errors@1.3.0", - "function-bind": "function-bind@1.1.2", - "has-proto": "has-proto@1.0.3", - "has-symbols": "has-symbols@1.0.3", - "hasown": "hasown@2.0.2" - } - }, - "get-port@7.1.0": { - "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", - "dependencies": {} - }, - "get-symbol-description@1.0.2": { - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "es-errors": "es-errors@1.3.0", - "get-intrinsic": "get-intrinsic@1.2.4" - } - }, - "gitignore-parser@0.0.2": { - "integrity": "sha512-X6mpqUv59uWLGD4n3hZ8Cu8KbF2PMWPSFYmxZjdkpm3yOU7hSUYnzTkZI1mcWqchphvqyuz3/BhgBR4E/JtkCg==", - "dependencies": {} - }, - "glob-parent@6.0.2": { - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "is-glob@4.0.3" - } - }, - "glob@7.2.3": { - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "fs.realpath@1.0.0", - "inflight": "inflight@1.0.6", - "inherits": "inherits@2.0.4", - "minimatch": "minimatch@3.1.2", - "once": "once@1.4.0", - "path-is-absolute": "path-is-absolute@1.0.1" - } - }, - "globals@13.24.0": { - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dependencies": { - "type-fest": "type-fest@0.20.2" - } - }, - "globals@14.0.0": { - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dependencies": {} - }, - "globalthis@1.0.4": { - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dependencies": { - "define-properties": "define-properties@1.2.1", - "gopd": "gopd@1.0.1" - } - }, - "gopd@1.0.1": { - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "get-intrinsic@1.2.4" - } - }, - "graphemer@1.4.0": { - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dependencies": {} - }, - "has-bigints@1.0.2": { - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dependencies": {} - }, - "has-flag@4.0.0": { - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dependencies": {} - }, - "has-property-descriptors@1.0.2": { - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "es-define-property@1.0.0" - } - }, - "has-proto@1.0.3": { - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dependencies": {} - }, - "has-symbols@1.0.3": { - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dependencies": {} - }, - "has-tostringtag@1.0.2": { - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dependencies": { - "has-symbols": "has-symbols@1.0.3" - } - }, - "hasown@2.0.2": { - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "function-bind@1.1.2" - } - }, - "http-errors@2.0.0": { - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "depd@2.0.0", - "inherits": "inherits@2.0.4", - "setprototypeof": "setprototypeof@1.2.0", - "statuses": "statuses@2.0.1", - "toidentifier": "toidentifier@1.0.1" - } - }, - "iconv-lite@0.4.24": { - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": "safer-buffer@2.1.2" - } - }, - "iconv-lite@0.6.3": { - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": "safer-buffer@2.1.2" - } - }, - "ignore@5.3.2": { - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dependencies": {} - }, - "immediate@3.0.6": { - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "dependencies": {} - }, - "import-fresh@3.3.0": { - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "parent-module@1.0.1", - "resolve-from": "resolve-from@4.0.0" - } - }, - "imurmurhash@0.1.4": { - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dependencies": {} - }, - "inflight@1.0.6": { - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "once@1.4.0", - "wrappy": "wrappy@1.0.2" - } - }, - "inherits@2.0.4": { - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dependencies": {} - }, - "internal-slot@1.0.7": { - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "dependencies": { - "es-errors": "es-errors@1.3.0", - "hasown": "hasown@2.0.2", - "side-channel": "side-channel@1.0.6" - } - }, - "ipaddr.js@1.9.1": { - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dependencies": {} - }, - "is-array-buffer@3.0.4": { - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "get-intrinsic": "get-intrinsic@1.2.4" - } - }, - "is-bigint@1.0.4": { - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "has-bigints@1.0.2" - } - }, - "is-boolean-object@1.1.2": { - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "has-tostringtag": "has-tostringtag@1.0.2" - } - }, - "is-callable@1.2.7": { - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dependencies": {} - }, - "is-core-module@2.15.1": { - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "dependencies": { - "hasown": "hasown@2.0.2" - } - }, - "is-data-view@1.0.1": { - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", - "dependencies": { - "is-typed-array": "is-typed-array@1.1.13" - } - }, - "is-date-object@1.0.5": { - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dependencies": { - "has-tostringtag": "has-tostringtag@1.0.2" - } - }, - "is-docker@3.0.0": { - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dependencies": {} - }, - "is-extglob@2.1.1": { - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dependencies": {} - }, - "is-glob@4.0.3": { - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "is-extglob@2.1.1" - } - }, - "is-inside-container@1.0.0": { - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dependencies": { - "is-docker": "is-docker@3.0.0" - } - }, - "is-negative-zero@2.0.3": { - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dependencies": {} - }, - "is-number-object@1.0.7": { - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dependencies": { - "has-tostringtag": "has-tostringtag@1.0.2" - } - }, - "is-path-inside@3.0.3": { - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dependencies": {} - }, - "is-regex@1.1.4": { - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "has-tostringtag": "has-tostringtag@1.0.2" - } - }, - "is-shared-array-buffer@1.0.3": { - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "dependencies": { - "call-bind": "call-bind@1.0.7" - } - }, - "is-string@1.0.7": { - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dependencies": { - "has-tostringtag": "has-tostringtag@1.0.2" - } - }, - "is-symbol@1.0.4": { - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dependencies": { - "has-symbols": "has-symbols@1.0.3" - } - }, - "is-typed-array@1.1.13": { - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dependencies": { - "which-typed-array": "which-typed-array@1.1.15" - } - }, - "is-weakref@1.0.2": { - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dependencies": { - "call-bind": "call-bind@1.0.7" - } - }, - "is-wsl@3.1.0": { - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", - "dependencies": { - "is-inside-container": "is-inside-container@1.0.0" - } - }, - "isarray@1.0.0": { - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dependencies": {} - }, - "isarray@2.0.5": { - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dependencies": {} - }, - "isexe@2.0.0": { - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dependencies": {} - }, - "isexe@3.1.1": { - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "dependencies": {} - }, - "js-yaml@4.1.0": { - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "argparse@2.0.1" - } - }, - "json-buffer@3.0.1": { - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dependencies": {} - }, - "json-schema-traverse@0.4.1": { - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dependencies": {} - }, - "json-stable-stringify-without-jsonify@1.0.1": { - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dependencies": {} - }, - "json5@1.0.2": { - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dependencies": { - "minimist": "minimist@1.2.8" - } - }, - "jszip@3.7.1": { - "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==", - "dependencies": { - "lie": "lie@3.3.0", - "pako": "pako@1.0.11", - "readable-stream": "readable-stream@2.3.8", - "set-immediate-shim": "set-immediate-shim@1.0.1" - } - }, - "keyv@4.5.4": { - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dependencies": { - "json-buffer": "json-buffer@3.0.1" - } - }, - "levn@0.4.1": { - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dependencies": { - "prelude-ls": "prelude-ls@1.2.1", - "type-check": "type-check@0.4.0" - } - }, - "lie@3.3.0": { - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dependencies": { - "immediate": "immediate@3.0.6" - } - }, - "locate-path@6.0.0": { - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dependencies": { - "p-locate": "p-locate@5.0.0" - } - }, - "lodash.merge@4.6.2": { - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dependencies": {} - }, - "media-typer@0.3.0": { - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dependencies": {} - }, - "merge-descriptors@1.0.1": { - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dependencies": {} - }, - "methods@1.1.2": { - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dependencies": {} - }, - "mime-db@1.52.0": { - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dependencies": {} - }, - "mime-types@2.1.35": { - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "mime-db@1.52.0" - } - }, - "mime@1.6.0": { - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dependencies": {} - }, - "minimatch@10.0.1": { - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "dependencies": { - "brace-expansion": "brace-expansion@2.0.1" - } - }, - "minimatch@3.1.2": { - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "brace-expansion@1.1.11" - } - }, - "minimist@1.2.8": { - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dependencies": {} - }, - "ms@2.0.0": { - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dependencies": {} - }, - "ms@2.1.3": { - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dependencies": {} - }, - "natural-compare@1.4.0": { - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dependencies": {} - }, - "negotiator@0.6.3": { - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dependencies": {} - }, - "object-inspect@1.13.2": { - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "dependencies": {} - }, - "object-keys@1.1.1": { - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dependencies": {} - }, - "object.assign@4.1.5": { - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "has-symbols": "has-symbols@1.0.3", - "object-keys": "object-keys@1.1.1" - } - }, - "object.fromentries@2.0.8": { - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "es-abstract": "es-abstract@1.23.3", - "es-object-atoms": "es-object-atoms@1.0.0" - } - }, - "object.groupby@1.0.3": { - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "es-abstract": "es-abstract@1.23.3" - } - }, - "object.values@1.2.0": { - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "es-object-atoms": "es-object-atoms@1.0.0" - } - }, - "on-finished@2.4.1": { - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "ee-first@1.1.1" - } - }, - "once@1.4.0": { - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "wrappy@1.0.2" - } - }, - "open@10.1.0": { - "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", - "dependencies": { - "default-browser": "default-browser@5.2.1", - "define-lazy-prop": "define-lazy-prop@3.0.0", - "is-inside-container": "is-inside-container@1.0.0", - "is-wsl": "is-wsl@3.1.0" - } - }, - "optionator@0.9.4": { - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dependencies": { - "deep-is": "deep-is@0.1.4", - "fast-levenshtein": "fast-levenshtein@2.0.6", - "levn": "levn@0.4.1", - "prelude-ls": "prelude-ls@1.2.1", - "type-check": "type-check@0.4.0", - "word-wrap": "word-wrap@1.2.5" - } - }, - "p-limit@3.1.0": { - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "yocto-queue@0.1.0" - } - }, - "p-locate@5.0.0": { - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dependencies": { - "p-limit": "p-limit@3.1.0" - } - }, - "pako@1.0.11": { - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dependencies": {} - }, - "parent-module@1.0.1": { - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "callsites@3.1.0" - } - }, - "parseurl@1.3.3": { - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dependencies": {} - }, - "path-exists@4.0.0": { - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dependencies": {} - }, - "path-is-absolute@1.0.1": { - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dependencies": {} - }, - "path-key@3.1.1": { - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dependencies": {} - }, - "path-parse@1.0.7": { - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dependencies": {} - }, - "path-to-regexp@0.1.7": { - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dependencies": {} - }, - "possible-typed-array-names@1.0.0": { - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dependencies": {} - }, - "prelude-ls@1.2.1": { - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dependencies": {} - }, - "process-nextick-args@2.0.1": { - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dependencies": {} - }, - "proxy-addr@2.0.7": { - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "forwarded@0.2.0", - "ipaddr.js": "ipaddr.js@1.9.1" - } - }, - "punycode@2.3.1": { - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dependencies": {} - }, - "qs@6.11.0": { - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "side-channel@1.0.6" - } - }, - "queue-microtask@1.2.3": { - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dependencies": {} - }, - "range-parser@1.2.1": { - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dependencies": {} - }, - "raw-body@2.5.2": { - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "bytes@3.1.2", - "http-errors": "http-errors@2.0.0", - "iconv-lite": "iconv-lite@0.4.24", - "unpipe": "unpipe@1.0.0" - } - }, - "readable-stream@2.3.8": { - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "core-util-is@1.0.3", - "inherits": "inherits@2.0.4", - "isarray": "isarray@1.0.0", - "process-nextick-args": "process-nextick-args@2.0.1", - "safe-buffer": "safe-buffer@5.1.2", - "string_decoder": "string_decoder@1.1.1", - "util-deprecate": "util-deprecate@1.0.2" - } - }, - "regexp.prototype.flags@1.5.2": { - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "es-errors": "es-errors@1.3.0", - "set-function-name": "set-function-name@2.0.2" - } - }, - "resolve-from@4.0.0": { - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dependencies": {} - }, - "resolve@1.22.8": { - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dependencies": { - "is-core-module": "is-core-module@2.15.1", - "path-parse": "path-parse@1.0.7", - "supports-preserve-symlinks-flag": "supports-preserve-symlinks-flag@1.0.0" - } - }, - "reusify@1.0.4": { - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dependencies": {} - }, - "rimraf@3.0.2": { - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "glob@7.2.3" - } - }, - "run-applescript@7.0.0": { - "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", - "dependencies": {} - }, - "run-parallel@1.2.0": { - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dependencies": { - "queue-microtask": "queue-microtask@1.2.3" - } - }, - "safe-array-concat@1.1.2": { - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "get-intrinsic": "get-intrinsic@1.2.4", - "has-symbols": "has-symbols@1.0.3", - "isarray": "isarray@2.0.5" - } - }, - "safe-buffer@5.1.2": { - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dependencies": {} - }, - "safe-buffer@5.2.1": { - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dependencies": {} - }, - "safe-regex-test@1.0.3": { - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "es-errors": "es-errors@1.3.0", - "is-regex": "is-regex@1.1.4" - } - }, - "safer-buffer@2.1.2": { - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dependencies": {} - }, - "semver@6.3.1": { - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dependencies": {} - }, - "send@0.18.0": { - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "debug@2.6.9", - "depd": "depd@2.0.0", - "destroy": "destroy@1.2.0", - "encodeurl": "encodeurl@1.0.2", - "escape-html": "escape-html@1.0.3", - "etag": "etag@1.8.1", - "fresh": "fresh@0.5.2", - "http-errors": "http-errors@2.0.0", - "mime": "mime@1.6.0", - "ms": "ms@2.1.3", - "on-finished": "on-finished@2.4.1", - "range-parser": "range-parser@1.2.1", - "statuses": "statuses@2.0.1" - } - }, - "serve-static@1.15.0": { - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "encodeurl@1.0.2", - "escape-html": "escape-html@1.0.3", - "parseurl": "parseurl@1.3.3", - "send": "send@0.18.0" - } - }, - "set-function-length@1.2.2": { - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "define-data-property@1.1.4", - "es-errors": "es-errors@1.3.0", - "function-bind": "function-bind@1.1.2", - "get-intrinsic": "get-intrinsic@1.2.4", - "gopd": "gopd@1.0.1", - "has-property-descriptors": "has-property-descriptors@1.0.2" - } - }, - "set-function-name@2.0.2": { - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dependencies": { - "define-data-property": "define-data-property@1.1.4", - "es-errors": "es-errors@1.3.0", - "functions-have-names": "functions-have-names@1.2.3", - "has-property-descriptors": "has-property-descriptors@1.0.2" - } - }, - "set-immediate-shim@1.0.1": { - "integrity": "sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ==", - "dependencies": {} - }, - "setprototypeof@1.2.0": { - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dependencies": {} - }, - "shebang-command@2.0.0": { - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "shebang-regex@3.0.0" - } - }, - "shebang-regex@3.0.0": { - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dependencies": {} - }, - "side-channel@1.0.6": { - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "es-errors": "es-errors@1.3.0", - "get-intrinsic": "get-intrinsic@1.2.4", - "object-inspect": "object-inspect@1.13.2" - } - }, - "statuses@2.0.1": { - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dependencies": {} - }, - "string.prototype.trim@1.2.9": { - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "es-abstract": "es-abstract@1.23.3", - "es-object-atoms": "es-object-atoms@1.0.0" - } - }, - "string.prototype.trimend@1.0.8": { - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "es-object-atoms": "es-object-atoms@1.0.0" - } - }, - "string.prototype.trimstart@1.0.8": { - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "define-properties": "define-properties@1.2.1", - "es-object-atoms": "es-object-atoms@1.0.0" - } - }, - "string_decoder@1.1.1": { - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "safe-buffer@5.1.2" - } - }, - "strip-ansi@6.0.1": { - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "ansi-regex@5.0.1" - } - }, - "strip-bom@3.0.0": { - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dependencies": {} - }, - "strip-json-comments@3.1.1": { - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dependencies": {} - }, - "sudo-prompt@9.2.1": { - "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==", - "dependencies": {} - }, - "supports-color@7.2.0": { - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "has-flag@4.0.0" - } - }, - "supports-preserve-symlinks-flag@1.0.0": { - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dependencies": {} - }, - "text-table@0.2.0": { - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dependencies": {} - }, - "toidentifier@1.0.1": { - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dependencies": {} - }, - "tsconfig-paths@3.15.0": { - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dependencies": { - "@types/json5": "@types/json5@0.0.29", - "json5": "json5@1.0.2", - "minimist": "minimist@1.2.8", - "strip-bom": "strip-bom@3.0.0" - } - }, - "tslib@2.3.1": { - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dependencies": {} - }, - "type-check@0.4.0": { - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dependencies": { - "prelude-ls": "prelude-ls@1.2.1" - } - }, - "type-fest@0.20.2": { - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dependencies": {} - }, - "type-is@1.6.18": { - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "media-typer@0.3.0", - "mime-types": "mime-types@2.1.35" - } - }, - "typed-array-buffer@1.0.2": { - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "es-errors": "es-errors@1.3.0", - "is-typed-array": "is-typed-array@1.1.13" - } - }, - "typed-array-byte-length@1.0.1": { - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "for-each": "for-each@0.3.3", - "gopd": "gopd@1.0.1", - "has-proto": "has-proto@1.0.3", - "is-typed-array": "is-typed-array@1.1.13" - } - }, - "typed-array-byte-offset@1.0.2": { - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dependencies": { - "available-typed-arrays": "available-typed-arrays@1.0.7", - "call-bind": "call-bind@1.0.7", - "for-each": "for-each@0.3.3", - "gopd": "gopd@1.0.1", - "has-proto": "has-proto@1.0.3", - "is-typed-array": "is-typed-array@1.1.13" - } - }, - "typed-array-length@1.0.6": { - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "for-each": "for-each@0.3.3", - "gopd": "gopd@1.0.1", - "has-proto": "has-proto@1.0.3", - "is-typed-array": "is-typed-array@1.1.13", - "possible-typed-array-names": "possible-typed-array-names@1.0.0" - } - }, - "unbox-primitive@1.0.2": { - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dependencies": { - "call-bind": "call-bind@1.0.7", - "has-bigints": "has-bigints@1.0.2", - "has-symbols": "has-symbols@1.0.3", - "which-boxed-primitive": "which-boxed-primitive@1.0.2" - } - }, - "undici@5.28.4": { - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", - "dependencies": { - "@fastify/busboy": "@fastify/busboy@2.1.1" - } - }, - "unpipe@1.0.0": { - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dependencies": {} - }, - "uri-js@4.4.1": { - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "punycode@2.3.1" - } - }, - "util-deprecate@1.0.2": { - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dependencies": {} - }, - "utils-merge@1.0.1": { - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dependencies": {} - }, - "vary@1.1.2": { - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dependencies": {} - }, - "which-boxed-primitive@1.0.2": { - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dependencies": { - "is-bigint": "is-bigint@1.0.4", - "is-boolean-object": "is-boolean-object@1.1.2", - "is-number-object": "is-number-object@1.0.7", - "is-string": "is-string@1.0.7", - "is-symbol": "is-symbol@1.0.4" - } - }, - "which-typed-array@1.1.15": { - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dependencies": { - "available-typed-arrays": "available-typed-arrays@1.0.7", - "call-bind": "call-bind@1.0.7", - "for-each": "for-each@0.3.3", - "gopd": "gopd@1.0.1", - "has-tostringtag": "has-tostringtag@1.0.2" - } - }, - "which@2.0.2": { - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "isexe@2.0.0" - } - }, - "which@4.0.0": { - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "dependencies": { - "isexe": "isexe@3.1.1" - } - }, - "windmill-client@1.364.0": { - "integrity": "sha512-UjCbBB2IeyMVoDyO16boHEsTus2npVtXyER2pfP0zm7rmp6ko65HA3OXvMzhMVq3hW9RQ5esc3mrkTnb19PIaw==", - "dependencies": {} - }, - "word-wrap@1.2.5": { - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dependencies": {} - }, - "wrappy@1.0.2": { - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dependencies": {} - }, - "ws@8.18.0": { - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "dependencies": {} - }, - "yocto-queue@0.1.0": { - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dependencies": {} - } + "@deno/dnt@0.41.3": { + "integrity": "b2ef2c8a5111eef86cb5bfcae103d6a2938e8e649e2461634a7befb7fc59d6d2", + "dependencies": [ + "jsr:@david/code-block-writer", + "jsr:@deno/cache-dir", + "jsr:@std/fmt@1", + "jsr:@std/fs@1", + "jsr:@std/path@1", + "jsr:@ts-morph/bootstrap" + ] + }, + "@deno/graph@0.73.1": { + "integrity": "cd69639d2709d479037d5ce191a422eabe8d71bb68b0098344f6b07411c84d41" + }, + "@std/assert@0.223.0": { + "integrity": "eb8d6d879d76e1cc431205bd346ed4d88dc051c6366365b1af47034b0670be24" + }, + "@std/assert@0.226.0": { + "integrity": "0dfb5f7c7723c18cec118e080fec76ce15b4c31154b15ad2bd74822603ef75b3" + }, + "@std/assert@1.0.0-rc.2": { + "integrity": "0484eab1d76b55fca1c3beaff485a274e67dd3b9f065edcbe70030dfc0b964d3" + }, + "@std/bytes@0.223.0": { + "integrity": "84b75052cd8680942c397c2631318772b295019098f40aac5c36cead4cba51a8" + }, + "@std/bytes@1.0.2": { + "integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57" + }, + "@std/cli@1.0.0-rc.2": { + "integrity": "97dfae82b9f0e189768ebfa7a5da53375955b94bad0a1804f8e3b73563b03787" + }, + "@std/encoding@1.0.0-rc.2": { + "integrity": "160d7674a20ebfbccdf610b3801fee91cf6e42d1c106dd46bbaf46e395cd35ef" + }, + "@std/encoding@1.0.4": { + "integrity": "2266cd516b32369e3dc5695717c96bf88343a1f761d6e6187a02a2bbe2af86ae" + }, + "@std/fmt@0.223.0": { + "integrity": "6deb37794127dfc7d7bded2586b9fc6f5d50e62a8134846608baf71ffc1a5208" + }, + "@std/fmt@0.225.6": { + "integrity": "aba6aea27f66813cecfd9484e074a9e9845782ab0685c030e453a8a70b37afc8" + }, + "@std/fmt@1.0.2": { + "integrity": "87e9dfcdd3ca7c066e0c3c657c1f987c82888eb8103a3a3baa62684ffeb0f7a7" + }, + "@std/fs@0.223.0": { + "integrity": "3b4b0550b2c524cbaaa5a9170c90e96cbb7354e837ad1bdaf15fc9df1ae9c31c" + }, + "@std/fs@0.229.3": { + "integrity": "783bca21f24da92e04c3893c9e79653227ab016c48e96b3078377ebd5222e6eb", + "dependencies": [ + "jsr:@std/path@1.0.0-rc.1" + ] + }, + "@std/fs@1.0.3": { + "integrity": "3cb839b1360b0a42d8b367c3093bfe4071798e6694fa44cf1963e04a8edba4fe", + "dependencies": [ + "jsr:@std/path@^1.0.4" + ] + }, + "@std/io@0.223.0": { + "integrity": "2d8c3c2ab3a515619b90da2c6ff5ea7b75a94383259ef4d02116b228393f84f1", + "dependencies": [ + "jsr:@std/assert@0.223", + "jsr:@std/bytes@0.223" + ] + }, + "@std/io@0.224.7": { + "integrity": "a70848793c44a7c100926571a8c9be68ba85487bfcd4d0540d86deabe1123dc9", + "dependencies": [ + "jsr:@std/bytes@^1.0.2" + ] + }, + "@std/log@0.224.7": { + "integrity": "021941e5cd16de60cb11599c9b36f892aea95987fe66c753922808da27909e18", + "dependencies": [ + "jsr:@std/fmt@^1.0.2", + "jsr:@std/fs@^1.0.3", + "jsr:@std/io@~0.224.7" + ] + }, + "@std/net@1.0.2": { + "integrity": "520c18ddb7f67d3830a1adfef03a155d496fe9683a9cb63bb823b5afb86484dc" + }, + "@std/path@0.223.0": { + "integrity": "593963402d7e6597f5a6e620931661053572c982fc014000459edc1f93cc3989", + "dependencies": [ + "jsr:@std/assert@0.223" + ] + }, + "@std/path@0.225.2": { + "integrity": "0f2db41d36b50ef048dcb0399aac720a5348638dd3cb5bf80685bf2a745aa506", + "dependencies": [ + "jsr:@std/assert@0.226" + ] + }, + "@std/path@1.0.0-rc.1": { + "integrity": "b8c00ae2f19106a6bb7cbf1ab9be52aa70de1605daeb2dbdc4f87a7cbaf10ff6" + }, + "@std/path@1.0.0-rc.2": { + "integrity": "39f20d37a44d1867abac8d91c169359ea6e942237a45a99ee1e091b32b921c7d" + }, + "@std/path@1.0.4": { + "integrity": "48dd5d8389bcfcd619338a01bdf862cb7799933390146a54ae59356a0acc7105" + }, + "@std/streams@1.0.4": { + "integrity": "a1a5b01c74ca1d2dcaacfe1d4bbb91392e765946d82a3471bd95539adc6da83a", + "dependencies": [ + "jsr:@std/bytes@^1.0.2" + ] + }, + "@std/text@1.0.0-rc.1": { + "integrity": "34c722203e87ee12792c8d4a0cd2ee0e001341cbce75b860fc21be19d62232b0" + }, + "@std/yaml@1.0.5": { + "integrity": "71ba3d334305ee2149391931508b2c293a8490f94a337eef3a09cade1a2a2742" + }, + "@ts-morph/bootstrap@0.24.0": { + "integrity": "a826a2ef7fa8a7c3f1042df2c034d20744d94da2ee32bf29275bcd4dffd3c060", + "dependencies": [ + "jsr:@ts-morph/common" + ] + }, + "@ts-morph/common@0.24.0": { + "integrity": "12b625b8e562446ba658cdbe9ad77774b4bd96b992ae8bd34c60dbf24d06c1f3", + "dependencies": [ + "jsr:@std/fs@~0.229.3", + "jsr:@std/path@~0.225.2" + ] + }, + "@windmill-labs/cliffy-ansi@1.0.0-rc.5": { + "integrity": "1109cbcb0c415b57779352f708f5969b8c645f56bc555cbafc6ea5e0c6a360a4", + "dependencies": [ + "jsr:@std/encoding@1.0.0-rc.2", + "jsr:@std/fmt@~0.225.4", + "jsr:@std/io@~0.224.2", + "jsr:@windmill-labs/cliffy-internal" + ] + }, + "@windmill-labs/cliffy-command@1.0.0-rc.5": { + "integrity": "3eaa9def5f5afa1028f4a60ee4d9065ccc5f194d032824365c6ebcb9f46db66e", + "dependencies": [ + "jsr:@std/fmt@~0.225.4", + "jsr:@std/text", + "jsr:@windmill-labs/cliffy-flags", + "jsr:@windmill-labs/cliffy-internal", + "jsr:@windmill-labs/cliffy-table@1.0.0-rc.5" + ] + }, + "@windmill-labs/cliffy-flags@1.0.0-rc.5": { + "integrity": "0e4b5b53a02295f8bf27d93b3bcca5d5d001a0286aea17404e6ef6347f69363c", + "dependencies": [ + "jsr:@std/text" + ] + }, + "@windmill-labs/cliffy-internal@1.0.0-rc.5": { + "integrity": "876b989ad2d1b739cc4a4f1386dbb80f819d2851ad583c1f486fc6ebe9beadf6" + }, + "@windmill-labs/cliffy-keycode@1.0.0-rc.5": { + "integrity": "2bc1b1af363528e38ed47bce525f417cab278b829a423353ffd589622f5a746e" + }, + "@windmill-labs/cliffy-prompt@1.0.0-rc.5": { + "integrity": "329a097911f219b15ea643ae83b6b360a11df7fc4cafdac1bf6869259475033a", + "dependencies": [ + "jsr:@std/assert@1.0.0-rc.2", + "jsr:@std/fmt@~0.225.4", + "jsr:@std/io@~0.224.2", + "jsr:@std/path@1.0.0-rc.2", + "jsr:@std/text", + "jsr:@windmill-labs/cliffy-ansi@1.0.0-rc.5", + "jsr:@windmill-labs/cliffy-internal", + "jsr:@windmill-labs/cliffy-keycode" + ] + }, + "@windmill-labs/cliffy-prompt@1.0.0-rc.6": { + "integrity": "ffe09bee0e1e07bc12b2147be509d10b47eb6a36cbfea80fc206b4ae86693205", + "dependencies": [ + "jsr:@std/assert@1.0.0-rc.2", + "jsr:@std/fmt@~0.225.4", + "jsr:@std/io@~0.224.2", + "jsr:@std/path@1.0.0-rc.2", + "jsr:@std/text", + "jsr:@windmill-labs/cliffy-ansi@1.0.0-rc.5", + "jsr:@windmill-labs/cliffy-internal", + "jsr:@windmill-labs/cliffy-keycode" + ] + }, + "@windmill-labs/cliffy-table@1.0.0-rc.5": { + "integrity": "5f26cb6ccbc2fbf1b1f79f9062d166e32e11d34398c0deac7e6f9d8378970546", + "dependencies": [ + "jsr:@std/cli", + "jsr:@std/fmt@~0.225.4" + ] + } + }, + "npm": { + "@ayonli/jsext@0.9.58": { + "integrity": "sha512-AwGf64K6VqGyYLFA6rgyuU6jBbwUNJctENpW1bZayvXcIprhdfs7cn7S+EXi0pnNLupT9ptODYkKneB3/YuWww==", + "dependencies": [ + "iconv-lite@0.6.3", + "sudo-prompt", + "ws" + ] + }, + "@deno/shim-crypto@0.3.1": { + "integrity": "sha512-ed4pNnfur6UbASEgF34gVxR9p7Mc3qF+Ygbmjiil8ws5IhNFhPDFy5vE5hQAUA9JmVsSxXPcVLM5Rf8LOZqQ5Q==" + }, + "@deno/shim-deno-test@0.5.0": { + "integrity": "sha512-4nMhecpGlPi0cSzT67L+Tm+GOJqvuk8gqHBziqcUQOarnuIax1z96/gJHCSIz2Z0zhxE6Rzwb3IZXPtFh51j+w==" + }, + "@deno/shim-deno@0.17.0": { + "integrity": "sha512-+FzsP65eehAgTQdzt1izLEV17ePCZqHxDQqRDbpRc1yJVYtDI2MvbRq5DvOj90uRt6zKn9qtWpEueDqG1QORhQ==", + "dependencies": [ + "@deno/shim-deno-test", + "which@4.0.0" + ] + }, + "@esbuild/aix-ppc64@0.23.0": { + "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==" + }, + "@esbuild/android-arm64@0.23.0": { + "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==" + }, + "@esbuild/android-arm@0.23.0": { + "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==" + }, + "@esbuild/android-x64@0.23.0": { + "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==" + }, + "@esbuild/darwin-arm64@0.23.0": { + "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==" + }, + "@esbuild/darwin-x64@0.23.0": { + "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==" + }, + "@esbuild/freebsd-arm64@0.23.0": { + "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==" + }, + "@esbuild/freebsd-x64@0.23.0": { + "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==" + }, + "@esbuild/linux-arm64@0.23.0": { + "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==" + }, + "@esbuild/linux-arm@0.23.0": { + "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==" + }, + "@esbuild/linux-ia32@0.23.0": { + "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==" + }, + "@esbuild/linux-loong64@0.23.0": { + "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==" + }, + "@esbuild/linux-mips64el@0.23.0": { + "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==" + }, + "@esbuild/linux-ppc64@0.23.0": { + "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==" + }, + "@esbuild/linux-riscv64@0.23.0": { + "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==" + }, + "@esbuild/linux-s390x@0.23.0": { + "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==" + }, + "@esbuild/linux-x64@0.23.0": { + "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==" + }, + "@esbuild/netbsd-x64@0.23.0": { + "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==" + }, + "@esbuild/openbsd-arm64@0.23.0": { + "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==" + }, + "@esbuild/openbsd-x64@0.23.0": { + "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==" + }, + "@esbuild/sunos-x64@0.23.0": { + "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==" + }, + "@esbuild/win32-arm64@0.23.0": { + "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==" + }, + "@esbuild/win32-ia32@0.23.0": { + "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==" + }, + "@esbuild/win32-x64@0.23.0": { + "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==" + }, + "@eslint-community/eslint-utils@4.4.0_eslint@8.57.1": { + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dependencies": [ + "eslint@8.57.1", + "eslint-visitor-keys@3.4.3" + ] + }, + "@eslint-community/eslint-utils@4.4.0_eslint@9.10.0": { + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dependencies": [ + "eslint@9.10.0", + "eslint-visitor-keys@3.4.3" + ] + }, + "@eslint-community/regexpp@4.11.1": { + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==" + }, + "@eslint/config-array@0.18.0": { + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dependencies": [ + "@eslint/object-schema", + "debug@4.3.7", + "minimatch@3.1.2" + ] + }, + "@eslint/eslintrc@2.1.4": { + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dependencies": [ + "ajv", + "debug@4.3.7", + "espree@9.6.1_acorn@8.12.1", + "globals@13.24.0", + "ignore", + "import-fresh", + "js-yaml", + "minimatch@3.1.2", + "strip-json-comments" + ] + }, + "@eslint/eslintrc@3.1.0": { + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dependencies": [ + "ajv", + "debug@4.3.7", + "espree@10.1.0_acorn@8.12.1", + "globals@14.0.0", + "ignore", + "import-fresh", + "js-yaml", + "minimatch@3.1.2", + "strip-json-comments" + ] + }, + "@eslint/js@8.57.1": { + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==" + }, + "@eslint/js@9.10.0": { + "integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==" + }, + "@eslint/object-schema@2.1.4": { + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==" + }, + "@eslint/plugin-kit@0.1.0": { + "integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==", + "dependencies": [ + "levn" + ] + }, + "@fastify/busboy@2.1.1": { + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==" + }, + "@humanwhocodes/config-array@0.13.0": { + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "dependencies": [ + "@humanwhocodes/object-schema", + "debug@4.3.7", + "minimatch@3.1.2" + ] + }, + "@humanwhocodes/module-importer@1.0.1": { + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" + }, + "@humanwhocodes/object-schema@2.0.3": { + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==" + }, + "@humanwhocodes/retry@0.3.0": { + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==" + }, + "@nodelib/fs.scandir@2.1.5": { + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": [ + "@nodelib/fs.stat", + "run-parallel" + ] + }, + "@nodelib/fs.stat@2.0.5": { + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk@1.2.8": { + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": [ + "@nodelib/fs.scandir", + "fastq" + ] + }, + "@oakserver/oak@12.6.2": { + "integrity": "sha512-q9LfyC9tWV68me0GEUuA66qbwH8ep0bBdq9V02fePlPmPVUBCAzQQkomyaI/L4Uur+YALVXgLoTaC2rviZ7I4w==", + "dependencies": [ + "@deno/shim-crypto", + "@deno/shim-deno", + "tslib", + "undici" + ] + }, + "@rtsao/scc@1.1.0": { + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==" + }, + "@types/diff@5.2.2": { + "integrity": "sha512-qVqLpd49rmJA2nZzLVsmfS/aiiBpfVE95dHhPVwG0NmSBAt+riPxnj53wq2oBq5m4Q2RF1IWFEUpnZTgrQZfEQ==" + }, + "@types/json5@0.0.29": { + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + }, + "@types/node@18.16.19": { + "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==" + }, + "@ungap/structured-clone@1.2.0": { + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, + "accepts@1.3.8": { + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": [ + "mime-types", + "negotiator" + ] + }, + "acorn-jsx@5.3.2_acorn@8.12.1": { + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dependencies": [ + "acorn" + ] + }, + "acorn@8.12.1": { + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==" + }, + "ajv@6.12.6": { + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": [ + "fast-deep-equal", + "fast-json-stable-stringify", + "json-schema-traverse", + "uri-js" + ] + }, + "ansi-regex@5.0.1": { + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles@4.3.0": { + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": [ + "color-convert" + ] + }, + "argparse@2.0.1": { + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "array-buffer-byte-length@1.0.1": { + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dependencies": [ + "call-bind", + "is-array-buffer" + ] + }, + "array-flatten@1.1.1": { + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "array-includes@3.1.8": { + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-object-atoms", + "get-intrinsic", + "is-string" + ] + }, + "array.prototype.findlastindex@1.2.5": { + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-errors", + "es-object-atoms", + "es-shim-unscopables" + ] + }, + "array.prototype.flat@1.3.2": { + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-shim-unscopables" + ] + }, + "array.prototype.flatmap@1.3.2": { + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-shim-unscopables" + ] + }, + "arraybuffer.prototype.slice@1.0.3": { + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dependencies": [ + "array-buffer-byte-length", + "call-bind", + "define-properties", + "es-abstract", + "es-errors", + "get-intrinsic", + "is-array-buffer", + "is-shared-array-buffer" + ] + }, + "available-typed-arrays@1.0.7": { + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": [ + "possible-typed-array-names" + ] + }, + "balanced-match@1.0.2": { + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "body-parser@1.20.2": { + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": [ + "bytes", + "content-type", + "debug@2.6.9", + "depd", + "destroy", + "http-errors", + "iconv-lite@0.4.24", + "on-finished", + "qs", + "raw-body", + "type-is", + "unpipe" + ] + }, + "brace-expansion@1.1.11": { + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": [ + "balanced-match", + "concat-map" + ] + }, + "brace-expansion@2.0.1": { + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": [ + "balanced-match" + ] + }, + "bundle-name@4.1.0": { + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dependencies": [ + "run-applescript" + ] + }, + "bytes@3.1.2": { + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "call-bind@1.0.7": { + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": [ + "es-define-property", + "es-errors", + "function-bind", + "get-intrinsic", + "set-function-length" + ] + }, + "callsites@3.1.0": { + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "chalk@4.1.2": { + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": [ + "ansi-styles", + "supports-color" + ] + }, + "color-convert@2.0.1": { + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": [ + "color-name" + ] + }, + "color-name@1.1.4": { + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "concat-map@0.0.1": { + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "content-disposition@0.5.4": { + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": [ + "safe-buffer@5.2.1" + ] + }, + "content-type@1.0.5": { + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "cookie-signature@1.0.6": { + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "cookie@0.6.0": { + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" + }, + "core-util-is@1.0.3": { + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "cross-spawn@7.0.3": { + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": [ + "path-key", + "shebang-command", + "which@2.0.2" + ] + }, + "data-view-buffer@1.0.1": { + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dependencies": [ + "call-bind", + "es-errors", + "is-data-view" + ] + }, + "data-view-byte-length@1.0.1": { + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dependencies": [ + "call-bind", + "es-errors", + "is-data-view" + ] + }, + "data-view-byte-offset@1.0.0": { + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dependencies": [ + "call-bind", + "es-errors", + "is-data-view" + ] + }, + "debug@2.6.9": { + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": [ + "ms@2.0.0" + ] + }, + "debug@3.2.7": { + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": [ + "ms@2.1.3" + ] + }, + "debug@4.3.7": { + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": [ + "ms@2.1.3" + ] + }, + "deep-is@0.1.4": { + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "default-browser-id@5.0.0": { + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==" + }, + "default-browser@5.2.1": { + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dependencies": [ + "bundle-name", + "default-browser-id" + ] + }, + "define-data-property@1.1.4": { + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": [ + "es-define-property", + "es-errors", + "gopd" + ] + }, + "define-lazy-prop@3.0.0": { + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==" + }, + "define-properties@1.2.1": { + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dependencies": [ + "define-data-property", + "has-property-descriptors", + "object-keys" + ] + }, + "depd@2.0.0": { + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy@1.2.0": { + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "diff@5.2.0": { + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==" + }, + "doctrine@2.1.0": { + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": [ + "esutils" + ] + }, + "doctrine@3.0.0": { + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dependencies": [ + "esutils" + ] + }, + "ee-first@1.1.1": { + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "encodeurl@1.0.2": { + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "es-abstract@1.23.3": { + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dependencies": [ + "array-buffer-byte-length", + "arraybuffer.prototype.slice", + "available-typed-arrays", + "call-bind", + "data-view-buffer", + "data-view-byte-length", + "data-view-byte-offset", + "es-define-property", + "es-errors", + "es-object-atoms", + "es-set-tostringtag", + "es-to-primitive", + "function.prototype.name", + "get-intrinsic", + "get-symbol-description", + "globalthis", + "gopd", + "has-property-descriptors", + "has-proto", + "has-symbols", + "hasown", + "internal-slot", + "is-array-buffer", + "is-callable", + "is-data-view", + "is-negative-zero", + "is-regex", + "is-shared-array-buffer", + "is-string", + "is-typed-array", + "is-weakref", + "object-inspect", + "object-keys", + "object.assign", + "regexp.prototype.flags", + "safe-array-concat", + "safe-regex-test", + "string.prototype.trim", + "string.prototype.trimend", + "string.prototype.trimstart", + "typed-array-buffer", + "typed-array-byte-length", + "typed-array-byte-offset", + "typed-array-length", + "unbox-primitive", + "which-typed-array" + ] + }, + "es-define-property@1.0.0": { + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": [ + "get-intrinsic" + ] + }, + "es-errors@1.3.0": { + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-main@1.3.0": { + "integrity": "sha512-AzORKdz1Zt97TzbYQnIrI3ZiibWpRXUfpo/w0xOJ20GpNYd2bd3MU9m31zS/aJ1TJl6JfLTok83Y8HjNunYT0A==" + }, + "es-object-atoms@1.0.0": { + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dependencies": [ + "es-errors" + ] + }, + "es-set-tostringtag@2.0.3": { + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dependencies": [ + "get-intrinsic", + "has-tostringtag", + "hasown" + ] + }, + "es-shim-unscopables@1.0.2": { + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dependencies": [ + "hasown" + ] + }, + "es-to-primitive@1.2.1": { + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": [ + "is-callable", + "is-date-object", + "is-symbol" + ] + }, + "esbuild@0.23.0": { + "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "dependencies": [ + "@esbuild/aix-ppc64", + "@esbuild/android-arm", + "@esbuild/android-arm64", + "@esbuild/android-x64", + "@esbuild/darwin-arm64", + "@esbuild/darwin-x64", + "@esbuild/freebsd-arm64", + "@esbuild/freebsd-x64", + "@esbuild/linux-arm", + "@esbuild/linux-arm64", + "@esbuild/linux-ia32", + "@esbuild/linux-loong64", + "@esbuild/linux-mips64el", + "@esbuild/linux-ppc64", + "@esbuild/linux-riscv64", + "@esbuild/linux-s390x", + "@esbuild/linux-x64", + "@esbuild/netbsd-x64", + "@esbuild/openbsd-arm64", + "@esbuild/openbsd-x64", + "@esbuild/sunos-x64", + "@esbuild/win32-arm64", + "@esbuild/win32-ia32", + "@esbuild/win32-x64" + ] + }, + "escape-html@1.0.3": { + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "escape-string-regexp@4.0.0": { + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "eslint-import-resolver-node@0.3.9": { + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dependencies": [ + "debug@3.2.7", + "is-core-module", + "resolve" + ] + }, + "eslint-module-utils@2.11.0": { + "integrity": "sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==", + "dependencies": [ + "debug@3.2.7" + ] + }, + "eslint-plugin-import@2.30.0_eslint@8.57.1": { + "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", + "dependencies": [ + "@rtsao/scc", + "array-includes", + "array.prototype.findlastindex", + "array.prototype.flat", + "array.prototype.flatmap", + "debug@3.2.7", + "doctrine@2.1.0", + "eslint@8.57.1", + "eslint-import-resolver-node", + "eslint-module-utils", + "hasown", + "is-core-module", + "is-glob", + "minimatch@3.1.2", + "object.fromentries", + "object.groupby", + "object.values", + "semver", + "tsconfig-paths" + ] + }, + "eslint-scope@7.2.2": { + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dependencies": [ + "esrecurse", + "estraverse" + ] + }, + "eslint-scope@8.0.2": { + "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", + "dependencies": [ + "esrecurse", + "estraverse" + ] + }, + "eslint-visitor-keys@3.4.3": { + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==" + }, + "eslint-visitor-keys@4.0.0": { + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==" + }, + "eslint@8.57.1": { + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "dependencies": [ + "@eslint-community/eslint-utils@4.4.0_eslint@8.57.1", + "@eslint-community/regexpp", + "@eslint/eslintrc@2.1.4", + "@eslint/js@8.57.1", + "@humanwhocodes/config-array", + "@humanwhocodes/module-importer", + "@nodelib/fs.walk", + "@ungap/structured-clone", + "ajv", + "chalk", + "cross-spawn", + "debug@4.3.7", + "doctrine@3.0.0", + "escape-string-regexp", + "eslint-scope@7.2.2", + "eslint-visitor-keys@3.4.3", + "espree@9.6.1_acorn@8.12.1", + "esquery", + "esutils", + "fast-deep-equal", + "file-entry-cache@6.0.1", + "find-up", + "glob-parent", + "globals@13.24.0", + "graphemer", + "ignore", + "imurmurhash", + "is-glob", + "is-path-inside", + "js-yaml", + "json-stable-stringify-without-jsonify", + "levn", + "lodash.merge", + "minimatch@3.1.2", + "natural-compare", + "optionator", + "strip-ansi", + "text-table" + ] + }, + "eslint@9.10.0": { + "integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==", + "dependencies": [ + "@eslint-community/eslint-utils@4.4.0_eslint@9.10.0", + "@eslint-community/regexpp", + "@eslint/config-array", + "@eslint/eslintrc@3.1.0", + "@eslint/js@9.10.0", + "@eslint/plugin-kit", + "@humanwhocodes/module-importer", + "@humanwhocodes/retry", + "@nodelib/fs.walk", + "ajv", + "chalk", + "cross-spawn", + "debug@4.3.7", + "escape-string-regexp", + "eslint-scope@8.0.2", + "eslint-visitor-keys@4.0.0", + "espree@10.1.0_acorn@8.12.1", + "esquery", + "esutils", + "fast-deep-equal", + "file-entry-cache@8.0.0", + "find-up", + "glob-parent", + "ignore", + "imurmurhash", + "is-glob", + "is-path-inside", + "json-stable-stringify-without-jsonify", + "lodash.merge", + "minimatch@3.1.2", + "natural-compare", + "optionator", + "strip-ansi", + "text-table" + ] + }, + "espree@10.1.0_acorn@8.12.1": { + "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "dependencies": [ + "acorn", + "acorn-jsx", + "eslint-visitor-keys@4.0.0" + ] + }, + "espree@9.6.1_acorn@8.12.1": { + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dependencies": [ + "acorn", + "acorn-jsx", + "eslint-visitor-keys@3.4.3" + ] + }, + "esquery@1.6.0": { + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dependencies": [ + "estraverse" + ] + }, + "esrecurse@4.3.0": { + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": [ + "estraverse" + ] + }, + "estraverse@5.3.0": { + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "esutils@2.0.3": { + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag@1.8.1": { + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "express@4.19.2": { + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dependencies": [ + "accepts", + "array-flatten", + "body-parser", + "content-disposition", + "content-type", + "cookie", + "cookie-signature", + "debug@2.6.9", + "depd", + "encodeurl", + "escape-html", + "etag", + "finalhandler", + "fresh", + "http-errors", + "merge-descriptors", + "methods", + "on-finished", + "parseurl", + "path-to-regexp", + "proxy-addr", + "qs", + "range-parser", + "safe-buffer@5.2.1", + "send", + "serve-static", + "setprototypeof", + "statuses", + "type-is", + "utils-merge", + "vary" + ] + }, + "fast-deep-equal@3.1.3": { + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify@2.1.0": { + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein@2.0.6": { + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "fastq@1.17.1": { + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dependencies": [ + "reusify" + ] + }, + "file-entry-cache@6.0.1": { + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dependencies": [ + "flat-cache@3.2.0" + ] + }, + "file-entry-cache@8.0.0": { + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dependencies": [ + "flat-cache@4.0.1" + ] + }, + "finalhandler@1.2.0": { + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": [ + "debug@2.6.9", + "encodeurl", + "escape-html", + "on-finished", + "parseurl", + "statuses", + "unpipe" + ] + }, + "find-up@5.0.0": { + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": [ + "locate-path", + "path-exists" + ] + }, + "flat-cache@3.2.0": { + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dependencies": [ + "flatted", + "keyv", + "rimraf" + ] + }, + "flat-cache@4.0.1": { + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dependencies": [ + "flatted", + "keyv" + ] + }, + "flatted@3.3.1": { + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" + }, + "for-each@0.3.3": { + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": [ + "is-callable" + ] + }, + "forwarded@0.2.0": { + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh@0.5.2": { + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "fs.realpath@1.0.0": { + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "function-bind@1.1.2": { + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "function.prototype.name@1.1.6": { + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "functions-have-names" + ] + }, + "functions-have-names@1.2.3": { + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + }, + "get-intrinsic@1.2.4": { + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": [ + "es-errors", + "function-bind", + "has-proto", + "has-symbols", + "hasown" + ] + }, + "get-port@7.1.0": { + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==" + }, + "get-symbol-description@1.0.2": { + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dependencies": [ + "call-bind", + "es-errors", + "get-intrinsic" + ] + }, + "gitignore-parser@0.0.2": { + "integrity": "sha512-X6mpqUv59uWLGD4n3hZ8Cu8KbF2PMWPSFYmxZjdkpm3yOU7hSUYnzTkZI1mcWqchphvqyuz3/BhgBR4E/JtkCg==" + }, + "glob-parent@6.0.2": { + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": [ + "is-glob" + ] + }, + "glob@7.2.3": { + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": [ + "fs.realpath", + "inflight", + "inherits", + "minimatch@3.1.2", + "once", + "path-is-absolute" + ] + }, + "globals@13.24.0": { + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dependencies": [ + "type-fest" + ] + }, + "globals@14.0.0": { + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==" + }, + "globalthis@1.0.4": { + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dependencies": [ + "define-properties", + "gopd" + ] + }, + "gopd@1.0.1": { + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": [ + "get-intrinsic" + ] + }, + "graphemer@1.4.0": { + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + }, + "has-bigints@1.0.2": { + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" + }, + "has-flag@4.0.0": { + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "has-property-descriptors@1.0.2": { + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": [ + "es-define-property" + ] + }, + "has-proto@1.0.3": { + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" + }, + "has-symbols@1.0.3": { + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag@1.0.2": { + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": [ + "has-symbols" + ] + }, + "hasown@2.0.2": { + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": [ + "function-bind" + ] + }, + "http-errors@2.0.0": { + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": [ + "depd", + "inherits", + "setprototypeof", + "statuses", + "toidentifier" + ] + }, + "iconv-lite@0.4.24": { + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": [ + "safer-buffer" + ] + }, + "iconv-lite@0.6.3": { + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": [ + "safer-buffer" + ] + }, + "ignore@5.3.2": { + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==" + }, + "immediate@3.0.6": { + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, + "import-fresh@3.3.0": { + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": [ + "parent-module", + "resolve-from" + ] + }, + "imurmurhash@0.1.4": { + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + }, + "inflight@1.0.6": { + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": [ + "once", + "wrappy" + ] + }, + "inherits@2.0.4": { + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "internal-slot@1.0.7": { + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dependencies": [ + "es-errors", + "hasown", + "side-channel" + ] + }, + "ipaddr.js@1.9.1": { + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-array-buffer@3.0.4": { + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dependencies": [ + "call-bind", + "get-intrinsic" + ] + }, + "is-bigint@1.0.4": { + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": [ + "has-bigints" + ] + }, + "is-boolean-object@1.1.2": { + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": [ + "call-bind", + "has-tostringtag" + ] + }, + "is-callable@1.2.7": { + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" + }, + "is-core-module@2.15.1": { + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dependencies": [ + "hasown" + ] + }, + "is-data-view@1.0.1": { + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dependencies": [ + "is-typed-array" + ] + }, + "is-date-object@1.0.5": { + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": [ + "has-tostringtag" + ] + }, + "is-docker@3.0.0": { + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==" + }, + "is-extglob@2.1.1": { + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-glob@4.0.3": { + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": [ + "is-extglob" + ] + }, + "is-inside-container@1.0.0": { + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dependencies": [ + "is-docker" + ] + }, + "is-negative-zero@2.0.3": { + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==" + }, + "is-number-object@1.0.7": { + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dependencies": [ + "has-tostringtag" + ] + }, + "is-path-inside@3.0.3": { + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + }, + "is-regex@1.1.4": { + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": [ + "call-bind", + "has-tostringtag" + ] + }, + "is-shared-array-buffer@1.0.3": { + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dependencies": [ + "call-bind" + ] + }, + "is-string@1.0.7": { + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": [ + "has-tostringtag" + ] + }, + "is-symbol@1.0.4": { + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": [ + "has-symbols" + ] + }, + "is-typed-array@1.1.13": { + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dependencies": [ + "which-typed-array" + ] + }, + "is-weakref@1.0.2": { + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": [ + "call-bind" + ] + }, + "is-wsl@3.1.0": { + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dependencies": [ + "is-inside-container" + ] + }, + "isarray@1.0.0": { + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "isarray@2.0.5": { + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "isexe@2.0.0": { + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "isexe@3.1.1": { + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==" + }, + "js-yaml@4.1.0": { + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": [ + "argparse" + ] + }, + "json-buffer@3.0.1": { + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "json-schema-traverse@0.4.1": { + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify@1.0.1": { + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "json5@1.0.2": { + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dependencies": [ + "minimist" + ] + }, + "jszip@3.7.1": { + "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==", + "dependencies": [ + "lie", + "pako", + "readable-stream", + "set-immediate-shim" + ] + }, + "keyv@4.5.4": { + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dependencies": [ + "json-buffer" + ] + }, + "levn@0.4.1": { + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dependencies": [ + "prelude-ls", + "type-check" + ] + }, + "lie@3.3.0": { + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dependencies": [ + "immediate" + ] + }, + "locate-path@6.0.0": { + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": [ + "p-locate" + ] + }, + "lodash.merge@4.6.2": { + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "media-typer@0.3.0": { + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "merge-descriptors@1.0.1": { + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "methods@1.1.2": { + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "mime-db@1.52.0": { + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types@2.1.35": { + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": [ + "mime-db" + ] + }, + "mime@1.6.0": { + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "minimatch@10.0.1": { + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dependencies": [ + "brace-expansion@2.0.1" + ] + }, + "minimatch@3.1.2": { + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": [ + "brace-expansion@1.1.11" + ] + }, + "minimist@1.2.8": { + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "ms@2.0.0": { + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "ms@2.1.3": { + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "natural-compare@1.4.0": { + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "negotiator@0.6.3": { + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "object-inspect@1.13.2": { + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==" + }, + "object-keys@1.1.1": { + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign@4.1.5": { + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dependencies": [ + "call-bind", + "define-properties", + "has-symbols", + "object-keys" + ] + }, + "object.fromentries@2.0.8": { + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-object-atoms" + ] + }, + "object.groupby@1.0.3": { + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract" + ] + }, + "object.values@1.2.0": { + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-object-atoms" + ] + }, + "on-finished@2.4.1": { + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": [ + "ee-first" + ] + }, + "once@1.4.0": { + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": [ + "wrappy" + ] + }, + "open@10.1.0": { + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "dependencies": [ + "default-browser", + "define-lazy-prop", + "is-inside-container", + "is-wsl" + ] + }, + "optionator@0.9.4": { + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dependencies": [ + "deep-is", + "fast-levenshtein", + "levn", + "prelude-ls", + "type-check", + "word-wrap" + ] + }, + "p-limit@3.1.0": { + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": [ + "yocto-queue" + ] + }, + "p-locate@5.0.0": { + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": [ + "p-limit" + ] + }, + "pako@1.0.11": { + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "parent-module@1.0.1": { + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": [ + "callsites" + ] + }, + "parseurl@1.3.3": { + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-exists@4.0.0": { + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-is-absolute@1.0.1": { + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, + "path-key@3.1.1": { + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse@1.0.7": { + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-to-regexp@0.1.7": { + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "possible-typed-array-names@1.0.0": { + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==" + }, + "prelude-ls@1.2.1": { + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" + }, + "process-nextick-args@2.0.1": { + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "proxy-addr@2.0.7": { + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": [ + "forwarded", + "ipaddr.js" + ] + }, + "punycode@2.3.1": { + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" + }, + "qs@6.11.0": { + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": [ + "side-channel" + ] + }, + "queue-microtask@1.2.3": { + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "range-parser@1.2.1": { + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body@2.5.2": { + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": [ + "bytes", + "http-errors", + "iconv-lite@0.4.24", + "unpipe" + ] + }, + "readable-stream@2.3.8": { + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": [ + "core-util-is", + "inherits", + "isarray@1.0.0", + "process-nextick-args", + "safe-buffer@5.1.2", + "string_decoder", + "util-deprecate" + ] + }, + "regexp.prototype.flags@1.5.2": { + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dependencies": [ + "call-bind", + "define-properties", + "es-errors", + "set-function-name" + ] + }, + "resolve-from@4.0.0": { + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "resolve@1.22.8": { + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": [ + "is-core-module", + "path-parse", + "supports-preserve-symlinks-flag" + ] + }, + "reusify@1.0.4": { + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rimraf@3.0.2": { + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": [ + "glob" + ] + }, + "run-applescript@7.0.0": { + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==" + }, + "run-parallel@1.2.0": { + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dependencies": [ + "queue-microtask" + ] + }, + "safe-array-concat@1.1.2": { + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dependencies": [ + "call-bind", + "get-intrinsic", + "has-symbols", + "isarray@2.0.5" + ] + }, + "safe-buffer@5.1.2": { + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-buffer@5.2.1": { + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-regex-test@1.0.3": { + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dependencies": [ + "call-bind", + "es-errors", + "is-regex" + ] + }, + "safer-buffer@2.1.2": { + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver@6.3.1": { + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + }, + "send@0.18.0": { + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": [ + "debug@2.6.9", + "depd", + "destroy", + "encodeurl", + "escape-html", + "etag", + "fresh", + "http-errors", + "mime", + "ms@2.1.3", + "on-finished", + "range-parser", + "statuses" + ] + }, + "serve-static@1.15.0": { + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": [ + "encodeurl", + "escape-html", + "parseurl", + "send" + ] + }, + "set-function-length@1.2.2": { + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": [ + "define-data-property", + "es-errors", + "function-bind", + "get-intrinsic", + "gopd", + "has-property-descriptors" + ] + }, + "set-function-name@2.0.2": { + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dependencies": [ + "define-data-property", + "es-errors", + "functions-have-names", + "has-property-descriptors" + ] + }, + "set-immediate-shim@1.0.1": { + "integrity": "sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ==" + }, + "setprototypeof@1.2.0": { + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "shebang-command@2.0.0": { + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": [ + "shebang-regex" + ] + }, + "shebang-regex@3.0.0": { + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "side-channel@1.0.6": { + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": [ + "call-bind", + "es-errors", + "get-intrinsic", + "object-inspect" + ] + }, + "statuses@2.0.1": { + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "string.prototype.trim@1.2.9": { + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dependencies": [ + "call-bind", + "define-properties", + "es-abstract", + "es-object-atoms" + ] + }, + "string.prototype.trimend@1.0.8": { + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dependencies": [ + "call-bind", + "define-properties", + "es-object-atoms" + ] + }, + "string.prototype.trimstart@1.0.8": { + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dependencies": [ + "call-bind", + "define-properties", + "es-object-atoms" + ] + }, + "string_decoder@1.1.1": { + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": [ + "safe-buffer@5.1.2" + ] + }, + "strip-ansi@6.0.1": { + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": [ + "ansi-regex" + ] + }, + "strip-bom@3.0.0": { + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" + }, + "strip-json-comments@3.1.1": { + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + }, + "sudo-prompt@9.2.1": { + "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==" + }, + "supports-color@7.2.0": { + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": [ + "has-flag" + ] + }, + "supports-preserve-symlinks-flag@1.0.0": { + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "text-table@0.2.0": { + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "toidentifier@1.0.1": { + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "tsconfig-paths@3.15.0": { + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dependencies": [ + "@types/json5", + "json5", + "minimist", + "strip-bom" + ] + }, + "tslib@2.3.1": { + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "type-check@0.4.0": { + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dependencies": [ + "prelude-ls" + ] + }, + "type-fest@0.20.2": { + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + }, + "type-is@1.6.18": { + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": [ + "media-typer", + "mime-types" + ] + }, + "typed-array-buffer@1.0.2": { + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dependencies": [ + "call-bind", + "es-errors", + "is-typed-array" + ] + }, + "typed-array-byte-length@1.0.1": { + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dependencies": [ + "call-bind", + "for-each", + "gopd", + "has-proto", + "is-typed-array" + ] + }, + "typed-array-byte-offset@1.0.2": { + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dependencies": [ + "available-typed-arrays", + "call-bind", + "for-each", + "gopd", + "has-proto", + "is-typed-array" + ] + }, + "typed-array-length@1.0.6": { + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dependencies": [ + "call-bind", + "for-each", + "gopd", + "has-proto", + "is-typed-array", + "possible-typed-array-names" + ] + }, + "unbox-primitive@1.0.2": { + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dependencies": [ + "call-bind", + "has-bigints", + "has-symbols", + "which-boxed-primitive" + ] + }, + "undici@5.28.4": { + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dependencies": [ + "@fastify/busboy" + ] + }, + "unpipe@1.0.0": { + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "uri-js@4.4.1": { + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": [ + "punycode" + ] + }, + "util-deprecate@1.0.2": { + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "utils-merge@1.0.1": { + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "vary@1.1.2": { + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "which-boxed-primitive@1.0.2": { + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": [ + "is-bigint", + "is-boolean-object", + "is-number-object", + "is-string", + "is-symbol" + ] + }, + "which-typed-array@1.1.15": { + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dependencies": [ + "available-typed-arrays", + "call-bind", + "for-each", + "gopd", + "has-tostringtag" + ] + }, + "which@2.0.2": { + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": [ + "isexe@2.0.0" + ] + }, + "which@4.0.0": { + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dependencies": [ + "isexe@3.1.1" + ] + }, + "windmill-client@1.364.0": { + "integrity": "sha512-UjCbBB2IeyMVoDyO16boHEsTus2npVtXyER2pfP0zm7rmp6ko65HA3OXvMzhMVq3hW9RQ5esc3mrkTnb19PIaw==" + }, + "windmill-parser-wasm-csharp@1.437.1": { + "integrity": "sha512-qzB/kUE9JCf1CYFDz+50AI+SUVaZYn3lSgqmJ11Iuibl41AC4EIvfH4zrsOU53lcTOb9b4ZH3D6z9FjCCfqWsw==" + }, + "windmill-parser-wasm-go@1.429.0": { + "integrity": "sha512-M3jeGDqeTyPj9HyyX3msdzMrqIIzlfMfxTMsXS8m7MJp4Cm60qifMxD29Ipxb2B4WdzyGwCSlaBjLsXu0b3c5g==" + }, + "windmill-parser-wasm-php@1.429.0": { + "integrity": "sha512-SGJAtNpfdRZftkGboxWsm/yQDnJBJodwPQUbX2cWk/aoNook6ULesZwsYtBC9WN1VH6TIskLiVPohMmu6jtXmw==" + }, + "windmill-parser-wasm-py@1.477.1": { + "integrity": "sha512-EY3mSMWpqFPzd7fsLg2/hSfQFU8HpW9nplFwm4JHHCDbcTpBzlvzjPJoHAAGO5kMzowAxjqi5ai/mXjeUWuiSg==" + }, + "windmill-parser-wasm-regex@1.439.0": { + "integrity": "sha512-v7vcEOWurGbqvoTdtQ8wauyUYeuQExRCmr7phPtwUwD+1cNbqDrS11kM8p2Na3DXjL9RqGAGjd6uRjNSccZjjQ==" + }, + "windmill-parser-wasm-rust@1.429.0": { + "integrity": "sha512-c8mjpiw8RxoaBDtecb+sKeWM/IOjNr4Y06nHudGu8sMM48MNO1LhgcISLv8wl6Z9zWd7OzQrECJ6RLorpii5Uw==" + }, + "windmill-parser-wasm-ts@1.438.2": { + "integrity": "sha512-PC1KzhJ47Y3fa4XV3uHtUQN52N2WZCA6OxVopwZIZtwaiRS+MY4zhPFUKqe3rc7uoSkzk3qhkIJoggygBBXLCw==" + }, + "windmill-parser-wasm-yaml@1.429.0": { + "integrity": "sha512-elQYkaWOvzB8LiwVV9NbNqrupSmdtRY3mMEl+qmKTJhGLvYrVOxA8zyBtwGK0MGiFhTOO3ZO96LWSlDfBnpN9g==" + }, + "word-wrap@1.2.5": { + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==" + }, + "wrappy@1.0.2": { + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "ws@8.18.0": { + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==" + }, + "yocto-queue@0.1.0": { + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" } }, - "remote": {}, "workspace": { "dependencies": [ - "jsr:@deno/dnt@^0.41.3", + "jsr:@deno/dnt@~0.41.3", "jsr:@std/encoding@^1.0.4", "jsr:@std/fs@^1.0.3", - "jsr:@std/io@^0.224.7", - "jsr:@std/log@^0.224.7", + "jsr:@std/io@~0.224.7", + "jsr:@std/log@~0.224.7", "jsr:@std/net@^1.0.2", "jsr:@std/path@^1.0.4", "jsr:@std/streams@^1.0.4", diff --git a/cli/deps.ts b/cli/deps.ts index 53e96a4f9c..23fd292365 100644 --- a/cli/deps.ts +++ b/cli/deps.ts @@ -15,7 +15,7 @@ export { CompletionsCommand } from "jsr:@windmill-labs/cliffy-command@1.0.0-rc.5 export { ensureDir } from "jsr:@std/fs"; export { SEPARATOR as SEP } from "jsr:@std/path"; export * as path from "jsr:@std/path"; -export { encodeHex } from "jsr:@std/encoding"; +export { encodeHex } from "jsr:@std/encoding@1.0.4"; export { writeAllSync } from "jsr:@std/io/write-all"; export { copy } from "jsr:@std/io/copy"; export { readAll } from "jsr:@std/io/read-all"; diff --git a/cli/dev.ts b/cli/dev.ts index 2da25ec4ac..7cc92c45ff 100644 --- a/cli/dev.ts +++ b/cli/dev.ts @@ -8,8 +8,9 @@ import { log, open, WebSocket, + yamlParseFile, } from "./deps.ts"; -import { GlobalOptions } from "./types.ts"; +import { getTypeStrFromPath, GlobalOptions } from "./types.ts"; import { ignoreF } from "./sync.ts"; import { requireLogin, resolveWorkspace } from "./context.ts"; import { @@ -17,8 +18,11 @@ import { mergeConfigWithConfigFile, readConfigFile, } from "./conf.ts"; -import { exts } from "./script.ts"; +import { exts, findGlobalDeps, removeExtensionToPath } from "./script.ts"; import { inferContentTypeFromFilePath } from "./script_common.ts"; +import { OpenFlow } from "./gen/types.gen.ts"; +import { FlowFile, replaceInlineScripts } from "./flow.ts"; +import { parseMetadataFile } from "./metadata.ts"; const PORT = 3001; async function dev(opts: GlobalOptions & SyncOptions) { @@ -27,54 +31,105 @@ async function dev(opts: GlobalOptions & SyncOptions) { log.info("Started dev mode"); const conf = await readConfigFile(); - let currentLastEdit: LastEdit | undefined = undefined; + let currentLastEdit: LastEditScript | LastEditFlow | undefined = undefined; const watcher = Deno.watchFs("."); const base = await Deno.realPath("."); opts = await mergeConfigWithConfigFile(opts); const ignore = await ignoreF(opts); + const changesTimeouts: Record = {}; async function watchChanges() { for await (const event of watcher) { - log.debug(">>>> event", event); - // Example event: { kind: "create", paths: [ "/home/alice/deno/foo.txt" ] } - await loadPaths(event.paths); + // console.log(">>>> event", event); + const key = event.paths.join(","); + if (changesTimeouts[key]) { + clearTimeout(changesTimeouts[key]); + } + // @ts-ignore + changesTimeouts[key] = setTimeout(async () => { + delete changesTimeouts[key]; + await loadPaths(event.paths); + }, 100); } } + const DOT_FLOW_SEP = ".flow" + SEP; async function loadPaths(pathsToLoad: string[]) { const paths = pathsToLoad.filter((path) => - exts.some((ext) => path.endsWith(ext)) + exts.some( + (ext) => path.endsWith(ext) || path.endsWith(DOT_FLOW_SEP + "flow.yaml") + ) ); if (paths.length == 0) { return; } const cpath = (await Deno.realPath(paths[0])).replace(base + SEP, ""); - console.log("Detected change in " + cpath); if (!ignore(cpath, false)) { - const content = await Deno.readTextFile(cpath); - const splitted = cpath.split("."); - const wmPath = splitted[0]; - const lang = inferContentTypeFromFilePath(cpath, conf.defaultTs); - currentLastEdit = { - content, - path: wmPath, - language: lang, - }; - broadcastChanges(currentLastEdit); - log.info("Updated " + wmPath); + const typ = getTypeStrFromPath(cpath); + log.info("Detected change in " + cpath + " (" + typ + ")"); + if (typ == "flow") { + const localPath = cpath.split(DOT_FLOW_SEP)[0] + DOT_FLOW_SEP; + const localFlow = (await yamlParseFile( + localPath + "flow.yaml" + )) as FlowFile; + replaceInlineScripts(localFlow.value.modules, localPath, undefined); + currentLastEdit = { + type: "flow", + flow: localFlow, + uriPath: localPath, + }; + log.info("Updated " + localPath); + broadcastChanges(currentLastEdit); + } else if (typ == "script") { + const content = await Deno.readTextFile(cpath); + const splitted = cpath.split("."); + const wmPath = splitted[0]; + const lang = inferContentTypeFromFilePath(cpath, conf.defaultTs); + const globalDeps = await findGlobalDeps(); + const typed = + (await parseMetadataFile( + removeExtensionToPath(cpath), + undefined, + globalDeps, + [] + ) + )?.payload + + + currentLastEdit = { + type: "script", + content, + path: wmPath, + language: lang, + tag: typed?.tag, + lock: typed?.lock, + }; + log.info("Updated " + wmPath); + broadcastChanges(currentLastEdit); + } } } - type LastEdit = { + type LastEditScript = { + type: "script"; content: string; path: string; language: string; + tag?: string; + lock?: string; + + }; + + type LastEditFlow = { + type: "flow"; + flow: OpenFlow; + uriPath: string; }; const connectedClients: Set = new Set(); // Function to send a message to all connected clients - function broadcastChanges(lastEdit: LastEdit) { + function broadcastChanges(lastEdit: LastEditScript | LastEditFlow) { for (const client of connectedClients.values()) { client.send(JSON.stringify(lastEdit)); } @@ -119,7 +174,7 @@ async function dev(opts: GlobalOptions & SyncOptions) { // Start the server const port = await getPort.default({ port: 3001 }); const url = - `${workspace.remote}scripts/dev?workspace=${workspace.workspaceId}&local=true` + + `${workspace.remote}dev?workspace=${workspace.workspaceId}&local=true&wm_token=${workspace.token}` + (port === PORT ? "" : `&port=${port}`); console.log(`Go to ${url}`); diff --git a/cli/dnt.ts b/cli/dnt.ts index d711434b88..3820439a7e 100644 --- a/cli/dnt.ts +++ b/cli/dnt.ts @@ -13,9 +13,18 @@ await build({ }, ], outDir: "./npm", - shims: { + shims: { // see JS docs for overview and more options deno: true, + // shims to only use in the tests + customDev: [{ + // this is what `timers: "dev"` does internally + package: { + name: "@deno/shim-timers", + version: "~0.1.0", + }, + globalNames: ["setTimeout", "setInterval"], + }], }, scriptModule: false, filterDiagnostic(diagnostic) { @@ -50,12 +59,26 @@ await build({ postBuild() { // steps to run after building and before running the tests // add shebang to npm/esm/main.js + const dirs = [ + "nu", + "ts", + "regex", + "python", + "go", + "php", + "rust", + "yaml", + "csharp", + "java", + ]; + for (const l of dirs) { + Deno.copyFileSync( + "wasm/" + l + "/windmill_parser_wasm_bg.wasm", + "npm/esm/wasm/" + l + "/windmill_parser_wasm_bg.wasm" + ); + } Deno.copyFileSync("../LICENSE", "npm/LICENSE"); Deno.copyFileSync("README.md", "npm/README.md"); - Deno.copyFileSync( - "wasm/windmill_parser_wasm_bg.wasm", - "npm/esm/wasm/windmill_parser_wasm_bg.wasm" - ); }, }); diff --git a/cli/flow.ts b/cli/flow.ts index edca024f66..121e81f72a 100644 --- a/cli/flow.ts +++ b/cli/flow.ts @@ -245,7 +245,7 @@ async function generateLocks( const ignore = await ignoreF(opts); const elems = Object.keys( await elementsToMap( - await FSFSElement(Deno.cwd(), []), + await FSFSElement(Deno.cwd(), [], true), (p, isD) => { return ( ignore(p, isD) || diff --git a/cli/gen/core/ApiError.ts b/cli/gen/core/ApiError.ts deleted file mode 100644 index 81aa78a668..0000000000 --- a/cli/gen/core/ApiError.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { ApiRequestOptions } from './ApiRequestOptions.ts'; -import type { ApiResult } from './ApiResult.ts'; - -export class ApiError extends Error { - public readonly url: string; - public readonly status: number; - public readonly statusText: string; - public readonly body: unknown; - public readonly request: ApiRequestOptions; - - constructor(request: ApiRequestOptions, response: ApiResult, message: string) { - super(message); - - this.name = 'ApiError'; - this.url = response.url; - this.status = response.status; - this.statusText = response.statusText; - this.body = response.body; - this.request = request; - } -} \ No newline at end of file diff --git a/cli/gen/core/ApiRequestOptions.ts b/cli/gen/core/ApiRequestOptions.ts deleted file mode 100644 index 939a0aa4c8..0000000000 --- a/cli/gen/core/ApiRequestOptions.ts +++ /dev/null @@ -1,21 +0,0 @@ -export type ApiRequestOptions = { - readonly body?: any; - readonly cookies?: Record; - readonly errors?: Record; - readonly formData?: Record | any[] | Blob | File; - readonly headers?: Record; - readonly mediaType?: string; - readonly method: - | 'DELETE' - | 'GET' - | 'HEAD' - | 'OPTIONS' - | 'PATCH' - | 'POST' - | 'PUT'; - readonly path?: Record; - readonly query?: Record; - readonly responseHeader?: string; - readonly responseTransformer?: (data: unknown) => Promise; - readonly url: string; -}; \ No newline at end of file diff --git a/cli/gen/core/ApiResult.ts b/cli/gen/core/ApiResult.ts deleted file mode 100644 index 4c58e39138..0000000000 --- a/cli/gen/core/ApiResult.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type ApiResult = { - readonly body: TData; - readonly ok: boolean; - readonly status: number; - readonly statusText: string; - readonly url: string; -}; \ No newline at end of file diff --git a/cli/gen/core/CancelablePromise.ts b/cli/gen/core/CancelablePromise.ts deleted file mode 100644 index ccc082e8f2..0000000000 --- a/cli/gen/core/CancelablePromise.ts +++ /dev/null @@ -1,126 +0,0 @@ -export class CancelError extends Error { - constructor(message: string) { - super(message); - this.name = 'CancelError'; - } - - public get isCancelled(): boolean { - return true; - } -} - -export interface OnCancel { - readonly isResolved: boolean; - readonly isRejected: boolean; - readonly isCancelled: boolean; - - (cancelHandler: () => void): void; -} - -export class CancelablePromise implements Promise { - private _isResolved: boolean; - private _isRejected: boolean; - private _isCancelled: boolean; - readonly cancelHandlers: (() => void)[]; - readonly promise: Promise; - private _resolve?: (value: T | PromiseLike) => void; - private _reject?: (reason?: unknown) => void; - - constructor( - executor: ( - resolve: (value: T | PromiseLike) => void, - reject: (reason?: unknown) => void, - onCancel: OnCancel - ) => void - ) { - this._isResolved = false; - this._isRejected = false; - this._isCancelled = false; - this.cancelHandlers = []; - this.promise = new Promise((resolve, reject) => { - this._resolve = resolve; - this._reject = reject; - - const onResolve = (value: T | PromiseLike): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { - return; - } - this._isResolved = true; - if (this._resolve) this._resolve(value); - }; - - const onReject = (reason?: unknown): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { - return; - } - this._isRejected = true; - if (this._reject) this._reject(reason); - }; - - const onCancel = (cancelHandler: () => void): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { - return; - } - this.cancelHandlers.push(cancelHandler); - }; - - Object.defineProperty(onCancel, 'isResolved', { - get: (): boolean => this._isResolved, - }); - - Object.defineProperty(onCancel, 'isRejected', { - get: (): boolean => this._isRejected, - }); - - Object.defineProperty(onCancel, 'isCancelled', { - get: (): boolean => this._isCancelled, - }); - - return executor(onResolve, onReject, onCancel as OnCancel); - }); - } - - get [Symbol.toStringTag]() { - return "Cancellable Promise"; - } - - public then( - onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null, - onRejected?: ((reason: unknown) => TResult2 | PromiseLike) | null - ): Promise { - return this.promise.then(onFulfilled, onRejected); - } - - public catch( - onRejected?: ((reason: unknown) => TResult | PromiseLike) | null - ): Promise { - return this.promise.catch(onRejected); - } - - public finally(onFinally?: (() => void) | null): Promise { - return this.promise.finally(onFinally); - } - - public cancel(): void { - if (this._isResolved || this._isRejected || this._isCancelled) { - return; - } - this._isCancelled = true; - if (this.cancelHandlers.length) { - try { - for (const cancelHandler of this.cancelHandlers) { - cancelHandler(); - } - } catch (error) { - console.warn('Cancellation threw an error', error); - return; - } - } - this.cancelHandlers.length = 0; - if (this._reject) this._reject(new CancelError('Request aborted')); - } - - public get isCancelled(): boolean { - return this._isCancelled; - } -} \ No newline at end of file diff --git a/cli/gen/core/OpenAPI.ts b/cli/gen/core/OpenAPI.ts deleted file mode 100644 index 38bbd34aac..0000000000 --- a/cli/gen/core/OpenAPI.ts +++ /dev/null @@ -1,63 +0,0 @@ -const getEnv = (key: string) => { - return Deno.env.get(key) -}; - -const baseUrl = getEnv("BASE_INTERNAL_URL") ?? getEnv("BASE_URL") ?? "http://localhost:8000"; -const baseUrlApi = (baseUrl ?? '') + "/api"; - -import type { ApiRequestOptions } from './ApiRequestOptions.ts'; - -type Headers = Record; -type Middleware = (value: T) => T | Promise; -type Resolver = (options: ApiRequestOptions) => Promise; - -export class Interceptors { - _fns: Middleware[]; - - constructor() { - this._fns = []; - } - - eject(fn: Middleware): void { - const index = this._fns.indexOf(fn); - if (index !== -1) { - this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)]; - } - } - - use(fn: Middleware): void { - this._fns = [...this._fns, fn]; - } -} - -export type OpenAPIConfig = { - BASE: string; - CREDENTIALS: 'include' | 'omit' | 'same-origin'; - ENCODE_PATH?: ((path: string) => string) | undefined; - HEADERS?: Headers | Resolver | undefined; - PASSWORD?: string | Resolver | undefined; - TOKEN?: string | Resolver | undefined; - USERNAME?: string | Resolver | undefined; - VERSION: string; - WITH_CREDENTIALS: boolean; - interceptors: { - request: Interceptors; - response: Interceptors; - }; -}; - -export const OpenAPI: OpenAPIConfig = { - BASE: baseUrlApi, - CREDENTIALS: 'include', - ENCODE_PATH: undefined, - HEADERS: undefined, - PASSWORD: undefined, - TOKEN: getEnv("WM_TOKEN"), - USERNAME: undefined, - VERSION: '1.454.1', - WITH_CREDENTIALS: true, - interceptors: { - request: new Interceptors(), - response: new Interceptors(), - }, -}; \ No newline at end of file diff --git a/cli/gen/core/request.ts b/cli/gen/core/request.ts deleted file mode 100644 index ed11eb4482..0000000000 --- a/cli/gen/core/request.ts +++ /dev/null @@ -1,350 +0,0 @@ -import { ApiError } from './ApiError.ts'; -import type { ApiRequestOptions } from './ApiRequestOptions.ts'; -import type { ApiResult } from './ApiResult.ts'; -import { CancelablePromise } from './CancelablePromise.ts'; -import type { OnCancel } from './CancelablePromise.ts'; -import type { OpenAPIConfig } from './OpenAPI.ts'; - -export const isString = (value: unknown): value is string => { - return typeof value === 'string'; -}; - -export const isStringWithValue = (value: unknown): value is string => { - return isString(value) && value !== ''; -}; - -export const isBlob = (value: any): value is Blob => { - return value instanceof Blob; -}; - -export const isFormData = (value: unknown): value is FormData => { - return value instanceof FormData; -}; - -export const base64 = (str: string): string => { - try { - return btoa(str); - } catch (err) { - // @ts-ignore - return Buffer.from(str).toString('base64'); - } -}; - -export const getQueryString = (params: Record): string => { - const qs: string[] = []; - - const append = (key: string, value: unknown) => { - qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); - }; - - const encodePair = (key: string, value: unknown) => { - if (value === undefined || value === null) { - return; - } - - if (value instanceof Date) { - append(key, value.toISOString()); - } else if (Array.isArray(value)) { - value.forEach(v => encodePair(key, v)); - } else if (typeof value === 'object') { - Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v)); - } else { - append(key, value); - } - }; - - Object.entries(params).forEach(([key, value]) => encodePair(key, value)); - - return qs.length ? `?${qs.join('&')}` : ''; -}; - -const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { - const encoder = config.ENCODE_PATH || encodeURI; - - const path = options.url - .replace('{api-version}', config.VERSION) - .replace(/{(.*?)}/g, (substring: string, group: string) => { - if (options.path?.hasOwnProperty(group)) { - return encoder(String(options.path[group])); - } - return substring; - }); - - const url = config.BASE + path; - return options.query ? url + getQueryString(options.query) : url; -}; - -export const getFormData = (options: ApiRequestOptions): FormData | undefined => { - if (options.formData) { - const formData = new FormData(); - - const process = (key: string, value: unknown) => { - if (isString(value) || isBlob(value)) { - formData.append(key, value); - } else { - formData.append(key, JSON.stringify(value)); - } - }; - - Object.entries(options.formData) - .filter(([, value]) => value !== undefined && value !== null) - .forEach(([key, value]) => { - if (Array.isArray(value)) { - value.forEach(v => process(key, v)); - } else { - process(key, value); - } - }); - - return formData; - } - return undefined; -}; - -type Resolver = (options: ApiRequestOptions) => Promise; - -export const resolve = async (options: ApiRequestOptions, resolver?: T | Resolver): Promise => { - if (typeof resolver === 'function') { - return (resolver as Resolver)(options); - } - return resolver; -}; - -export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise => { - const [token, username, password, additionalHeaders] = await Promise.all([ - // @ts-ignore - resolve(options, config.TOKEN), - // @ts-ignore - resolve(options, config.USERNAME), - // @ts-ignore - resolve(options, config.PASSWORD), - // @ts-ignore - resolve(options, config.HEADERS), - ]); - - const headers = Object.entries({ - Accept: 'application/json', - ...additionalHeaders, - ...options.headers, - }) - .filter(([, value]) => value !== undefined && value !== null) - .reduce((headers, [key, value]) => ({ - ...headers, - [key]: String(value), - }), {} as Record); - - if (isStringWithValue(token)) { - headers['Authorization'] = `Bearer ${token}`; - } - - if (isStringWithValue(username) && isStringWithValue(password)) { - const credentials = base64(`${username}:${password}`); - headers['Authorization'] = `Basic ${credentials}`; - } - - if (options.body !== undefined) { - if (options.mediaType) { - headers['Content-Type'] = options.mediaType; - } else if (isBlob(options.body)) { - headers['Content-Type'] = options.body.type || 'application/octet-stream'; - } else if (isString(options.body)) { - headers['Content-Type'] = 'text/plain'; - } else if (!isFormData(options.body)) { - headers['Content-Type'] = 'application/json'; - } - } - - return new Headers(headers); -}; - -export const getRequestBody = (options: ApiRequestOptions): unknown => { - if (options.body !== undefined) { - if (options.mediaType?.includes('application/json') || options.mediaType?.includes('+json')) { - return JSON.stringify(options.body); - } else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) { - return options.body; - } else { - return JSON.stringify(options.body); - } - } - return undefined; -}; - -export const sendRequest = async ( - config: OpenAPIConfig, - options: ApiRequestOptions, - url: string, - body: any, - formData: FormData | undefined, - headers: Headers, - onCancel: OnCancel -): Promise => { - const controller = new AbortController(); - - let request: RequestInit = { - headers, - body: body ?? formData, - method: options.method, - signal: controller.signal, - }; - - if (config.WITH_CREDENTIALS) { - request.credentials = config.CREDENTIALS; - } - - for (const fn of config.interceptors.request._fns) { - request = await fn(request); - } - - onCancel(() => controller.abort()); - - return await fetch(url, request); -}; - -export const getResponseHeader = (response: Response, responseHeader?: string): string | undefined => { - if (responseHeader) { - const content = response.headers.get(responseHeader); - if (isString(content)) { - return content; - } - } - return undefined; -}; - -export const getResponseBody = async (response: Response): Promise => { - if (response.status !== 204) { - try { - const contentType = response.headers.get('Content-Type'); - if (contentType) { - const binaryTypes = ['application/octet-stream', 'application/pdf', 'application/zip', 'audio/', 'image/', 'video/']; - if (contentType.includes('application/json') || contentType.includes('+json')) { - return await response.json(); - } else if (binaryTypes.some(type => contentType.includes(type))) { - return await response.blob(); - } else if (contentType.includes('multipart/form-data')) { - return await response.formData(); - } else if (contentType.includes('text/')) { - return await response.text(); - } - } - } catch (error) { - console.error(error); - } - } - return undefined; -}; - -export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { - const errors: Record = { - 400: 'Bad Request', - 401: 'Unauthorized', - 402: 'Payment Required', - 403: 'Forbidden', - 404: 'Not Found', - 405: 'Method Not Allowed', - 406: 'Not Acceptable', - 407: 'Proxy Authentication Required', - 408: 'Request Timeout', - 409: 'Conflict', - 410: 'Gone', - 411: 'Length Required', - 412: 'Precondition Failed', - 413: 'Payload Too Large', - 414: 'URI Too Long', - 415: 'Unsupported Media Type', - 416: 'Range Not Satisfiable', - 417: 'Expectation Failed', - 418: 'Im a teapot', - 421: 'Misdirected Request', - 422: 'Unprocessable Content', - 423: 'Locked', - 424: 'Failed Dependency', - 425: 'Too Early', - 426: 'Upgrade Required', - 428: 'Precondition Required', - 429: 'Too Many Requests', - 431: 'Request Header Fields Too Large', - 451: 'Unavailable For Legal Reasons', - 500: 'Internal Server Error', - 501: 'Not Implemented', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - 504: 'Gateway Timeout', - 505: 'HTTP Version Not Supported', - 506: 'Variant Also Negotiates', - 507: 'Insufficient Storage', - 508: 'Loop Detected', - 510: 'Not Extended', - 511: 'Network Authentication Required', - ...options.errors, - } - - const error = errors[result.status]; - if (error) { - throw new ApiError(options, result, error); - } - - if (!result.ok) { - const errorStatus = result.status ?? 'unknown'; - const errorStatusText = result.statusText ?? 'unknown'; - const errorBody = (() => { - try { - return JSON.stringify(result.body, null, 2); - } catch (e) { - return undefined; - } - })(); - - throw new ApiError(options, result, - `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}` - ); - } -}; - -/** - * Request method - * @param config The OpenAPI configuration object - * @param options The request options from the service - * @returns CancelablePromise - * @throws ApiError - */ -export const request = (config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise => { - return new CancelablePromise(async (resolve, reject, onCancel) => { - try { - const url = getUrl(config, options); - const formData = getFormData(options); - const body = getRequestBody(options); - const headers = await getHeaders(config, options); - - if (!onCancel.isCancelled) { - let response = await sendRequest(config, options, url, body, formData, headers, onCancel); - - for (const fn of config.interceptors.response._fns) { - response = await fn(response); - } - - const responseBody = await getResponseBody(response); - const responseHeader = getResponseHeader(response, options.responseHeader); - - let transformedBody = responseBody; - if (options.responseTransformer && response.ok) { - transformedBody = await options.responseTransformer(responseBody) - } - - const result: ApiResult = { - url, - ok: response.ok, - status: response.status, - statusText: response.statusText, - body: responseHeader ?? transformedBody, - }; - - catchErrorCodes(options, result); - - resolve(result.body); - } - } catch (error) { - reject(error); - } - }); -}; \ No newline at end of file diff --git a/cli/gen/index.ts b/cli/gen/index.ts deleted file mode 100644 index 77b08aeebb..0000000000 --- a/cli/gen/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts -export { ApiError } from './core/ApiError.ts'; -export { CancelablePromise, CancelError } from './core/CancelablePromise.ts'; -export { OpenAPI, type OpenAPIConfig } from './core/OpenAPI.ts'; -export * from './services.gen.ts'; -export * from './types.gen.ts'; \ No newline at end of file diff --git a/cli/gen/services.gen.ts b/cli/gen/services.gen.ts deleted file mode 100644 index 66481a2857..0000000000 --- a/cli/gen/services.gen.ts +++ /dev/null @@ -1,8067 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import type { CancelablePromise } from './core/CancelablePromise.ts'; -import { OpenAPI } from './core/OpenAPI.ts'; -import { request as __request } from './core/request.ts'; -import type { BackendVersionResponse, BackendUptodateResponse, GetLicenseIdResponse, GetOpenApiYamlResponse, GetAuditLogData, GetAuditLogResponse, ListAuditLogsData, ListAuditLogsResponse, LoginData, LoginResponse, LogoutResponse, GetUserData, GetUserResponse, UpdateUserData, UpdateUserResponse, IsOwnerOfPathData, IsOwnerOfPathResponse, SetPasswordData, SetPasswordResponse, SetPasswordForUserData, SetPasswordForUserResponse, SetLoginTypeForUserData, SetLoginTypeForUserResponse, CreateUserGloballyData, CreateUserGloballyResponse, GlobalUserUpdateData, GlobalUserUpdateResponse, GlobalUsernameInfoData, GlobalUsernameInfoResponse, GlobalUserRenameData, GlobalUserRenameResponse, GlobalUserDeleteData, GlobalUserDeleteResponse, GlobalUsersOverwriteData, GlobalUsersOverwriteResponse, GlobalUsersExportResponse, DeleteUserData, DeleteUserResponse, ListWorkspacesResponse, IsDomainAllowedResponse, ListUserWorkspacesResponse, ListWorkspacesAsSuperAdminData, ListWorkspacesAsSuperAdminResponse, CreateWorkspaceData, CreateWorkspaceResponse, ExistsWorkspaceData, ExistsWorkspaceResponse, ExistsUsernameData, ExistsUsernameResponse, GetGlobalData, GetGlobalResponse, SetGlobalData, SetGlobalResponse, GetLocalResponse, TestSmtpData, TestSmtpResponse, TestCriticalChannelsData, TestCriticalChannelsResponse, GetCriticalAlertsData, GetCriticalAlertsResponse, AcknowledgeCriticalAlertData, AcknowledgeCriticalAlertResponse, AcknowledgeAllCriticalAlertsResponse, TestLicenseKeyData, TestLicenseKeyResponse, TestObjectStorageConfigData, TestObjectStorageConfigResponse, SendStatsResponse, GetLatestKeyRenewalAttemptResponse, RenewLicenseKeyData, RenewLicenseKeyResponse, CreateCustomerPortalSessionData, CreateCustomerPortalSessionResponse, TestMetadataData, TestMetadataResponse, ListGlobalSettingsResponse, GetCurrentEmailResponse, RefreshUserTokenData, RefreshUserTokenResponse, GetTutorialProgressResponse, UpdateTutorialProgressData, UpdateTutorialProgressResponse, LeaveInstanceResponse, GetUsageResponse, GetRunnableResponse, GlobalWhoamiResponse, ListWorkspaceInvitesResponse, WhoamiData, WhoamiResponse, AcceptInviteData, AcceptInviteResponse, DeclineInviteData, DeclineInviteResponse, InviteUserData, InviteUserResponse, AddUserData, AddUserResponse, DeleteInviteData, DeleteInviteResponse, ArchiveWorkspaceData, ArchiveWorkspaceResponse, UnarchiveWorkspaceData, UnarchiveWorkspaceResponse, DeleteWorkspaceData, DeleteWorkspaceResponse, LeaveWorkspaceData, LeaveWorkspaceResponse, GetWorkspaceNameData, GetWorkspaceNameResponse, ChangeWorkspaceNameData, ChangeWorkspaceNameResponse, ChangeWorkspaceIdData, ChangeWorkspaceIdResponse, ChangeWorkspaceColorData, ChangeWorkspaceColorResponse, WhoisData, WhoisResponse, UpdateOperatorSettingsData, UpdateOperatorSettingsResponse, ExistsEmailData, ExistsEmailResponse, ListUsersAsSuperAdminData, ListUsersAsSuperAdminResponse, ListPendingInvitesData, ListPendingInvitesResponse, GetSettingsData, GetSettingsResponse, GetDeployToData, GetDeployToResponse, GetIsPremiumData, GetIsPremiumResponse, GetPremiumInfoData, GetPremiumInfoResponse, SetAutomaticBillingData, SetAutomaticBillingResponse, GetThresholdAlertData, GetThresholdAlertResponse, SetThresholdAlertData, SetThresholdAlertResponse, EditSlackCommandData, EditSlackCommandResponse, RunSlackMessageTestJobData, RunSlackMessageTestJobResponse, EditDeployToData, EditDeployToResponse, EditAutoInviteData, EditAutoInviteResponse, EditWebhookData, EditWebhookResponse, EditCopilotConfigData, EditCopilotConfigResponse, GetCopilotInfoData, GetCopilotInfoResponse, EditErrorHandlerData, EditErrorHandlerResponse, EditLargeFileStorageConfigData, EditLargeFileStorageConfigResponse, EditWorkspaceGitSyncConfigData, EditWorkspaceGitSyncConfigResponse, EditWorkspaceDeployUiSettingsData, EditWorkspaceDeployUiSettingsResponse, EditWorkspaceDefaultAppData, EditWorkspaceDefaultAppResponse, EditDefaultScriptsData, EditDefaultScriptsResponse, GetDefaultScriptsData, GetDefaultScriptsResponse, SetEnvironmentVariableData, SetEnvironmentVariableResponse, GetWorkspaceEncryptionKeyData, GetWorkspaceEncryptionKeyResponse, SetWorkspaceEncryptionKeyData, SetWorkspaceEncryptionKeyResponse, GetWorkspaceDefaultAppData, GetWorkspaceDefaultAppResponse, GetLargeFileStorageConfigData, GetLargeFileStorageConfigResponse, GetWorkspaceUsageData, GetWorkspaceUsageResponse, GetUsedTriggersData, GetUsedTriggersResponse, ListUsersData, ListUsersResponse, ListUsersUsageData, ListUsersUsageResponse, ListUsernamesData, ListUsernamesResponse, UsernameToEmailData, UsernameToEmailResponse, CreateTokenData, CreateTokenResponse, CreateTokenImpersonateData, CreateTokenImpersonateResponse, DeleteTokenData, DeleteTokenResponse, ListTokensData, ListTokensResponse, GetOidcTokenData, GetOidcTokenResponse, CreateVariableData, CreateVariableResponse, EncryptValueData, EncryptValueResponse, DeleteVariableData, DeleteVariableResponse, UpdateVariableData, UpdateVariableResponse, GetVariableData, GetVariableResponse, GetVariableValueData, GetVariableValueResponse, ExistsVariableData, ExistsVariableResponse, ListVariableData, ListVariableResponse, ListContextualVariablesData, ListContextualVariablesResponse, WorkspaceGetCriticalAlertsData, WorkspaceGetCriticalAlertsResponse, WorkspaceAcknowledgeCriticalAlertData, WorkspaceAcknowledgeCriticalAlertResponse, WorkspaceAcknowledgeAllCriticalAlertsData, WorkspaceAcknowledgeAllCriticalAlertsResponse, WorkspaceMuteCriticalAlertsUiData, WorkspaceMuteCriticalAlertsUiResponse, LoginWithOauthData, LoginWithOauthResponse, ConnectSlackCallbackData, ConnectSlackCallbackResponse, ConnectSlackCallbackInstanceData, ConnectSlackCallbackInstanceResponse, ConnectCallbackData, ConnectCallbackResponse, CreateAccountData, CreateAccountResponse, RefreshTokenData, RefreshTokenResponse, DisconnectAccountData, DisconnectAccountResponse, DisconnectSlackData, DisconnectSlackResponse, ListOauthLoginsResponse, ListOauthConnectsResponse, GetOauthConnectData, GetOauthConnectResponse, SyncTeamsResponse, CreateResourceData, CreateResourceResponse, DeleteResourceData, DeleteResourceResponse, UpdateResourceData, UpdateResourceResponse, UpdateResourceValueData, UpdateResourceValueResponse, GetResourceData, GetResourceResponse, GetResourceValueInterpolatedData, GetResourceValueInterpolatedResponse, GetResourceValueData, GetResourceValueResponse, ExistsResourceData, ExistsResourceResponse, ListResourceData, ListResourceResponse, ListSearchResourceData, ListSearchResourceResponse, ListResourceNamesData, ListResourceNamesResponse, CreateResourceTypeData, CreateResourceTypeResponse, FileResourceTypeToFileExtMapData, FileResourceTypeToFileExtMapResponse, DeleteResourceTypeData, DeleteResourceTypeResponse, UpdateResourceTypeData, UpdateResourceTypeResponse, GetResourceTypeData, GetResourceTypeResponse, ExistsResourceTypeData, ExistsResourceTypeResponse, ListResourceTypeData, ListResourceTypeResponse, ListResourceTypeNamesData, ListResourceTypeNamesResponse, QueryResourceTypesData, QueryResourceTypesResponse, ListHubIntegrationsData, ListHubIntegrationsResponse, ListHubFlowsResponse, GetHubFlowByIdData, GetHubFlowByIdResponse, ListHubAppsResponse, GetHubAppByIdData, GetHubAppByIdResponse, GetPublicAppByCustomPathData, GetPublicAppByCustomPathResponse, GetHubScriptContentByPathData, GetHubScriptContentByPathResponse, GetHubScriptByPathData, GetHubScriptByPathResponse, GetTopHubScriptsData, GetTopHubScriptsResponse, QueryHubScriptsData, QueryHubScriptsResponse, ListSearchScriptData, ListSearchScriptResponse, ListScriptsData, ListScriptsResponse, ListScriptPathsData, ListScriptPathsResponse, CreateDraftData, CreateDraftResponse, DeleteDraftData, DeleteDraftResponse, CreateScriptData, CreateScriptResponse, ToggleWorkspaceErrorHandlerForScriptData, ToggleWorkspaceErrorHandlerForScriptResponse, GetCustomTagsData, GetCustomTagsResponse, GeDefaultTagsResponse, IsDefaultTagsPerWorkspaceResponse, ArchiveScriptByPathData, ArchiveScriptByPathResponse, ArchiveScriptByHashData, ArchiveScriptByHashResponse, DeleteScriptByHashData, DeleteScriptByHashResponse, DeleteScriptByPathData, DeleteScriptByPathResponse, GetScriptByPathData, GetScriptByPathResponse, GetTriggersCountOfScriptData, GetTriggersCountOfScriptResponse, ListTokensOfScriptData, ListTokensOfScriptResponse, GetScriptByPathWithDraftData, GetScriptByPathWithDraftResponse, GetScriptHistoryByPathData, GetScriptHistoryByPathResponse, GetScriptLatestVersionData, GetScriptLatestVersionResponse, UpdateScriptHistoryData, UpdateScriptHistoryResponse, RawScriptByPathData, RawScriptByPathResponse, RawScriptByPathTokenedData, RawScriptByPathTokenedResponse, ExistsScriptByPathData, ExistsScriptByPathResponse, GetScriptByHashData, GetScriptByHashResponse, RawScriptByHashData, RawScriptByHashResponse, GetScriptDeploymentStatusData, GetScriptDeploymentStatusResponse, RunScriptByPathData, RunScriptByPathResponse, OpenaiSyncScriptByPathData, OpenaiSyncScriptByPathResponse, RunWaitResultScriptByPathData, RunWaitResultScriptByPathResponse, RunWaitResultScriptByPathGetData, RunWaitResultScriptByPathGetResponse, OpenaiSyncFlowByPathData, OpenaiSyncFlowByPathResponse, RunWaitResultFlowByPathData, RunWaitResultFlowByPathResponse, ResultByIdData, ResultByIdResponse, ListFlowPathsData, ListFlowPathsResponse, ListSearchFlowData, ListSearchFlowResponse, ListFlowsData, ListFlowsResponse, GetFlowHistoryData, GetFlowHistoryResponse, GetFlowLatestVersionData, GetFlowLatestVersionResponse, GetFlowVersionData, GetFlowVersionResponse, UpdateFlowHistoryData, UpdateFlowHistoryResponse, GetFlowByPathData, GetFlowByPathResponse, GetTriggersCountOfFlowData, GetTriggersCountOfFlowResponse, ListTokensOfFlowData, ListTokensOfFlowResponse, ToggleWorkspaceErrorHandlerForFlowData, ToggleWorkspaceErrorHandlerForFlowResponse, GetFlowByPathWithDraftData, GetFlowByPathWithDraftResponse, ExistsFlowByPathData, ExistsFlowByPathResponse, CreateFlowData, CreateFlowResponse, UpdateFlowData, UpdateFlowResponse, ArchiveFlowByPathData, ArchiveFlowByPathResponse, DeleteFlowByPathData, DeleteFlowByPathResponse, ListRawAppsData, ListRawAppsResponse, ExistsRawAppData, ExistsRawAppResponse, GetRawAppDataData, GetRawAppDataResponse, ListSearchAppData, ListSearchAppResponse, ListAppsData, ListAppsResponse, CreateAppData, CreateAppResponse, ExistsAppData, ExistsAppResponse, GetAppByPathData, GetAppByPathResponse, GetAppLiteByPathData, GetAppLiteByPathResponse, GetAppByPathWithDraftData, GetAppByPathWithDraftResponse, GetAppHistoryByPathData, GetAppHistoryByPathResponse, GetAppLatestVersionData, GetAppLatestVersionResponse, UpdateAppHistoryData, UpdateAppHistoryResponse, GetPublicAppBySecretData, GetPublicAppBySecretResponse, GetPublicResourceData, GetPublicResourceResponse, GetPublicSecretOfAppData, GetPublicSecretOfAppResponse, GetAppByVersionData, GetAppByVersionResponse, CreateRawAppData, CreateRawAppResponse, UpdateRawAppData, UpdateRawAppResponse, DeleteRawAppData, DeleteRawAppResponse, DeleteAppData, DeleteAppResponse, UpdateAppData, UpdateAppResponse, CustomPathExistsData, CustomPathExistsResponse, ExecuteComponentData, ExecuteComponentResponse, RunFlowByPathData, RunFlowByPathResponse, RestartFlowAtStepData, RestartFlowAtStepResponse, RunScriptByHashData, RunScriptByHashResponse, RunScriptPreviewData, RunScriptPreviewResponse, RunCodeWorkflowTaskData, RunCodeWorkflowTaskResponse, RunRawScriptDependenciesData, RunRawScriptDependenciesResponse, RunFlowPreviewData, RunFlowPreviewResponse, ListQueueData, ListQueueResponse, GetQueueCountData, GetQueueCountResponse, GetCompletedCountData, GetCompletedCountResponse, CountCompletedJobsData, CountCompletedJobsResponse, ListFilteredUuidsData, ListFilteredUuidsResponse, CancelSelectionData, CancelSelectionResponse, ListCompletedJobsData, ListCompletedJobsResponse, ListJobsData, ListJobsResponse, GetDbClockResponse, CountJobsByTagData, CountJobsByTagResponse, GetJobData, GetJobResponse, GetRootJobIdData, GetRootJobIdResponse, GetJobLogsData, GetJobLogsResponse, GetJobArgsData, GetJobArgsResponse, GetJobUpdatesData, GetJobUpdatesResponse, GetLogFileFromStoreData, GetLogFileFromStoreResponse, GetFlowDebugInfoData, GetFlowDebugInfoResponse, GetCompletedJobData, GetCompletedJobResponse, GetCompletedJobResultData, GetCompletedJobResultResponse, GetCompletedJobResultMaybeData, GetCompletedJobResultMaybeResponse, DeleteCompletedJobData, DeleteCompletedJobResponse, CancelQueuedJobData, CancelQueuedJobResponse, CancelPersistentQueuedJobsData, CancelPersistentQueuedJobsResponse, ForceCancelQueuedJobData, ForceCancelQueuedJobResponse, CreateJobSignatureData, CreateJobSignatureResponse, GetResumeUrlsData, GetResumeUrlsResponse, GetSlackApprovalPayloadData, GetSlackApprovalPayloadResponse, ResumeSuspendedJobGetData, ResumeSuspendedJobGetResponse, ResumeSuspendedJobPostData, ResumeSuspendedJobPostResponse, SetFlowUserStateData, SetFlowUserStateResponse, GetFlowUserStateData, GetFlowUserStateResponse, ResumeSuspendedFlowAsOwnerData, ResumeSuspendedFlowAsOwnerResponse, CancelSuspendedJobGetData, CancelSuspendedJobGetResponse, CancelSuspendedJobPostData, CancelSuspendedJobPostResponse, GetSuspendedJobFlowData, GetSuspendedJobFlowResponse, PreviewScheduleData, PreviewScheduleResponse, CreateScheduleData, CreateScheduleResponse, UpdateScheduleData, UpdateScheduleResponse, SetScheduleEnabledData, SetScheduleEnabledResponse, DeleteScheduleData, DeleteScheduleResponse, GetScheduleData, GetScheduleResponse, ExistsScheduleData, ExistsScheduleResponse, ListSchedulesData, ListSchedulesResponse, ListSchedulesWithJobsData, ListSchedulesWithJobsResponse, SetDefaultErrorOrRecoveryHandlerData, SetDefaultErrorOrRecoveryHandlerResponse, CreateHttpTriggerData, CreateHttpTriggerResponse, UpdateHttpTriggerData, UpdateHttpTriggerResponse, DeleteHttpTriggerData, DeleteHttpTriggerResponse, GetHttpTriggerData, GetHttpTriggerResponse, ListHttpTriggersData, ListHttpTriggersResponse, ExistsHttpTriggerData, ExistsHttpTriggerResponse, ExistsRouteData, ExistsRouteResponse, CreateWebsocketTriggerData, CreateWebsocketTriggerResponse, UpdateWebsocketTriggerData, UpdateWebsocketTriggerResponse, DeleteWebsocketTriggerData, DeleteWebsocketTriggerResponse, GetWebsocketTriggerData, GetWebsocketTriggerResponse, ListWebsocketTriggersData, ListWebsocketTriggersResponse, ExistsWebsocketTriggerData, ExistsWebsocketTriggerResponse, SetWebsocketTriggerEnabledData, SetWebsocketTriggerEnabledResponse, TestWebsocketConnectionData, TestWebsocketConnectionResponse, CreateKafkaTriggerData, CreateKafkaTriggerResponse, UpdateKafkaTriggerData, UpdateKafkaTriggerResponse, DeleteKafkaTriggerData, DeleteKafkaTriggerResponse, GetKafkaTriggerData, GetKafkaTriggerResponse, ListKafkaTriggersData, ListKafkaTriggersResponse, ExistsKafkaTriggerData, ExistsKafkaTriggerResponse, SetKafkaTriggerEnabledData, SetKafkaTriggerEnabledResponse, TestKafkaConnectionData, TestKafkaConnectionResponse, CreateNatsTriggerData, CreateNatsTriggerResponse, UpdateNatsTriggerData, UpdateNatsTriggerResponse, DeleteNatsTriggerData, DeleteNatsTriggerResponse, GetNatsTriggerData, GetNatsTriggerResponse, ListNatsTriggersData, ListNatsTriggersResponse, ExistsNatsTriggerData, ExistsNatsTriggerResponse, SetNatsTriggerEnabledData, SetNatsTriggerEnabledResponse, TestNatsConnectionData, TestNatsConnectionResponse, IsValidPostgresConfigurationData, IsValidPostgresConfigurationResponse, CreateTemplateScriptData, CreateTemplateScriptResponse, GetTemplateScriptData, GetTemplateScriptResponse, ListPostgresReplicationSlotData, ListPostgresReplicationSlotResponse, CreatePostgresReplicationSlotData, CreatePostgresReplicationSlotResponse, DeletePostgresReplicationSlotData, DeletePostgresReplicationSlotResponse, ListPostgresPublicationData, ListPostgresPublicationResponse, GetPostgresPublicationData, GetPostgresPublicationResponse, CreatePostgresPublicationData, CreatePostgresPublicationResponse, UpdatePostgresPublicationData, UpdatePostgresPublicationResponse, DeletePostgresPublicationData, DeletePostgresPublicationResponse, CreatePostgresTriggerData, CreatePostgresTriggerResponse, UpdatePostgresTriggerData, UpdatePostgresTriggerResponse, DeletePostgresTriggerData, DeletePostgresTriggerResponse, GetPostgresTriggerData, GetPostgresTriggerResponse, ListPostgresTriggersData, ListPostgresTriggersResponse, ExistsPostgresTriggerData, ExistsPostgresTriggerResponse, SetPostgresTriggerEnabledData, SetPostgresTriggerEnabledResponse, ListInstanceGroupsResponse, GetInstanceGroupData, GetInstanceGroupResponse, CreateInstanceGroupData, CreateInstanceGroupResponse, UpdateInstanceGroupData, UpdateInstanceGroupResponse, DeleteInstanceGroupData, DeleteInstanceGroupResponse, AddUserToInstanceGroupData, AddUserToInstanceGroupResponse, RemoveUserFromInstanceGroupData, RemoveUserFromInstanceGroupResponse, ExportInstanceGroupsResponse, OverwriteInstanceGroupsData, OverwriteInstanceGroupsResponse, ListGroupsData, ListGroupsResponse, ListGroupNamesData, ListGroupNamesResponse, CreateGroupData, CreateGroupResponse, UpdateGroupData, UpdateGroupResponse, DeleteGroupData, DeleteGroupResponse, GetGroupData, GetGroupResponse, AddUserToGroupData, AddUserToGroupResponse, RemoveUserToGroupData, RemoveUserToGroupResponse, ListFoldersData, ListFoldersResponse, ListFolderNamesData, ListFolderNamesResponse, CreateFolderData, CreateFolderResponse, UpdateFolderData, UpdateFolderResponse, DeleteFolderData, DeleteFolderResponse, GetFolderData, GetFolderResponse, GetFolderUsageData, GetFolderUsageResponse, AddOwnerToFolderData, AddOwnerToFolderResponse, RemoveOwnerToFolderData, RemoveOwnerToFolderResponse, ListWorkersData, ListWorkersResponse, ExistsWorkerWithTagData, ExistsWorkerWithTagResponse, GetQueueMetricsResponse, GetCountsOfJobsWaitingPerTagResponse, ListWorkerGroupsResponse, GetConfigData, GetConfigResponse, UpdateConfigData, UpdateConfigResponse, DeleteConfigData, DeleteConfigResponse, ListConfigsResponse, ListAutoscalingEventsData, ListAutoscalingEventsResponse, GetGranularAclsData, GetGranularAclsResponse, AddGranularAclsData, AddGranularAclsResponse, RemoveGranularAclsData, RemoveGranularAclsResponse, SetCaptureConfigData, SetCaptureConfigResponse, PingCaptureConfigData, PingCaptureConfigResponse, GetCaptureConfigsData, GetCaptureConfigsResponse, ListCapturesData, ListCapturesResponse, GetCaptureData, GetCaptureResponse, DeleteCaptureData, DeleteCaptureResponse, StarData, StarResponse, UnstarData, UnstarResponse, GetInputHistoryData, GetInputHistoryResponse, GetArgsFromHistoryOrSavedInputData, GetArgsFromHistoryOrSavedInputResponse, ListInputsData, ListInputsResponse, CreateInputData, CreateInputResponse, UpdateInputData, UpdateInputResponse, DeleteInputData, DeleteInputResponse, DuckdbConnectionSettingsData, DuckdbConnectionSettingsResponse, DuckdbConnectionSettingsV2Data, DuckdbConnectionSettingsV2Response, PolarsConnectionSettingsData, PolarsConnectionSettingsResponse, PolarsConnectionSettingsV2Data, PolarsConnectionSettingsV2Response, S3ResourceInfoData, S3ResourceInfoResponse, DatasetStorageTestConnectionData, DatasetStorageTestConnectionResponse, ListStoredFilesData, ListStoredFilesResponse, LoadFileMetadataData, LoadFileMetadataResponse, LoadFilePreviewData, LoadFilePreviewResponse, LoadParquetPreviewData, LoadParquetPreviewResponse, LoadTableRowCountData, LoadTableRowCountResponse, LoadCsvPreviewData, LoadCsvPreviewResponse, DeleteS3FileData, DeleteS3FileResponse, MoveS3FileData, MoveS3FileResponse, FileUploadData, FileUploadResponse, FileDownloadData, FileDownloadResponse, FileDownloadParquetAsCsvData, FileDownloadParquetAsCsvResponse, GetJobMetricsData, GetJobMetricsResponse, SetJobProgressData, SetJobProgressResponse, GetJobProgressData, GetJobProgressResponse, ListLogFilesData, ListLogFilesResponse, GetLogFileData, GetLogFileResponse, ListConcurrencyGroupsResponse, DeleteConcurrencyGroupData, DeleteConcurrencyGroupResponse, GetConcurrencyKeyData, GetConcurrencyKeyResponse, ListExtendedJobsData, ListExtendedJobsResponse, SearchJobsIndexData, SearchJobsIndexResponse, SearchLogsIndexData, SearchLogsIndexResponse, CountSearchLogsIndexData, CountSearchLogsIndexResponse, ClearIndexData, ClearIndexResponse } from './types.gen.ts'; - -/** - * get backend version - * @returns string git version of backend - * @throws ApiError - */ -export const backendVersion = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/version' -}); }; - -/** - * is backend up to date - * @returns string is backend up to date - * @throws ApiError - */ -export const backendUptodate = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/uptodate' -}); }; - -/** - * get license id - * @returns string get license id (empty if not ee) - * @throws ApiError - */ -export const getLicenseId = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/ee_license' -}); }; - -/** - * get openapi yaml spec - * @returns string openapi yaml file content - * @throws ApiError - */ -export const getOpenApiYaml = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/openapi.yaml' -}); }; - -/** - * get audit log (requires admin privilege) - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @returns AuditLog an audit log - * @throws ApiError - */ -export const getAuditLog = (data: GetAuditLogData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/audit/get/{id}', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * list audit logs (requires admin privilege) - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.before filter on started before (inclusive) timestamp - * @param data.after filter on created after (exclusive) timestamp - * @param data.username filter on exact username of user - * @param data.operation filter on exact or prefix name of operation - * @param data.operations comma separated list of exact operations to include - * @param data.excludeOperations comma separated list of operations to exclude - * @param data.resource filter on exact or prefix name of resource - * @param data.actionKind filter on type of operation - * @returns AuditLog a list of audit logs - * @throws ApiError - */ -export const listAuditLogs = (data: ListAuditLogsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/audit/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage, - before: data.before, - after: data.after, - username: data.username, - operation: data.operation, - operations: data.operations, - exclude_operations: data.excludeOperations, - resource: data.resource, - action_kind: data.actionKind - } -}); }; - -/** - * login with password - * @param data The data for the request. - * @param data.requestBody credentials - * @returns string Successfully authenticated. The session ID is returned in a cookie named `token` and as plaintext response. Preferred method of authorization is through the bearer token. The cookie is only for browser convenience. - * - * @throws ApiError - */ -export const login = (data: LoginData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/auth/login', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * logout - * @returns string clear cookies and clear token (if applicable) - * @throws ApiError - */ -export const logout = (): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/auth/logout' -}); }; - -/** - * get user (require admin privilege) - * @param data The data for the request. - * @param data.workspace - * @param data.username - * @returns User user created - * @throws ApiError - */ -export const getUser = (data: GetUserData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/users/get/{username}', - path: { - workspace: data.workspace, - username: data.username - } -}); }; - -/** - * update user (require admin privilege) - * @param data The data for the request. - * @param data.workspace - * @param data.username - * @param data.requestBody new user - * @returns string edited user - * @throws ApiError - */ -export const updateUser = (data: UpdateUserData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/users/update/{username}', - path: { - workspace: data.workspace, - username: data.username - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * is owner of path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean is owner - * @throws ApiError - */ -export const isOwnerOfPath = (data: IsOwnerOfPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/users/is_owner/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * set password - * @param data The data for the request. - * @param data.requestBody set password - * @returns string password set - * @throws ApiError - */ -export const setPassword = (data: SetPasswordData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/users/setpassword', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * set password for a specific user (require super admin) - * @param data The data for the request. - * @param data.user - * @param data.requestBody set password - * @returns string password set - * @throws ApiError - */ -export const setPasswordForUser = (data: SetPasswordForUserData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/users/set_password_of/{user}', - path: { - user: data.user - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * set login type for a specific user (require super admin) - * @param data The data for the request. - * @param data.user - * @param data.requestBody set login type - * @returns string login type set - * @throws ApiError - */ -export const setLoginTypeForUser = (data: SetLoginTypeForUserData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/users/set_login_type/{user}', - path: { - user: data.user - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * create user - * @param data The data for the request. - * @param data.requestBody user info - * @returns string user created - * @throws ApiError - */ -export const createUserGlobally = (data: CreateUserGloballyData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/users/create', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * global update user (require super admin) - * @param data The data for the request. - * @param data.email - * @param data.requestBody new user info - * @returns string user updated - * @throws ApiError - */ -export const globalUserUpdate = (data: GlobalUserUpdateData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/users/update/{email}', - path: { - email: data.email - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * global username info (require super admin) - * @param data The data for the request. - * @param data.email - * @returns unknown user renamed - * @throws ApiError - */ -export const globalUsernameInfo = (data: GlobalUsernameInfoData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/users/username_info/{email}', - path: { - email: data.email - } -}); }; - -/** - * global rename user (require super admin) - * @param data The data for the request. - * @param data.email - * @param data.requestBody new username - * @returns string user renamed - * @throws ApiError - */ -export const globalUserRename = (data: GlobalUserRenameData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/users/rename/{email}', - path: { - email: data.email - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * global delete user (require super admin) - * @param data The data for the request. - * @param data.email - * @returns string user deleted - * @throws ApiError - */ -export const globalUserDelete = (data: GlobalUserDeleteData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/users/delete/{email}', - path: { - email: data.email - } -}); }; - -/** - * global overwrite users (require super admin and EE) - * @param data The data for the request. - * @param data.requestBody List of users - * @returns string Success message - * @throws ApiError - */ -export const globalUsersOverwrite = (data: GlobalUsersOverwriteData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/users/overwrite', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * global export users (require super admin and EE) - * @returns ExportedUser exported users - * @throws ApiError - */ -export const globalUsersExport = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/users/export' -}); }; - -/** - * delete user (require admin privilege) - * @param data The data for the request. - * @param data.workspace - * @param data.username - * @returns string delete user - * @throws ApiError - */ -export const deleteUser = (data: DeleteUserData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/users/delete/{username}', - path: { - workspace: data.workspace, - username: data.username - } -}); }; - -/** - * list all workspaces visible to me - * @returns Workspace all workspaces - * @throws ApiError - */ -export const listWorkspaces = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/workspaces/list' -}); }; - -/** - * is domain allowed for auto invi - * @returns boolean domain allowed or not - * @throws ApiError - */ -export const isDomainAllowed = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/workspaces/allowed_domain_auto_invite' -}); }; - -/** - * list all workspaces visible to me with user info - * @returns UserWorkspaceList workspace with associated username - * @throws ApiError - */ -export const listUserWorkspaces = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/workspaces/users' -}); }; - -/** - * list all workspaces as super admin (require to be super admin) - * @param data The data for the request. - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @returns Workspace workspaces - * @throws ApiError - */ -export const listWorkspacesAsSuperAdmin = (data: ListWorkspacesAsSuperAdminData = {}): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/workspaces/list_as_superadmin', - query: { - page: data.page, - per_page: data.perPage - } -}); }; - -/** - * create workspace - * @param data The data for the request. - * @param data.requestBody new token - * @returns string token created - * @throws ApiError - */ -export const createWorkspace = (data: CreateWorkspaceData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/workspaces/create', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * exists workspace - * @param data The data for the request. - * @param data.requestBody id of workspace - * @returns boolean status - * @throws ApiError - */ -export const existsWorkspace = (data: ExistsWorkspaceData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/workspaces/exists', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * exists username - * @param data The data for the request. - * @param data.requestBody - * @returns boolean status - * @throws ApiError - */ -export const existsUsername = (data: ExistsUsernameData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/workspaces/exists_username', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get global settings - * @param data The data for the request. - * @param data.key - * @returns unknown status - * @throws ApiError - */ -export const getGlobal = (data: GetGlobalData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/settings/global/{key}', - path: { - key: data.key - } -}); }; - -/** - * post global settings - * @param data The data for the request. - * @param data.key - * @param data.requestBody value set - * @returns string status - * @throws ApiError - */ -export const setGlobal = (data: SetGlobalData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/settings/global/{key}', - path: { - key: data.key - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get local settings - * @returns unknown status - * @throws ApiError - */ -export const getLocal = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/settings/local' -}); }; - -/** - * test smtp - * @param data The data for the request. - * @param data.requestBody test smtp payload - * @returns string status - * @throws ApiError - */ -export const testSmtp = (data: TestSmtpData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/settings/test_smtp', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * test critical channels - * @param data The data for the request. - * @param data.requestBody test critical channel payload - * @returns string status - * @throws ApiError - */ -export const testCriticalChannels = (data: TestCriticalChannelsData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/settings/test_critical_channels', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * Get all critical alerts - * @param data The data for the request. - * @param data.page - * @param data.pageSize - * @param data.acknowledged - * @returns unknown Successfully retrieved all critical alerts - * @throws ApiError - */ -export const getCriticalAlerts = (data: GetCriticalAlertsData = {}): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/settings/critical_alerts', - query: { - page: data.page, - page_size: data.pageSize, - acknowledged: data.acknowledged - } -}); }; - -/** - * Acknowledge a critical alert - * @param data The data for the request. - * @param data.id The ID of the critical alert to acknowledge - * @returns string Successfully acknowledged the critical alert - * @throws ApiError - */ -export const acknowledgeCriticalAlert = (data: AcknowledgeCriticalAlertData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/settings/critical_alerts/{id}/acknowledge', - path: { - id: data.id - } -}); }; - -/** - * Acknowledge all unacknowledged critical alerts - * @returns string Successfully acknowledged all unacknowledged critical alerts. - * @throws ApiError - */ -export const acknowledgeAllCriticalAlerts = (): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/settings/critical_alerts/acknowledge_all' -}); }; - -/** - * test license key - * @param data The data for the request. - * @param data.requestBody test license key - * @returns string status - * @throws ApiError - */ -export const testLicenseKey = (data: TestLicenseKeyData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/settings/test_license_key', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * test object storage config - * @param data The data for the request. - * @param data.requestBody test object storage config - * @returns string status - * @throws ApiError - */ -export const testObjectStorageConfig = (data: TestObjectStorageConfigData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/settings/test_object_storage_config', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * send stats - * @returns string status - * @throws ApiError - */ -export const sendStats = (): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/settings/send_stats' -}); }; - -/** - * get latest key renewal attempt - * @returns unknown status - * @throws ApiError - */ -export const getLatestKeyRenewalAttempt = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/settings/latest_key_renewal_attempt' -}); }; - -/** - * renew license key - * @param data The data for the request. - * @param data.licenseKey - * @returns string status - * @throws ApiError - */ -export const renewLicenseKey = (data: RenewLicenseKeyData = {}): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/settings/renew_license_key', - query: { - license_key: data.licenseKey - } -}); }; - -/** - * create customer portal session - * @param data The data for the request. - * @param data.licenseKey - * @returns string url to portal - * @throws ApiError - */ -export const createCustomerPortalSession = (data: CreateCustomerPortalSessionData = {}): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/settings/customer_portal', - query: { - license_key: data.licenseKey - } -}); }; - -/** - * test metadata - * @param data The data for the request. - * @param data.requestBody test metadata - * @returns string status - * @throws ApiError - */ -export const testMetadata = (data: TestMetadataData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/saml/test_metadata', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * list global settings - * @returns GlobalSetting list of settings - * @throws ApiError - */ -export const listGlobalSettings = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/settings/list_global' -}); }; - -/** - * get current user email (if logged in) - * @returns string user email - * @throws ApiError - */ -export const getCurrentEmail = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/users/email' -}); }; - -/** - * refresh the current token - * @param data The data for the request. - * @param data.ifExpiringInLessThanS - * @returns string new token - * @throws ApiError - */ -export const refreshUserToken = (data: RefreshUserTokenData = {}): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/users/refresh_token', - query: { - if_expiring_in_less_than_s: data.ifExpiringInLessThanS - } -}); }; - -/** - * get tutorial progress - * @returns unknown tutorial progress - * @throws ApiError - */ -export const getTutorialProgress = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/users/tutorial_progress' -}); }; - -/** - * update tutorial progress - * @param data The data for the request. - * @param data.requestBody progress update - * @returns string tutorial progress - * @throws ApiError - */ -export const updateTutorialProgress = (data: UpdateTutorialProgressData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/users/tutorial_progress', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * leave instance - * @returns string status - * @throws ApiError - */ -export const leaveInstance = (): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/users/leave_instance' -}); }; - -/** - * get current usage outside of premium workspaces - * @returns number free usage - * @throws ApiError - */ -export const getUsage = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/users/usage' -}); }; - -/** - * get all runnables in every workspace - * @returns unknown free all runnables - * @throws ApiError - */ -export const getRunnable = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/users/all_runnables' -}); }; - -/** - * get current global whoami (if logged in) - * @returns GlobalUserInfo user email - * @throws ApiError - */ -export const globalWhoami = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/users/whoami' -}); }; - -/** - * list all workspace invites - * @returns WorkspaceInvite list all workspace invites - * @throws ApiError - */ -export const listWorkspaceInvites = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/users/list_invites' -}); }; - -/** - * whoami - * @param data The data for the request. - * @param data.workspace - * @returns User user - * @throws ApiError - */ -export const whoami = (data: WhoamiData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/users/whoami', - path: { - workspace: data.workspace - } -}); }; - -/** - * accept invite to workspace - * @param data The data for the request. - * @param data.requestBody accept invite - * @returns string status - * @throws ApiError - */ -export const acceptInvite = (data: AcceptInviteData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/users/accept_invite', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * decline invite to workspace - * @param data The data for the request. - * @param data.requestBody decline invite - * @returns string status - * @throws ApiError - */ -export const declineInvite = (data: DeclineInviteData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/users/decline_invite', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * invite user to workspace - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody WorkspaceInvite - * @returns string status - * @throws ApiError - */ -export const inviteUser = (data: InviteUserData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/invite_user', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * add user to workspace - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody WorkspaceInvite - * @returns string status - * @throws ApiError - */ -export const addUser = (data: AddUserData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/add_user', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete user invite - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody WorkspaceInvite - * @returns string status - * @throws ApiError - */ -export const deleteInvite = (data: DeleteInviteData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/delete_invite', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * archive workspace - * @param data The data for the request. - * @param data.workspace - * @returns string status - * @throws ApiError - */ -export const archiveWorkspace = (data: ArchiveWorkspaceData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/archive', - path: { - workspace: data.workspace - } -}); }; - -/** - * unarchive workspace - * @param data The data for the request. - * @param data.workspace - * @returns string status - * @throws ApiError - */ -export const unarchiveWorkspace = (data: UnarchiveWorkspaceData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/workspaces/unarchive/{workspace}', - path: { - workspace: data.workspace - } -}); }; - -/** - * delete workspace (require super admin) - * @param data The data for the request. - * @param data.workspace - * @returns string status - * @throws ApiError - */ -export const deleteWorkspace = (data: DeleteWorkspaceData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/workspaces/delete/{workspace}', - path: { - workspace: data.workspace - } -}); }; - -/** - * leave workspace - * @param data The data for the request. - * @param data.workspace - * @returns string status - * @throws ApiError - */ -export const leaveWorkspace = (data: LeaveWorkspaceData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/leave', - path: { - workspace: data.workspace - } -}); }; - -/** - * get workspace name - * @param data The data for the request. - * @param data.workspace - * @returns string status - * @throws ApiError - */ -export const getWorkspaceName = (data: GetWorkspaceNameData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/get_workspace_name', - path: { - workspace: data.workspace - } -}); }; - -/** - * change workspace name - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody - * @returns string status - * @throws ApiError - */ -export const changeWorkspaceName = (data: ChangeWorkspaceNameData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/change_workspace_name', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * change workspace id - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody - * @returns string status - * @throws ApiError - */ -export const changeWorkspaceId = (data: ChangeWorkspaceIdData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/change_workspace_id', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * change workspace id - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody - * @returns string status - * @throws ApiError - */ -export const changeWorkspaceColor = (data: ChangeWorkspaceColorData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/change_workspace_color', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * whois - * @param data The data for the request. - * @param data.workspace - * @param data.username - * @returns User user - * @throws ApiError - */ -export const whois = (data: WhoisData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/users/whois/{username}', - path: { - workspace: data.workspace, - username: data.username - } -}); }; - -/** - * Update operator settings for a workspace - * Updates the operator settings for a specific workspace. Requires workspace admin privileges. - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody - * @returns string Operator settings updated successfully - * @throws ApiError - */ -export const updateOperatorSettings = (data: UpdateOperatorSettingsData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/operator_settings', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * exists email - * @param data The data for the request. - * @param data.email - * @returns boolean user - * @throws ApiError - */ -export const existsEmail = (data: ExistsEmailData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/users/exists/{email}', - path: { - email: data.email - } -}); }; - -/** - * list all users as super admin (require to be super amdin) - * @param data The data for the request. - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.activeOnly filter only active users - * @returns GlobalUserInfo user - * @throws ApiError - */ -export const listUsersAsSuperAdmin = (data: ListUsersAsSuperAdminData = {}): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/users/list_as_super_admin', - query: { - page: data.page, - per_page: data.perPage, - active_only: data.activeOnly - } -}); }; - -/** - * list pending invites for a workspace - * @param data The data for the request. - * @param data.workspace - * @returns WorkspaceInvite user - * @throws ApiError - */ -export const listPendingInvites = (data: ListPendingInvitesData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/list_pending_invites', - path: { - workspace: data.workspace - } -}); }; - -/** - * get settings - * @param data The data for the request. - * @param data.workspace - * @returns unknown status - * @throws ApiError - */ -export const getSettings = (data: GetSettingsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/get_settings', - path: { - workspace: data.workspace - } -}); }; - -/** - * get deploy to - * @param data The data for the request. - * @param data.workspace - * @returns unknown status - * @throws ApiError - */ -export const getDeployTo = (data: GetDeployToData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/get_deploy_to', - path: { - workspace: data.workspace - } -}); }; - -/** - * get if workspace is premium - * @param data The data for the request. - * @param data.workspace - * @returns boolean status - * @throws ApiError - */ -export const getIsPremium = (data: GetIsPremiumData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/is_premium', - path: { - workspace: data.workspace - } -}); }; - -/** - * get premium info - * @param data The data for the request. - * @param data.workspace - * @returns unknown status - * @throws ApiError - */ -export const getPremiumInfo = (data: GetPremiumInfoData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/premium_info', - path: { - workspace: data.workspace - } -}); }; - -/** - * set automatic billing - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody automatic billing - * @returns string status - * @throws ApiError - */ -export const setAutomaticBilling = (data: SetAutomaticBillingData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/set_automatic_billing', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get threshold alert info - * @param data The data for the request. - * @param data.workspace - * @returns unknown status - * @throws ApiError - */ -export const getThresholdAlert = (data: GetThresholdAlertData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/threshold_alert', - path: { - workspace: data.workspace - } -}); }; - -/** - * set threshold alert info - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody threshold alert info - * @returns string status - * @throws ApiError - */ -export const setThresholdAlert = (data: SetThresholdAlertData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/threshold_alert', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * edit slack command - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody WorkspaceInvite - * @returns string status - * @throws ApiError - */ -export const editSlackCommand = (data: EditSlackCommandData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/edit_slack_command', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * run a job that sends a message to Slack - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody path to hub script to run and its corresponding args - * @returns unknown status - * @throws ApiError - */ -export const runSlackMessageTestJob = (data: RunSlackMessageTestJobData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/run_slack_message_test_job', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * edit deploy to - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody - * @returns string status - * @throws ApiError - */ -export const editDeployTo = (data: EditDeployToData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/edit_deploy_to', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * edit auto invite - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody WorkspaceInvite - * @returns string status - * @throws ApiError - */ -export const editAutoInvite = (data: EditAutoInviteData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/edit_auto_invite', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * edit webhook - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody WorkspaceWebhook - * @returns string status - * @throws ApiError - */ -export const editWebhook = (data: EditWebhookData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/edit_webhook', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * edit copilot config - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody WorkspaceCopilotConfig - * @returns string status - * @throws ApiError - */ -export const editCopilotConfig = (data: EditCopilotConfigData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/edit_copilot_config', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get copilot info - * @param data The data for the request. - * @param data.workspace - * @returns unknown status - * @throws ApiError - */ -export const getCopilotInfo = (data: GetCopilotInfoData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/get_copilot_info', - path: { - workspace: data.workspace - } -}); }; - -/** - * edit error handler - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody WorkspaceErrorHandler - * @returns string status - * @throws ApiError - */ -export const editErrorHandler = (data: EditErrorHandlerData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/edit_error_handler', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * edit large file storage settings - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody LargeFileStorage info - * @returns unknown status - * @throws ApiError - */ -export const editLargeFileStorageConfig = (data: EditLargeFileStorageConfigData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/edit_large_file_storage_config', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * edit workspace git sync settings - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody Workspace Git sync settings - * @returns unknown status - * @throws ApiError - */ -export const editWorkspaceGitSyncConfig = (data: EditWorkspaceGitSyncConfigData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/edit_git_sync_config', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * edit workspace deploy ui settings - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody Workspace deploy UI settings - * @returns unknown status - * @throws ApiError - */ -export const editWorkspaceDeployUiSettings = (data: EditWorkspaceDeployUiSettingsData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/edit_deploy_ui_config', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * edit default app for workspace - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody Workspace default app - * @returns string status - * @throws ApiError - */ -export const editWorkspaceDefaultApp = (data: EditWorkspaceDefaultAppData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/edit_default_app', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * edit default scripts for workspace - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody Workspace default app - * @returns string status - * @throws ApiError - */ -export const editDefaultScripts = (data: EditDefaultScriptsData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/default_scripts', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get default scripts for workspace - * @param data The data for the request. - * @param data.workspace - * @returns WorkspaceDefaultScripts status - * @throws ApiError - */ -export const getDefaultScripts = (data: GetDefaultScriptsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/default_scripts', - path: { - workspace: data.workspace - } -}); }; - -/** - * set environment variable - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody Workspace default app - * @returns string status - * @throws ApiError - */ -export const setEnvironmentVariable = (data: SetEnvironmentVariableData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/set_environment_variable', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * retrieves the encryption key for this workspace - * @param data The data for the request. - * @param data.workspace - * @returns unknown status - * @throws ApiError - */ -export const getWorkspaceEncryptionKey = (data: GetWorkspaceEncryptionKeyData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/encryption_key', - path: { - workspace: data.workspace - } -}); }; - -/** - * update the encryption key for this workspace - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody New encryption key - * @returns string status - * @throws ApiError - */ -export const setWorkspaceEncryptionKey = (data: SetWorkspaceEncryptionKeyData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/encryption_key', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get default app for workspace - * @param data The data for the request. - * @param data.workspace - * @returns unknown status - * @throws ApiError - */ -export const getWorkspaceDefaultApp = (data: GetWorkspaceDefaultAppData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/default_app', - path: { - workspace: data.workspace - } -}); }; - -/** - * get large file storage config - * @param data The data for the request. - * @param data.workspace - * @returns LargeFileStorage status - * @throws ApiError - */ -export const getLargeFileStorageConfig = (data: GetLargeFileStorageConfigData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/get_large_file_storage_config', - path: { - workspace: data.workspace - } -}); }; - -/** - * get usage - * @param data The data for the request. - * @param data.workspace - * @returns number usage - * @throws ApiError - */ -export const getWorkspaceUsage = (data: GetWorkspaceUsageData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/usage', - path: { - workspace: data.workspace - } -}); }; - -/** - * get used triggers - * @param data The data for the request. - * @param data.workspace - * @returns unknown status - * @throws ApiError - */ -export const getUsedTriggers = (data: GetUsedTriggersData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/used_triggers', - path: { - workspace: data.workspace - } -}); }; - -/** - * list users - * @param data The data for the request. - * @param data.workspace - * @returns User user - * @throws ApiError - */ -export const listUsers = (data: ListUsersData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/users/list', - path: { - workspace: data.workspace - } -}); }; - -/** - * list users usage - * @param data The data for the request. - * @param data.workspace - * @returns UserUsage user - * @throws ApiError - */ -export const listUsersUsage = (data: ListUsersUsageData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/users/list_usage', - path: { - workspace: data.workspace - } -}); }; - -/** - * list usernames - * @param data The data for the request. - * @param data.workspace - * @returns string user - * @throws ApiError - */ -export const listUsernames = (data: ListUsernamesData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/users/list_usernames', - path: { - workspace: data.workspace - } -}); }; - -/** - * get email from username - * @param data The data for the request. - * @param data.workspace - * @param data.username - * @returns string email - * @throws ApiError - */ -export const usernameToEmail = (data: UsernameToEmailData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/users/username_to_email/{username}', - path: { - workspace: data.workspace, - username: data.username - } -}); }; - -/** - * create token - * @param data The data for the request. - * @param data.requestBody new token - * @returns string token created - * @throws ApiError - */ -export const createToken = (data: CreateTokenData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/users/tokens/create', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * create token to impersonate a user (require superadmin) - * @param data The data for the request. - * @param data.requestBody new token - * @returns string token created - * @throws ApiError - */ -export const createTokenImpersonate = (data: CreateTokenImpersonateData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/users/tokens/impersonate', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete token - * @param data The data for the request. - * @param data.tokenPrefix - * @returns string delete token - * @throws ApiError - */ -export const deleteToken = (data: DeleteTokenData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/users/tokens/delete/{token_prefix}', - path: { - token_prefix: data.tokenPrefix - } -}); }; - -/** - * list token - * @param data The data for the request. - * @param data.excludeEphemeral - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @returns TruncatedToken truncated token - * @throws ApiError - */ -export const listTokens = (data: ListTokensData = {}): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/users/tokens/list', - query: { - exclude_ephemeral: data.excludeEphemeral, - page: data.page, - per_page: data.perPage - } -}); }; - -/** - * get OIDC token (ee only) - * @param data The data for the request. - * @param data.workspace - * @param data.audience - * @returns string new oidc token - * @throws ApiError - */ -export const getOidcToken = (data: GetOidcTokenData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/oidc/token/{audience}', - path: { - workspace: data.workspace, - audience: data.audience - } -}); }; - -/** - * create variable - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody new variable - * @param data.alreadyEncrypted - * @returns string variable created - * @throws ApiError - */ -export const createVariable = (data: CreateVariableData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/variables/create', - path: { - workspace: data.workspace - }, - query: { - already_encrypted: data.alreadyEncrypted - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * encrypt value - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody new variable - * @returns string encrypted value - * @throws ApiError - */ -export const encryptValue = (data: EncryptValueData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/variables/encrypt', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete variable - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string variable deleted - * @throws ApiError - */ -export const deleteVariable = (data: DeleteVariableData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/variables/delete/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * update variable - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated variable - * @param data.alreadyEncrypted - * @returns string variable updated - * @throws ApiError - */ -export const updateVariable = (data: UpdateVariableData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/variables/update/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - already_encrypted: data.alreadyEncrypted - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get variable - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.decryptSecret ask to decrypt secret if this variable is secret - * (if not secret no effect, default: true) - * - * @param data.includeEncrypted ask to include the encrypted value if secret and decrypt secret is not true (default: false) - * - * @returns ListableVariable variable - * @throws ApiError - */ -export const getVariable = (data: GetVariableData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/variables/get/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - decrypt_secret: data.decryptSecret, - include_encrypted: data.includeEncrypted - } -}); }; - -/** - * get variable value - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string variable - * @throws ApiError - */ -export const getVariableValue = (data: GetVariableValueData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/variables/get_value/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * does variable exists at path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean variable - * @throws ApiError - */ -export const existsVariable = (data: ExistsVariableData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/variables/exists/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * list variables - * @param data The data for the request. - * @param data.workspace - * @param data.pathStart - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @returns ListableVariable variable list - * @throws ApiError - */ -export const listVariable = (data: ListVariableData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/variables/list', - path: { - workspace: data.workspace - }, - query: { - path_start: data.pathStart, - page: data.page, - per_page: data.perPage - } -}); }; - -/** - * list contextual variables - * @param data The data for the request. - * @param data.workspace - * @returns ContextualVariable contextual variable list - * @throws ApiError - */ -export const listContextualVariables = (data: ListContextualVariablesData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/variables/list_contextual', - path: { - workspace: data.workspace - } -}); }; - -/** - * Get all critical alerts for this workspace - * @param data The data for the request. - * @param data.workspace - * @param data.page - * @param data.pageSize - * @param data.acknowledged - * @returns unknown Successfully retrieved all critical alerts - * @throws ApiError - */ -export const workspaceGetCriticalAlerts = (data: WorkspaceGetCriticalAlertsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/workspaces/critical_alerts', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - page_size: data.pageSize, - acknowledged: data.acknowledged - } -}); }; - -/** - * Acknowledge a critical alert for this workspace - * @param data The data for the request. - * @param data.workspace - * @param data.id The ID of the critical alert to acknowledge - * @returns string Successfully acknowledged the critical alert - * @throws ApiError - */ -export const workspaceAcknowledgeCriticalAlert = (data: WorkspaceAcknowledgeCriticalAlertData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/critical_alerts/{id}/acknowledge', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * Acknowledge all unacknowledged critical alerts for this workspace - * @param data The data for the request. - * @param data.workspace - * @returns string Successfully acknowledged all unacknowledged critical alerts. - * @throws ApiError - */ -export const workspaceAcknowledgeAllCriticalAlerts = (data: WorkspaceAcknowledgeAllCriticalAlertsData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/critical_alerts/acknowledge_all', - path: { - workspace: data.workspace - } -}); }; - -/** - * Mute critical alert UI for this workspace - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody Boolean flag to mute critical alerts. - * @returns string Successfully updated mute critical alert settings. - * @throws ApiError - */ -export const workspaceMuteCriticalAlertsUi = (data: WorkspaceMuteCriticalAlertsUiData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/workspaces/critical_alerts/mute', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * login with oauth authorization flow - * @param data The data for the request. - * @param data.clientName - * @param data.requestBody Partially filled script - * @returns string Successfully authenticated. The session ID is returned in a cookie named `token` and as plaintext response. Preferred method of authorization is through the bearer token. The cookie is only for browser convenience. - * - * @throws ApiError - */ -export const loginWithOauth = (data: LoginWithOauthData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/oauth/login_callback/{client_name}', - path: { - client_name: data.clientName - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * connect slack callback - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody code endpoint - * @returns string slack token - * @throws ApiError - */ -export const connectSlackCallback = (data: ConnectSlackCallbackData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/oauth/connect_slack_callback', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * connect slack callback instance - * @param data The data for the request. - * @param data.requestBody code endpoint - * @returns string success message - * @throws ApiError - */ -export const connectSlackCallbackInstance = (data: ConnectSlackCallbackInstanceData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/oauth/connect_slack_callback', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * connect callback - * @param data The data for the request. - * @param data.clientName - * @param data.requestBody code endpoint - * @returns TokenResponse oauth token - * @throws ApiError - */ -export const connectCallback = (data: ConnectCallbackData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/oauth/connect_callback/{client_name}', - path: { - client_name: data.clientName - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * create OAuth account - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody code endpoint - * @returns string account set - * @throws ApiError - */ -export const createAccount = (data: CreateAccountData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/oauth/create_account', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * refresh token - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.requestBody variable path - * @returns string token refreshed - * @throws ApiError - */ -export const refreshToken = (data: RefreshTokenData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/oauth/refresh_token/{id}', - path: { - workspace: data.workspace, - id: data.id - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * disconnect account - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @returns string disconnected client - * @throws ApiError - */ -export const disconnectAccount = (data: DisconnectAccountData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/oauth/disconnect/{id}', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * disconnect slack - * @param data The data for the request. - * @param data.workspace - * @returns string disconnected slack - * @throws ApiError - */ -export const disconnectSlack = (data: DisconnectSlackData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/oauth/disconnect_slack', - path: { - workspace: data.workspace - } -}); }; - -/** - * list oauth logins - * @returns unknown list of oauth and saml login clients - * @throws ApiError - */ -export const listOauthLogins = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/oauth/list_logins' -}); }; - -/** - * list oauth connects - * @returns string list of oauth connects clients - * @throws ApiError - */ -export const listOauthConnects = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/oauth/list_connects' -}); }; - -/** - * get oauth connect - * @param data The data for the request. - * @param data.client client name - * @returns unknown get - * @throws ApiError - */ -export const getOauthConnect = (data: GetOauthConnectData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/oauth/get_connect/{client}', - path: { - client: data.client - } -}); }; - -/** - * synchronize Microsoft Teams information (teams/channels) - * @returns TeamInfo Teams information successfully synchronized - * @throws ApiError - */ -export const syncTeams = (): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/teams/sync' -}); }; - -/** - * create resource - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody new resource - * @param data.updateIfExists - * @returns string resource created - * @throws ApiError - */ -export const createResource = (data: CreateResourceData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/resources/create', - path: { - workspace: data.workspace - }, - query: { - update_if_exists: data.updateIfExists - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete resource - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string resource deleted - * @throws ApiError - */ -export const deleteResource = (data: DeleteResourceData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/resources/delete/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * update resource - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated resource - * @returns string resource updated - * @throws ApiError - */ -export const updateResource = (data: UpdateResourceData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/resources/update/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * update resource value - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated resource - * @returns string resource value updated - * @throws ApiError - */ -export const updateResourceValue = (data: UpdateResourceValueData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/resources/update_value/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get resource - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns Resource resource - * @throws ApiError - */ -export const getResource = (data: GetResourceData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/resources/get/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get resource interpolated (variables and resources are fully unrolled) - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.jobId job id - * @returns unknown resource value - * @throws ApiError - */ -export const getResourceValueInterpolated = (data: GetResourceValueInterpolatedData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/resources/get_value_interpolated/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - job_id: data.jobId - } -}); }; - -/** - * get resource value - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns unknown resource value - * @throws ApiError - */ -export const getResourceValue = (data: GetResourceValueData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/resources/get_value/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * does resource exists - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean does resource exists - * @throws ApiError - */ -export const existsResource = (data: ExistsResourceData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/resources/exists/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * list resources - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.resourceType resource_types to list from, separated by ',', - * @param data.resourceTypeExclude resource_types to not list from, separated by ',', - * @param data.pathStart - * @returns ListableResource resource list - * @throws ApiError - */ -export const listResource = (data: ListResourceData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/resources/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage, - resource_type: data.resourceType, - resource_type_exclude: data.resourceTypeExclude, - path_start: data.pathStart - } -}); }; - -/** - * list resources for search - * @param data The data for the request. - * @param data.workspace - * @returns unknown resource list - * @throws ApiError - */ -export const listSearchResource = (data: ListSearchResourceData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/resources/list_search', - path: { - workspace: data.workspace - } -}); }; - -/** - * list resource names - * @param data The data for the request. - * @param data.workspace - * @param data.name - * @returns unknown resource list names - * @throws ApiError - */ -export const listResourceNames = (data: ListResourceNamesData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/resources/list_names/{name}', - path: { - workspace: data.workspace, - name: data.name - } -}); }; - -/** - * create resource_type - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody new resource_type - * @returns string resource_type created - * @throws ApiError - */ -export const createResourceType = (data: CreateResourceTypeData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/resources/type/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get map from resource type to format extension - * @param data The data for the request. - * @param data.workspace - * @returns unknown map from resource type to file ext - * @throws ApiError - */ -export const fileResourceTypeToFileExtMap = (data: FileResourceTypeToFileExtMapData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/resources/file_resource_type_to_file_ext_map', - path: { - workspace: data.workspace - } -}); }; - -/** - * delete resource_type - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string resource_type deleted - * @throws ApiError - */ -export const deleteResourceType = (data: DeleteResourceTypeData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/resources/type/delete/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * update resource_type - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated resource_type - * @returns string resource_type updated - * @throws ApiError - */ -export const updateResourceType = (data: UpdateResourceTypeData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/resources/type/update/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get resource_type - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns ResourceType resource_type deleted - * @throws ApiError - */ -export const getResourceType = (data: GetResourceTypeData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/resources/type/get/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * does resource_type exists - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean does resource_type exist - * @throws ApiError - */ -export const existsResourceType = (data: ExistsResourceTypeData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/resources/type/exists/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * list resource_types - * @param data The data for the request. - * @param data.workspace - * @returns ResourceType resource_type list - * @throws ApiError - */ -export const listResourceType = (data: ListResourceTypeData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/resources/type/list', - path: { - workspace: data.workspace - } -}); }; - -/** - * list resource_types names - * @param data The data for the request. - * @param data.workspace - * @returns string resource_type list - * @throws ApiError - */ -export const listResourceTypeNames = (data: ListResourceTypeNamesData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/resources/type/listnames', - path: { - workspace: data.workspace - } -}); }; - -/** - * query resource types by similarity - * @param data The data for the request. - * @param data.workspace - * @param data.text query text - * @param data.limit query limit - * @returns unknown resource type details - * @throws ApiError - */ -export const queryResourceTypes = (data: QueryResourceTypesData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/embeddings/query_resource_types', - path: { - workspace: data.workspace - }, - query: { - text: data.text, - limit: data.limit - } -}); }; - -/** - * list hub integrations - * @param data The data for the request. - * @param data.kind query integrations kind - * @returns unknown integrations details - * @throws ApiError - */ -export const listHubIntegrations = (data: ListHubIntegrationsData = {}): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/integrations/hub/list', - query: { - kind: data.kind - } -}); }; - -/** - * list all hub flows - * @returns unknown hub flows list - * @throws ApiError - */ -export const listHubFlows = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/flows/hub/list' -}); }; - -/** - * get hub flow by id - * @param data The data for the request. - * @param data.id - * @returns unknown flow - * @throws ApiError - */ -export const getHubFlowById = (data: GetHubFlowByIdData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/flows/hub/get/{id}', - path: { - id: data.id - } -}); }; - -/** - * list all hub apps - * @returns unknown hub apps list - * @throws ApiError - */ -export const listHubApps = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/apps/hub/list' -}); }; - -/** - * get hub app by id - * @param data The data for the request. - * @param data.id - * @returns unknown app - * @throws ApiError - */ -export const getHubAppById = (data: GetHubAppByIdData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/apps/hub/get/{id}', - path: { - id: data.id - } -}); }; - -/** - * get public app by custom path - * @param data The data for the request. - * @param data.customPath - * @returns unknown app details - * @throws ApiError - */ -export const getPublicAppByCustomPath = (data: GetPublicAppByCustomPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/apps_u/public_app_by_custom_path/{custom_path}', - path: { - custom_path: data.customPath - } -}); }; - -/** - * get hub script content by path - * @param data The data for the request. - * @param data.path - * @returns string script details - * @throws ApiError - */ -export const getHubScriptContentByPath = (data: GetHubScriptContentByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/scripts/hub/get/{path}', - path: { - path: data.path - } -}); }; - -/** - * get full hub script by path - * @param data The data for the request. - * @param data.path - * @returns unknown script details - * @throws ApiError - */ -export const getHubScriptByPath = (data: GetHubScriptByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/scripts/hub/get_full/{path}', - path: { - path: data.path - } -}); }; - -/** - * get top hub scripts - * @param data The data for the request. - * @param data.limit query limit - * @param data.app query scripts app - * @param data.kind query scripts kind - * @returns unknown hub scripts list - * @throws ApiError - */ -export const getTopHubScripts = (data: GetTopHubScriptsData = {}): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/scripts/hub/top', - query: { - limit: data.limit, - app: data.app, - kind: data.kind - } -}); }; - -/** - * query hub scripts by similarity - * @param data The data for the request. - * @param data.text query text - * @param data.kind query scripts kind - * @param data.limit query limit - * @param data.app query scripts app - * @returns unknown script details - * @throws ApiError - */ -export const queryHubScripts = (data: QueryHubScriptsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/embeddings/query_hub_scripts', - query: { - text: data.text, - kind: data.kind, - limit: data.limit, - app: data.app - } -}); }; - -/** - * list scripts for search - * @param data The data for the request. - * @param data.workspace - * @returns unknown script list - * @throws ApiError - */ -export const listSearchScript = (data: ListSearchScriptData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/list_search', - path: { - workspace: data.workspace - } -}); }; - -/** - * list all scripts - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.orderDesc order by desc order (default true) - * @param data.createdBy mask to filter exact matching user creator - * @param data.pathStart mask to filter matching starting path - * @param data.pathExact mask to filter exact matching path - * @param data.firstParentHash mask to filter scripts whom first direct parent has exact hash - * @param data.lastParentHash mask to filter scripts whom last parent in the chain has exact hash. - * Beware that each script stores only a limited number of parents. Hence - * the last parent hash for a script is not necessarily its top-most parent. - * To find the top-most parent you will have to jump from last to last hash - * until finding the parent - * - * @param data.parentHash is the hash present in the array of stored parent hashes for this script. - * The same warning applies than for last_parent_hash. A script only store a - * limited number of direct parent - * - * @param data.showArchived (default false) - * show only the archived files. - * when multiple archived hash share the same path, only the ones with the latest create_at - * are - * ed. - * - * @param data.includeWithoutMain (default false) - * include scripts without an exported main function - * - * @param data.includeDraftOnly (default false) - * include scripts that have no deployed version - * - * @param data.isTemplate (default regardless) - * if true show only the templates - * if false show only the non templates - * if not defined, show all regardless of if the script is a template - * - * @param data.kinds (default regardless) - * script kinds to filter, split by comma - * - * @param data.starredOnly (default false) - * show only the starred items - * - * @param data.withDeploymentMsg (default false) - * include deployment message - * - * @returns Script All scripts - * @throws ApiError - */ -export const listScripts = (data: ListScriptsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage, - order_desc: data.orderDesc, - created_by: data.createdBy, - path_start: data.pathStart, - path_exact: data.pathExact, - first_parent_hash: data.firstParentHash, - last_parent_hash: data.lastParentHash, - parent_hash: data.parentHash, - show_archived: data.showArchived, - include_without_main: data.includeWithoutMain, - include_draft_only: data.includeDraftOnly, - is_template: data.isTemplate, - kinds: data.kinds, - starred_only: data.starredOnly, - with_deployment_msg: data.withDeploymentMsg - } -}); }; - -/** - * list all scripts paths - * @param data The data for the request. - * @param data.workspace - * @returns string list of script paths - * @throws ApiError - */ -export const listScriptPaths = (data: ListScriptPathsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/list_paths', - path: { - workspace: data.workspace - } -}); }; - -/** - * create draft - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody - * @returns string draft created - * @throws ApiError - */ -export const createDraft = (data: CreateDraftData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/drafts/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete draft - * @param data The data for the request. - * @param data.workspace - * @param data.kind - * @param data.path - * @returns string draft deleted - * @throws ApiError - */ -export const deleteDraft = (data: DeleteDraftData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/drafts/delete/{kind}/{path}', - path: { - workspace: data.workspace, - kind: data.kind, - path: data.path - } -}); }; - -/** - * create script - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody Partially filled script - * @returns string script created - * @throws ApiError - */ -export const createScript = (data: CreateScriptData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/scripts/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * Toggle ON and OFF the workspace error handler for a given script - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody Workspace error handler enabled - * @returns string error handler toggled - * @throws ApiError - */ -export const toggleWorkspaceErrorHandlerForScript = (data: ToggleWorkspaceErrorHandlerForScriptData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/scripts/toggle_workspace_error_handler/p/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get all instance custom tags (tags are used to dispatch jobs to different worker groups) - * @param data The data for the request. - * @param data.workspace - * @param data.showWorkspaceRestriction - * @returns string list of custom tags - * @throws ApiError - */ -export const getCustomTags = (data: GetCustomTagsData = {}): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/workers/custom_tags', - query: { - workspace: data.workspace, - show_workspace_restriction: data.showWorkspaceRestriction - } -}); }; - -/** - * get all instance default tags - * @returns string list of default tags - * @throws ApiError - */ -export const geDefaultTags = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/workers/get_default_tags' -}); }; - -/** - * is default tags per workspace - * @returns boolean is the default tags per workspace - * @throws ApiError - */ -export const isDefaultTagsPerWorkspace = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/workers/is_default_tags_per_workspace' -}); }; - -/** - * archive script by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string script archived - * @throws ApiError - */ -export const archiveScriptByPath = (data: ArchiveScriptByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/scripts/archive/p/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * archive script by hash - * @param data The data for the request. - * @param data.workspace - * @param data.hash - * @returns Script script details - * @throws ApiError - */ -export const archiveScriptByHash = (data: ArchiveScriptByHashData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/scripts/archive/h/{hash}', - path: { - workspace: data.workspace, - hash: data.hash - } -}); }; - -/** - * delete script by hash (erase content but keep hash, require admin) - * @param data The data for the request. - * @param data.workspace - * @param data.hash - * @returns Script script details - * @throws ApiError - */ -export const deleteScriptByHash = (data: DeleteScriptByHashData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/scripts/delete/h/{hash}', - path: { - workspace: data.workspace, - hash: data.hash - } -}); }; - -/** - * delete all scripts at a given path (require admin) - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string script path - * @throws ApiError - */ -export const deleteScriptByPath = (data: DeleteScriptByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/scripts/delete/p/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get script by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.withStarredInfo - * @returns Script script details - * @throws ApiError - */ -export const getScriptByPath = (data: GetScriptByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/get/p/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - with_starred_info: data.withStarredInfo - } -}); }; - -/** - * get triggers count of script - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns TriggersCount triggers count - * @throws ApiError - */ -export const getTriggersCountOfScript = (data: GetTriggersCountOfScriptData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/get_triggers_count/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get tokens with script scope - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns TruncatedToken tokens list - * @throws ApiError - */ -export const listTokensOfScript = (data: ListTokensOfScriptData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/list_tokens/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get script by path with draft - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns NewScriptWithDraft script details - * @throws ApiError - */ -export const getScriptByPathWithDraft = (data: GetScriptByPathWithDraftData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/get/draft/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get history of a script by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns ScriptHistory script history - * @throws ApiError - */ -export const getScriptHistoryByPath = (data: GetScriptHistoryByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/history/p/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get scripts's latest version (hash) - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns ScriptHistory Script version/hash - * @throws ApiError - */ -export const getScriptLatestVersion = (data: GetScriptLatestVersionData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/get_latest_version/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * update history of a script - * @param data The data for the request. - * @param data.workspace - * @param data.hash - * @param data.path - * @param data.requestBody Script deployment message - * @returns string success - * @throws ApiError - */ -export const updateScriptHistory = (data: UpdateScriptHistoryData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/scripts/history_update/h/{hash}/p/{path}', - path: { - workspace: data.workspace, - hash: data.hash, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * raw script by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string script content - * @throws ApiError - */ -export const rawScriptByPath = (data: RawScriptByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/raw/p/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * raw script by path with a token (mostly used by lsp to be used with import maps to resolve scripts) - * @param data The data for the request. - * @param data.workspace - * @param data.token - * @param data.path - * @returns string script content - * @throws ApiError - */ -export const rawScriptByPathTokened = (data: RawScriptByPathTokenedData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/scripts_u/tokened_raw/{workspace}/{token}/{path}', - path: { - workspace: data.workspace, - token: data.token, - path: data.path - } -}); }; - -/** - * exists script by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean does it exists - * @throws ApiError - */ -export const existsScriptByPath = (data: ExistsScriptByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/exists/p/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get script by hash - * @param data The data for the request. - * @param data.workspace - * @param data.hash - * @param data.withStarredInfo - * @returns Script script details - * @throws ApiError - */ -export const getScriptByHash = (data: GetScriptByHashData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/get/h/{hash}', - path: { - workspace: data.workspace, - hash: data.hash - }, - query: { - with_starred_info: data.withStarredInfo - } -}); }; - -/** - * raw script by hash - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string script content - * @throws ApiError - */ -export const rawScriptByHash = (data: RawScriptByHashData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/raw/h/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get script deployment status - * @param data The data for the request. - * @param data.workspace - * @param data.hash - * @returns unknown script details - * @throws ApiError - */ -export const getScriptDeploymentStatus = (data: GetScriptDeploymentStatusData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/scripts/deployment_status/h/{hash}', - path: { - workspace: data.workspace, - hash: data.hash - } -}); }; - -/** - * run script by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody script args - * @param data.scheduledFor when to schedule this job (leave empty for immediate run) - * @param data.scheduledInSecs schedule the script to execute in the number of seconds starting now - * @param data.skipPreprocessor skip the preprocessor - * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any - * @param data.tag Override the tag to use - * @param data.cacheTtl Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl - * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) - * @param data.invisibleToOwner make the run invisible to the the script owner (default false) - * @returns string job created - * @throws ApiError - */ -export const runScriptByPath = (data: RunScriptByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/run/p/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - scheduled_for: data.scheduledFor, - scheduled_in_secs: data.scheduledInSecs, - skip_preprocessor: data.skipPreprocessor, - parent_job: data.parentJob, - tag: data.tag, - cache_ttl: data.cacheTtl, - job_id: data.jobId, - invisible_to_owner: data.invisibleToOwner - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * run script by path in openai format - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody script args - * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any - * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) - * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args - * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key - * - * @param data.queueLimit The maximum size of the queue for which the request would get rejected if that job would push it above that limit - * - * @returns unknown job result - * @throws ApiError - */ -export const openaiSyncScriptByPath = (data: OpenaiSyncScriptByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/openai_sync/p/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - parent_job: data.parentJob, - job_id: data.jobId, - include_header: data.includeHeader, - queue_limit: data.queueLimit - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * run script by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody script args - * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any - * @param data.tag Override the tag to use - * @param data.cacheTtl Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl - * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) - * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args - * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key - * - * @param data.queueLimit The maximum size of the queue for which the request would get rejected if that job would push it above that limit - * - * @returns unknown job result - * @throws ApiError - */ -export const runWaitResultScriptByPath = (data: RunWaitResultScriptByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/run_wait_result/p/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - parent_job: data.parentJob, - tag: data.tag, - cache_ttl: data.cacheTtl, - job_id: data.jobId, - include_header: data.includeHeader, - queue_limit: data.queueLimit - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * run script by path with get - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any - * @param data.tag Override the tag to use - * @param data.cacheTtl Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl - * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) - * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args - * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key - * - * @param data.queueLimit The maximum size of the queue for which the request would get rejected if that job would push it above that limit - * - * @param data.payload The base64 encoded payload that has been encoded as a JSON. e.g how to encode such payload encodeURIComponent - * `encodeURIComponent(btoa(JSON.stringify({a: 2})))` - * - * @returns unknown job result - * @throws ApiError - */ -export const runWaitResultScriptByPathGet = (data: RunWaitResultScriptByPathGetData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs/run_wait_result/p/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - parent_job: data.parentJob, - tag: data.tag, - cache_ttl: data.cacheTtl, - job_id: data.jobId, - include_header: data.includeHeader, - queue_limit: data.queueLimit, - payload: data.payload - } -}); }; - -/** - * run flow by path and wait until completion in openai format - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody script args - * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args - * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key - * - * @param data.queueLimit The maximum size of the queue for which the request would get rejected if that job would push it above that limit - * - * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) - * @returns unknown job result - * @throws ApiError - */ -export const openaiSyncFlowByPath = (data: OpenaiSyncFlowByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/openai_sync/f/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - include_header: data.includeHeader, - queue_limit: data.queueLimit, - job_id: data.jobId - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * run flow by path and wait until completion - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody script args - * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args - * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key - * - * @param data.queueLimit The maximum size of the queue for which the request would get rejected if that job would push it above that limit - * - * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) - * @returns unknown job result - * @throws ApiError - */ -export const runWaitResultFlowByPath = (data: RunWaitResultFlowByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/run_wait_result/f/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - include_header: data.includeHeader, - queue_limit: data.queueLimit, - job_id: data.jobId - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get job result by id - * @param data The data for the request. - * @param data.workspace - * @param data.flowJobId - * @param data.nodeId - * @returns unknown job result - * @throws ApiError - */ -export const resultById = (data: ResultByIdData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs/result_by_id/{flow_job_id}/{node_id}', - path: { - workspace: data.workspace, - flow_job_id: data.flowJobId, - node_id: data.nodeId - } -}); }; - -/** - * list all flow paths - * @param data The data for the request. - * @param data.workspace - * @returns string list of flow paths - * @throws ApiError - */ -export const listFlowPaths = (data: ListFlowPathsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/flows/list_paths', - path: { - workspace: data.workspace - } -}); }; - -/** - * list flows for search - * @param data The data for the request. - * @param data.workspace - * @returns unknown flow list - * @throws ApiError - */ -export const listSearchFlow = (data: ListSearchFlowData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/flows/list_search', - path: { - workspace: data.workspace - } -}); }; - -/** - * list all flows - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.orderDesc order by desc order (default true) - * @param data.createdBy mask to filter exact matching user creator - * @param data.pathStart mask to filter matching starting path - * @param data.pathExact mask to filter exact matching path - * @param data.showArchived (default false) - * show only the archived files. - * when multiple archived hash share the same path, only the ones with the latest create_at - * are displayed. - * - * @param data.starredOnly (default false) - * show only the starred items - * - * @param data.includeDraftOnly (default false) - * include items that have no deployed version - * - * @param data.withDeploymentMsg (default false) - * include deployment message - * - * @returns unknown All flow - * @throws ApiError - */ -export const listFlows = (data: ListFlowsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/flows/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage, - order_desc: data.orderDesc, - created_by: data.createdBy, - path_start: data.pathStart, - path_exact: data.pathExact, - show_archived: data.showArchived, - starred_only: data.starredOnly, - include_draft_only: data.includeDraftOnly, - with_deployment_msg: data.withDeploymentMsg - } -}); }; - -/** - * get flow history by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns FlowVersion Flow history - * @throws ApiError - */ -export const getFlowHistory = (data: GetFlowHistoryData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/flows/history/p/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get flow's latest version - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns FlowVersion Flow version - * @throws ApiError - */ -export const getFlowLatestVersion = (data: GetFlowLatestVersionData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/flows/get_latest_version/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get flow version - * @param data The data for the request. - * @param data.workspace - * @param data.version - * @param data.path - * @returns Flow flow details - * @throws ApiError - */ -export const getFlowVersion = (data: GetFlowVersionData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/flows/get/v/{version}/p/{path}', - path: { - workspace: data.workspace, - version: data.version, - path: data.path - } -}); }; - -/** - * update flow history - * @param data The data for the request. - * @param data.workspace - * @param data.version - * @param data.path - * @param data.requestBody Flow deployment message - * @returns string success - * @throws ApiError - */ -export const updateFlowHistory = (data: UpdateFlowHistoryData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/flows/history_update/v/{version}/p/{path}', - path: { - workspace: data.workspace, - version: data.version, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get flow by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.withStarredInfo - * @returns Flow flow details - * @throws ApiError - */ -export const getFlowByPath = (data: GetFlowByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/flows/get/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - with_starred_info: data.withStarredInfo - } -}); }; - -/** - * get triggers count of flow - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns TriggersCount triggers count - * @throws ApiError - */ -export const getTriggersCountOfFlow = (data: GetTriggersCountOfFlowData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/flows/get_triggers_count/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get tokens with flow scope - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns TruncatedToken tokens list - * @throws ApiError - */ -export const listTokensOfFlow = (data: ListTokensOfFlowData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/flows/list_tokens/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * Toggle ON and OFF the workspace error handler for a given flow - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody Workspace error handler enabled - * @returns string error handler toggled - * @throws ApiError - */ -export const toggleWorkspaceErrorHandlerForFlow = (data: ToggleWorkspaceErrorHandlerForFlowData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/flows/toggle_workspace_error_handler/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get flow by path with draft - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns unknown flow details with draft - * @throws ApiError - */ -export const getFlowByPathWithDraft = (data: GetFlowByPathWithDraftData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/flows/get/draft/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * exists flow by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean flow details - * @throws ApiError - */ -export const existsFlowByPath = (data: ExistsFlowByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/flows/exists/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * create flow - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody Partially filled flow - * @returns string flow created - * @throws ApiError - */ -export const createFlow = (data: CreateFlowData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/flows/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * update flow - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody Partially filled flow - * @returns string flow updated - * @throws ApiError - */ -export const updateFlow = (data: UpdateFlowData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/flows/update/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * archive flow by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody archiveFlow - * @returns string flow archived - * @throws ApiError - */ -export const archiveFlowByPath = (data: ArchiveFlowByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/flows/archive/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete flow by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string flow delete - * @throws ApiError - */ -export const deleteFlowByPath = (data: DeleteFlowByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/flows/delete/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * list all raw apps - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.orderDesc order by desc order (default true) - * @param data.createdBy mask to filter exact matching user creator - * @param data.pathStart mask to filter matching starting path - * @param data.pathExact mask to filter exact matching path - * @param data.starredOnly (default false) - * show only the starred items - * - * @returns ListableRawApp All raw apps - * @throws ApiError - */ -export const listRawApps = (data: ListRawAppsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/raw_apps/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage, - order_desc: data.orderDesc, - created_by: data.createdBy, - path_start: data.pathStart, - path_exact: data.pathExact, - starred_only: data.starredOnly - } -}); }; - -/** - * does an app exisst at path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean app exists - * @throws ApiError - */ -export const existsRawApp = (data: ExistsRawAppData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/raw_apps/exists/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get app by path - * @param data The data for the request. - * @param data.workspace - * @param data.version - * @param data.path - * @returns string app details - * @throws ApiError - */ -export const getRawAppData = (data: GetRawAppDataData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps/get_data/{version}/{path}', - path: { - workspace: data.workspace, - version: data.version, - path: data.path - } -}); }; - -/** - * list apps for search - * @param data The data for the request. - * @param data.workspace - * @returns unknown app list - * @throws ApiError - */ -export const listSearchApp = (data: ListSearchAppData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps/list_search', - path: { - workspace: data.workspace - } -}); }; - -/** - * list all apps - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.orderDesc order by desc order (default true) - * @param data.createdBy mask to filter exact matching user creator - * @param data.pathStart mask to filter matching starting path - * @param data.pathExact mask to filter exact matching path - * @param data.starredOnly (default false) - * show only the starred items - * - * @param data.includeDraftOnly (default false) - * include items that have no deployed version - * - * @param data.withDeploymentMsg (default false) - * include deployment message - * - * @returns ListableApp All apps - * @throws ApiError - */ -export const listApps = (data: ListAppsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage, - order_desc: data.orderDesc, - created_by: data.createdBy, - path_start: data.pathStart, - path_exact: data.pathExact, - starred_only: data.starredOnly, - include_draft_only: data.includeDraftOnly, - with_deployment_msg: data.withDeploymentMsg - } -}); }; - -/** - * create app - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody new app - * @returns string app created - * @throws ApiError - */ -export const createApp = (data: CreateAppData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/apps/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * does an app exisst at path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean app exists - * @throws ApiError - */ -export const existsApp = (data: ExistsAppData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps/exists/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get app by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.withStarredInfo - * @returns AppWithLastVersion app details - * @throws ApiError - */ -export const getAppByPath = (data: GetAppByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps/get/p/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - with_starred_info: data.withStarredInfo - } -}); }; - -/** - * get app lite by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns AppWithLastVersion app lite details - * @throws ApiError - */ -export const getAppLiteByPath = (data: GetAppLiteByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps/get/lite/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get app by path with draft - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns AppWithLastVersionWDraft app details with draft - * @throws ApiError - */ -export const getAppByPathWithDraft = (data: GetAppByPathWithDraftData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps/get/draft/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get app history by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns AppHistory app history - * @throws ApiError - */ -export const getAppHistoryByPath = (data: GetAppHistoryByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps/history/p/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get apps's latest version - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns AppHistory App version - * @throws ApiError - */ -export const getAppLatestVersion = (data: GetAppLatestVersionData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps/get_latest_version/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * update app history - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.version - * @param data.requestBody App deployment message - * @returns string success - * @throws ApiError - */ -export const updateAppHistory = (data: UpdateAppHistoryData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/apps/history_update/a/{id}/v/{version}', - path: { - workspace: data.workspace, - id: data.id, - version: data.version - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get public app by secret - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns AppWithLastVersion app details - * @throws ApiError - */ -export const getPublicAppBySecret = (data: GetPublicAppBySecretData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps_u/public_app/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get public resource - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns unknown resource value - * @throws ApiError - */ -export const getPublicResource = (data: GetPublicResourceData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps_u/public_resource/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get public secret of app - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string app secret - * @throws ApiError - */ -export const getPublicSecretOfApp = (data: GetPublicSecretOfAppData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps/secret_of/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get app by version - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @returns AppWithLastVersion app details - * @throws ApiError - */ -export const getAppByVersion = (data: GetAppByVersionData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps/get/v/{id}', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * create raw app - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody new raw app - * @returns string raw app created - * @throws ApiError - */ -export const createRawApp = (data: CreateRawAppData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/raw_apps/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * update app - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updateraw app - * @returns string app updated - * @throws ApiError - */ -export const updateRawApp = (data: UpdateRawAppData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/raw_apps/update/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete raw app - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string app deleted - * @throws ApiError - */ -export const deleteRawApp = (data: DeleteRawAppData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/raw_apps/delete/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * delete app - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string app deleted - * @throws ApiError - */ -export const deleteApp = (data: DeleteAppData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/apps/delete/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * update app - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody update app - * @returns string app updated - * @throws ApiError - */ -export const updateApp = (data: UpdateAppData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/apps/update/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * check if custom path exists - * @param data The data for the request. - * @param data.workspace - * @param data.customPath - * @returns boolean custom path exists - * @throws ApiError - */ -export const customPathExists = (data: CustomPathExistsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/apps/custom_path_exists/{custom_path}', - path: { - workspace: data.workspace, - custom_path: data.customPath - } -}); }; - -/** - * executeComponent - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody update app - * @returns string job uuid - * @throws ApiError - */ -export const executeComponent = (data: ExecuteComponentData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/apps_u/execute_component/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * run flow by path - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody flow args - * @param data.scheduledFor when to schedule this job (leave empty for immediate run) - * @param data.scheduledInSecs schedule the script to execute in the number of seconds starting now - * @param data.skipPreprocessor skip the preprocessor - * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any - * @param data.tag Override the tag to use - * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) - * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args - * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key - * - * @param data.invisibleToOwner make the run invisible to the the flow owner (default false) - * @returns string job created - * @throws ApiError - */ -export const runFlowByPath = (data: RunFlowByPathData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/run/f/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - scheduled_for: data.scheduledFor, - scheduled_in_secs: data.scheduledInSecs, - skip_preprocessor: data.skipPreprocessor, - parent_job: data.parentJob, - tag: data.tag, - job_id: data.jobId, - include_header: data.includeHeader, - invisible_to_owner: data.invisibleToOwner - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * restart a completed flow at a given step - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.stepId step id to restart the flow from - * @param data.branchOrIterationN for branchall or loop, the iteration at which the flow should restart - * @param data.requestBody flow args - * @param data.scheduledFor when to schedule this job (leave empty for immediate run) - * @param data.scheduledInSecs schedule the script to execute in the number of seconds starting now - * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any - * @param data.tag Override the tag to use - * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) - * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args - * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key - * - * @param data.invisibleToOwner make the run invisible to the the flow owner (default false) - * @returns string job created - * @throws ApiError - */ -export const restartFlowAtStep = (data: RestartFlowAtStepData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/restart/f/{id}/from/{step_id}/{branch_or_iteration_n}', - path: { - workspace: data.workspace, - id: data.id, - step_id: data.stepId, - branch_or_iteration_n: data.branchOrIterationN - }, - query: { - scheduled_for: data.scheduledFor, - scheduled_in_secs: data.scheduledInSecs, - parent_job: data.parentJob, - tag: data.tag, - job_id: data.jobId, - include_header: data.includeHeader, - invisible_to_owner: data.invisibleToOwner - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * run script by hash - * @param data The data for the request. - * @param data.workspace - * @param data.hash - * @param data.requestBody Partially filled args - * @param data.scheduledFor when to schedule this job (leave empty for immediate run) - * @param data.scheduledInSecs schedule the script to execute in the number of seconds starting now - * @param data.skipPreprocessor skip the preprocessor - * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any - * @param data.tag Override the tag to use - * @param data.cacheTtl Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl - * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) - * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args - * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key - * - * @param data.invisibleToOwner make the run invisible to the the script owner (default false) - * @returns string job created - * @throws ApiError - */ -export const runScriptByHash = (data: RunScriptByHashData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/run/h/{hash}', - path: { - workspace: data.workspace, - hash: data.hash - }, - query: { - scheduled_for: data.scheduledFor, - scheduled_in_secs: data.scheduledInSecs, - skip_preprocessor: data.skipPreprocessor, - parent_job: data.parentJob, - tag: data.tag, - cache_ttl: data.cacheTtl, - job_id: data.jobId, - include_header: data.includeHeader, - invisible_to_owner: data.invisibleToOwner - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * run script preview - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody preview - * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args - * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key - * - * @param data.invisibleToOwner make the run invisible to the the script owner (default false) - * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) - * @returns string job created - * @throws ApiError - */ -export const runScriptPreview = (data: RunScriptPreviewData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/run/preview', - path: { - workspace: data.workspace - }, - query: { - include_header: data.includeHeader, - invisible_to_owner: data.invisibleToOwner, - job_id: data.jobId - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * run code-workflow task - * @param data The data for the request. - * @param data.workspace - * @param data.jobId - * @param data.entrypoint - * @param data.requestBody preview - * @returns string job created - * @throws ApiError - */ -export const runCodeWorkflowTask = (data: RunCodeWorkflowTaskData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/workflow_as_code/{job_id}/{entrypoint}', - path: { - workspace: data.workspace, - job_id: data.jobId, - entrypoint: data.entrypoint - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * run a one-off dependencies job - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody raw script content - * @returns unknown dependency job result - * @throws ApiError - */ -export const runRawScriptDependencies = (data: RunRawScriptDependenciesData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/run/dependencies', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * run flow preview - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody preview - * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args - * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key - * - * @param data.invisibleToOwner make the run invisible to the the script owner (default false) - * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) - * @returns string job created - * @throws ApiError - */ -export const runFlowPreview = (data: RunFlowPreviewData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/run/preview_flow', - path: { - workspace: data.workspace - }, - query: { - include_header: data.includeHeader, - invisible_to_owner: data.invisibleToOwner, - job_id: data.jobId - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * list all queued jobs - * @param data The data for the request. - * @param data.workspace - * @param data.orderDesc order by desc order (default true) - * @param data.createdBy mask to filter exact matching user creator - * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any - * @param data.scriptPathExact mask to filter exact matching path - * @param data.scriptPathStart mask to filter matching starting path - * @param data.schedulePath mask to filter by schedule path - * @param data.scriptHash mask to filter exact matching path - * @param data.startedBefore filter on started before (inclusive) timestamp - * @param data.startedAfter filter on started after (exclusive) timestamp - * @param data.success filter on successful jobs - * @param data.scheduledForBeforeNow filter on jobs scheduled_for before now (hence waitinf for a worker) - * @param data.jobKinds filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, - * @param data.suspended filter on suspended jobs - * @param data.running filter on running jobs - * @param data.args filter on jobs containing those args as a json subset (@> in postgres) - * @param data.result filter on jobs containing those result as a json subset (@> in postgres) - * @param data.tag filter on jobs with a given tag/worker group - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.allWorkspaces get jobs from all workspaces (only valid if request come from the `admins` workspace) - * @param data.isNotSchedule is not a scheduled job - * @returns QueuedJob All queued jobs - * @throws ApiError - */ -export const listQueue = (data: ListQueueData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs/queue/list', - path: { - workspace: data.workspace - }, - query: { - order_desc: data.orderDesc, - created_by: data.createdBy, - parent_job: data.parentJob, - script_path_exact: data.scriptPathExact, - script_path_start: data.scriptPathStart, - schedule_path: data.schedulePath, - script_hash: data.scriptHash, - started_before: data.startedBefore, - started_after: data.startedAfter, - success: data.success, - scheduled_for_before_now: data.scheduledForBeforeNow, - job_kinds: data.jobKinds, - suspended: data.suspended, - running: data.running, - args: data.args, - result: data.result, - tag: data.tag, - page: data.page, - per_page: data.perPage, - all_workspaces: data.allWorkspaces, - is_not_schedule: data.isNotSchedule - } -}); }; - -/** - * get queue count - * @param data The data for the request. - * @param data.workspace - * @param data.allWorkspaces get jobs from all workspaces (only valid if request come from the `admins` workspace) - * @returns unknown queue count - * @throws ApiError - */ -export const getQueueCount = (data: GetQueueCountData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs/queue/count', - path: { - workspace: data.workspace - }, - query: { - all_workspaces: data.allWorkspaces - } -}); }; - -/** - * get completed count - * @param data The data for the request. - * @param data.workspace - * @returns unknown completed count - * @throws ApiError - */ -export const getCompletedCount = (data: GetCompletedCountData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs/completed/count', - path: { - workspace: data.workspace - } -}); }; - -/** - * count number of completed jobs with filter - * @param data The data for the request. - * @param data.workspace - * @param data.completedAfterSAgo - * @param data.success - * @param data.tags - * @param data.allWorkspaces - * @returns number Count of completed jobs - * @throws ApiError - */ -export const countCompletedJobs = (data: CountCompletedJobsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs/completed/count_jobs', - path: { - workspace: data.workspace - }, - query: { - completed_after_s_ago: data.completedAfterSAgo, - success: data.success, - tags: data.tags, - all_workspaces: data.allWorkspaces - } -}); }; - -/** - * get the ids of all jobs matching the given filters - * @param data The data for the request. - * @param data.workspace - * @param data.orderDesc order by desc order (default true) - * @param data.createdBy mask to filter exact matching user creator - * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any - * @param data.scriptPathExact mask to filter exact matching path - * @param data.scriptPathStart mask to filter matching starting path - * @param data.schedulePath mask to filter by schedule path - * @param data.scriptHash mask to filter exact matching path - * @param data.startedBefore filter on started before (inclusive) timestamp - * @param data.startedAfter filter on started after (exclusive) timestamp - * @param data.success filter on successful jobs - * @param data.scheduledForBeforeNow filter on jobs scheduled_for before now (hence waitinf for a worker) - * @param data.jobKinds filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, - * @param data.suspended filter on suspended jobs - * @param data.running filter on running jobs - * @param data.args filter on jobs containing those args as a json subset (@> in postgres) - * @param data.result filter on jobs containing those result as a json subset (@> in postgres) - * @param data.tag filter on jobs with a given tag/worker group - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.concurrencyKey - * @param data.allWorkspaces get jobs from all workspaces (only valid if request come from the `admins` workspace) - * @param data.isNotSchedule is not a scheduled job - * @returns string uuids of jobs - * @throws ApiError - */ -export const listFilteredUuids = (data: ListFilteredUuidsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs/queue/list_filtered_uuids', - path: { - workspace: data.workspace - }, - query: { - order_desc: data.orderDesc, - created_by: data.createdBy, - parent_job: data.parentJob, - script_path_exact: data.scriptPathExact, - script_path_start: data.scriptPathStart, - schedule_path: data.schedulePath, - script_hash: data.scriptHash, - started_before: data.startedBefore, - started_after: data.startedAfter, - success: data.success, - scheduled_for_before_now: data.scheduledForBeforeNow, - job_kinds: data.jobKinds, - suspended: data.suspended, - running: data.running, - args: data.args, - result: data.result, - tag: data.tag, - page: data.page, - per_page: data.perPage, - concurrency_key: data.concurrencyKey, - all_workspaces: data.allWorkspaces, - is_not_schedule: data.isNotSchedule - } -}); }; - -/** - * cancel jobs based on the given uuids - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody uuids of the jobs to cancel - * @returns string uuids of canceled jobs - * @throws ApiError - */ -export const cancelSelection = (data: CancelSelectionData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/queue/cancel_selection', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * list all completed jobs - * @param data The data for the request. - * @param data.workspace - * @param data.orderDesc order by desc order (default true) - * @param data.createdBy mask to filter exact matching user creator - * @param data.label mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') - * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any - * @param data.scriptPathExact mask to filter exact matching path - * @param data.scriptPathStart mask to filter matching starting path - * @param data.schedulePath mask to filter by schedule path - * @param data.scriptHash mask to filter exact matching path - * @param data.startedBefore filter on started before (inclusive) timestamp - * @param data.startedAfter filter on started after (exclusive) timestamp - * @param data.success filter on successful jobs - * @param data.jobKinds filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, - * @param data.args filter on jobs containing those args as a json subset (@> in postgres) - * @param data.result filter on jobs containing those result as a json subset (@> in postgres) - * @param data.tag filter on jobs with a given tag/worker group - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.isSkipped is the job skipped - * @param data.isFlowStep is the job a flow step - * @param data.hasNullParent has null parent - * @param data.isNotSchedule is not a scheduled job - * @returns CompletedJob All completed jobs - * @throws ApiError - */ -export const listCompletedJobs = (data: ListCompletedJobsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs/completed/list', - path: { - workspace: data.workspace - }, - query: { - order_desc: data.orderDesc, - created_by: data.createdBy, - label: data.label, - parent_job: data.parentJob, - script_path_exact: data.scriptPathExact, - script_path_start: data.scriptPathStart, - schedule_path: data.schedulePath, - script_hash: data.scriptHash, - started_before: data.startedBefore, - started_after: data.startedAfter, - success: data.success, - job_kinds: data.jobKinds, - args: data.args, - result: data.result, - tag: data.tag, - page: data.page, - per_page: data.perPage, - is_skipped: data.isSkipped, - is_flow_step: data.isFlowStep, - has_null_parent: data.hasNullParent, - is_not_schedule: data.isNotSchedule - } -}); }; - -/** - * list all jobs - * @param data The data for the request. - * @param data.workspace - * @param data.createdBy mask to filter exact matching user creator - * @param data.label mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') - * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any - * @param data.scriptPathExact mask to filter exact matching path - * @param data.scriptPathStart mask to filter matching starting path - * @param data.schedulePath mask to filter by schedule path - * @param data.scriptHash mask to filter exact matching path - * @param data.startedBefore filter on started before (inclusive) timestamp - * @param data.startedAfter filter on started after (exclusive) timestamp - * @param data.createdBefore filter on created before (inclusive) timestamp - * @param data.createdAfter filter on created after (exclusive) timestamp - * @param data.createdOrStartedBefore filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp - * @param data.running filter on running jobs - * @param data.scheduledForBeforeNow filter on jobs scheduled_for before now (hence waitinf for a worker) - * @param data.createdOrStartedAfter filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp - * @param data.createdOrStartedAfterCompletedJobs filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp but only for the completed jobs - * @param data.jobKinds filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, - * @param data.suspended filter on suspended jobs - * @param data.args filter on jobs containing those args as a json subset (@> in postgres) - * @param data.tag filter on jobs with a given tag/worker group - * @param data.result filter on jobs containing those result as a json subset (@> in postgres) - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.isSkipped is the job skipped - * @param data.isFlowStep is the job a flow step - * @param data.hasNullParent has null parent - * @param data.success filter on successful jobs - * @param data.allWorkspaces get jobs from all workspaces (only valid if request come from the `admins` workspace) - * @param data.isNotSchedule is not a scheduled job - * @returns Job All jobs - * @throws ApiError - */ -export const listJobs = (data: ListJobsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs/list', - path: { - workspace: data.workspace - }, - query: { - created_by: data.createdBy, - label: data.label, - parent_job: data.parentJob, - script_path_exact: data.scriptPathExact, - script_path_start: data.scriptPathStart, - schedule_path: data.schedulePath, - script_hash: data.scriptHash, - started_before: data.startedBefore, - started_after: data.startedAfter, - created_before: data.createdBefore, - created_after: data.createdAfter, - created_or_started_before: data.createdOrStartedBefore, - running: data.running, - scheduled_for_before_now: data.scheduledForBeforeNow, - created_or_started_after: data.createdOrStartedAfter, - created_or_started_after_completed_jobs: data.createdOrStartedAfterCompletedJobs, - job_kinds: data.jobKinds, - suspended: data.suspended, - args: data.args, - tag: data.tag, - result: data.result, - page: data.page, - per_page: data.perPage, - is_skipped: data.isSkipped, - is_flow_step: data.isFlowStep, - has_null_parent: data.hasNullParent, - success: data.success, - all_workspaces: data.allWorkspaces, - is_not_schedule: data.isNotSchedule - } -}); }; - -/** - * get db clock - * @returns number the timestamp of the db that can be used to compute the drift - * @throws ApiError - */ -export const getDbClock = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/jobs/db_clock' -}); }; - -/** - * Count jobs by tag - * @param data The data for the request. - * @param data.horizonSecs Past Time horizon in seconds (when to start the count = now - horizon) (default is 3600) - * @param data.workspaceId Specific workspace ID to filter results (optional) - * @returns unknown Job counts by tag - * @throws ApiError - */ -export const countJobsByTag = (data: CountJobsByTagData = {}): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/jobs/completed/count_by_tag', - query: { - horizon_secs: data.horizonSecs, - workspace_id: data.workspaceId - } -}); }; - -/** - * get job - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.noLogs - * @returns Job job details - * @throws ApiError - */ -export const getJob = (data: GetJobData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs_u/get/{id}', - path: { - workspace: data.workspace, - id: data.id - }, - query: { - no_logs: data.noLogs - } -}); }; - -/** - * get root job id - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @returns string get root job id - * @throws ApiError - */ -export const getRootJobId = (data: GetRootJobIdData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs_u/get_root_job_id/{id}', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * get job logs - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @returns string job details - * @throws ApiError - */ -export const getJobLogs = (data: GetJobLogsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs_u/get_logs/{id}', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * get job args - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @returns unknown job args - * @throws ApiError - */ -export const getJobArgs = (data: GetJobArgsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs_u/get_args/{id}', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * get job updates - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.running - * @param data.logOffset - * @param data.getProgress - * @returns unknown job details - * @throws ApiError - */ -export const getJobUpdates = (data: GetJobUpdatesData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs_u/getupdate/{id}', - path: { - workspace: data.workspace, - id: data.id - }, - query: { - running: data.running, - log_offset: data.logOffset, - get_progress: data.getProgress - } -}); }; - -/** - * get log file from object store - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns unknown job log - * @throws ApiError - */ -export const getLogFileFromStore = (data: GetLogFileFromStoreData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs_u/get_log_file/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get flow debug info - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @returns unknown flow debug info details - * @throws ApiError - */ -export const getFlowDebugInfo = (data: GetFlowDebugInfoData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs_u/get_flow_debug_info/{id}', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * get completed job - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @returns CompletedJob job details - * @throws ApiError - */ -export const getCompletedJob = (data: GetCompletedJobData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs_u/completed/get/{id}', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * get completed job result - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.suspendedJob - * @param data.resumeId - * @param data.secret - * @param data.approver - * @returns unknown result - * @throws ApiError - */ -export const getCompletedJobResult = (data: GetCompletedJobResultData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs_u/completed/get_result/{id}', - path: { - workspace: data.workspace, - id: data.id - }, - query: { - suspended_job: data.suspendedJob, - resume_id: data.resumeId, - secret: data.secret, - approver: data.approver - } -}); }; - -/** - * get completed job result if job is completed - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.getStarted - * @returns unknown result - * @throws ApiError - */ -export const getCompletedJobResultMaybe = (data: GetCompletedJobResultMaybeData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs_u/completed/get_result_maybe/{id}', - path: { - workspace: data.workspace, - id: data.id - }, - query: { - get_started: data.getStarted - } -}); }; - -/** - * delete completed job (erase content but keep run id) - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @returns CompletedJob job details - * @throws ApiError - */ -export const deleteCompletedJob = (data: DeleteCompletedJobData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/completed/delete/{id}', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * cancel queued or running job - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.requestBody reason - * @returns string job canceled - * @throws ApiError - */ -export const cancelQueuedJob = (data: CancelQueuedJobData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs_u/queue/cancel/{id}', - path: { - workspace: data.workspace, - id: data.id - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * cancel all queued jobs for persistent script - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody reason - * @returns string persistent job scaled down to zero - * @throws ApiError - */ -export const cancelPersistentQueuedJobs = (data: CancelPersistentQueuedJobsData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs_u/queue/cancel_persistent/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * force cancel queued job - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.requestBody reason - * @returns string job canceled - * @throws ApiError - */ -export const forceCancelQueuedJob = (data: ForceCancelQueuedJobData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs_u/queue/force_cancel/{id}', - path: { - workspace: data.workspace, - id: data.id - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * create an HMac signature given a job id and a resume id - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.resumeId - * @param data.approver - * @returns string job signature - * @throws ApiError - */ -export const createJobSignature = (data: CreateJobSignatureData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs/job_signature/{id}/{resume_id}', - path: { - workspace: data.workspace, - id: data.id, - resume_id: data.resumeId - }, - query: { - approver: data.approver - } -}); }; - -/** - * get resume urls given a job_id, resume_id and a nonce to resume a flow - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.resumeId - * @param data.approver - * @returns unknown url endpoints - * @throws ApiError - */ -export const getResumeUrls = (data: GetResumeUrlsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs/resume_urls/{id}/{resume_id}', - path: { - workspace: data.workspace, - id: data.id, - resume_id: data.resumeId - }, - query: { - approver: data.approver - } -}); }; - -/** - * generate interactive slack approval for suspended job - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.slackResourcePath - * @param data.channelId - * @param data.flowStepId - * @param data.approver - * @param data.message - * @param data.defaultArgsJson - * @param data.dynamicEnumsJson - * @returns unknown Interactive slack approval message sent successfully - * @throws ApiError - */ -export const getSlackApprovalPayload = (data: GetSlackApprovalPayloadData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs/slack_approval/{id}', - path: { - workspace: data.workspace, - id: data.id - }, - query: { - approver: data.approver, - message: data.message, - slack_resource_path: data.slackResourcePath, - channel_id: data.channelId, - flow_step_id: data.flowStepId, - default_args_json: data.defaultArgsJson, - dynamic_enums_json: data.dynamicEnumsJson - } -}); }; - -/** - * resume a job for a suspended flow - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.resumeId - * @param data.signature - * @param data.payload The base64 encoded payload that has been encoded as a JSON. e.g how to encode such payload encodeURIComponent - * `encodeURIComponent(btoa(JSON.stringify({a: 2})))` - * - * @param data.approver - * @returns string job resumed - * @throws ApiError - */ -export const resumeSuspendedJobGet = (data: ResumeSuspendedJobGetData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs_u/resume/{id}/{resume_id}/{signature}', - path: { - workspace: data.workspace, - id: data.id, - resume_id: data.resumeId, - signature: data.signature - }, - query: { - payload: data.payload, - approver: data.approver - } -}); }; - -/** - * resume a job for a suspended flow - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.resumeId - * @param data.signature - * @param data.requestBody - * @param data.approver - * @returns string job resumed - * @throws ApiError - */ -export const resumeSuspendedJobPost = (data: ResumeSuspendedJobPostData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs_u/resume/{id}/{resume_id}/{signature}', - path: { - workspace: data.workspace, - id: data.id, - resume_id: data.resumeId, - signature: data.signature - }, - query: { - approver: data.approver - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * set flow user state at a given key - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.key - * @param data.requestBody new value - * @returns string flow user state updated - * @throws ApiError - */ -export const setFlowUserState = (data: SetFlowUserStateData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/flow/user_states/{id}/{key}', - path: { - workspace: data.workspace, - id: data.id, - key: data.key - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get flow user state at a given key - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.key - * @returns unknown flow user state updated - * @throws ApiError - */ -export const getFlowUserState = (data: GetFlowUserStateData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs/flow/user_states/{id}/{key}', - path: { - workspace: data.workspace, - id: data.id, - key: data.key - } -}); }; - -/** - * resume a job for a suspended flow as an owner - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.requestBody - * @returns string job resumed - * @throws ApiError - */ -export const resumeSuspendedFlowAsOwner = (data: ResumeSuspendedFlowAsOwnerData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs/flow/resume/{id}', - path: { - workspace: data.workspace, - id: data.id - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * cancel a job for a suspended flow - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.resumeId - * @param data.signature - * @param data.approver - * @returns string job canceled - * @throws ApiError - */ -export const cancelSuspendedJobGet = (data: CancelSuspendedJobGetData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs_u/cancel/{id}/{resume_id}/{signature}', - path: { - workspace: data.workspace, - id: data.id, - resume_id: data.resumeId, - signature: data.signature - }, - query: { - approver: data.approver - } -}); }; - -/** - * cancel a job for a suspended flow - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.resumeId - * @param data.signature - * @param data.requestBody - * @param data.approver - * @returns string job canceled - * @throws ApiError - */ -export const cancelSuspendedJobPost = (data: CancelSuspendedJobPostData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/jobs_u/cancel/{id}/{resume_id}/{signature}', - path: { - workspace: data.workspace, - id: data.id, - resume_id: data.resumeId, - signature: data.signature - }, - query: { - approver: data.approver - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get parent flow job of suspended job - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.resumeId - * @param data.signature - * @param data.approver - * @returns unknown parent flow details - * @throws ApiError - */ -export const getSuspendedJobFlow = (data: GetSuspendedJobFlowData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/jobs_u/get_flow/{id}/{resume_id}/{signature}', - path: { - workspace: data.workspace, - id: data.id, - resume_id: data.resumeId, - signature: data.signature - }, - query: { - approver: data.approver - } -}); }; - -/** - * preview schedule - * @param data The data for the request. - * @param data.requestBody schedule - * @returns string List of 5 estimated upcoming execution events (in UTC) - * @throws ApiError - */ -export const previewSchedule = (data: PreviewScheduleData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/schedules/preview', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * create schedule - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody new schedule - * @returns string schedule created - * @throws ApiError - */ -export const createSchedule = (data: CreateScheduleData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/schedules/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * update schedule - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated schedule - * @returns string schedule updated - * @throws ApiError - */ -export const updateSchedule = (data: UpdateScheduleData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/schedules/update/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * set enabled schedule - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated schedule enable - * @returns string schedule enabled set - * @throws ApiError - */ -export const setScheduleEnabled = (data: SetScheduleEnabledData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/schedules/setenabled/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete schedule - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string schedule deleted - * @throws ApiError - */ -export const deleteSchedule = (data: DeleteScheduleData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/schedules/delete/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get schedule - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns Schedule schedule deleted - * @throws ApiError - */ -export const getSchedule = (data: GetScheduleData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/schedules/get/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * does schedule exists - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean schedule exists - * @throws ApiError - */ -export const existsSchedule = (data: ExistsScheduleData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/schedules/exists/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * list schedules - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.args filter on jobs containing those args as a json subset (@> in postgres) - * @param data.path filter by path - * @param data.isFlow - * @param data.pathStart - * @returns Schedule schedule list - * @throws ApiError - */ -export const listSchedules = (data: ListSchedulesData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/schedules/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage, - args: data.args, - path: data.path, - is_flow: data.isFlow, - path_start: data.pathStart - } -}); }; - -/** - * list schedules with last 20 jobs - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @returns ScheduleWJobs schedule list - * @throws ApiError - */ -export const listSchedulesWithJobs = (data: ListSchedulesWithJobsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/schedules/list_with_jobs', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage - } -}); }; - -/** - * Set default error or recoevery handler - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody Handler description - * @returns unknown default error handler set - * @throws ApiError - */ -export const setDefaultErrorOrRecoveryHandler = (data: SetDefaultErrorOrRecoveryHandlerData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/schedules/setdefaulthandler', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * create http trigger - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody new http trigger - * @returns string http trigger created - * @throws ApiError - */ -export const createHttpTrigger = (data: CreateHttpTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/http_triggers/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * update http trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated trigger - * @returns string http trigger updated - * @throws ApiError - */ -export const updateHttpTrigger = (data: UpdateHttpTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/http_triggers/update/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete http trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string http trigger deleted - * @throws ApiError - */ -export const deleteHttpTrigger = (data: DeleteHttpTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/http_triggers/delete/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get http trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns HttpTrigger http trigger deleted - * @throws ApiError - */ -export const getHttpTrigger = (data: GetHttpTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/http_triggers/get/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * list http triggers - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.path filter by path - * @param data.isFlow - * @param data.pathStart - * @returns HttpTrigger http trigger list - * @throws ApiError - */ -export const listHttpTriggers = (data: ListHttpTriggersData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/http_triggers/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage, - path: data.path, - is_flow: data.isFlow, - path_start: data.pathStart - } -}); }; - -/** - * does http trigger exists - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean http trigger exists - * @throws ApiError - */ -export const existsHttpTrigger = (data: ExistsHttpTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/http_triggers/exists/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * does route exists - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody route exists request - * @returns boolean route exists - * @throws ApiError - */ -export const existsRoute = (data: ExistsRouteData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/http_triggers/route_exists', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * create websocket trigger - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody new websocket trigger - * @returns string websocket trigger created - * @throws ApiError - */ -export const createWebsocketTrigger = (data: CreateWebsocketTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/websocket_triggers/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * update websocket trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated trigger - * @returns string websocket trigger updated - * @throws ApiError - */ -export const updateWebsocketTrigger = (data: UpdateWebsocketTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/websocket_triggers/update/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete websocket trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string websocket trigger deleted - * @throws ApiError - */ -export const deleteWebsocketTrigger = (data: DeleteWebsocketTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/websocket_triggers/delete/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get websocket trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns WebsocketTrigger websocket trigger deleted - * @throws ApiError - */ -export const getWebsocketTrigger = (data: GetWebsocketTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/websocket_triggers/get/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * list websocket triggers - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.path filter by path - * @param data.isFlow - * @param data.pathStart - * @returns WebsocketTrigger websocket trigger list - * @throws ApiError - */ -export const listWebsocketTriggers = (data: ListWebsocketTriggersData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/websocket_triggers/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage, - path: data.path, - is_flow: data.isFlow, - path_start: data.pathStart - } -}); }; - -/** - * does websocket trigger exists - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean websocket trigger exists - * @throws ApiError - */ -export const existsWebsocketTrigger = (data: ExistsWebsocketTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/websocket_triggers/exists/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * set enabled websocket trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated websocket trigger enable - * @returns string websocket trigger enabled set - * @throws ApiError - */ -export const setWebsocketTriggerEnabled = (data: SetWebsocketTriggerEnabledData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/websocket_triggers/setenabled/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * test websocket connection - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody test websocket connection - * @returns string successfuly connected to websocket - * @throws ApiError - */ -export const testWebsocketConnection = (data: TestWebsocketConnectionData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/websocket_triggers/test', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * create kafka trigger - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody new kafka trigger - * @returns string kafka trigger created - * @throws ApiError - */ -export const createKafkaTrigger = (data: CreateKafkaTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/kafka_triggers/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * update kafka trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated trigger - * @returns string kafka trigger updated - * @throws ApiError - */ -export const updateKafkaTrigger = (data: UpdateKafkaTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/kafka_triggers/update/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete kafka trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string kafka trigger deleted - * @throws ApiError - */ -export const deleteKafkaTrigger = (data: DeleteKafkaTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/kafka_triggers/delete/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get kafka trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns KafkaTrigger kafka trigger deleted - * @throws ApiError - */ -export const getKafkaTrigger = (data: GetKafkaTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/kafka_triggers/get/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * list kafka triggers - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.path filter by path - * @param data.isFlow - * @param data.pathStart - * @returns KafkaTrigger kafka trigger list - * @throws ApiError - */ -export const listKafkaTriggers = (data: ListKafkaTriggersData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/kafka_triggers/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage, - path: data.path, - is_flow: data.isFlow, - path_start: data.pathStart - } -}); }; - -/** - * does kafka trigger exists - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean kafka trigger exists - * @throws ApiError - */ -export const existsKafkaTrigger = (data: ExistsKafkaTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/kafka_triggers/exists/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * set enabled kafka trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated kafka trigger enable - * @returns string kafka trigger enabled set - * @throws ApiError - */ -export const setKafkaTriggerEnabled = (data: SetKafkaTriggerEnabledData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/kafka_triggers/setenabled/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * test kafka connection - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody test kafka connection - * @returns string successfuly connected to kafka brokers - * @throws ApiError - */ -export const testKafkaConnection = (data: TestKafkaConnectionData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/kafka_triggers/test', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * create nats trigger - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody new nats trigger - * @returns string nats trigger created - * @throws ApiError - */ -export const createNatsTrigger = (data: CreateNatsTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/nats_triggers/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * update nats trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated trigger - * @returns string nats trigger updated - * @throws ApiError - */ -export const updateNatsTrigger = (data: UpdateNatsTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/nats_triggers/update/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete nats trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string nats trigger deleted - * @throws ApiError - */ -export const deleteNatsTrigger = (data: DeleteNatsTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/nats_triggers/delete/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get nats trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns NatsTrigger nats trigger deleted - * @throws ApiError - */ -export const getNatsTrigger = (data: GetNatsTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/nats_triggers/get/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * list nats triggers - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.path filter by path - * @param data.isFlow - * @param data.pathStart - * @returns NatsTrigger nats trigger list - * @throws ApiError - */ -export const listNatsTriggers = (data: ListNatsTriggersData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/nats_triggers/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage, - path: data.path, - is_flow: data.isFlow, - path_start: data.pathStart - } -}); }; - -/** - * does nats trigger exists - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean nats trigger exists - * @throws ApiError - */ -export const existsNatsTrigger = (data: ExistsNatsTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/nats_triggers/exists/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * set enabled nats trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated nats trigger enable - * @returns string nats trigger enabled set - * @throws ApiError - */ -export const setNatsTriggerEnabled = (data: SetNatsTriggerEnabledData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/nats_triggers/setenabled/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * test NATS connection - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody test nats connection - * @returns string successfuly connected to NATS servers - * @throws ApiError - */ -export const testNatsConnection = (data: TestNatsConnectionData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/nats_triggers/test', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * check if postgres configuration is set to logical - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean boolean that indicates if postgres is set to logical level or not - * @throws ApiError - */ -export const isValidPostgresConfiguration = (data: IsValidPostgresConfigurationData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/postgres_triggers/is_valid_postgres_configuration/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * create template script - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody template script - * @returns string custom id to retrieve template script - * @throws ApiError - */ -export const createTemplateScript = (data: CreateTemplateScriptData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/postgres_triggers/create_template_script', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get template script - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @returns string template script - * @throws ApiError - */ -export const getTemplateScript = (data: GetTemplateScriptData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/postgres_triggers/get_template_script/{id}', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * list postgres replication slot - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns SlotList list postgres slot - * @throws ApiError - */ -export const listPostgresReplicationSlot = (data: ListPostgresReplicationSlotData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/postgres_triggers/slot/list/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * create replication slot for postgres - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody new slot for postgres - * @returns string slot created - * @throws ApiError - */ -export const createPostgresReplicationSlot = (data: CreatePostgresReplicationSlotData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/postgres_triggers/slot/create/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete postgres replication slot - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody replication slot of postgres - * @returns string postgres replication slot deleted - * @throws ApiError - */ -export const deletePostgresReplicationSlot = (data: DeletePostgresReplicationSlotData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/postgres_triggers/slot/delete/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * list postgres publication - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string database publication list - * @throws ApiError - */ -export const listPostgresPublication = (data: ListPostgresPublicationData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/postgres_triggers/publication/list/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get postgres publication - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.publication - * @returns PublicationData postgres publication get - * @throws ApiError - */ -export const getPostgresPublication = (data: GetPostgresPublicationData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/postgres_triggers/publication/get/{publication}/{path}', - path: { - workspace: data.workspace, - path: data.path, - publication: data.publication - } -}); }; - -/** - * create publication for postgres - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.publication - * @param data.requestBody new publication for postgres - * @returns string publication created - * @throws ApiError - */ -export const createPostgresPublication = (data: CreatePostgresPublicationData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/postgres_triggers/publication/create/{publication}/{path}', - path: { - workspace: data.workspace, - path: data.path, - publication: data.publication - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * update publication for postgres - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.publication - * @param data.requestBody update publication for postgres - * @returns string publication updated - * @throws ApiError - */ -export const updatePostgresPublication = (data: UpdatePostgresPublicationData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/postgres_triggers/publication/update/{publication}/{path}', - path: { - workspace: data.workspace, - path: data.path, - publication: data.publication - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete postgres publication - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.publication - * @returns string postgres publication deleted - * @throws ApiError - */ -export const deletePostgresPublication = (data: DeletePostgresPublicationData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/postgres_triggers/publication/delete/{publication}/{path}', - path: { - workspace: data.workspace, - path: data.path, - publication: data.publication - } -}); }; - -/** - * create postgres trigger - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody new postgres trigger - * @returns string postgres trigger created - * @throws ApiError - */ -export const createPostgresTrigger = (data: CreatePostgresTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/postgres_triggers/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * update postgres trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated trigger - * @returns string postgres trigger updated - * @throws ApiError - */ -export const updatePostgresTrigger = (data: UpdatePostgresTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/postgres_triggers/update/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete postgres trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns string postgres trigger deleted - * @throws ApiError - */ -export const deletePostgresTrigger = (data: DeletePostgresTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/postgres_triggers/delete/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * get postgres trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns PostgresTrigger get postgres trigger - * @throws ApiError - */ -export const getPostgresTrigger = (data: GetPostgresTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/postgres_triggers/get/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * list postgres triggers - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.path filter by path - * @param data.isFlow - * @param data.pathStart - * @returns PostgresTrigger postgres trigger list - * @throws ApiError - */ -export const listPostgresTriggers = (data: ListPostgresTriggersData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/postgres_triggers/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage, - path: data.path, - is_flow: data.isFlow, - path_start: data.pathStart - } -}); }; - -/** - * does postgres trigger exists - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @returns boolean postgres trigger exists - * @throws ApiError - */ -export const existsPostgresTrigger = (data: ExistsPostgresTriggerData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/postgres_triggers/exists/{path}', - path: { - workspace: data.workspace, - path: data.path - } -}); }; - -/** - * set enabled postgres trigger - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.requestBody updated postgres trigger enable - * @returns string postgres trigger enabled set - * @throws ApiError - */ -export const setPostgresTriggerEnabled = (data: SetPostgresTriggerEnabledData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/postgres_triggers/setenabled/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * list instance groups - * @returns InstanceGroup instance group list - * @throws ApiError - */ -export const listInstanceGroups = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/groups/list' -}); }; - -/** - * get instance group - * @param data The data for the request. - * @param data.name - * @returns InstanceGroup instance group - * @throws ApiError - */ -export const getInstanceGroup = (data: GetInstanceGroupData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/groups/get/{name}', - path: { - name: data.name - } -}); }; - -/** - * create instance group - * @param data The data for the request. - * @param data.requestBody create instance group - * @returns string instance group created - * @throws ApiError - */ -export const createInstanceGroup = (data: CreateInstanceGroupData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/groups/create', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * update instance group - * @param data The data for the request. - * @param data.name - * @param data.requestBody update instance group - * @returns string instance group updated - * @throws ApiError - */ -export const updateInstanceGroup = (data: UpdateInstanceGroupData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/groups/update/{name}', - path: { - name: data.name - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete instance group - * @param data The data for the request. - * @param data.name - * @returns string instance group deleted - * @throws ApiError - */ -export const deleteInstanceGroup = (data: DeleteInstanceGroupData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/groups/delete/{name}', - path: { - name: data.name - } -}); }; - -/** - * add user to instance group - * @param data The data for the request. - * @param data.name - * @param data.requestBody user to add to instance group - * @returns string user added to instance group - * @throws ApiError - */ -export const addUserToInstanceGroup = (data: AddUserToInstanceGroupData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/groups/adduser/{name}', - path: { - name: data.name - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * remove user from instance group - * @param data The data for the request. - * @param data.name - * @param data.requestBody user to remove from instance group - * @returns string user removed from instance group - * @throws ApiError - */ -export const removeUserFromInstanceGroup = (data: RemoveUserFromInstanceGroupData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/groups/removeuser/{name}', - path: { - name: data.name - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * export instance groups - * @returns ExportedInstanceGroup exported instance groups - * @throws ApiError - */ -export const exportInstanceGroups = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/groups/export' -}); }; - -/** - * overwrite instance groups - * @param data The data for the request. - * @param data.requestBody overwrite instance groups - * @returns string success message - * @throws ApiError - */ -export const overwriteInstanceGroups = (data: OverwriteInstanceGroupsData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/groups/overwrite', - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * list groups - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @returns Group group list - * @throws ApiError - */ -export const listGroups = (data: ListGroupsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/groups/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage - } -}); }; - -/** - * list group names - * @param data The data for the request. - * @param data.workspace - * @param data.onlyMemberOf only list the groups the user is member of (default false) - * @returns string group list - * @throws ApiError - */ -export const listGroupNames = (data: ListGroupNamesData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/groups/listnames', - path: { - workspace: data.workspace - }, - query: { - only_member_of: data.onlyMemberOf - } -}); }; - -/** - * create group - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody create group - * @returns string group created - * @throws ApiError - */ -export const createGroup = (data: CreateGroupData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/groups/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * update group - * @param data The data for the request. - * @param data.workspace - * @param data.name - * @param data.requestBody updated group - * @returns string group updated - * @throws ApiError - */ -export const updateGroup = (data: UpdateGroupData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/groups/update/{name}', - path: { - workspace: data.workspace, - name: data.name - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete group - * @param data The data for the request. - * @param data.workspace - * @param data.name - * @returns string group deleted - * @throws ApiError - */ -export const deleteGroup = (data: DeleteGroupData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/groups/delete/{name}', - path: { - workspace: data.workspace, - name: data.name - } -}); }; - -/** - * get group - * @param data The data for the request. - * @param data.workspace - * @param data.name - * @returns Group group - * @throws ApiError - */ -export const getGroup = (data: GetGroupData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/groups/get/{name}', - path: { - workspace: data.workspace, - name: data.name - } -}); }; - -/** - * add user to group - * @param data The data for the request. - * @param data.workspace - * @param data.name - * @param data.requestBody added user to group - * @returns string user added to group - * @throws ApiError - */ -export const addUserToGroup = (data: AddUserToGroupData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/groups/adduser/{name}', - path: { - workspace: data.workspace, - name: data.name - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * remove user to group - * @param data The data for the request. - * @param data.workspace - * @param data.name - * @param data.requestBody added user to group - * @returns string user removed from group - * @throws ApiError - */ -export const removeUserToGroup = (data: RemoveUserToGroupData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/groups/removeuser/{name}', - path: { - workspace: data.workspace, - name: data.name - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * list folders - * @param data The data for the request. - * @param data.workspace - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @returns Folder folder list - * @throws ApiError - */ -export const listFolders = (data: ListFoldersData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/folders/list', - path: { - workspace: data.workspace - }, - query: { - page: data.page, - per_page: data.perPage - } -}); }; - -/** - * list folder names - * @param data The data for the request. - * @param data.workspace - * @param data.onlyMemberOf only list the folders the user is member of (default false) - * @returns string folder list - * @throws ApiError - */ -export const listFolderNames = (data: ListFolderNamesData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/folders/listnames', - path: { - workspace: data.workspace - }, - query: { - only_member_of: data.onlyMemberOf - } -}); }; - -/** - * create folder - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody create folder - * @returns string folder created - * @throws ApiError - */ -export const createFolder = (data: CreateFolderData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/folders/create', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * update folder - * @param data The data for the request. - * @param data.workspace - * @param data.name - * @param data.requestBody update folder - * @returns string folder updated - * @throws ApiError - */ -export const updateFolder = (data: UpdateFolderData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/folders/update/{name}', - path: { - workspace: data.workspace, - name: data.name - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * delete folder - * @param data The data for the request. - * @param data.workspace - * @param data.name - * @returns string folder deleted - * @throws ApiError - */ -export const deleteFolder = (data: DeleteFolderData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/folders/delete/{name}', - path: { - workspace: data.workspace, - name: data.name - } -}); }; - -/** - * get folder - * @param data The data for the request. - * @param data.workspace - * @param data.name - * @returns Folder folder - * @throws ApiError - */ -export const getFolder = (data: GetFolderData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/folders/get/{name}', - path: { - workspace: data.workspace, - name: data.name - } -}); }; - -/** - * get folder usage - * @param data The data for the request. - * @param data.workspace - * @param data.name - * @returns unknown folder - * @throws ApiError - */ -export const getFolderUsage = (data: GetFolderUsageData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/folders/getusage/{name}', - path: { - workspace: data.workspace, - name: data.name - } -}); }; - -/** - * add owner to folder - * @param data The data for the request. - * @param data.workspace - * @param data.name - * @param data.requestBody owner user to folder - * @returns string owner added to folder - * @throws ApiError - */ -export const addOwnerToFolder = (data: AddOwnerToFolderData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/folders/addowner/{name}', - path: { - workspace: data.workspace, - name: data.name - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * remove owner to folder - * @param data The data for the request. - * @param data.workspace - * @param data.name - * @param data.requestBody added owner to folder - * @returns string owner removed from folder - * @throws ApiError - */ -export const removeOwnerToFolder = (data: RemoveOwnerToFolderData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/folders/removeowner/{name}', - path: { - workspace: data.workspace, - name: data.name - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * list workers - * @param data The data for the request. - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.pingSince number of seconds the worker must have had a last ping more recent of (default to 300) - * @returns WorkerPing a list of workers - * @throws ApiError - */ -export const listWorkers = (data: ListWorkersData = {}): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/workers/list', - query: { - page: data.page, - per_page: data.perPage, - ping_since: data.pingSince - } -}); }; - -/** - * exists worker with tag - * @param data The data for the request. - * @param data.tag - * @returns boolean whether a worker with the tag exists - * @throws ApiError - */ -export const existsWorkerWithTag = (data: ExistsWorkerWithTagData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/workers/exists_worker_with_tag', - query: { - tag: data.tag - } -}); }; - -/** - * get queue metrics - * @returns unknown metrics - * @throws ApiError - */ -export const getQueueMetrics = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/workers/queue_metrics' -}); }; - -/** - * get counts of jobs waiting for an executor per tag - * @returns number queue counts - * @throws ApiError - */ -export const getCountsOfJobsWaitingPerTag = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/workers/queue_counts' -}); }; - -/** - * list worker groups - * @returns unknown a list of worker group configs - * @throws ApiError - */ -export const listWorkerGroups = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/configs/list_worker_groups' -}); }; - -/** - * get config - * @param data The data for the request. - * @param data.name - * @returns unknown a config - * @throws ApiError - */ -export const getConfig = (data: GetConfigData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/configs/get/{name}', - path: { - name: data.name - } -}); }; - -/** - * Update config - * @param data The data for the request. - * @param data.name - * @param data.requestBody worker group - * @returns string Update a worker group - * @throws ApiError - */ -export const updateConfig = (data: UpdateConfigData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/configs/update/{name}', - path: { - name: data.name - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * Delete Config - * @param data The data for the request. - * @param data.name - * @returns string Delete config - * @throws ApiError - */ -export const deleteConfig = (data: DeleteConfigData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/configs/update/{name}', - path: { - name: data.name - } -}); }; - -/** - * list configs - * @returns Config list of configs - * @throws ApiError - */ -export const listConfigs = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/configs/list' -}); }; - -/** - * List autoscaling events - * @param data The data for the request. - * @param data.workerGroup - * @returns AutoscalingEvent List of autoscaling events - * @throws ApiError - */ -export const listAutoscalingEvents = (data: ListAutoscalingEventsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/configs/list_autoscaling_events/{worker_group}', - path: { - worker_group: data.workerGroup - } -}); }; - -/** - * get granular acls - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.kind - * @returns boolean acls - * @throws ApiError - */ -export const getGranularAcls = (data: GetGranularAclsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/acls/get/{kind}/{path}', - path: { - workspace: data.workspace, - path: data.path, - kind: data.kind - } -}); }; - -/** - * add granular acls - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.kind - * @param data.requestBody acl to add - * @returns string granular acl added - * @throws ApiError - */ -export const addGranularAcls = (data: AddGranularAclsData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/acls/add/{kind}/{path}', - path: { - workspace: data.workspace, - path: data.path, - kind: data.kind - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * remove granular acls - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.kind - * @param data.requestBody acl to add - * @returns string granular acl removed - * @throws ApiError - */ -export const removeGranularAcls = (data: RemoveGranularAclsData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/acls/remove/{kind}/{path}', - path: { - workspace: data.workspace, - path: data.path, - kind: data.kind - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * set capture config - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody capture config - * @returns unknown capture config set - * @throws ApiError - */ -export const setCaptureConfig = (data: SetCaptureConfigData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/capture/set_config', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * ping capture config - * @param data The data for the request. - * @param data.workspace - * @param data.triggerKind - * @param data.runnableKind - * @param data.path - * @returns unknown capture config pinged - * @throws ApiError - */ -export const pingCaptureConfig = (data: PingCaptureConfigData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/capture/ping_config/{trigger_kind}/{runnable_kind}/{path}', - path: { - workspace: data.workspace, - trigger_kind: data.triggerKind, - runnable_kind: data.runnableKind, - path: data.path - } -}); }; - -/** - * get capture configs for a script or flow - * @param data The data for the request. - * @param data.workspace - * @param data.runnableKind - * @param data.path - * @returns CaptureConfig capture configs for a script or flow - * @throws ApiError - */ -export const getCaptureConfigs = (data: GetCaptureConfigsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/capture/get_configs/{runnable_kind}/{path}', - path: { - workspace: data.workspace, - runnable_kind: data.runnableKind, - path: data.path - } -}); }; - -/** - * list captures for a script or flow - * @param data The data for the request. - * @param data.workspace - * @param data.runnableKind - * @param data.path - * @param data.triggerKind - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @returns Capture list of captures for a script or flow - * @throws ApiError - */ -export const listCaptures = (data: ListCapturesData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/capture/list/{runnable_kind}/{path}', - path: { - workspace: data.workspace, - runnable_kind: data.runnableKind, - path: data.path - }, - query: { - trigger_kind: data.triggerKind, - page: data.page, - per_page: data.perPage - } -}); }; - -/** - * get a capture - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @returns Capture capture - * @throws ApiError - */ -export const getCapture = (data: GetCaptureData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/capture/{id}', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * delete a capture - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @returns unknown capture deleted - * @throws ApiError - */ -export const deleteCapture = (data: DeleteCaptureData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/capture/{id}', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * star item - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody - * @returns unknown star item - * @throws ApiError - */ -export const star = (data: StarData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/favorites/star', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * unstar item - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody - * @returns unknown unstar item - * @throws ApiError - */ -export const unstar = (data: UnstarData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/favorites/unstar', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * List Inputs used in previously completed jobs - * @param data The data for the request. - * @param data.workspace - * @param data.runnableId - * @param data.runnableType - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.includePreview - * @returns Input Input history for completed jobs - * @throws ApiError - */ -export const getInputHistory = (data: GetInputHistoryData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/inputs/history', - path: { - workspace: data.workspace - }, - query: { - runnable_id: data.runnableId, - runnable_type: data.runnableType, - page: data.page, - per_page: data.perPage, - include_preview: data.includePreview - } -}); }; - -/** - * Get args from history or saved input - * @param data The data for the request. - * @param data.workspace - * @param data.jobOrInputId - * @param data.input - * @param data.allowLarge - * @returns unknown args - * @throws ApiError - */ -export const getArgsFromHistoryOrSavedInput = (data: GetArgsFromHistoryOrSavedInputData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/inputs/{jobOrInputId}/args', - path: { - workspace: data.workspace, - jobOrInputId: data.jobOrInputId - }, - query: { - input: data.input, - allow_large: data.allowLarge - } -}); }; - -/** - * List saved Inputs for a Runnable - * @param data The data for the request. - * @param data.workspace - * @param data.runnableId - * @param data.runnableType - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @returns Input Saved Inputs for a Runnable - * @throws ApiError - */ -export const listInputs = (data: ListInputsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/inputs/list', - path: { - workspace: data.workspace - }, - query: { - runnable_id: data.runnableId, - runnable_type: data.runnableType, - page: data.page, - per_page: data.perPage - } -}); }; - -/** - * Create an Input for future use in a script or flow - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody Input - * @param data.runnableId - * @param data.runnableType - * @returns string Input created - * @throws ApiError - */ -export const createInput = (data: CreateInputData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/inputs/create', - path: { - workspace: data.workspace - }, - query: { - runnable_id: data.runnableId, - runnable_type: data.runnableType - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * Update an Input - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody UpdateInput - * @returns string Input updated - * @throws ApiError - */ -export const updateInput = (data: UpdateInputData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/inputs/update', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * Delete a Saved Input - * @param data The data for the request. - * @param data.workspace - * @param data.input - * @returns string Input deleted - * @throws ApiError - */ -export const deleteInput = (data: DeleteInputData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/inputs/delete/{input}', - path: { - workspace: data.workspace, - input: data.input - } -}); }; - -/** - * Converts an S3 resource to the set of instructions necessary to connect DuckDB to an S3 bucket - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody S3 resource to connect to - * @returns unknown Connection settings - * @throws ApiError - */ -export const duckdbConnectionSettings = (data: DuckdbConnectionSettingsData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/job_helpers/duckdb_connection_settings', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * Converts an S3 resource to the set of instructions necessary to connect DuckDB to an S3 bucket - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody S3 resource path to use to generate the connection settings. If empty, the S3 resource defined in the workspace settings will be used - * @returns unknown Connection settings - * @throws ApiError - */ -export const duckdbConnectionSettingsV2 = (data: DuckdbConnectionSettingsV2Data): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/job_helpers/v2/duckdb_connection_settings', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * Converts an S3 resource to the set of arguments necessary to connect Polars to an S3 bucket - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody S3 resource to connect to - * @returns unknown Connection settings - * @throws ApiError - */ -export const polarsConnectionSettings = (data: PolarsConnectionSettingsData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/job_helpers/polars_connection_settings', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * Converts an S3 resource to the set of arguments necessary to connect Polars to an S3 bucket - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody S3 resource path to use to generate the connection settings. If empty, the S3 resource defined in the workspace settings will be used - * @returns unknown Connection settings - * @throws ApiError - */ -export const polarsConnectionSettingsV2 = (data: PolarsConnectionSettingsV2Data): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/job_helpers/v2/polars_connection_settings', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * Returns the s3 resource associated to the provided path, or the workspace default S3 resource - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody S3 resource path to use. If empty, the S3 resource defined in the workspace settings will be used - * @returns S3Resource Connection settings - * @throws ApiError - */ -export const s3ResourceInfo = (data: S3ResourceInfoData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/job_helpers/v2/s3_resource_info', - path: { - workspace: data.workspace - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * Test connection to the workspace object storage - * @param data The data for the request. - * @param data.workspace - * @param data.storage - * @returns unknown Connection settings - * @throws ApiError - */ -export const datasetStorageTestConnection = (data: DatasetStorageTestConnectionData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/job_helpers/test_connection', - path: { - workspace: data.workspace - }, - query: { - storage: data.storage - } -}); }; - -/** - * List the file keys available in a workspace object storage - * @param data The data for the request. - * @param data.workspace - * @param data.maxKeys - * @param data.marker - * @param data.prefix - * @param data.storage - * @returns unknown List of file keys - * @throws ApiError - */ -export const listStoredFiles = (data: ListStoredFilesData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/job_helpers/list_stored_files', - path: { - workspace: data.workspace - }, - query: { - max_keys: data.maxKeys, - marker: data.marker, - prefix: data.prefix, - storage: data.storage - } -}); }; - -/** - * Load metadata of the file - * @param data The data for the request. - * @param data.workspace - * @param data.fileKey - * @param data.storage - * @returns WindmillFileMetadata FileMetadata - * @throws ApiError - */ -export const loadFileMetadata = (data: LoadFileMetadataData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/job_helpers/load_file_metadata', - path: { - workspace: data.workspace - }, - query: { - file_key: data.fileKey, - storage: data.storage - } -}); }; - -/** - * Load a preview of the file - * @param data The data for the request. - * @param data.workspace - * @param data.fileKey - * @param data.fileSizeInBytes - * @param data.fileMimeType - * @param data.csvSeparator - * @param data.csvHasHeader - * @param data.readBytesFrom - * @param data.readBytesLength - * @param data.storage - * @returns WindmillFilePreview FilePreview - * @throws ApiError - */ -export const loadFilePreview = (data: LoadFilePreviewData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/job_helpers/load_file_preview', - path: { - workspace: data.workspace - }, - query: { - file_key: data.fileKey, - file_size_in_bytes: data.fileSizeInBytes, - file_mime_type: data.fileMimeType, - csv_separator: data.csvSeparator, - csv_has_header: data.csvHasHeader, - read_bytes_from: data.readBytesFrom, - read_bytes_length: data.readBytesLength, - storage: data.storage - } -}); }; - -/** - * Load a preview of a parquet file - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.offset - * @param data.limit - * @param data.sortCol - * @param data.sortDesc - * @param data.searchCol - * @param data.searchTerm - * @param data.storage - * @returns unknown Parquet Preview - * @throws ApiError - */ -export const loadParquetPreview = (data: LoadParquetPreviewData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/job_helpers/load_parquet_preview/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - offset: data.offset, - limit: data.limit, - sort_col: data.sortCol, - sort_desc: data.sortDesc, - search_col: data.searchCol, - search_term: data.searchTerm, - storage: data.storage - } -}); }; - -/** - * Load the table row count - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.searchCol - * @param data.searchTerm - * @param data.storage - * @returns unknown Table count - * @throws ApiError - */ -export const loadTableRowCount = (data: LoadTableRowCountData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/job_helpers/load_table_count/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - search_col: data.searchCol, - search_term: data.searchTerm, - storage: data.storage - } -}); }; - -/** - * Load a preview of a csv file - * @param data The data for the request. - * @param data.workspace - * @param data.path - * @param data.offset - * @param data.limit - * @param data.sortCol - * @param data.sortDesc - * @param data.searchCol - * @param data.searchTerm - * @param data.storage - * @param data.csvSeparator - * @returns unknown Csv Preview - * @throws ApiError - */ -export const loadCsvPreview = (data: LoadCsvPreviewData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/job_helpers/load_csv_preview/{path}', - path: { - workspace: data.workspace, - path: data.path - }, - query: { - offset: data.offset, - limit: data.limit, - sort_col: data.sortCol, - sort_desc: data.sortDesc, - search_col: data.searchCol, - search_term: data.searchTerm, - storage: data.storage, - csv_separator: data.csvSeparator - } -}); }; - -/** - * Permanently delete file from S3 - * @param data The data for the request. - * @param data.workspace - * @param data.fileKey - * @param data.storage - * @returns unknown Confirmation - * @throws ApiError - */ -export const deleteS3File = (data: DeleteS3FileData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/w/{workspace}/job_helpers/delete_s3_file', - path: { - workspace: data.workspace - }, - query: { - file_key: data.fileKey, - storage: data.storage - } -}); }; - -/** - * Move a S3 file from one path to the other within the same bucket - * @param data The data for the request. - * @param data.workspace - * @param data.srcFileKey - * @param data.destFileKey - * @param data.storage - * @returns unknown Confirmation - * @throws ApiError - */ -export const moveS3File = (data: MoveS3FileData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/job_helpers/move_s3_file', - path: { - workspace: data.workspace - }, - query: { - src_file_key: data.srcFileKey, - dest_file_key: data.destFileKey, - storage: data.storage - } -}); }; - -/** - * Upload file to S3 bucket - * @param data The data for the request. - * @param data.workspace - * @param data.requestBody File content - * @param data.fileKey - * @param data.fileExtension - * @param data.s3ResourcePath - * @param data.resourceType - * @param data.storage - * @param data.contentType - * @param data.contentDisposition - * @returns unknown File upload status - * @throws ApiError - */ -export const fileUpload = (data: FileUploadData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/job_helpers/upload_s3_file', - path: { - workspace: data.workspace - }, - query: { - file_key: data.fileKey, - file_extension: data.fileExtension, - s3_resource_path: data.s3ResourcePath, - resource_type: data.resourceType, - storage: data.storage, - content_type: data.contentType, - content_disposition: data.contentDisposition - }, - body: data.requestBody, - mediaType: 'application/octet-stream' -}); }; - -/** - * Download file to S3 bucket - * @param data The data for the request. - * @param data.workspace - * @param data.fileKey - * @param data.s3ResourcePath - * @param data.resourceType - * @param data.storage - * @returns binary Chunk of the downloaded file - * @throws ApiError - */ -export const fileDownload = (data: FileDownloadData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/job_helpers/download_s3_file', - path: { - workspace: data.workspace - }, - query: { - file_key: data.fileKey, - s3_resource_path: data.s3ResourcePath, - resource_type: data.resourceType, - storage: data.storage - } -}); }; - -/** - * Download file to S3 bucket - * @param data The data for the request. - * @param data.workspace - * @param data.fileKey - * @param data.s3ResourcePath - * @param data.resourceType - * @returns string The downloaded file - * @throws ApiError - */ -export const fileDownloadParquetAsCsv = (data: FileDownloadParquetAsCsvData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/job_helpers/download_s3_parquet_file_as_csv', - path: { - workspace: data.workspace - }, - query: { - file_key: data.fileKey, - s3_resource_path: data.s3ResourcePath, - resource_type: data.resourceType - } -}); }; - -/** - * get job metrics - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.requestBody parameters for statistics retrieval - * @returns unknown job details - * @throws ApiError - */ -export const getJobMetrics = (data: GetJobMetricsData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/job_metrics/get/{id}', - path: { - workspace: data.workspace, - id: data.id - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * set job metrics - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @param data.requestBody parameters for statistics retrieval - * @returns unknown Job progress updated - * @throws ApiError - */ -export const setJobProgress = (data: SetJobProgressData): CancelablePromise => { return __request(OpenAPI, { - method: 'POST', - url: '/w/{workspace}/job_metrics/set_progress/{id}', - path: { - workspace: data.workspace, - id: data.id - }, - body: data.requestBody, - mediaType: 'application/json' -}); }; - -/** - * get job progress - * @param data The data for the request. - * @param data.workspace - * @param data.id - * @returns number job progress between 0 and 99 - * @throws ApiError - */ -export const getJobProgress = (data: GetJobProgressData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/job_metrics/get_progress/{id}', - path: { - workspace: data.workspace, - id: data.id - } -}); }; - -/** - * list log files ordered by timestamp - * @param data The data for the request. - * @param data.before filter on started before (inclusive) timestamp - * @param data.after filter on created after (exclusive) timestamp - * @param data.withError - * @returns unknown time - * @throws ApiError - */ -export const listLogFiles = (data: ListLogFilesData = {}): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/service_logs/list_files', - query: { - before: data.before, - after: data.after, - with_error: data.withError - } -}); }; - -/** - * get log file by path - * @param data The data for the request. - * @param data.path - * @returns string log stream - * @throws ApiError - */ -export const getLogFile = (data: GetLogFileData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/service_logs/get_log_file/{path}', - path: { - path: data.path - } -}); }; - -/** - * List all concurrency groups - * @returns ConcurrencyGroup all concurrency groups - * @throws ApiError - */ -export const listConcurrencyGroups = (): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/concurrency_groups/list' -}); }; - -/** - * Delete concurrency group - * @param data The data for the request. - * @param data.concurrencyId - * @returns unknown concurrency group removed - * @throws ApiError - */ -export const deleteConcurrencyGroup = (data: DeleteConcurrencyGroupData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/concurrency_groups/prune/{concurrency_id}', - path: { - concurrency_id: data.concurrencyId - } -}); }; - -/** - * Get the concurrency key for a job that has concurrency limits enabled - * @param data The data for the request. - * @param data.id - * @returns string concurrency key for given job - * @throws ApiError - */ -export const getConcurrencyKey = (data: GetConcurrencyKeyData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/concurrency_groups/{id}/key', - path: { - id: data.id - } -}); }; - -/** - * Get intervals of job runtime concurrency - * @param data The data for the request. - * @param data.workspace - * @param data.concurrencyKey - * @param data.rowLimit - * @param data.createdBy mask to filter exact matching user creator - * @param data.label mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') - * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any - * @param data.scriptPathExact mask to filter exact matching path - * @param data.scriptPathStart mask to filter matching starting path - * @param data.schedulePath mask to filter by schedule path - * @param data.scriptHash mask to filter exact matching path - * @param data.startedBefore filter on started before (inclusive) timestamp - * @param data.startedAfter filter on started after (exclusive) timestamp - * @param data.createdOrStartedBefore filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp - * @param data.running filter on running jobs - * @param data.scheduledForBeforeNow filter on jobs scheduled_for before now (hence waitinf for a worker) - * @param data.createdOrStartedAfter filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp - * @param data.createdOrStartedAfterCompletedJobs filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp but only for the completed jobs - * @param data.jobKinds filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, - * @param data.args filter on jobs containing those args as a json subset (@> in postgres) - * @param data.tag filter on jobs with a given tag/worker group - * @param data.result filter on jobs containing those result as a json subset (@> in postgres) - * @param data.page which page to return (start at 1, default 1) - * @param data.perPage number of items to return for a given page (default 30, max 100) - * @param data.isSkipped is the job skipped - * @param data.isFlowStep is the job a flow step - * @param data.hasNullParent has null parent - * @param data.success filter on successful jobs - * @param data.allWorkspaces get jobs from all workspaces (only valid if request come from the `admins` workspace) - * @param data.isNotSchedule is not a scheduled job - * @returns ExtendedJobs time - * @throws ApiError - */ -export const listExtendedJobs = (data: ListExtendedJobsData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/w/{workspace}/concurrency_groups/list_jobs', - path: { - workspace: data.workspace - }, - query: { - concurrency_key: data.concurrencyKey, - row_limit: data.rowLimit, - created_by: data.createdBy, - label: data.label, - parent_job: data.parentJob, - script_path_exact: data.scriptPathExact, - script_path_start: data.scriptPathStart, - schedule_path: data.schedulePath, - script_hash: data.scriptHash, - started_before: data.startedBefore, - started_after: data.startedAfter, - created_or_started_before: data.createdOrStartedBefore, - running: data.running, - scheduled_for_before_now: data.scheduledForBeforeNow, - created_or_started_after: data.createdOrStartedAfter, - created_or_started_after_completed_jobs: data.createdOrStartedAfterCompletedJobs, - job_kinds: data.jobKinds, - args: data.args, - tag: data.tag, - result: data.result, - page: data.page, - per_page: data.perPage, - is_skipped: data.isSkipped, - is_flow_step: data.isFlowStep, - has_null_parent: data.hasNullParent, - success: data.success, - all_workspaces: data.allWorkspaces, - is_not_schedule: data.isNotSchedule - } -}); }; - -/** - * Search through jobs with a string query - * @param data The data for the request. - * @param data.workspace - * @param data.searchQuery - * @returns unknown search results - * @throws ApiError - */ -export const searchJobsIndex = (data: SearchJobsIndexData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/srch/w/{workspace}/index/search/job', - path: { - workspace: data.workspace - }, - query: { - search_query: data.searchQuery - } -}); }; - -/** - * Search through service logs with a string query - * @param data The data for the request. - * @param data.searchQuery - * @param data.mode - * @param data.hostname - * @param data.workerGroup - * @param data.minTs - * @param data.maxTs - * @returns unknown search results - * @throws ApiError - */ -export const searchLogsIndex = (data: SearchLogsIndexData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/srch/index/search/service_logs', - query: { - search_query: data.searchQuery, - mode: data.mode, - worker_group: data.workerGroup, - hostname: data.hostname, - min_ts: data.minTs, - max_ts: data.maxTs - } -}); }; - -/** - * Search and count the log line hits on every provided host - * @param data The data for the request. - * @param data.searchQuery - * @param data.minTs - * @param data.maxTs - * @returns unknown search results - * @throws ApiError - */ -export const countSearchLogsIndex = (data: CountSearchLogsIndexData): CancelablePromise => { return __request(OpenAPI, { - method: 'GET', - url: '/srch/index/search/count_service_logs', - query: { - search_query: data.searchQuery, - min_ts: data.minTs, - max_ts: data.maxTs - } -}); }; - -/** - * Restart container and delete the index to recreate it. - * @param data The data for the request. - * @param data.idxName - * @returns string idx to be deleted and container restarting - * @throws ApiError - */ -export const clearIndex = (data: ClearIndexData): CancelablePromise => { return __request(OpenAPI, { - method: 'DELETE', - url: '/srch/index/delete/{idx_name}', - path: { - idx_name: data.idxName - } -}); }; \ No newline at end of file diff --git a/cli/gen/types.gen.ts b/cli/gen/types.gen.ts deleted file mode 100644 index 4695b99980..0000000000 --- a/cli/gen/types.gen.ts +++ /dev/null @@ -1,7048 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -export type AIProvider = 'openai' | 'anthropic' | 'mistral' | 'deepseek' | 'customai'; - -export type AIResource = { - path: string; - provider: AIProvider; -}; - -export type Script = { - workspace_id?: string; - hash: string; - path: string; - /** - * The first element is the direct parent of the script, the second is the parent of the first, etc - * - */ - parent_hashes?: Array<(string)>; - summary: string; - description: string; - content: string; - created_by: string; - created_at: string; - archived: boolean; - schema?: { - [key: string]: unknown; - }; - deleted: boolean; - is_template: boolean; - extra_perms: { - [key: string]: (boolean); - }; - lock?: string; - lock_error_logs?: string; - language: ScriptLang; - kind: 'script' | 'failure' | 'trigger' | 'command' | 'approval' | 'preprocessor'; - starred: boolean; - tag?: string; - has_draft?: boolean; - draft_only?: boolean; - envs?: Array<(string)>; - concurrent_limit?: number; - concurrency_time_window_s?: number; - concurrency_key?: string; - cache_ttl?: number; - dedicated_worker?: boolean; - ws_error_handler_muted?: boolean; - priority?: number; - restart_unless_cancelled?: boolean; - timeout?: number; - delete_after_use?: boolean; - visible_to_runner_only?: boolean; - no_main_func: boolean; - codebase?: string; - has_preprocessor: boolean; - on_behalf_of_email?: string; -}; - -export type kind = 'script' | 'failure' | 'trigger' | 'command' | 'approval' | 'preprocessor'; - -export type NewScript = { - path: string; - parent_hash?: string; - summary: string; - description: string; - content: string; - schema?: { - [key: string]: unknown; - }; - is_template?: boolean; - lock?: string; - language: ScriptLang; - kind?: 'script' | 'failure' | 'trigger' | 'command' | 'approval' | 'preprocessor'; - tag?: string; - draft_only?: boolean; - envs?: Array<(string)>; - concurrent_limit?: number; - concurrency_time_window_s?: number; - cache_ttl?: number; - dedicated_worker?: boolean; - ws_error_handler_muted?: boolean; - priority?: number; - restart_unless_cancelled?: boolean; - timeout?: number; - delete_after_use?: boolean; - deployment_message?: string; - concurrency_key?: string; - visible_to_runner_only?: boolean; - no_main_func?: boolean; - codebase?: string; - has_preprocessor?: boolean; - on_behalf_of_email?: string; -}; - -export type NewScriptWithDraft = NewScript & { - draft?: NewScript; - hash: string; -}; - -export type ScriptHistory = { - script_hash: string; - deployment_msg?: string; -}; - -export type ScriptArgs = { - [key: string]: unknown; -}; - -export type Input = { - id: string; - name: string; - created_by: string; - created_at: string; - is_public: boolean; - success?: boolean; -}; - -export type CreateInput = { - name: string; - args: { - [key: string]: unknown; - }; -}; - -export type UpdateInput = { - id: string; - name: string; - is_public: boolean; -}; - -export type RunnableType = 'ScriptHash' | 'ScriptPath' | 'FlowPath'; - -export type QueuedJob = { - workspace_id?: string; - id: string; - parent_job?: string; - created_by?: string; - created_at?: string; - started_at?: string; - scheduled_for?: string; - running: boolean; - script_path?: string; - script_hash?: string; - args?: ScriptArgs; - logs?: string; - raw_code?: string; - canceled: boolean; - canceled_by?: string; - canceled_reason?: string; - last_ping?: string; - job_kind: 'script' | 'preview' | 'dependencies' | 'flowdependencies' | 'appdependencies' | 'flow' | 'flowpreview' | 'script_hub' | 'identity' | 'deploymentcallback' | 'singlescriptflow' | 'flowscript' | 'flownode' | 'appscript'; - schedule_path?: string; - /** - * The user (u/userfoo) or group (g/groupfoo) whom - * the execution of this script will be permissioned_as and by extension its DT_TOKEN. - * - */ - permissioned_as: string; - flow_status?: FlowStatus; - raw_flow?: FlowValue; - is_flow_step: boolean; - language?: ScriptLang; - email: string; - visible_to_owner: boolean; - mem_peak?: number; - tag: string; - priority?: number; - self_wait_time_ms?: number; - aggregate_wait_time_ms?: number; - suspend?: number; -}; - -export type job_kind = 'script' | 'preview' | 'dependencies' | 'flowdependencies' | 'appdependencies' | 'flow' | 'flowpreview' | 'script_hub' | 'identity' | 'deploymentcallback' | 'singlescriptflow' | 'flowscript' | 'flownode' | 'appscript'; - -export type CompletedJob = { - workspace_id?: string; - id: string; - parent_job?: string; - created_by: string; - created_at: string; - started_at: string; - duration_ms: number; - success: boolean; - script_path?: string; - script_hash?: string; - args?: ScriptArgs; - result?: unknown; - logs?: string; - deleted?: boolean; - raw_code?: string; - canceled: boolean; - canceled_by?: string; - canceled_reason?: string; - job_kind: 'script' | 'preview' | 'dependencies' | 'flow' | 'flowdependencies' | 'appdependencies' | 'flowpreview' | 'script_hub' | 'identity' | 'deploymentcallback' | 'singlescriptflow' | 'flowscript' | 'flownode' | 'appscript'; - schedule_path?: string; - /** - * The user (u/userfoo) or group (g/groupfoo) whom - * the execution of this script will be permissioned_as and by extension its DT_TOKEN. - * - */ - permissioned_as: string; - flow_status?: FlowStatus; - raw_flow?: FlowValue; - is_flow_step: boolean; - language?: ScriptLang; - is_skipped: boolean; - email: string; - visible_to_owner: boolean; - mem_peak?: number; - tag: string; - priority?: number; - labels?: Array<(string)>; - self_wait_time_ms?: number; - aggregate_wait_time_ms?: number; -}; - -export type ObscuredJob = { - typ?: string; - started_at?: string; - duration_ms?: number; -}; - -export type Job = (CompletedJob & { - type?: 'CompletedJob'; -}) | (QueuedJob & { - type?: 'QueuedJob'; -}); - -export type type = 'CompletedJob'; - -export type User = { - email: string; - username: string; - is_admin: boolean; - name?: string; - is_super_admin: boolean; - created_at: string; - operator: boolean; - disabled: boolean; - groups?: Array<(string)>; - folders: Array<(string)>; - folders_owners: Array<(string)>; -}; - -export type UserUsage = { - email?: string; - executions?: number; -}; - -export type Login = { - email: string; - password: string; -}; - -export type EditWorkspaceUser = { - is_admin?: boolean; - operator?: boolean; - disabled?: boolean; -}; - -export type TruncatedToken = { - label?: string; - expiration?: string; - token_prefix: string; - created_at: string; - last_used_at: string; - scopes?: Array<(string)>; - email?: string; -}; - -export type NewToken = { - label?: string; - expiration?: string; - scopes?: Array<(string)>; - workspace_id?: string; -}; - -export type NewTokenImpersonate = { - label?: string; - expiration?: string; - impersonate_email: string; - workspace_id?: string; -}; - -export type ListableVariable = { - workspace_id: string; - path: string; - value?: string; - is_secret: boolean; - description?: string; - account?: number; - is_oauth?: boolean; - extra_perms: { - [key: string]: (boolean); - }; - is_expired?: boolean; - refresh_error?: string; - is_linked?: boolean; - is_refreshed?: boolean; - expires_at?: string; -}; - -export type ContextualVariable = { - name: string; - value: string; - description: string; - is_custom: boolean; -}; - -export type CreateVariable = { - path: string; - value: string; - is_secret: boolean; - description: string; - account?: number; - is_oauth?: boolean; - expires_at?: string; -}; - -export type EditVariable = { - path?: string; - value?: string; - is_secret?: boolean; - description?: string; -}; - -export type AuditLog = { - id: number; - timestamp: string; - username: string; - operation: 'jobs.run' | 'jobs.run.script' | 'jobs.run.preview' | 'jobs.run.flow' | 'jobs.run.flow_preview' | 'jobs.run.script_hub' | 'jobs.run.dependencies' | 'jobs.run.identity' | 'jobs.run.noop' | 'jobs.flow_dependencies' | 'jobs' | 'jobs.cancel' | 'jobs.force_cancel' | 'jobs.disapproval' | 'jobs.delete' | 'account.delete' | 'ai.request' | 'resources.create' | 'resources.update' | 'resources.delete' | 'resource_types.create' | 'resource_types.update' | 'resource_types.delete' | 'schedule.create' | 'schedule.setenabled' | 'schedule.edit' | 'schedule.delete' | 'scripts.create' | 'scripts.update' | 'scripts.archive' | 'scripts.delete' | 'users.create' | 'users.delete' | 'users.update' | 'users.login' | 'users.login_failure' | 'users.logout' | 'users.accept_invite' | 'users.decline_invite' | 'users.token.create' | 'users.token.delete' | 'users.add_to_workspace' | 'users.add_global' | 'users.setpassword' | 'users.impersonate' | 'users.leave_workspace' | 'oauth.login' | 'oauth.login_failure' | 'oauth.signup' | 'variables.create' | 'variables.delete' | 'variables.update' | 'flows.create' | 'flows.update' | 'flows.delete' | 'flows.archive' | 'apps.create' | 'apps.update' | 'apps.delete' | 'folder.create' | 'folder.update' | 'folder.delete' | 'folder.add_owner' | 'folder.remove_owner' | 'group.create' | 'group.delete' | 'group.edit' | 'group.adduser' | 'group.removeuser' | 'igroup.create' | 'igroup.delete' | 'igroup.adduser' | 'igroup.removeuser' | 'variables.decrypt_secret' | 'workspaces.edit_command_script' | 'workspaces.edit_deploy_to' | 'workspaces.edit_auto_invite_domain' | 'workspaces.edit_webhook' | 'workspaces.edit_copilot_config' | 'workspaces.edit_error_handler' | 'workspaces.create' | 'workspaces.update' | 'workspaces.archive' | 'workspaces.unarchive' | 'workspaces.delete'; - action_kind: 'Created' | 'Updated' | 'Delete' | 'Execute'; - resource?: string; - parameters?: { - [key: string]: unknown; - }; -}; - -export type operation = 'jobs.run' | 'jobs.run.script' | 'jobs.run.preview' | 'jobs.run.flow' | 'jobs.run.flow_preview' | 'jobs.run.script_hub' | 'jobs.run.dependencies' | 'jobs.run.identity' | 'jobs.run.noop' | 'jobs.flow_dependencies' | 'jobs' | 'jobs.cancel' | 'jobs.force_cancel' | 'jobs.disapproval' | 'jobs.delete' | 'account.delete' | 'ai.request' | 'resources.create' | 'resources.update' | 'resources.delete' | 'resource_types.create' | 'resource_types.update' | 'resource_types.delete' | 'schedule.create' | 'schedule.setenabled' | 'schedule.edit' | 'schedule.delete' | 'scripts.create' | 'scripts.update' | 'scripts.archive' | 'scripts.delete' | 'users.create' | 'users.delete' | 'users.update' | 'users.login' | 'users.login_failure' | 'users.logout' | 'users.accept_invite' | 'users.decline_invite' | 'users.token.create' | 'users.token.delete' | 'users.add_to_workspace' | 'users.add_global' | 'users.setpassword' | 'users.impersonate' | 'users.leave_workspace' | 'oauth.login' | 'oauth.login_failure' | 'oauth.signup' | 'variables.create' | 'variables.delete' | 'variables.update' | 'flows.create' | 'flows.update' | 'flows.delete' | 'flows.archive' | 'apps.create' | 'apps.update' | 'apps.delete' | 'folder.create' | 'folder.update' | 'folder.delete' | 'folder.add_owner' | 'folder.remove_owner' | 'group.create' | 'group.delete' | 'group.edit' | 'group.adduser' | 'group.removeuser' | 'igroup.create' | 'igroup.delete' | 'igroup.adduser' | 'igroup.removeuser' | 'variables.decrypt_secret' | 'workspaces.edit_command_script' | 'workspaces.edit_deploy_to' | 'workspaces.edit_auto_invite_domain' | 'workspaces.edit_webhook' | 'workspaces.edit_copilot_config' | 'workspaces.edit_error_handler' | 'workspaces.create' | 'workspaces.update' | 'workspaces.archive' | 'workspaces.unarchive' | 'workspaces.delete'; - -export type action_kind = 'Created' | 'Updated' | 'Delete' | 'Execute'; - -export type MainArgSignature = { - type: 'Valid' | 'Invalid'; - error: string; - star_args: boolean; - star_kwargs?: boolean; - args: Array<{ - name: string; - typ: ('float' | 'int' | 'bool' | 'email' | 'unknown' | 'bytes' | 'dict' | 'datetime' | 'sql' | { - resource: (string) | null; -} | { - str: Array<(string)> | null; -} | { - object: Array<{ - key: string; - typ: ('float' | 'int' | 'bool' | 'email' | 'unknown' | 'bytes' | 'dict' | 'datetime' | 'sql' | { - str: unknown; -}); - }>; -} | { - list: (('float' | 'int' | 'bool' | 'email' | 'unknown' | 'bytes' | 'dict' | 'datetime' | 'sql' | { - str: unknown; -}) | null); -}); - has_default?: boolean; - default?: unknown; - }>; - no_main_func: (boolean) | null; - has_preprocessor: (boolean) | null; -}; - -export type type2 = 'Valid' | 'Invalid'; - -export type ScriptLang = 'python3' | 'deno' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'oracledb' | 'graphql' | 'nativets' | 'bun' | 'php' | 'rust' | 'ansible' | 'csharp'; - -export type Preview = { - content?: string; - path?: string; - args: ScriptArgs; - language?: ScriptLang; - tag?: string; - kind?: 'code' | 'identity' | 'http'; - dedicated_worker?: boolean; - lock?: string; -}; - -export type kind2 = 'code' | 'identity' | 'http'; - -export type WorkflowTask = { - args: ScriptArgs; -}; - -export type WorkflowStatusRecord = { - [key: string]: WorkflowStatus; -}; - -export type WorkflowStatus = { - scheduled_for?: string; - started_at?: string; - duration_ms?: number; - name?: string; -}; - -export type CreateResource = { - path: string; - value: unknown; - description?: string; - resource_type: string; -}; - -export type EditResource = { - path?: string; - description?: string; - value?: unknown; -}; - -export type Resource = { - workspace_id?: string; - path: string; - description?: string; - resource_type: string; - value?: unknown; - is_oauth: boolean; - extra_perms?: { - [key: string]: (boolean); - }; - created_by?: string; - edited_at?: string; -}; - -export type ListableResource = { - workspace_id?: string; - path: string; - description?: string; - resource_type: string; - value?: unknown; - is_oauth: boolean; - extra_perms?: { - [key: string]: (boolean); - }; - is_expired?: boolean; - refresh_error?: string; - is_linked: boolean; - is_refreshed: boolean; - account?: number; - created_by?: string; - edited_at?: string; -}; - -export type ResourceType = { - workspace_id?: string; - name: string; - schema?: unknown; - description?: string; - created_by?: string; - edited_at?: string; - format_extension?: string; -}; - -export type EditResourceType = { - schema?: unknown; - description?: string; -}; - -export type Schedule = { - path: string; - edited_by: string; - edited_at: string; - schedule: string; - timezone: string; - enabled: boolean; - script_path: string; - is_flow: boolean; - args?: ScriptArgs; - extra_perms: { - [key: string]: (boolean); - }; - email: string; - error?: string; - on_failure?: string; - on_failure_times?: number; - on_failure_exact?: boolean; - on_failure_extra_args?: ScriptArgs; - on_recovery?: string; - on_recovery_times?: number; - on_recovery_extra_args?: ScriptArgs; - on_success?: string; - on_success_extra_args?: ScriptArgs; - ws_error_handler_muted?: boolean; - retry?: Retry; - summary?: string; - no_flow_overlap?: boolean; - tag?: string; - paused_until?: string; - cron_version?: string; -}; - -export type ScheduleWJobs = Schedule & { - jobs?: Array<{ - id: string; - success: boolean; - duration_ms: number; - }>; -}; - -export type NewSchedule = { - path: string; - schedule: string; - timezone: string; - script_path: string; - is_flow: boolean; - args: ScriptArgs; - enabled?: boolean; - on_failure?: string; - on_failure_times?: number; - on_failure_exact?: boolean; - on_failure_extra_args?: ScriptArgs; - on_recovery?: string; - on_recovery_times?: number; - on_recovery_extra_args?: ScriptArgs; - on_success?: string; - on_success_extra_args?: ScriptArgs; - ws_error_handler_muted?: boolean; - retry?: Retry; - no_flow_overlap?: boolean; - summary?: string; - tag?: string; - paused_until?: string; - cron_version?: string; -}; - -export type EditSchedule = { - schedule: string; - timezone: string; - args: ScriptArgs; - on_failure?: string; - on_failure_times?: number; - on_failure_exact?: boolean; - on_failure_extra_args?: ScriptArgs; - on_recovery?: string; - on_recovery_times?: number; - on_recovery_extra_args?: ScriptArgs; - on_success?: string; - on_success_extra_args?: ScriptArgs; - ws_error_handler_muted?: boolean; - retry?: Retry; - no_flow_overlap?: boolean; - summary?: string; - tag?: string; - paused_until?: string; - cron_version?: string; -}; - -export type TriggerExtraProperty = { - email: string; - extra_perms: { - [key: string]: (boolean); - }; - workspace_id: string; - edited_by: string; - edited_at: string; -}; - -export type HttpTrigger = TriggerExtraProperty & { - path: string; - script_path: string; - route_path: string; - static_asset_config?: { - s3: string; - storage?: string; - filename?: string; - }; - is_flow: boolean; - http_method: 'get' | 'post' | 'put' | 'delete' | 'patch'; - is_async: boolean; - requires_auth: boolean; -}; - -export type http_method = 'get' | 'post' | 'put' | 'delete' | 'patch'; - -export type NewHttpTrigger = { - path: string; - script_path: string; - route_path: string; - static_asset_config?: { - s3: string; - storage?: string; - filename?: string; - }; - is_flow: boolean; - http_method: 'get' | 'post' | 'put' | 'delete' | 'patch'; - is_async: boolean; - requires_auth: boolean; -}; - -export type EditHttpTrigger = { - path: string; - script_path: string; - route_path?: string; - static_asset_config?: { - s3: string; - storage?: string; - filename?: string; - }; - is_flow: boolean; - http_method: 'get' | 'post' | 'put' | 'delete' | 'patch'; - is_async: boolean; - requires_auth: boolean; -}; - -export type TriggersCount = { - primary_schedule?: { - schedule?: string; - }; - schedule_count?: number; - http_routes_count?: number; - webhook_count?: number; - email_count?: number; - websocket_count?: number; - postgres_count?: number; - kafka_count?: number; - nats_count?: number; -}; - -export type WebsocketTrigger = TriggerExtraProperty & { - path: string; - script_path: string; - url: string; - is_flow: boolean; - server_id?: string; - last_server_ping?: string; - error?: string; - enabled: boolean; - filters: Array<{ - key: string; - value: unknown; - }>; - initial_messages?: Array; - url_runnable_args?: ScriptArgs; - can_return_message: boolean; -}; - -export type NewWebsocketTrigger = { - path: string; - script_path: string; - is_flow: boolean; - url: string; - enabled?: boolean; - filters: Array<{ - key: string; - value: unknown; - }>; - initial_messages?: Array; - url_runnable_args?: ScriptArgs; - can_return_message: boolean; -}; - -export type EditWebsocketTrigger = { - url: string; - path: string; - script_path: string; - is_flow: boolean; - filters: Array<{ - key: string; - value: unknown; - }>; - initial_messages?: Array; - url_runnable_args?: ScriptArgs; - can_return_message: boolean; -}; - -export type WebsocketTriggerInitialMessage = { - raw_message: string; -} | { - runnable_result: { - path: string; - args: ScriptArgs; - is_flow: boolean; - }; -}; - -export type Slot = { - name?: string; -}; - -export type SlotList = { - slot_name?: string; - active?: boolean; -}; - -export type PublicationData = { - table_to_track?: Array; - transaction_to_track: Array<(string)>; -}; - -export type TableToTrack = Array<{ - table_name: string; - columns_name?: Array<(string)>; - where_clause?: string; -}>; - -export type Relations = { - schema_name: string; - table_to_track: TableToTrack; -}; - -export type Language = 'Typescript'; - -export type TemplateScript = { - postgres_resource_path: string; - relations: Array; - language: Language; -}; - -export type PostgresTrigger = TriggerExtraProperty & { - path: string; - script_path: string; - is_flow: boolean; - enabled: boolean; - postgres_resource_path: string; - publication_name: string; - server_id?: string; - replication_slot_name: string; - error?: string; - last_server_ping?: string; -}; - -export type NewPostgresTrigger = { - replication_slot_name?: string; - publication_name?: string; - path: string; - script_path: string; - is_flow: boolean; - enabled: boolean; - postgres_resource_path: string; - publication?: PublicationData; -}; - -export type EditPostgresTrigger = { - replication_slot_name: string; - publication_name: string; - path: string; - script_path: string; - is_flow: boolean; - enabled: boolean; - postgres_resource_path: string; - publication?: PublicationData; -}; - -export type KafkaTrigger = { - path: string; - edited_by: string; - edited_at: string; - script_path: string; - kafka_resource_path: string; - group_id: string; - topics: Array<(string)>; - is_flow: boolean; - extra_perms: { - [key: string]: (boolean); - }; - email: string; - workspace_id: string; - server_id?: string; - last_server_ping?: string; - error?: string; - enabled: boolean; -}; - -export type NewKafkaTrigger = { - path: string; - script_path: string; - is_flow: boolean; - kafka_resource_path: string; - group_id: string; - topics: Array<(string)>; - enabled?: boolean; -}; - -export type EditKafkaTrigger = { - kafka_resource_path: string; - group_id: string; - topics: Array<(string)>; - path: string; - script_path: string; - is_flow: boolean; -}; - -export type NatsTrigger = { - path: string; - edited_by: string; - edited_at: string; - script_path: string; - nats_resource_path: string; - use_jetstream: boolean; - stream_name?: string; - consumer_name?: string; - subjects: Array<(string)>; - is_flow: boolean; - extra_perms: { - [key: string]: (boolean); - }; - email: string; - workspace_id: string; - server_id?: string; - last_server_ping?: string; - error?: string; - enabled: boolean; -}; - -export type NewNatsTrigger = { - path: string; - script_path: string; - is_flow: boolean; - nats_resource_path: string; - use_jetstream: boolean; - stream_name?: string; - consumer_name?: string; - subjects: Array<(string)>; - enabled?: boolean; -}; - -export type EditNatsTrigger = { - nats_resource_path: string; - use_jetstream: boolean; - stream_name?: string; - consumer_name?: string; - subjects: Array<(string)>; - path: string; - script_path: string; - is_flow: boolean; -}; - -export type Group = { - name: string; - summary?: string; - members?: Array<(string)>; - extra_perms?: { - [key: string]: (boolean); - }; -}; - -export type InstanceGroup = { - name: string; - summary?: string; - emails?: Array<(string)>; -}; - -export type Folder = { - name: string; - owners: Array<(string)>; - extra_perms: { - [key: string]: (boolean); - }; - summary?: string; - created_by?: string; - edited_at?: string; -}; - -export type WorkerPing = { - worker: string; - worker_instance: string; - last_ping?: number; - started_at: string; - ip: string; - jobs_executed: number; - custom_tags?: Array<(string)>; - worker_group: string; - wm_version: string; - last_job_id?: string; - last_job_workspace_id?: string; - occupancy_rate?: number; - occupancy_rate_15s?: number; - occupancy_rate_5m?: number; - occupancy_rate_30m?: number; - memory?: number; - vcpus?: number; - memory_usage?: number; - wm_memory_usage?: number; -}; - -export type UserWorkspaceList = { - email: string; - workspaces: Array<{ - id: string; - name: string; - username: string; - color: string; - operator_settings?: OperatorSettings; - }>; -}; - -export type CreateWorkspace = { - id: string; - name: string; - username?: string; - color?: string; -}; - -export type Workspace = { - id: string; - name: string; - owner: string; - domain?: string; - color?: string; -}; - -export type WorkspaceInvite = { - workspace_id: string; - email: string; - is_admin: boolean; - operator: boolean; -}; - -export type GlobalUserInfo = { - email: string; - login_type: 'password' | 'github'; - super_admin: boolean; - devops?: boolean; - verified: boolean; - name?: string; - company?: string; - username?: string; - operator_only?: boolean; -}; - -export type login_type = 'password' | 'github'; - -export type Flow = OpenFlow & FlowMetadata; - -export type ExtraPerms = { - [key: string]: (boolean); -}; - -export type FlowMetadata = { - workspace_id?: string; - path: string; - edited_by: string; - edited_at: string; - archived: boolean; - extra_perms: ExtraPerms; - starred?: boolean; - draft_only?: boolean; - tag?: string; - ws_error_handler_muted?: boolean; - priority?: number; - dedicated_worker?: boolean; - timeout?: number; - visible_to_runner_only?: boolean; - on_behalf_of_email?: string; -}; - -export type OpenFlowWPath = OpenFlow & { - path: string; - tag?: string; - ws_error_handler_muted?: boolean; - priority?: number; - dedicated_worker?: boolean; - timeout?: number; - visible_to_runner_only?: boolean; - on_behalf_of_email?: string; -}; - -export type FlowPreview = { - value: FlowValue; - path?: string; - args: ScriptArgs; - tag?: string; - restarted_from?: RestartedFrom; -}; - -export type RestartedFrom = { - flow_job_id?: string; - step_id?: string; - branch_or_iteration_n?: number; -}; - -export type Policy = { - triggerables?: { - [key: string]: { - [key: string]: unknown; - }; - }; - triggerables_v2?: { - [key: string]: { - [key: string]: unknown; - }; - }; - s3_inputs?: Array<{ - [key: string]: unknown; - }>; - execution_mode?: 'viewer' | 'publisher' | 'anonymous'; - on_behalf_of?: string; - on_behalf_of_email?: string; -}; - -export type execution_mode = 'viewer' | 'publisher' | 'anonymous'; - -export type ListableApp = { - id: number; - workspace_id: string; - path: string; - summary: string; - version: number; - extra_perms: { - [key: string]: (boolean); - }; - starred?: boolean; - edited_at: string; - execution_mode: 'viewer' | 'publisher' | 'anonymous'; -}; - -export type ListableRawApp = { - workspace_id: string; - path: string; - summary: string; - extra_perms: { - [key: string]: (boolean); - }; - starred?: boolean; - version: number; - edited_at: string; -}; - -export type AppWithLastVersion = { - id: number; - workspace_id: string; - path: string; - summary: string; - versions: Array<(number)>; - created_by: string; - created_at: string; - value: { - [key: string]: unknown; - }; - policy: Policy; - execution_mode: 'viewer' | 'publisher' | 'anonymous'; - extra_perms: { - [key: string]: (boolean); - }; - custom_path?: string; -}; - -export type AppWithLastVersionWDraft = AppWithLastVersion & { - draft_only?: boolean; - draft?: unknown; -}; - -export type AppHistory = { - version: number; - deployment_msg?: string; -}; - -export type FlowVersion = { - id: number; - created_at: string; - deployment_msg?: string; -}; - -export type SlackToken = { - access_token: string; - team_id: string; - team_name: string; - bot: { - bot_access_token?: string; - }; -}; - -export type TokenResponse = { - access_token: string; - expires_in?: number; - refresh_token?: string; - scope?: Array<(string)>; -}; - -export type HubScriptKind = unknown; - -export type PolarsClientKwargs = { - region_name: string; -}; - -export type LargeFileStorage = { - type?: 'S3Storage' | 'AzureBlobStorage' | 'AzureWorkloadIdentity' | 'S3AwsOidc'; - s3_resource_path?: string; - azure_blob_resource_path?: string; - public_resource?: boolean; - secondary_storage?: { - [key: string]: { - type?: 'S3Storage' | 'AzureBlobStorage' | 'AzureWorkloadIdentity' | 'S3AwsOidc'; - s3_resource_path?: string; - azure_blob_resource_path?: string; - public_resource?: boolean; - }; - }; -}; - -export type type3 = 'S3Storage' | 'AzureBlobStorage' | 'AzureWorkloadIdentity' | 'S3AwsOidc'; - -export type WindmillLargeFile = { - s3: string; -}; - -export type WindmillFileMetadata = { - mime_type?: string; - size_in_bytes?: number; - last_modified?: string; - expires?: string; - version_id?: string; -}; - -export type WindmillFilePreview = { - msg?: string; - content?: string; - content_type: 'RawText' | 'Csv' | 'Parquet' | 'Unknown'; -}; - -export type content_type = 'RawText' | 'Csv' | 'Parquet' | 'Unknown'; - -export type S3Resource = { - bucket: string; - region: string; - endPoint: string; - useSSL: boolean; - accessKey?: string; - secretKey?: string; - pathStyle: boolean; -}; - -export type WorkspaceGitSyncSettings = { - include_path?: Array<(string)>; - include_type?: Array<('script' | 'flow' | 'app' | 'folder' | 'resource' | 'variable' | 'secret' | 'resourcetype' | 'schedule' | 'user' | 'group')>; - repositories?: Array; -}; - -export type WorkspaceDeployUISettings = { - include_path?: Array<(string)>; - include_type?: Array<('script' | 'flow' | 'app' | 'resource' | 'variable' | 'secret')>; -}; - -export type WorkspaceDefaultScripts = { - order?: Array<(string)>; - hidden?: Array<(string)>; - default_script_content?: { - [key: string]: (string); - }; -}; - -export type GitRepositorySettings = { - script_path: string; - git_repo_resource_path: string; - use_individual_branch?: boolean; - group_by_folder?: boolean; - exclude_types_override?: Array<('script' | 'flow' | 'app' | 'folder' | 'resource' | 'variable' | 'secret' | 'resourcetype' | 'schedule' | 'user' | 'group')>; -}; - -export type UploadFilePart = { - part_number: number; - tag: string; -}; - -export type MetricMetadata = { - id: string; - name?: string; -}; - -export type ScalarMetric = { - metric_id?: string; - value: number; -}; - -export type TimeseriesMetric = { - metric_id?: string; - values: Array; -}; - -export type MetricDataPoint = { - timestamp: string; - value: number; -}; - -export type RawScriptForDependencies = { - raw_code: string; - path: string; - language: ScriptLang; -}; - -export type ConcurrencyGroup = { - concurrency_key: string; - total_running: number; -}; - -export type ExtendedJobs = { - jobs: Array; - obscured_jobs: Array; - /** - * Obscured jobs omitted for security because of too specific filtering - */ - omitted_obscured_jobs?: boolean; -}; - -export type ExportedUser = { - email: string; - password_hash?: string; - super_admin: boolean; - verified: boolean; - name?: string; - company?: string; - first_time_user: boolean; - username?: string; -}; - -export type GlobalSetting = { - name: string; - value: { - [key: string]: unknown; - }; -}; - -export type Config = { - name: string; - config?: { - [key: string]: unknown; - }; -}; - -export type ExportedInstanceGroup = { - name: string; - summary?: string; - emails?: Array<(string)>; - id?: string; - scim_display_name?: string; - external_id?: string; -}; - -export type JobSearchHit = { - dancer?: string; -}; - -export type LogSearchHit = { - dancer?: string; -}; - -export type AutoscalingEvent = { - id?: number; - worker_group?: string; - event_type?: string; - desired_workers?: number; - reason?: string; - applied_at?: string; -}; - -export type CriticalAlert = { - /** - * Unique identifier for the alert - */ - id?: number; - /** - * Type of alert (e.g., critical_error) - */ - alert_type?: string; - /** - * The message content of the alert - */ - message?: string; - /** - * Time when the alert was created - */ - created_at?: string; - /** - * Acknowledgment status of the alert, can be true, false, or null if not set - */ - acknowledged?: (boolean) | null; - /** - * Workspace id if the alert is in the scope of a workspace - */ - workspace_id?: (string) | null; -}; - -export type CaptureTriggerKind = 'webhook' | 'http' | 'websocket' | 'kafka' | 'email' | 'nats'; - -export type Capture = { - trigger_kind: CaptureTriggerKind; - payload: unknown; - trigger_extra?: unknown; - id: number; - created_at: string; -}; - -export type CaptureConfig = { - trigger_config?: unknown; - trigger_kind: CaptureTriggerKind; - error?: string; - last_server_ping?: string; -}; - -export type OperatorSettings = { - /** - * Whether operators can view runs - */ - runs: boolean; - /** - * Whether operators can view schedules - */ - schedules: boolean; - /** - * Whether operators can view resources - */ - resources: boolean; - /** - * Whether operators can view variables - */ - variables: boolean; - /** - * Whether operators can view audit logs - */ - audit_logs: boolean; - /** - * Whether operators can view triggers - */ - triggers: boolean; - /** - * Whether operators can view groups page - */ - groups: boolean; - /** - * Whether operators can view folders page - */ - folders: boolean; - /** - * Whether operators can view workers page - */ - workers: boolean; -} | null; - -export type TeamInfo = { - /** - * The unique identifier of the Microsoft Teams team - */ - team_id: string; - /** - * The display name of the Microsoft Teams team - */ - team_name: string; - /** - * List of channels within the team - */ - channels: Array; -}; - -export type ChannelInfo = { - /** - * The unique identifier of the channel - */ - channel_id: string; - /** - * The display name of the channel - */ - channel_name: string; - /** - * The Microsoft Teams tenant identifier - */ - tenant_id: string; - /** - * The service URL for the channel - */ - service_url: string; -}; - -export type OpenFlow = { - summary: string; - description?: string; - value: FlowValue; - schema?: { - [key: string]: unknown; - }; -}; - -export type FlowValue = { - modules: Array; - failure_module?: FlowModule; - preprocessor_module?: FlowModule; - same_worker?: boolean; - concurrent_limit?: number; - concurrency_key?: string; - concurrency_time_window_s?: number; - skip_expr?: string; - cache_ttl?: number; - priority?: number; - early_return?: string; -}; - -export type Retry = { - constant?: { - attempts?: number; - seconds?: number; - }; - exponential?: { - attempts?: number; - multiplier?: number; - seconds?: number; - random_factor?: number; - }; -}; - -export type FlowModule = { - id: string; - value: FlowModuleValue; - stop_after_if?: { - skip_if_stopped?: boolean; - expr: string; - }; - stop_after_all_iters_if?: { - skip_if_stopped?: boolean; - expr: string; - }; - skip_if?: { - expr: string; - }; - sleep?: InputTransform; - cache_ttl?: number; - timeout?: number; - delete_after_use?: boolean; - summary?: string; - mock?: { - enabled?: boolean; - return_value?: unknown; - }; - suspend?: { - required_events?: number; - timeout?: number; - resume_form?: { - schema?: { - [key: string]: unknown; - }; - }; - user_auth_required?: boolean; - user_groups_required?: InputTransform; - self_approval_disabled?: boolean; - hide_cancel?: boolean; - continue_on_disapprove_timeout?: boolean; - }; - priority?: number; - continue_on_error?: boolean; - retry?: Retry; -}; - -export type InputTransform = StaticTransform | JavascriptTransform; - -export type StaticTransform = { - value?: unknown; - type: 'static'; -}; - -export type JavascriptTransform = { - expr: string; - type: 'javascript'; -}; - -export type FlowModuleValue = RawScript | PathScript | PathFlow | ForloopFlow | WhileloopFlow | BranchOne | BranchAll | Identity; - -export type RawScript = { - input_transforms: { - [key: string]: InputTransform; - }; - content: string; - language: 'deno' | 'bun' | 'python3' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'oracledb' | 'graphql' | 'nativets' | 'php'; - path?: string; - lock?: string; - type: 'rawscript'; - tag?: string; - concurrent_limit?: number; - concurrency_time_window_s?: number; - custom_concurrency_key?: string; - is_trigger?: boolean; -}; - -export type language = 'deno' | 'bun' | 'python3' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'oracledb' | 'graphql' | 'nativets' | 'php'; - -export type PathScript = { - input_transforms: { - [key: string]: InputTransform; - }; - path: string; - hash?: string; - type: 'script'; - tag_override?: string; - is_trigger?: boolean; -}; - -export type PathFlow = { - input_transforms: { - [key: string]: InputTransform; - }; - path: string; - type: 'flow'; -}; - -export type ForloopFlow = { - modules: Array; - iterator: InputTransform; - skip_failures: boolean; - type: 'forloopflow'; - parallel?: boolean; - parallelism?: number; -}; - -export type WhileloopFlow = { - modules: Array; - skip_failures: boolean; - type: 'whileloopflow'; - parallel?: boolean; - parallelism?: number; -}; - -export type BranchOne = { - branches: Array<{ - summary?: string; - expr: string; - modules: Array; - }>; - default: Array; - type: 'branchone'; -}; - -export type BranchAll = { - branches: Array<{ - summary?: string; - skip_failure?: boolean; - modules: Array; - }>; - type: 'branchall'; - parallel?: boolean; -}; - -export type Identity = { - type: 'identity'; - flow?: boolean; -}; - -export type FlowStatus = { - step: number; - modules: Array; - user_states?: { - [key: string]: unknown; - }; - preprocessor_module?: (FlowStatusModule); - failure_module: (FlowStatusModule & { - parent_module?: string; -}); - retry?: { - fail_count?: number; - failed_jobs?: Array<(string)>; - }; -}; - -export type FlowStatusModule = { - type: 'WaitingForPriorSteps' | 'WaitingForEvents' | 'WaitingForExecutor' | 'InProgress' | 'Success' | 'Failure'; - id?: string; - job?: string; - count?: number; - progress?: number; - iterator?: { - index?: number; - itered?: Array; - args?: unknown; - }; - flow_jobs?: Array<(string)>; - flow_jobs_success?: Array<(boolean)>; - branch_chosen?: { - type: 'branch' | 'default'; - branch?: number; - }; - branchall?: { - branch: number; - len: number; - }; - approvers?: Array<{ - resume_id: number; - approver: string; - }>; - failed_retries?: Array<(string)>; - skipped?: boolean; -}; - -export type type4 = 'WaitingForPriorSteps' | 'WaitingForEvents' | 'WaitingForExecutor' | 'InProgress' | 'Success' | 'Failure'; - -export type ParameterId = string; - -export type ParameterKey = string; - -export type ParameterWorkspaceId = string; - -export type ParameterPublicationName = string; - -export type ParameterVersionId = number; - -export type ParameterToken = string; - -export type ParameterAccountId = number; - -export type ParameterClientName = string; - -export type ParameterScriptPath = string; - -export type ParameterScriptHash = string; - -export type ParameterJobId = string; - -export type ParameterPath = string; - -export type ParameterCustomPath = string; - -export type ParameterPathId = number; - -export type ParameterPathVersion = number; - -export type ParameterName = string; - -/** - * which page to return (start at 1, default 1) - */ -export type ParameterPage = number; - -/** - * number of items to return for a given page (default 30, max 100) - */ -export type ParameterPerPage = number; - -/** - * order by desc order (default true) - */ -export type ParameterOrderDesc = boolean; - -/** - * mask to filter exact matching user creator - */ -export type ParameterCreatedBy = string; - -/** - * mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') - */ -export type ParameterLabel = string; - -/** - * The parent job that is at the origin and responsible for the execution of this script if any - */ -export type ParameterParentJob = string; - -/** - * Override the tag to use - */ -export type ParameterWorkerTag = string; - -/** - * Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl - */ -export type ParameterCacheTtl = string; - -/** - * The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) - */ -export type ParameterNewJobId = string; - -/** - * List of headers's keys (separated with ',') whove value are added to the args - * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key - * - */ -export type ParameterIncludeHeader = string; - -/** - * The maximum size of the queue for which the request would get rejected if that job would push it above that limit - * - */ -export type ParameterQueueLimit = string; - -/** - * The base64 encoded payload that has been encoded as a JSON. e.g how to encode such payload encodeURIComponent - * `encodeURIComponent(btoa(JSON.stringify({a: 2})))` - * - */ -export type ParameterPayload = string; - -/** - * mask to filter matching starting path - */ -export type ParameterScriptStartPath = string; - -/** - * mask to filter by schedule path - */ -export type ParameterSchedulePath = string; - -/** - * mask to filter exact matching path - */ -export type ParameterScriptExactPath = string; - -/** - * mask to filter exact matching path - */ -export type ParameterScriptExactHash = string; - -/** - * filter on created before (inclusive) timestamp - */ -export type ParameterCreatedBefore = string; - -/** - * filter on created after (exclusive) timestamp - */ -export type ParameterCreatedAfter = string; - -/** - * filter on started before (inclusive) timestamp - */ -export type ParameterStartedBefore = string; - -/** - * filter on started after (exclusive) timestamp - */ -export type ParameterStartedAfter = string; - -/** - * filter on started before (inclusive) timestamp - */ -export type ParameterBefore = string; - -/** - * filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp - */ -export type ParameterCreatedOrStartedAfter = string; - -/** - * filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp but only for the completed jobs - */ -export type ParameterCreatedOrStartedAfterCompletedJob = string; - -/** - * filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp - */ -export type ParameterCreatedOrStartedBefore = string; - -/** - * filter on successful jobs - */ -export type ParameterSuccess = boolean; - -/** - * filter on jobs scheduled_for before now (hence waitinf for a worker) - */ -export type ParameterScheduledForBeforeNow = boolean; - -/** - * filter on suspended jobs - */ -export type ParameterSuspended = boolean; - -/** - * filter on running jobs - */ -export type ParameterRunning = boolean; - -/** - * filter on jobs containing those args as a json subset (@> in postgres) - */ -export type ParameterArgsFilter = string; - -/** - * filter on jobs with a given tag/worker group - */ -export type ParameterTag = string; - -/** - * filter on jobs containing those result as a json subset (@> in postgres) - */ -export type ParameterResultFilter = string; - -/** - * filter on created after (exclusive) timestamp - */ -export type ParameterAfter = string; - -/** - * filter on exact username of user - */ -export type ParameterUsername = string; - -/** - * filter on exact or prefix name of operation - */ -export type ParameterOperation = string; - -/** - * filter on exact or prefix name of resource - */ -export type ParameterResourceName = string; - -/** - * filter on type of operation - */ -export type ParameterActionKind = 'Create' | 'Update' | 'Delete' | 'Execute'; - -/** - * filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, - */ -export type ParameterJobKinds = string; - -export type ParameterRunnableId = string; - -export type ParameterRunnableTypeQuery = RunnableType; - -export type ParameterInputId = string; - -export type ParameterGetStarted = boolean; - -export type ParameterConcurrencyId = string; - -export type ParameterRunnableKind = 'script' | 'flow'; - -export type BackendVersionResponse = (string); - -export type BackendUptodateResponse = (string); - -export type GetLicenseIdResponse = (string); - -export type GetOpenApiYamlResponse = (string); - -export type GetAuditLogData = { - id: number; - workspace: string; -}; - -export type GetAuditLogResponse = (AuditLog); - -export type ListAuditLogsData = { - /** - * filter on type of operation - */ - actionKind?: 'Create' | 'Update' | 'Delete' | 'Execute'; - /** - * filter on created after (exclusive) timestamp - */ - after?: string; - /** - * filter on started before (inclusive) timestamp - */ - before?: string; - /** - * comma separated list of operations to exclude - */ - excludeOperations?: string; - /** - * filter on exact or prefix name of operation - */ - operation?: string; - /** - * comma separated list of exact operations to include - */ - operations?: string; - /** - * which page to return (start at 1, default 1) - */ - page?: number; - /** - * number of items to return for a given page (default 30, max 100) - */ - perPage?: number; - /** - * filter on exact or prefix name of resource - */ - resource?: string; - /** - * filter on exact username of user - */ - username?: string; - workspace: string; -}; - -export type ListAuditLogsResponse = (Array); - -export type LoginData = { - /** - * credentials - */ - requestBody: Login; -}; - -export type LoginResponse = (string); - -export type LogoutResponse = (string); - -export type GetUserData = { - username: string; - workspace: string; -}; - -export type GetUserResponse = (User); - -export type UpdateUserData = { - /** - * new user - */ - requestBody: EditWorkspaceUser; - username: string; - workspace: string; -}; - -export type UpdateUserResponse = (string); - -export type IsOwnerOfPathData = { - path: string; - workspace: string; -}; - -export type IsOwnerOfPathResponse = (boolean); - -export type SetPasswordData = { - /** - * set password - */ - requestBody: { - password: string; - }; -}; - -export type SetPasswordResponse = (string); - -export type SetPasswordForUserData = { - /** - * set password - */ - requestBody: { - password: string; - }; - user: string; -}; - -export type SetPasswordForUserResponse = (string); - -export type SetLoginTypeForUserData = { - /** - * set login type - */ - requestBody: { - login_type: string; - }; - user: string; -}; - -export type SetLoginTypeForUserResponse = (string); - -export type CreateUserGloballyData = { - /** - * user info - */ - requestBody: { - email: string; - password: string; - super_admin: boolean; - name?: string; - company?: string; - }; -}; - -export type CreateUserGloballyResponse = (string); - -export type GlobalUserUpdateData = { - email: string; - /** - * new user info - */ - requestBody: { - is_super_admin?: boolean; - is_devops?: boolean; - name?: string; - }; -}; - -export type GlobalUserUpdateResponse = (string); - -export type GlobalUsernameInfoData = { - email: string; -}; - -export type GlobalUsernameInfoResponse = ({ - username: string; - workspace_usernames: Array<{ - workspace_id: string; - username: string; - }>; -}); - -export type GlobalUserRenameData = { - email: string; - /** - * new username - */ - requestBody: { - new_username: string; - }; -}; - -export type GlobalUserRenameResponse = (string); - -export type GlobalUserDeleteData = { - email: string; -}; - -export type GlobalUserDeleteResponse = (string); - -export type GlobalUsersOverwriteData = { - /** - * List of users - */ - requestBody: Array; -}; - -export type GlobalUsersOverwriteResponse = (string); - -export type GlobalUsersExportResponse = (Array); - -export type DeleteUserData = { - username: string; - workspace: string; -}; - -export type DeleteUserResponse = (string); - -export type ListWorkspacesResponse = (Array); - -export type IsDomainAllowedResponse = (boolean); - -export type ListUserWorkspacesResponse = (UserWorkspaceList); - -export type ListWorkspacesAsSuperAdminData = { - /** - * which page to return (start at 1, default 1) - */ - page?: number; - /** - * number of items to return for a given page (default 30, max 100) - */ - perPage?: number; -}; - -export type ListWorkspacesAsSuperAdminResponse = (Array); - -export type CreateWorkspaceData = { - /** - * new token - */ - requestBody: CreateWorkspace; -}; - -export type CreateWorkspaceResponse = (string); - -export type ExistsWorkspaceData = { - /** - * id of workspace - */ - requestBody: { - id: string; - }; -}; - -export type ExistsWorkspaceResponse = (boolean); - -export type ExistsUsernameData = { - requestBody: { - id: string; - username: string; - }; -}; - -export type ExistsUsernameResponse = (boolean); - -export type GetGlobalData = { - key: string; -}; - -export type GetGlobalResponse = (unknown); - -export type SetGlobalData = { - key: string; - /** - * value set - */ - requestBody: { - value?: unknown; - }; -}; - -export type SetGlobalResponse = (string); - -export type GetLocalResponse = (unknown); - -export type TestSmtpData = { - /** - * test smtp payload - */ - requestBody: { - to: string; - smtp: { - host: string; - username: string; - password: string; - port: number; - from: string; - tls_implicit: boolean; - disable_tls: boolean; - }; - }; -}; - -export type TestSmtpResponse = (string); - -export type TestCriticalChannelsData = { - /** - * test critical channel payload - */ - requestBody: Array<{ - email?: string; - slack_channel?: string; - }>; -}; - -export type TestCriticalChannelsResponse = (string); - -export type GetCriticalAlertsData = { - acknowledged?: (boolean) | null; - page?: number; - pageSize?: number; -}; - -export type GetCriticalAlertsResponse = ({ - alerts?: Array; - /** - * Total number of rows matching the query. - */ - total_rows?: number; - /** - * Total number of pages based on the page size. - */ - total_pages?: number; -}); - -export type AcknowledgeCriticalAlertData = { - /** - * The ID of the critical alert to acknowledge - */ - id: number; -}; - -export type AcknowledgeCriticalAlertResponse = (string); - -export type AcknowledgeAllCriticalAlertsResponse = (string); - -export type TestLicenseKeyData = { - /** - * test license key - */ - requestBody: { - license_key: string; - }; -}; - -export type TestLicenseKeyResponse = (string); - -export type TestObjectStorageConfigData = { - /** - * test object storage config - */ - requestBody: { - [key: string]: unknown; - }; -}; - -export type TestObjectStorageConfigResponse = (string); - -export type SendStatsResponse = (string); - -export type GetLatestKeyRenewalAttemptResponse = ({ - result: string; - attempted_at: string; -} | null); - -export type RenewLicenseKeyData = { - licenseKey?: string; -}; - -export type RenewLicenseKeyResponse = (string); - -export type CreateCustomerPortalSessionData = { - licenseKey?: string; -}; - -export type CreateCustomerPortalSessionResponse = (string); - -export type TestMetadataData = { - /** - * test metadata - */ - requestBody: string; -}; - -export type TestMetadataResponse = (string); - -export type ListGlobalSettingsResponse = (Array); - -export type GetCurrentEmailResponse = (string); - -export type RefreshUserTokenData = { - ifExpiringInLessThanS?: number; -}; - -export type RefreshUserTokenResponse = (string); - -export type GetTutorialProgressResponse = ({ - progress?: number; -}); - -export type UpdateTutorialProgressData = { - /** - * progress update - */ - requestBody: { - progress?: number; - }; -}; - -export type UpdateTutorialProgressResponse = (string); - -export type LeaveInstanceResponse = (string); - -export type GetUsageResponse = (number); - -export type GetRunnableResponse = ({ - workspace: string; - endpoint_async: string; - endpoint_sync: string; - endpoint_openai_sync: string; - summary: string; - description?: string; - kind: string; -}); - -export type GlobalWhoamiResponse = (GlobalUserInfo); - -export type ListWorkspaceInvitesResponse = (Array); - -export type WhoamiData = { - workspace: string; -}; - -export type WhoamiResponse = (User); - -export type AcceptInviteData = { - /** - * accept invite - */ - requestBody: { - workspace_id: string; - username?: string; - }; -}; - -export type AcceptInviteResponse = (string); - -export type DeclineInviteData = { - /** - * decline invite - */ - requestBody: { - workspace_id: string; - }; -}; - -export type DeclineInviteResponse = (string); - -export type InviteUserData = { - /** - * WorkspaceInvite - */ - requestBody: { - email: string; - is_admin: boolean; - operator: boolean; - }; - workspace: string; -}; - -export type InviteUserResponse = (string); - -export type AddUserData = { - /** - * WorkspaceInvite - */ - requestBody: { - email: string; - is_admin: boolean; - username?: string; - operator: boolean; - }; - workspace: string; -}; - -export type AddUserResponse = (string); - -export type DeleteInviteData = { - /** - * WorkspaceInvite - */ - requestBody: { - email: string; - is_admin: boolean; - operator: boolean; - }; - workspace: string; -}; - -export type DeleteInviteResponse = (string); - -export type ArchiveWorkspaceData = { - workspace: string; -}; - -export type ArchiveWorkspaceResponse = (string); - -export type UnarchiveWorkspaceData = { - workspace: string; -}; - -export type UnarchiveWorkspaceResponse = (string); - -export type DeleteWorkspaceData = { - workspace: string; -}; - -export type DeleteWorkspaceResponse = (string); - -export type LeaveWorkspaceData = { - workspace: string; -}; - -export type LeaveWorkspaceResponse = (string); - -export type GetWorkspaceNameData = { - workspace: string; -}; - -export type GetWorkspaceNameResponse = (string); - -export type ChangeWorkspaceNameData = { - requestBody?: { - new_name?: string; - }; - workspace: string; -}; - -export type ChangeWorkspaceNameResponse = (string); - -export type ChangeWorkspaceIdData = { - requestBody?: { - new_id?: string; - new_name?: string; - }; - workspace: string; -}; - -export type ChangeWorkspaceIdResponse = (string); - -export type ChangeWorkspaceColorData = { - requestBody?: { - color?: string; - }; - workspace: string; -}; - -export type ChangeWorkspaceColorResponse = (string); - -export type WhoisData = { - username: string; - workspace: string; -}; - -export type WhoisResponse = (User); - -export type UpdateOperatorSettingsData = { - requestBody: OperatorSettings; - workspace: string; -}; - -export type UpdateOperatorSettingsResponse = (string); - -export type ExistsEmailData = { - email: string; -}; - -export type ExistsEmailResponse = (boolean); - -export type ListUsersAsSuperAdminData = { - /** - * filter only active users - */ - activeOnly?: boolean; - /** - * which page to return (start at 1, default 1) - */ - page?: number; - /** - * number of items to return for a given page (default 30, max 100) - */ - perPage?: number; -}; - -export type ListUsersAsSuperAdminResponse = (Array); - -export type ListPendingInvitesData = { - workspace: string; -}; - -export type ListPendingInvitesResponse = (Array); - -export type GetSettingsData = { - workspace: string; -}; - -export type GetSettingsResponse = ({ - workspace_id?: string; - slack_name?: string; - slack_team_id?: string; - slack_command_script?: string; - auto_invite_domain?: string; - auto_invite_operator?: boolean; - auto_add?: boolean; - plan?: string; - automatic_billing: boolean; - customer_id?: string; - webhook?: string; - deploy_to?: string; - ai_resource?: AIResource; - code_completion_model?: string; - ai_models: Array<(string)>; - error_handler?: string; - error_handler_extra_args?: ScriptArgs; - error_handler_muted_on_cancel: boolean; - large_file_storage?: LargeFileStorage; - git_sync?: WorkspaceGitSyncSettings; - deploy_ui?: WorkspaceDeployUISettings; - default_app?: string; - default_scripts?: WorkspaceDefaultScripts; - mute_critical_alerts?: boolean; - color?: string; - operator_settings?: OperatorSettings; -}); - -export type GetDeployToData = { - workspace: string; -}; - -export type GetDeployToResponse = ({ - deploy_to?: string; -}); - -export type GetIsPremiumData = { - workspace: string; -}; - -export type GetIsPremiumResponse = (boolean); - -export type GetPremiumInfoData = { - workspace: string; -}; - -export type GetPremiumInfoResponse = ({ - premium: boolean; - usage?: number; - seats?: number; - automatic_billing: boolean; - owner: string; -}); - -export type SetAutomaticBillingData = { - /** - * automatic billing - */ - requestBody: { - automatic_billing: boolean; - seats?: number; - }; - workspace: string; -}; - -export type SetAutomaticBillingResponse = (string); - -export type GetThresholdAlertData = { - workspace: string; -}; - -export type GetThresholdAlertResponse = ({ - threshold_alert_amount?: number; - last_alert_sent?: string; -}); - -export type SetThresholdAlertData = { - /** - * threshold alert info - */ - requestBody: { - threshold_alert_amount?: number; - }; - workspace: string; -}; - -export type SetThresholdAlertResponse = (string); - -export type EditSlackCommandData = { - /** - * WorkspaceInvite - */ - requestBody: { - slack_command_script?: string; - }; - workspace: string; -}; - -export type EditSlackCommandResponse = (string); - -export type RunSlackMessageTestJobData = { - /** - * path to hub script to run and its corresponding args - */ - requestBody: { - hub_script_path?: string; - channel?: string; - test_msg?: string; - }; - workspace: string; -}; - -export type RunSlackMessageTestJobResponse = ({ - job_uuid?: string; -}); - -export type EditDeployToData = { - requestBody: { - deploy_to?: string; - }; - workspace: string; -}; - -export type EditDeployToResponse = (string); - -export type EditAutoInviteData = { - /** - * WorkspaceInvite - */ - requestBody: { - operator?: boolean; - invite_all?: boolean; - auto_add?: boolean; - }; - workspace: string; -}; - -export type EditAutoInviteResponse = (string); - -export type EditWebhookData = { - /** - * WorkspaceWebhook - */ - requestBody: { - webhook?: string; - }; - workspace: string; -}; - -export type EditWebhookResponse = (string); - -export type EditCopilotConfigData = { - /** - * WorkspaceCopilotConfig - */ - requestBody: { - ai_resource?: AIResource; - code_completion_model?: string; - ai_models: Array<(string)>; - }; - workspace: string; -}; - -export type EditCopilotConfigResponse = (string); - -export type GetCopilotInfoData = { - workspace: string; -}; - -export type GetCopilotInfoResponse = ({ - ai_provider: AIProvider; - exists_ai_resource: boolean; - code_completion_model?: string; - ai_models: Array<(string)>; -}); - -export type EditErrorHandlerData = { - /** - * WorkspaceErrorHandler - */ - requestBody: { - error_handler?: string; - error_handler_extra_args?: ScriptArgs; - error_handler_muted_on_cancel?: boolean; - }; - workspace: string; -}; - -export type EditErrorHandlerResponse = (string); - -export type EditLargeFileStorageConfigData = { - /** - * LargeFileStorage info - */ - requestBody: { - large_file_storage?: LargeFileStorage; - }; - workspace: string; -}; - -export type EditLargeFileStorageConfigResponse = (unknown); - -export type EditWorkspaceGitSyncConfigData = { - /** - * Workspace Git sync settings - */ - requestBody: { - git_sync_settings?: WorkspaceGitSyncSettings; - }; - workspace: string; -}; - -export type EditWorkspaceGitSyncConfigResponse = (unknown); - -export type EditWorkspaceDeployUiSettingsData = { - /** - * Workspace deploy UI settings - */ - requestBody: { - deploy_ui_settings?: WorkspaceDeployUISettings; - }; - workspace: string; -}; - -export type EditWorkspaceDeployUiSettingsResponse = (unknown); - -export type EditWorkspaceDefaultAppData = { - /** - * Workspace default app - */ - requestBody: { - default_app_path?: string; - }; - workspace: string; -}; - -export type EditWorkspaceDefaultAppResponse = (string); - -export type EditDefaultScriptsData = { - /** - * Workspace default app - */ - requestBody?: WorkspaceDefaultScripts; - workspace: string; -}; - -export type EditDefaultScriptsResponse = (string); - -export type GetDefaultScriptsData = { - workspace: string; -}; - -export type GetDefaultScriptsResponse = (WorkspaceDefaultScripts); - -export type SetEnvironmentVariableData = { - /** - * Workspace default app - */ - requestBody: { - name: string; - value?: string; - }; - workspace: string; -}; - -export type SetEnvironmentVariableResponse = (string); - -export type GetWorkspaceEncryptionKeyData = { - workspace: string; -}; - -export type GetWorkspaceEncryptionKeyResponse = ({ - key: string; -}); - -export type SetWorkspaceEncryptionKeyData = { - /** - * New encryption key - */ - requestBody: { - new_key: string; - skip_reencrypt?: boolean; - }; - workspace: string; -}; - -export type SetWorkspaceEncryptionKeyResponse = (string); - -export type GetWorkspaceDefaultAppData = { - workspace: string; -}; - -export type GetWorkspaceDefaultAppResponse = ({ - default_app_path?: string; -}); - -export type GetLargeFileStorageConfigData = { - workspace: string; -}; - -export type GetLargeFileStorageConfigResponse = (LargeFileStorage); - -export type GetWorkspaceUsageData = { - workspace: string; -}; - -export type GetWorkspaceUsageResponse = (number); - -export type GetUsedTriggersData = { - workspace: string; -}; - -export type GetUsedTriggersResponse = ({ - http_routes_used: boolean; - websocket_used: boolean; - kafka_used: boolean; - nats_used: boolean; - postgres_used: boolean; -}); - -export type ListUsersData = { - workspace: string; -}; - -export type ListUsersResponse = (Array); - -export type ListUsersUsageData = { - workspace: string; -}; - -export type ListUsersUsageResponse = (Array); - -export type ListUsernamesData = { - workspace: string; -}; - -export type ListUsernamesResponse = (Array<(string)>); - -export type UsernameToEmailData = { - username: string; - workspace: string; -}; - -export type UsernameToEmailResponse = (string); - -export type CreateTokenData = { - /** - * new token - */ - requestBody: NewToken; -}; - -export type CreateTokenResponse = (string); - -export type CreateTokenImpersonateData = { - /** - * new token - */ - requestBody: NewTokenImpersonate; -}; - -export type CreateTokenImpersonateResponse = (string); - -export type DeleteTokenData = { - tokenPrefix: string; -}; - -export type DeleteTokenResponse = (string); - -export type ListTokensData = { - excludeEphemeral?: boolean; - /** - * which page to return (start at 1, default 1) - */ - page?: number; - /** - * number of items to return for a given page (default 30, max 100) - */ - perPage?: number; -}; - -export type ListTokensResponse = (Array); - -export type GetOidcTokenData = { - audience: string; - workspace: string; -}; - -export type GetOidcTokenResponse = (string); - -export type CreateVariableData = { - alreadyEncrypted?: boolean; - /** - * new variable - */ - requestBody: CreateVariable; - workspace: string; -}; - -export type CreateVariableResponse = (string); - -export type EncryptValueData = { - /** - * new variable - */ - requestBody: string; - workspace: string; -}; - -export type EncryptValueResponse = (string); - -export type DeleteVariableData = { - path: string; - workspace: string; -}; - -export type DeleteVariableResponse = (string); - -export type UpdateVariableData = { - alreadyEncrypted?: boolean; - path: string; - /** - * updated variable - */ - requestBody: EditVariable; - workspace: string; -}; - -export type UpdateVariableResponse = (string); - -export type GetVariableData = { - /** - * ask to decrypt secret if this variable is secret - * (if not secret no effect, default: true) - * - */ - decryptSecret?: boolean; - /** - * ask to include the encrypted value if secret and decrypt secret is not true (default: false) - * - */ - includeEncrypted?: boolean; - path: string; - workspace: string; -}; - -export type GetVariableResponse = (ListableVariable); - -export type GetVariableValueData = { - path: string; - workspace: string; -}; - -export type GetVariableValueResponse = (string); - -export type ExistsVariableData = { - path: string; - workspace: string; -}; - -export type ExistsVariableResponse = (boolean); - -export type ListVariableData = { - /** - * which page to return (start at 1, default 1) - */ - page?: number; - pathStart?: string; - /** - * number of items to return for a given page (default 30, max 100) - */ - perPage?: number; - workspace: string; -}; - -export type ListVariableResponse = (Array); - -export type ListContextualVariablesData = { - workspace: string; -}; - -export type ListContextualVariablesResponse = (Array); - -export type WorkspaceGetCriticalAlertsData = { - acknowledged?: (boolean) | null; - page?: number; - pageSize?: number; - workspace: string; -}; - -export type WorkspaceGetCriticalAlertsResponse = ({ - alerts?: Array; - /** - * Total number of rows matching the query. - */ - total_rows?: number; - /** - * Total number of pages based on the page size. - */ - total_pages?: number; -}); - -export type WorkspaceAcknowledgeCriticalAlertData = { - /** - * The ID of the critical alert to acknowledge - */ - id: number; - workspace: string; -}; - -export type WorkspaceAcknowledgeCriticalAlertResponse = (string); - -export type WorkspaceAcknowledgeAllCriticalAlertsData = { - workspace: string; -}; - -export type WorkspaceAcknowledgeAllCriticalAlertsResponse = (string); - -export type WorkspaceMuteCriticalAlertsUiData = { - /** - * Boolean flag to mute critical alerts. - */ - requestBody: { - /** - * Whether critical alerts should be muted. - */ - mute_critical_alerts?: boolean; - }; - workspace: string; -}; - -export type WorkspaceMuteCriticalAlertsUiResponse = (string); - -export type LoginWithOauthData = { - clientName: string; - /** - * Partially filled script - */ - requestBody: { - code?: string; - state?: string; - }; -}; - -export type LoginWithOauthResponse = (string); - -export type ConnectSlackCallbackData = { - /** - * code endpoint - */ - requestBody: { - code: string; - state: string; - }; - workspace: string; -}; - -export type ConnectSlackCallbackResponse = (string); - -export type ConnectSlackCallbackInstanceData = { - /** - * code endpoint - */ - requestBody: { - code: string; - state: string; - }; -}; - -export type ConnectSlackCallbackInstanceResponse = (string); - -export type ConnectCallbackData = { - clientName: string; - /** - * code endpoint - */ - requestBody: { - code: string; - state: string; - }; -}; - -export type ConnectCallbackResponse = (TokenResponse); - -export type CreateAccountData = { - /** - * code endpoint - */ - requestBody: { - refresh_token?: string; - expires_in: number; - client: string; - }; - workspace: string; -}; - -export type CreateAccountResponse = (string); - -export type RefreshTokenData = { - id: number; - /** - * variable path - */ - requestBody: { - path: string; - }; - workspace: string; -}; - -export type RefreshTokenResponse = (string); - -export type DisconnectAccountData = { - id: number; - workspace: string; -}; - -export type DisconnectAccountResponse = (string); - -export type DisconnectSlackData = { - workspace: string; -}; - -export type DisconnectSlackResponse = (string); - -export type ListOauthLoginsResponse = ({ - oauth: Array<{ - type: string; - display_name?: string; - }>; - saml?: string; -}); - -export type ListOauthConnectsResponse = (Array<(string)>); - -export type GetOauthConnectData = { - /** - * client name - */ - client: string; -}; - -export type GetOauthConnectResponse = ({ - extra_params?: { - [key: string]: unknown; - }; - scopes?: Array<(string)>; -}); - -export type SyncTeamsResponse = (Array); - -export type CreateResourceData = { - /** - * new resource - */ - requestBody: CreateResource; - updateIfExists?: boolean; - workspace: string; -}; - -export type CreateResourceResponse = (string); - -export type DeleteResourceData = { - path: string; - workspace: string; -}; - -export type DeleteResourceResponse = (string); - -export type UpdateResourceData = { - path: string; - /** - * updated resource - */ - requestBody: EditResource; - workspace: string; -}; - -export type UpdateResourceResponse = (string); - -export type UpdateResourceValueData = { - path: string; - /** - * updated resource - */ - requestBody: { - value?: unknown; - }; - workspace: string; -}; - -export type UpdateResourceValueResponse = (string); - -export type GetResourceData = { - path: string; - workspace: string; -}; - -export type GetResourceResponse = (Resource); - -export type GetResourceValueInterpolatedData = { - /** - * job id - */ - jobId?: string; - path: string; - workspace: string; -}; - -export type GetResourceValueInterpolatedResponse = (unknown); - -export type GetResourceValueData = { - path: string; - workspace: string; -}; - -export type GetResourceValueResponse = (unknown); - -export type ExistsResourceData = { - path: string; - workspace: string; -}; - -export type ExistsResourceResponse = (boolean); - -export type ListResourceData = { - /** - * which page to return (start at 1, default 1) - */ - page?: number; - pathStart?: string; - /** - * number of items to return for a given page (default 30, max 100) - */ - perPage?: number; - /** - * resource_types to list from, separated by ',', - */ - resourceType?: string; - /** - * resource_types to not list from, separated by ',', - */ - resourceTypeExclude?: string; - workspace: string; -}; - -export type ListResourceResponse = (Array); - -export type ListSearchResourceData = { - workspace: string; -}; - -export type ListSearchResourceResponse = (Array<{ - path: string; - value: unknown; -}>); - -export type ListResourceNamesData = { - name: string; - workspace: string; -}; - -export type ListResourceNamesResponse = (Array<{ - name: string; - path: string; -}>); - -export type CreateResourceTypeData = { - /** - * new resource_type - */ - requestBody: ResourceType; - workspace: string; -}; - -export type CreateResourceTypeResponse = (string); - -export type FileResourceTypeToFileExtMapData = { - workspace: string; -}; - -export type FileResourceTypeToFileExtMapResponse = (unknown); - -export type DeleteResourceTypeData = { - path: string; - workspace: string; -}; - -export type DeleteResourceTypeResponse = (string); - -export type UpdateResourceTypeData = { - path: string; - /** - * updated resource_type - */ - requestBody: EditResourceType; - workspace: string; -}; - -export type UpdateResourceTypeResponse = (string); - -export type GetResourceTypeData = { - path: string; - workspace: string; -}; - -export type GetResourceTypeResponse = (ResourceType); - -export type ExistsResourceTypeData = { - path: string; - workspace: string; -}; - -export type ExistsResourceTypeResponse = (boolean); - -export type ListResourceTypeData = { - workspace: string; -}; - -export type ListResourceTypeResponse = (Array); - -export type ListResourceTypeNamesData = { - workspace: string; -}; - -export type ListResourceTypeNamesResponse = (Array<(string)>); - -export type QueryResourceTypesData = { - /** - * query limit - */ - limit?: number; - /** - * query text - */ - text: string; - workspace: string; -}; - -export type QueryResourceTypesResponse = (Array<{ - name: string; - score: number; - schema?: unknown; -}>); - -export type ListHubIntegrationsData = { - /** - * query integrations kind - */ - kind?: string; -}; - -export type ListHubIntegrationsResponse = (Array<{ - name: string; -}>); - -export type ListHubFlowsResponse = ({ - flows?: Array<{ - id: number; - flow_id: number; - summary: string; - apps: Array<(string)>; - approved: boolean; - votes: number; - }>; -}); - -export type GetHubFlowByIdData = { - id: number; -}; - -export type GetHubFlowByIdResponse = ({ - flow?: OpenFlow; -}); - -export type ListHubAppsResponse = ({ - apps?: Array<{ - id: number; - app_id: number; - summary: string; - apps: Array<(string)>; - approved: boolean; - votes: number; - }>; -}); - -export type GetHubAppByIdData = { - id: number; -}; - -export type GetHubAppByIdResponse = ({ - app: { - summary: string; - value: unknown; - }; -}); - -export type GetPublicAppByCustomPathData = { - customPath: string; -}; - -export type GetPublicAppByCustomPathResponse = ((AppWithLastVersion & { - workspace_id?: string; -})); - -export type GetHubScriptContentByPathData = { - path: string; -}; - -export type GetHubScriptContentByPathResponse = (string); - -export type GetHubScriptByPathData = { - path: string; -}; - -export type GetHubScriptByPathResponse = ({ - content: string; - lockfile?: string; - schema?: unknown; - language: string; - summary?: string; -}); - -export type GetTopHubScriptsData = { - /** - * query scripts app - */ - app?: string; - /** - * query scripts kind - */ - kind?: string; - /** - * query limit - */ - limit?: number; -}; - -export type GetTopHubScriptsResponse = ({ - asks?: Array<{ - id: number; - ask_id: number; - summary: string; - app: string; - version_id: number; - kind: HubScriptKind; - votes: number; - views: number; - }>; -}); - -export type QueryHubScriptsData = { - /** - * query scripts app - */ - app?: string; - /** - * query scripts kind - */ - kind?: string; - /** - * query limit - */ - limit?: number; - /** - * query text - */ - text: string; -}; - -export type QueryHubScriptsResponse = (Array<{ - ask_id: number; - id: number; - version_id: number; - summary: string; - app: string; - kind: HubScriptKind; - score: number; -}>); - -export type ListSearchScriptData = { - workspace: string; -}; - -export type ListSearchScriptResponse = (Array<{ - path: string; - content: string; -}>); - -export type ListScriptsData = { - /** - * mask to filter exact matching user creator - */ - createdBy?: string; - /** - * mask to filter scripts whom first direct parent has exact hash - */ - firstParentHash?: string; - /** - * (default false) - * include scripts that have no deployed version - * - */ - includeDraftOnly?: boolean; - /** - * (default false) - * include scripts without an exported main function - * - */ - includeWithoutMain?: boolean; - /** - * (default regardless) - * if true show only the templates - * if false show only the non templates - * if not defined, show all regardless of if the script is a template - * - */ - isTemplate?: boolean; - /** - * (default regardless) - * script kinds to filter, split by comma - * - */ - kinds?: string; - /** - * mask to filter scripts whom last parent in the chain has exact hash. - * Beware that each script stores only a limited number of parents. Hence - * the last parent hash for a script is not necessarily its top-most parent. - * To find the top-most parent you will have to jump from last to last hash - * until finding the parent - * - */ - lastParentHash?: string; - /** - * order by desc order (default true) - */ - orderDesc?: boolean; - /** - * which page to return (start at 1, default 1) - */ - page?: number; - /** - * is the hash present in the array of stored parent hashes for this script. - * The same warning applies than for last_parent_hash. A script only store a - * limited number of direct parent - * - */ - parentHash?: string; - /** - * mask to filter exact matching path - */ - pathExact?: string; - /** - * mask to filter matching starting path - */ - pathStart?: string; - /** - * number of items to return for a given page (default 30, max 100) - */ - perPage?: number; - /** - * (default false) - * show only the archived files. - * when multiple archived hash share the same path, only the ones with the latest create_at - * are - * ed. - * - */ - showArchived?: boolean; - /** - * (default false) - * show only the starred items - * - */ - starredOnly?: boolean; - /** - * (default false) - * include deployment message - * - */ - withDeploymentMsg?: boolean; - workspace: string; -}; - -export type ListScriptsResponse = (Array - - + + -
- Add a new user + +
+ Add a new user - Email - + Email + - {#if !automateUsernameCreation} - Username - - {/if} + {#if !automateUsernameCreation} + Username + + {/if} - Role - - Role + + + + + + -
- + on:click={() => { + addUser().then(() => { + // @ts-ignore + email = undefined + // @ts-ignore + username = undefined + }) + }} + disabled={email === undefined || (!automateUsernameCreation && username === undefined)} + > + Add + +
+
+ diff --git a/frontend/src/lib/components/ApiConnectForm.svelte b/frontend/src/lib/components/ApiConnectForm.svelte index 0e90ec56c7..53a6bd278e 100644 --- a/frontend/src/lib/components/ApiConnectForm.svelte +++ b/frontend/src/lib/components/ApiConnectForm.svelte @@ -1,6 +1,17 @@ {#if !notFound} @@ -129,13 +341,12 @@ /> {#if resourceType == 'postgresql'} - - + + +
+
+
+ + +
+ {#if !validConnectionString} +

Could not parse connection string

+ {/if}
- {#if !validConnectionString} -

Could not parse connection string

- {/if}
- -
+ + {/if} {#if resourceType == 'postgresql' && supabaseWizard}
Connect Supabase {/if} + {#if resourceType == 'git_repository' && $workspaceStore && $userStore?.is_admin} + {#if !loadingGithubInstallations} + + + +
+
+ {#if workspaceGithubInstallations.length > 0} +
+

Select Repository

+
+
+

Github Account ID

+ +
+ {#if selectedGHAppAccountId} +
+

Repository

+
+ +
+
+ {/if} +
+ +
+
+
+ {/if} + +
0 + ? 'border-t border-gray-200 dark:border-gray-700' + : '' + } pt-4`} + > +
+
+ +
+ {#if workspaceGithubInstallations.length > 0} +
+

Current installations:

+
+ + + + + + + + + + + {#each workspaceGithubInstallations as installation} + + + + + + + {/each} + +
OrgWorkspaceRepos
{installation.account_id} + {#if $workspaceColor} + + {installation.workspace_id} + + {:else} + {installation.workspace_id} + {/if} + + {installation.repositories.length} repos + +
+ + +
+
+
+
+ {/if} + {#if githubInstallationsNotInWorkspace.length > 0} +
+

Installations in other workspaces:

+
+ + + + + + + + + + + {#each githubInstallationsNotInWorkspace as installation} + + + + + + + {/each} + +
OrgWorkspaceRepos
{installation.account_id} + {#if $userWorkspaces.find((w) => w.id === installation.workspace_id)?.color} + + {installation.workspace_id} + + {:else} + {installation.workspace_id} + {/if} + + {installation.repositories.length} repos + + +
+
+
+ {/if} +
+
+ +
+

Import installation from other instance:

+
+ + +
+
+
+
+
+ + {:else} + + {/if} + {/if} {:else}

{error}{:else}

{/if} + >{:else}
{/if}
{#await import('$lib/components/SimpleEditor.svelte')} @@ -212,7 +706,7 @@
File content ({resourceTypeInfo.format_extension})
-
+
{#await import('$lib/components/SimpleEditor.svelte')} diff --git a/frontend/src/lib/components/AppConnectInner.svelte b/frontend/src/lib/components/AppConnectInner.svelte index 02136a91dc..6f73fff916 100644 --- a/frontend/src/lib/components/AppConnectInner.svelte +++ b/frontend/src/lib/components/AppConnectInner.svelte @@ -480,7 +480,7 @@ {/if}
-
+
{#if filteredConnectsManual} {#each filteredConnectsManual as [key, _]} @@ -552,18 +552,19 @@ {#if renderDescription}
GH Markdown
-
{:else if description == undefined || description == ''}
No description provided
{:else} -
+
{/if}
{#key resourceTypeInfo}
{#if step > 2} - + {/if} - {:else} {/if} +
{:else if inputCat == 'object' || inputCat == 'resource-object' || isListJson} {#if oneOf && oneOf.length >= 2}
{#if oneOf && oneOf.length >= 2} { - value = { label: oneOfSelected } - redraw += 1 + selected={oneOfSelected} + on:selected={({ detail }) => { + oneOfSelected = detail + const prevValueKeys = Object.keys( + oneOf?.find((o) => o.title == detail)?.properties ?? {} + ) + const toKeep = {} + for (const key of prevValueKeys) { + toKeep[key] = value[key] + } + value = { ...toKeep, label: detail } }} + let:item > {#each oneOf as obj} - + {/each} {#if oneOfSelected} @@ -781,7 +849,7 @@ }} bind:args={value} dndType={`nested-${title}`} - schemaSkippedValues={['label']} + hiddenArgs={['label']} on:reorder={(e) => { if (oneOf && oneOf[objIdx]) { const keys = e.detail @@ -800,7 +868,7 @@ {onlyMaskPassword} {disablePortal} {disabled} - schemaSkippedValues={['label']} + hiddenArgs={['label']} schema={{ properties: obj.properties, order: obj.order, @@ -819,7 +887,7 @@
{/key} {:else if disabled} - {:else} {#await import('$lib/components/JsonEditor.svelte')} @@ -833,13 +901,15 @@ dispatch('blur') }} code={rawValue} - bind:value + on:changeValue={(e) => { + setNewValueFromCode(e.detail) + }} /> {/await} {/if} {/if} {:else if disabled} - {:else} {#await import('$lib/components/JsonEditor.svelte')} @@ -853,7 +923,9 @@ dispatch('blur') }} code={rawValue} - bind:value + on:change={(e) => { + value = e.detail + }} /> {/await} {/if} @@ -924,7 +996,7 @@ {/if}
{:else if disabled} - {:else} {#await import('$lib/components/JsonEditor.svelte')} @@ -938,7 +1010,9 @@ dispatch('blur') }} code={rawValue} - bind:value + on:changeValue={(e) => { + setNewValueFromCode(e.detail) + }} /> {/await} {/if} @@ -977,29 +1051,36 @@
{:else if inputCat == 'date'} {#if format === 'date'} - + {:else} - + + {/if} + {:else if isRawStringEditor(inputCat)} + {#if disabled} + + {:else} +
+ {#await import('$lib/components/SimpleEditor.svelte')} + + {:then Module} + { + dispatch('focus') + }} + on:blur={(e) => { + dispatch('blur') + }} + on:change={(e) => { + setNewValueFromCode(e.detail?.code) + }} + bind:this={editor} + lang={inputCat} + code={typeof rawValue == 'string' ? rawValue : JSON.stringify(rawValue, null, 2)} + autoHeight + /> + {/await} +
{/if} - {:else if inputCat == 'sql' || inputCat == 'yaml'} -
- {#await import('$lib/components/SimpleEditor.svelte')} - - {:then Module} - { - dispatch('focus') - }} - on:blur={(e) => { - dispatch('blur') - }} - bind:this={editor} - lang={inputCat} - bind:code={value} - autoHeight - /> - {/await} -
{:else if inputCat == 'base64'}
+ > {/key} {#if !disabled && itemPicker && extra?.['disableVariablePicker'] != true} @@ -1122,7 +1203,7 @@ {/if}
{:else if !noMargin} -
+
{/if}
diff --git a/frontend/src/lib/components/ArrayTypeNarrowing.svelte b/frontend/src/lib/components/ArrayTypeNarrowing.svelte index 8e2fe827c3..2d37233086 100644 --- a/frontend/src/lib/components/ArrayTypeNarrowing.svelte +++ b/frontend/src/lib/components/ArrayTypeNarrowing.svelte @@ -25,8 +25,8 @@ itemsType?.type != 'string' ? itemsType?.type : Array.isArray(itemsType?.enum) - ? 'enum' - : 'string' + ? 'enum' + : 'string' let schema = { properties: itemsType?.properties || {}, @@ -105,16 +105,17 @@