Compare commits

..

3 Commits

Author SHA1 Message Date
Ruben Fiszel
c90d0965a5 all 2024-06-26 19:01:06 +02:00
Ruben Fiszel
2c4c443f75 Merge branch 'main' into rf/extraDocker 2024-06-26 18:36:54 +02:00
Ruben Fiszel
afa73ad6ea all 2024-06-26 17:50:03 +02:00
191 changed files with 1751 additions and 4717 deletions

View File

@@ -52,3 +52,26 @@ jobs:
labels: |
${{ steps.meta.outputs.labels }}
org.opencontainers.image.licenses=AGPLv3
publish_privately:
needs: [publish_multiplayer]
runs-on: ubicloud-standard-8
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to ECR
uses: docker/login-action@v2
with:
registry: ${{ env.ECR_REGISTRY }}
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Push image to ECR
run: |
docker buildx imagetools create \
--tag ${{ env.ECR_REGISTRY }}/${{ env.IMAGE_NAME }}:latest \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest

View File

@@ -20,4 +20,5 @@ jobs:
run: |
cd backend
cargo generate-lockfile
cargo update -p bit-vec@0.7.0 --precise 0.6.3
- uses: stefanzweifel/git-auto-commit-action@v4

View File

@@ -1,5 +1,6 @@
env:
REGISTRY: ghcr.io
ECR_REGISTRY: 976079455550.dkr.ecr.us-east-1.amazonaws.com
IMAGE_NAME: ${{ github.repository }}
name: Build windmill:main
@@ -468,7 +469,7 @@ jobs:
uses: depot/build-push-action@v1
with:
context: .
platforms: linux/amd64,linux/arm64
platforms: linux/amd64
push: true
file: "./docker/DockerfileNsjail"
tags: |
@@ -489,19 +490,12 @@ jobs:
- uses: depot/setup-action@v1
- name: Login to registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta-ee-public
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-reports
${{ env.ECR_REGISTRY }}/${{ env.IMAGE_NAME }}-ee-reports
tags: |
type=ref,event=branch
type=ref,event=pr
@@ -509,11 +503,19 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
type=sha,enable=true,priority=100,prefix=,suffix=,format=short
- name: Login to ECR
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
registry: ${{ env.ECR_REGISTRY }}
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Build and push publicly ee reports
uses: depot/build-push-action@v1
with:
context: .
platforms: linux/amd64,linux/arm64
platforms: linux/amd64
push: true
file: "./docker/DockerfileReports"
tags: |
@@ -524,7 +526,7 @@ jobs:
publish_ecr_s3:
needs: [build_ee_nsjail]
runs-on: ubicloud-standard-2-arm
runs-on: ubicloud
if: github.event_name != 'pull_request'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
@@ -537,18 +539,22 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to registry
- name: Login to ECR
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: get git hash
registry: ${{ env.ECR_REGISTRY }}
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Push image to ECR
if: github.event_name != 'pull_request'
id: git_hash
id: push_ecr
run: |
git_hash=$(git rev-parse --short "$GITHUB_SHA")
docker buildx imagetools create \
--tag ${{ env.ECR_REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${git_hash:0:7} \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-nsjail:main
echo "GIT_HASH=${git_hash:0:7}" >> "$GITHUB_OUTPUT"
- uses: shrink/actions-docker-extract@v3
@@ -556,7 +562,7 @@ jobs:
id: extract
with:
image: |-
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-nsjail:${{ steps.git_hash.outputs.GIT_HASH }}
${{ env.ECR_REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ steps.push_ecr.outputs.GIT_HASH }}
path: "/static_frontend/."
- uses: reggionick/s3-deploy@v3
@@ -612,50 +618,3 @@ jobs:
labels: |
${{ steps.meta-ee-public.outputs.labels }}
org.opencontainers.image.licenses=Windmill-Enterprise-License
build_ee_slim:
if: ${{ startsWith(github.ref, 'refs/tags/') }}
needs: [build_ee]
runs-on: ubicloud
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: Docker meta
id: meta-ee-public
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-slim
flavor: |
latest=false
tags: |
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Login to registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push publicly ee
uses: depot/build-push-action@v1
with:
context: .
platforms: linux/amd64
push: true
file: "./docker/DockerfileSlim"
tags: |
${{ steps.meta-ee-public.outputs.tags }}
labels: |
${{ steps.meta-ee-public.outputs.labels }}
org.opencontainers.image.licenses=Windmill-Enterprise-License

View File

@@ -62,3 +62,26 @@ jobs:
labels: |
${{ steps.meta.outputs.labels }}
org.opencontainers.image.licenses=AGPLv3
publish_lsp_private:
needs: [publish_lsp]
runs-on: ubicloud-standard-8
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to ECR
uses: docker/login-action@v2
with:
registry: ${{ env.ECR_REGISTRY }}
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Push image to ECR
run: |
docker buildx imagetools create \
--tag ${{ env.ECR_REGISTRY }}/${{ env.IMAGE_NAME }}:latest \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest

View File

@@ -1,99 +1,5 @@
# Changelog
## [1.358.0](https://github.com/windmill-labs/windmill/compare/v1.357.0...v1.358.0) (2024-07-05)
### Features
* **backend:** better filtering for audit logs API ([#4023](https://github.com/windmill-labs/windmill/issues/4023)) ([fde9d2a](https://github.com/windmill-labs/windmill/commit/fde9d2a3f3e673df121e87bc7c5cf567e0641e29))
* **frontend:** add vertical navbars ([#4027](https://github.com/windmill-labs/windmill/issues/4027)) ([aa4967e](https://github.com/windmill-labs/windmill/commit/aa4967ef1e7edf0a794f4202a31f7233f09e2fd8))
* **frontend:** app navbar ([#3992](https://github.com/windmill-labs/windmill/issues/3992)) ([7434edd](https://github.com/windmill-labs/windmill/commit/7434edda065701e0cf41b9c72e96cead37b30b32))
* restore flow version as fork ([#4032](https://github.com/windmill-labs/windmill/issues/4032)) ([d724761](https://github.com/windmill-labs/windmill/commit/d724761ecc02bf8a515e4562f4330258a4ef52d2))
### Bug Fixes
* **frontend:** fix navbar navigation for not deployed apps ([#4033](https://github.com/windmill-labs/windmill/issues/4033)) ([a9c4555](https://github.com/windmill-labs/windmill/commit/a9c455567b386d92e13a7d9c51dc05ca3e51e0db))
* **frontend:** fix theme leak ([#4029](https://github.com/windmill-labs/windmill/issues/4029)) ([c4673f8](https://github.com/windmill-labs/windmill/commit/c4673f8dc5a664773800bfa24b901e36d63e8b70))
* **frontend:** improve component list ([#4028](https://github.com/windmill-labs/windmill/issues/4028)) ([448d5ad](https://github.com/windmill-labs/windmill/commit/448d5ad2283bc82d9ac90fdde45a76879f3758ff))
## [1.357.0](https://github.com/windmill-labs/windmill/compare/v1.356.1...v1.357.0) (2024-07-04)
### Features
* flow versioning ([#4009](https://github.com/windmill-labs/windmill/issues/4009)) ([e50f175](https://github.com/windmill-labs/windmill/commit/e50f1752da472d2b8e8b3793f1b3bc91697a5802))
### Bug Fixes
* **backend:** correct version join of list search flows ([#4022](https://github.com/windmill-labs/windmill/issues/4022)) ([e361acc](https://github.com/windmill-labs/windmill/commit/e361accf47285c37aed9b242a8cc7b0765c4f17d))
* **backend:** switch job run to user db ([#4017](https://github.com/windmill-labs/windmill/issues/4017)) ([26fd427](https://github.com/windmill-labs/windmill/commit/26fd4271803107f739dad782be87a4037af40350))
* fix wrong interaction between suspended steps and forloop parallelism ([4399730](https://github.com/windmill-labs/windmill/commit/4399730ab493112676d7f7d79fb42a638cab9d83))
* **frontend:** improve flow input completion prompt when not in a loop ([#4021](https://github.com/windmill-labs/windmill/issues/4021)) ([55eac8f](https://github.com/windmill-labs/windmill/commit/55eac8ffec54a59b3b7a387b92691766f4c1fa73))
* **frontend:** only load flow/app versions on drawer opening ([#4020](https://github.com/windmill-labs/windmill/issues/4020)) ([3e783e3](https://github.com/windmill-labs/windmill/commit/3e783e30e6ffea606b4c974b315d5ea400d07355))
* improve dedicated workers for flows ([17d12b5](https://github.com/windmill-labs/windmill/commit/17d12b512bb3bdfa3617d608f725f444853ed868))
* improve dedicated workers for flows ([f58d82f](https://github.com/windmill-labs/windmill/commit/f58d82fedff0f406e1d5ab255695ac34d9962e1d))
* improve flow cancellation ([#4013](https://github.com/windmill-labs/windmill/issues/4013)) ([1a403f8](https://github.com/windmill-labs/windmill/commit/1a403f8f39a100a68ca0346e3439ed29e26b8d4f))
* memory optimization for flow with big args ([#4019](https://github.com/windmill-labs/windmill/issues/4019)) ([708b270](https://github.com/windmill-labs/windmill/commit/708b2702cada2713304a13b00846b789586b393f))
* update bun 1.1.18 ([c2cd18d](https://github.com/windmill-labs/windmill/commit/c2cd18d4dad8c575fd4c82056224c19667012b5f))
## [1.356.1](https://github.com/windmill-labs/windmill/compare/v1.356.0...v1.356.1) (2024-07-03)
### Bug Fixes
* 50MB limitation only on non-enterprise ([0753072](https://github.com/windmill-labs/windmill/commit/07530725e6785cef151d2460a3f5043935475065))
* add s3 object support to lightweight arg input ([45cd01c](https://github.com/windmill-labs/windmill/commit/45cd01c6ed083ca67f61ab00cbbbcac5d2964b2c))
## [1.356.0](https://github.com/windmill-labs/windmill/compare/v1.355.4...v1.356.0) (2024-07-02)
### Features
* allow downloading args over the size limit ([d13a357](https://github.com/windmill-labs/windmill/commit/d13a357b2609a8ee4f3fac43f248eede3f8ad38d))
* include token label as end user ([#3988](https://github.com/windmill-labs/windmill/issues/3988)) ([991031b](https://github.com/windmill-labs/windmill/commit/991031b96d3abb8d3c0f40a53f95e3c32e4434cd))
* runs page have sharable args in hash of links ([792bbb3](https://github.com/windmill-labs/windmill/commit/792bbb3ff99818ccc39c1b0d2e3ec08698d26d47))
### Bug Fixes
* add support for result.json for powershell ([155ca5f](https://github.com/windmill-labs/windmill/commit/155ca5fe26eeb988282040557e4068307bf81def))
* improve browser history navigation (back, forward) ([7248b0a](https://github.com/windmill-labs/windmill/commit/7248b0a5a96b6b1aed661d80f4e479d867751ad1))
* lookback selector to manually look for older jobs and prevent inaccurate concurrency graphs ([#4007](https://github.com/windmill-labs/windmill/issues/4007)) ([ca22a87](https://github.com/windmill-labs/windmill/commit/ca22a879dc121a96e50e18ed14f9f9dabca451a5))
* make gql params optional by default + use default value if undefined ([#4003](https://github.com/windmill-labs/windmill/issues/4003)) ([d736b86](https://github.com/windmill-labs/windmill/commit/d736b8692f232be6d9764cb5ee5e4bca26a998b1))
## [1.355.4](https://github.com/windmill-labs/windmill/compare/v1.355.3...v1.355.4) (2024-06-30)
### Bug Fixes
* **frontend:** fix Decision Tree + fix Infinite list default ts code … ([#3993](https://github.com/windmill-labs/windmill/issues/3993)) ([49b6cf1](https://github.com/windmill-labs/windmill/commit/49b6cf1f37aa276cc70e5619d87b68c729f12a73))
* improve runs page performance when minTs is defined ([2e184c9](https://github.com/windmill-labs/windmill/commit/2e184c9894ee6f900368908f4176cbeb6783e15f))
* improve runs page performance when minTs is defined ([e662439](https://github.com/windmill-labs/windmill/commit/e66243969e2578e3bb50e0e88ecd84088437767c))
## [1.355.3](https://github.com/windmill-labs/windmill/compare/v1.355.2...v1.355.3) (2024-06-27)
### Bug Fixes
* fix OIDC ([81c7edf](https://github.com/windmill-labs/windmill/commit/81c7edffeabbfc0fd628a0e5b1d60d2c52e12249))
* snowflake datetime display format ([#3995](https://github.com/windmill-labs/windmill/issues/3995)) ([963d0a4](https://github.com/windmill-labs/windmill/commit/963d0a4dfe94c43d6ca94dcacda43cb0e563a857))
## [1.355.2](https://github.com/windmill-labs/windmill/compare/v1.355.1...v1.355.2) (2024-06-26)
### Bug Fixes
* non-ASCII support in parsers ([#3986](https://github.com/windmill-labs/windmill/issues/3986)) ([94b6e9e](https://github.com/windmill-labs/windmill/commit/94b6e9efa77c410444d4b4e78148dbb8e44a727e))
## [1.355.1](https://github.com/windmill-labs/windmill/compare/v1.355.0...v1.355.1) (2024-06-26)
### Bug Fixes
* fix build ([17c586e](https://github.com/windmill-labs/windmill/commit/17c586ed8f7bbad4e5e5db26a909e8a06521696d))
## [1.355.0](https://github.com/windmill-labs/windmill/compare/v1.354.0...v1.355.0) (2024-06-26)

View File

@@ -23,6 +23,8 @@ ENV SQLX_OFFLINE=true
FROM node:20-alpine as frontend
ARG WITH_DENO=true
# install dependencies
WORKDIR /frontend
COPY ./frontend/package.json ./frontend/package-lock.json ./
@@ -76,6 +78,26 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
CARGO_NET_GIT_FETCH_WITH_CLI=true cargo build --release --features "$features"
FROM ${DEBIAN_IMAGE} as downloader
ARG TARGETPLATFORM
SHELL ["/bin/bash", "-c"]
RUN apt update -y
RUN apt install -y unzip curl
RUN unzip deno.zip && rm deno.zip
ARG BUILD_ENV=copy
# FROM alpine as build_copy
# ONBUILD COPY file /file
# FROM alpine as build_no_copy
# ONBUILD RUN echo "I don't copy"
FROM ${PYTHON_IMAGE}
ARG TARGETPLATFORM
@@ -87,6 +109,10 @@ ARG APP=/usr/src/app
ARG WITH_POWERSHELL=true
ARG WITH_KUBECTL=true
ARG WITH_HELM=true
ARG WITH_GO=true
ARG WITH_BUN=true
ARG WITH_PYTHON=true
RUN apt-get update \
@@ -122,7 +148,8 @@ RUN if [ "$WITH_KUBECTL" = "true" ]; then \
else echo 'Building the image without kubectl'; fi
RUN set -eux; \
RUN if [ "$WITH_GO" = "true" ]; then \
set -eux; \
arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
case "$arch" in \
'amd64') \
@@ -136,7 +163,8 @@ RUN set -eux; \
;; \
*) echo >&2 "error: unsupported architecture '$arch' (likely packaging update needed)"; exit 1 ;; \
esac; \
wget "https://golang.org/dl/$targz" -nv && tar -C /usr/local -xzf "$targz" && rm "$targz";
wget "https://golang.org/dl/$targz" -nv && tar -C /usr/local -xzf "$targz" && rm "$targz"; \
else echo 'Building the image without go'; fi
ENV PATH="${PATH}:/usr/local/go/bin"
ENV GO_PATH=/usr/local/go/bin/go
@@ -145,7 +173,9 @@ RUN curl -sL https://deb.nodesource.com/setup_20.x | bash -
RUN apt-get -y update && apt-get install -y curl nodejs awscli
# go build is slower the first time it is ran, so we prewarm it in the build
RUN mkdir -p /tmp/gobuildwarm && cd /tmp/gobuildwarm && go mod init gobuildwarm && printf "package foo\nimport (\"fmt\")\nfunc main() { fmt.Println(42) }" > warm.go && go mod tidy && go build -x && rm -rf /tmp/gobuildwarm
RUN if [ "$WITH_GO" = "true" ]; then \
mkdir -p /tmp/gobuildwarm && cd /tmp/gobuildwarm && go mod init gobuildwarm && printf "package foo\nimport (\"fmt\")\nfunc main() { fmt.Println(42) }" > warm.go && go mod tidy && go build -x && rm -rf /tmp/gobuildwarm \
else echo 'Building the image without go'; fi
ENV TZ=Etc/UTC
@@ -154,9 +184,10 @@ 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:1.44.4 --chmod=755 /usr/bin/deno /usr/bin/deno
COPY --from=oven/bun:1.1.18 /usr/local/bin/bun /usr/bin/bun
COPY --from=downloader --chmod=755 /deno /usr/bin/deno
COPY --from=oven/bun:1.1.8 /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

View File

@@ -1,16 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE flow_version SET path = REGEXP_REPLACE(path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE path LIKE ('u/' || $2 || '/%') AND workspace_id = $3",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "07d03985bb2c58d52c1ffd6ab5a6d37457e7520642a5e70bb4000e4923720957"
}

View File

@@ -1,26 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO flow_version (workspace_id, path, value, schema, created_by) \n VALUES ($1, $2, $3, $4::text::json, $5)\n RETURNING id",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Jsonb",
"Text",
"Varchar"
]
},
"nullable": [
false
]
},
"hash": "07f5290e90533eac50b890a0d7f4a5e73ac111c838f687fe8647636827aae8b5"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO flow \n (workspace_id, path, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, concurrency_key, versions, value, schema, edited_by, edited_at) \n SELECT $1, path, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, concurrency_key, versions, value, schema, edited_by, edited_at\n FROM flow WHERE workspace_id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text"
]
},
"nullable": []
},
"hash": "13358ffeb0917dd9dff9f8527a59dfee63bb704c3f712af179732dc281411917"
}

View File

@@ -1,16 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO flow\n (workspace_id, path, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, concurrency_key, versions, value, schema, edited_by, edited_at) \n SELECT workspace_id, REGEXP_REPLACE(path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1'), summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, concurrency_key, versions, value, schema, edited_by, edited_at\n FROM flow \n WHERE path LIKE ('u/' || $2 || '/%') AND workspace_id = $3",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "25155e44372aecbb38d042bfc2772ed0c01a0bb974488530cd713b834f537f4a"
}

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT id FROM queue WHERE id = ANY($1) AND schedule_path IS NULL",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Uuid"
}
],
"parameters": {
"Left": [
"UuidArray"
]
},
"nullable": [
false
]
},
"hash": "273d275be89516b135d7846d179c84ba0d684a620e9e5c0c87f82bdc9cd57dbe"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT flow.workspace_id as workspace, flow.path, summary, description, flow_version.schema \n FROM flow \n LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]\n WHERE flow.workspace_id = $1",
"query": "SELECT workspace_id as workspace, path, summary, description, schema FROM flow WHERE workspace_id = $1",
"describe": {
"columns": [
{
@@ -42,5 +42,5 @@
true
]
},
"hash": "974c7e623f3dfa440e134eaaa8d029334c0645147200219c39b2c00b30941172"
"hash": "31075ff185a9ab857459bc539eadd1022c1e5bf0cfbd02c97739f5b83350f050"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE flow_version SET value = $1 WHERE id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Jsonb",
"Int8"
]
},
"nullable": []
},
"hash": "34dee810f99ef41727ab3231a1746be80d60050f8cbaf779d391c4e08eb0c438"
}

View File

@@ -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, value, schema, edited_by, edited_at) \n VALUES ($1, $2, $3, $4, NULL, $5, $6, $7, $8, $9, $10::text::json, $11, now())",
"query": "INSERT INTO flow (workspace_id, path, summary, description, value, edited_by, edited_at, schema, dependency_job, draft_only, tag, dedicated_worker, visible_to_runner_only) VALUES ($1, $2, $3, $4, $5, $6, now(), $7::text::json, NULL, $8, $9, $10, $11)",
"describe": {
"columns": [],
"parameters": {
@@ -9,16 +9,16 @@
"Varchar",
"Text",
"Text",
"Jsonb",
"Varchar",
"Text",
"Bool",
"Varchar",
"Bool",
"Bool",
"Jsonb",
"Text",
"Varchar"
"Bool"
]
},
"nullable": []
},
"hash": "9e64c6b6db2155ad8e6e514b08da92d80caafd1a217813bcd354aa264810f690"
"hash": "35e6af0b203e3e4fac9020b037a3c41af92537c0fd5683227767f6a1bd17339f"
}

View File

@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT flow_version.value \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": "value",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
true
]
},
"hash": "4a764cdcb847b71183425a7a0e863708ef7fe2c88b28d0667988a47b9e995c0e"
}

View File

@@ -0,0 +1,34 @@
{
"db_name": "PostgreSQL",
"query": "SELECT id, is_flow_step, running FROM queue WHERE id = ANY($1) AND schedule_path IS NULL",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Uuid"
},
{
"ordinal": 1,
"name": "is_flow_step",
"type_info": "Bool"
},
{
"ordinal": 2,
"name": "running",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"UuidArray"
]
},
"nullable": [
false,
true,
false
]
},
"hash": "4c529e29a0eb084a92f4e952506d701592bc707fd8a8cb3311bf146be6078f26"
}

View File

@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT versions[array_upper(versions, 1)] FROM flow WHERE path = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "versions",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "526bfaccaafbe2e6f70dd5e6cd21c0c60d4ec155f79d067a8b74cf24eebad88c"
}

View File

@@ -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"
}

View File

@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "SELECT value FROM flow WHERE path = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "value",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false
]
},
"hash": "6b1ea6f39c6f41a093112418ac4c1b69a57de50fdeb4bdcd8d4cab0553af42ea"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE flow_version SET path = $1 WHERE path = $2 AND workspace_id = $3",
"query": "UPDATE flow SET edited_by = $1 WHERE edited_by = $2 AND workspace_id = $3",
"describe": {
"columns": [],
"parameters": {
@@ -12,5 +12,5 @@
},
"nullable": []
},
"hash": "4502ed44e69b0501ef187be9cc2de22c4dc5dafb13ef56c297ea62464e74c323"
"hash": "729bf764b10dc821594bbffbc157c061ec6eb09b3cc22c672e0e17da455ac7ef"
}

View File

@@ -1,26 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO flow_version (workspace_id, path, value, schema, created_by) VALUES ($1, $2, $3, $4::text::json, $5) RETURNING id",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Jsonb",
"Text",
"Varchar"
]
},
"nullable": [
false
]
},
"hash": "76e1de02790d23394997eeec6a5ee46d1da97b94bdd4a07af3dc57b7f8e6f089"
}

View File

@@ -1,24 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT flow.path FROM flow\n LEFT JOIN flow_version\n ON flow_version.path = flow.path AND flow_version.workspace_id = flow.workspace_id\n WHERE flow.path = $1 AND flow.workspace_id = $2 AND flow_version.id = $3",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "path",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text",
"Text",
"Int8"
]
},
"nullable": [
false
]
},
"hash": "79464d5ef46a05ff9c05a4f1f4419ffac7e82c985d59a2e20e5c616461dbfe7b"
}

View File

@@ -1,18 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO deployment_metadata (workspace_id, path, flow_version, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)",
"query": "INSERT INTO deployment_metadata (workspace_id, path, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path) WHERE script_hash IS NULL AND app_version IS NULL DO UPDATE SET callback_job_ids = $3, deployment_msg = $4",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Int8",
"UuidArray",
"Text"
]
},
"nullable": []
},
"hash": "4968e9edac534657c808b891cbf93c8c0a57f93b7b445171b1cc3f4428ee6e53"
"hash": "7f9f1ce221835fc3ff6c864c362519fde3d08e496119e51d8ed4fe65792a85c1"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT tag, dedicated_worker, flow_version.value->>'early_return' as early_return \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",
"query": "SELECT tag, dedicated_worker, value->>'early_return' as early_return from flow WHERE path = $1 and workspace_id = $2",
"describe": {
"columns": [
{
@@ -31,5 +31,5 @@
null
]
},
"hash": "872dcaec230579e4480adf23075e323557efbe52c813e3a6a0da6b855291951e"
"hash": "8e0679c2b1bd451691fe5c69a2841ddc9f211311316ec6b9d4699b2c70997a19"
}

View File

@@ -1,35 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT flow_version.id, flow_version.created_at, deployment_metadata.deployment_msg FROM flow_version \n LEFT JOIN deployment_metadata ON flow_version.id = deployment_metadata.flow_version\n WHERE flow_version.path = $1 AND flow_version.workspace_id = $2 \n ORDER BY flow_version.created_at DESC",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "created_at",
"type_info": "Timestamptz"
},
{
"ordinal": 2,
"name": "deployment_msg",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false,
false,
true
]
},
"hash": "a0f1c0df6bc2f1fbca50edee90e42c94445536e201b322eda6f7a90bdf38f36a"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE flow SET path = REGEXP_REPLACE(path,'u/' || $2 || '/(.*)','u/' || $1 || '/\\1') WHERE path LIKE ('u/' || $2 || '/%') AND workspace_id = $3",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "a459d973c6392af6af1af8b17bc735128fa06ee80e867e2421e70df7a722081a"
}

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "SELECT value->>'concurrency_key' FROM flow WHERE path = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "?column?",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "a875cb56485b812e9d4739afd0915067f7e5abe0ca0adf264b792fccf21e005b"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue SET canceled = true, canceled_reason = 'http connection broke', canceled_by = queue.created_by WHERE id = $1 AND workspace_id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid",
"Text"
]
},
"nullable": []
},
"hash": "a96dd57b127a1adbdca13867a76d7fd9f9a9c76b02bcebd2830d928821f3abc0"
}

View File

@@ -1,17 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO deployment_metadata (workspace_id, path, flow_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL DO UPDATE SET deployment_msg = $4",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Int8",
"Text"
]
},
"nullable": []
},
"hash": "afc7c23c057748f6d4a61dbef17e433b8875c6588b91e38e8141d12189118412"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue SET suspend = 0 WHERE parent_job = $1 AND suspend = $2 AND (flow_status->'step')::int = 0",
"query": "UPDATE queue SET suspend = 0 WHERE parent_job = $1 AND suspend = $2",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
"hash": "38846b12201990f8e776b256ac419b24ffec46fa02d710544a3074745be9455f"
"hash": "d323e898931b200eeec8c5608c37043ed1c47b6f6a53dbf562ad09ece29e82e4"
}

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue SET canceled = true, canceled_by = $1, scheduled_for = now(), suspend = 0 WHERE id = $2 RETURNING 1 as one",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "one",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Varchar",
"Uuid"
]
},
"nullable": [
null
]
},
"hash": "d4fb94ee8198592c24e85d29078feb5220ab367e339510ee0e83bb7b5abfd184"
}

View File

@@ -1,16 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO flow \n (workspace_id, path, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, concurrency_key, versions, value, schema, edited_by, edited_at) \n SELECT workspace_id, $1, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, concurrency_key, versions, value, schema, edited_by, edited_at\n FROM flow\n WHERE path = $2 AND workspace_id = $3",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "d860cd90e583e4666beb37b283bd990dbcb91b781728b5b6253051decfda83d6"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE flow_version SET workspace_id = $1 WHERE workspace_id = $2",
"query": "UPDATE flow SET workspace_id = $1 WHERE workspace_id = $2",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
"hash": "bafff2205d9ca74b033d1d1faf6b1a3398ce387847d73f894dfe850e096326c0"
"hash": "e26e976a64d267557c528ce1bf006b97de9f89e53cb36a3b0ed11e7c18caf55f"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM flow WHERE path LIKE ('u/' || $1 || '/%') AND workspace_id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": []
},
"hash": "f095f413aef2ad008f4cb3d6c9517ce8abb1fa3b34e6a62864ec37ab9442de9d"
}

View File

@@ -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, value = $7, schema = $8::text::json, edited_by = $9, edited_at = now()\n WHERE path = $10 AND workspace_id = $11",
"query": "UPDATE flow SET path = $1, summary = $2, description = $3, value = $4, edited_by = $5, edited_at = now(), schema = $6::text::json, dependency_job = NULL, draft_only = NULL, tag = $9, dedicated_worker = $10, visible_to_runner_only = $11\n WHERE path = $7 AND workspace_id = $8",
"describe": {
"columns": [],
"parameters": {
@@ -8,17 +8,17 @@
"Varchar",
"Text",
"Text",
"Varchar",
"Bool",
"Bool",
"Jsonb",
"Text",
"Varchar",
"Text",
"Text"
"Text",
"Text",
"Varchar",
"Bool",
"Bool"
]
},
"nullable": []
},
"hash": "899a406cc03659e29bad831cc4d3d1dcda1a39b826a03136353344ccae29871b"
"hash": "f6fd65fbe36502923ab4ccf1a22f748cb854e23049d0cf73a42229eccc88c4e6"
}

View File

@@ -1,16 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE flow SET versions = array_append(versions, $1) WHERE path = $2 AND workspace_id = $3",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "fcf885a2214d5ae47e652f6c003cf537e011efd44c256301357079283dfc8e02"
}

318
backend/Cargo.lock generated
View File

@@ -774,9 +774,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "aws-config"
version = "1.5.3"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2368fb843e9eec932f7789d64d0e05850f4a79067188c657e572f1f5a7589df0"
checksum = "2ac9889352d632214df943e26740c46a0f3da6e329fbd28164fe7ae1b061da7b"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -840,9 +840,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sso"
version = "1.33.0"
version = "1.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8aee358b755b2738b3ffb8a5b54ee991b28c8a07483a0ff7d49a58305cc2609"
checksum = "da75cf91cbb46686a27436d639a720a3a198b148efa76dc2467b7e5374a67fc0"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -862,9 +862,9 @@ dependencies = [
[[package]]
name = "aws-sdk-ssooidc"
version = "1.34.0"
version = "1.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d5ce026f0ae73e06b20be5932150dd0e9b063417fd7c3acf5ca97018b9cbd64"
checksum = "cf2ec8a6687299685ed0a4a3137c129cdb132b5235bc3aa3443f6cffe468b9ff"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -884,9 +884,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sts"
version = "1.33.0"
version = "1.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c820248cb02e4ea83630ad2e43d0721cdbccedba5ac902cd0b6fb84d7271f205"
checksum = "458f1031e094b1411b59b49b19e4118f069e1fe13a9c5b8888e933daaf7ffdd6"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -941,9 +941,9 @@ dependencies = [
[[package]]
name = "aws-smithy-http"
version = "0.60.9"
version = "0.60.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9cd0ae3d97daa0a2bf377a4d8e8e1362cae590c4a1aad0d40058ebca18eb91e"
checksum = "4a7de001a1b9a25601016d8057ea16e31a45fdca3751304c8edf4ad72e706c08"
dependencies = [
"aws-smithy-runtime-api",
"aws-smithy-types",
@@ -980,9 +980,9 @@ dependencies = [
[[package]]
name = "aws-smithy-runtime"
version = "1.6.1"
version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df4217d39fe940066174e6238310167bf466bfbebf3be0661e53cacccde6313"
checksum = "d0d3965f6417a92a6d1009c5958a67042f57e46342afb37ca58f9ad26744ec73"
dependencies = [
"aws-smithy-async",
"aws-smithy-http",
@@ -994,7 +994,6 @@ dependencies = [
"http 0.2.12",
"http-body 0.4.6",
"http-body 1.0.0",
"httparse",
"hyper 0.14.29",
"hyper-rustls 0.24.2",
"once_cell",
@@ -1007,9 +1006,9 @@ dependencies = [
[[package]]
name = "aws-smithy-runtime-api"
version = "1.7.1"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30819352ed0a04ecf6a2f3477e344d2d1ba33d43e0f09ad9047c12e0d923616f"
checksum = "1b570ea39eb95bd32543f6e4032bce172cb6209b9bc8c83c770d08169e875afc"
dependencies = [
"aws-smithy-async",
"aws-smithy-types",
@@ -1084,7 +1083,7 @@ dependencies = [
"http 1.1.0",
"http-body 1.0.0",
"http-body-util",
"hyper 1.4.0",
"hyper 1.3.1",
"hyper-util",
"itoa",
"matchit",
@@ -1270,11 +1269,11 @@ dependencies = [
[[package]]
name = "bit-set"
version = "0.6.0"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f"
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
dependencies = [
"bit-vec 0.7.0",
"bit-vec",
]
[[package]]
@@ -1283,12 +1282,6 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bit-vec"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22"
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -1610,9 +1603,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.104"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490"
checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d"
dependencies = [
"jobserver",
"libc",
@@ -1652,7 +1645,7 @@ dependencies = [
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets 0.52.6",
"windows-targets 0.52.5",
]
[[package]]
@@ -1709,9 +1702,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.8"
version = "4.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
dependencies = [
"clap_builder",
"clap_derive",
@@ -1719,9 +1712,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.8"
version = "4.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
dependencies = [
"anstream",
"anstyle",
@@ -1731,9 +1724,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.8"
version = "4.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@@ -2600,7 +2593,7 @@ dependencies = [
"anyhow",
"bincode",
"bit-set",
"bit-vec 0.7.0",
"bit-vec",
"bytes",
"cooked-waker",
"deno_core_icudata",
@@ -2751,11 +2744,10 @@ dependencies = [
[[package]]
name = "deno_unsync"
version = "0.3.10"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c8b95582c2023dbb66fccc37421b374026f5915fa507d437cb566904db9a3a"
checksum = "6cfb230b6e1965cd2695f7c4082adb278e0b999175a0fbb0852c7e67d26654b1"
dependencies = [
"parking_lot",
"tokio",
]
@@ -4131,9 +4123,9 @@ dependencies = [
[[package]]
name = "hyper"
version = "1.4.0"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4fe55fb7a772d59a5ff1dfbff4fe0258d19b89fec4b233e75d35d5d2316badc"
checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d"
dependencies = [
"bytes",
"futures-channel",
@@ -4174,10 +4166,10 @@ checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
dependencies = [
"futures-util",
"http 1.1.0",
"hyper 1.4.0",
"hyper 1.3.1",
"hyper-util",
"rustls 0.23.10",
"rustls-native-certs 0.7.1",
"rustls-native-certs 0.7.0",
"rustls-pki-types",
"tokio",
"tokio-rustls 0.26.0",
@@ -4205,7 +4197,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"http-body-util",
"hyper 1.4.0",
"hyper 1.3.1",
"hyper-util",
"native-tls",
"tokio",
@@ -4215,16 +4207,16 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.6"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"http 1.1.0",
"http-body 1.0.0",
"hyper 1.4.0",
"hyper 1.3.1",
"pin-project-lite",
"socket2 0.5.7",
"tokio",
@@ -4456,9 +4448,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.69"
version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
dependencies = [
"wasm-bindgen",
]
@@ -4620,7 +4612,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
dependencies = [
"cfg-if",
"windows-targets 0.52.6",
"windows-targets 0.52.5",
]
[[package]]
@@ -4703,9 +4695,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.22"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
dependencies = [
"value-bag",
]
@@ -4802,18 +4794,18 @@ dependencies = [
[[package]]
name = "mail-send"
version = "0.4.9"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a575d25cf00ed68e5790b473b29242a47e991c6187785d47b45e31fc5816554"
checksum = "5d9f578319ab6d0ef6bf39ca7051ad4ae960e1d647a55cc71b86e04236b25167"
dependencies = [
"base64 0.22.1",
"gethostname",
"mail-builder",
"rustls 0.23.10",
"rustls 0.22.4",
"rustls-pki-types",
"smtp-proto",
"tokio",
"tokio-rustls 0.26.0",
"tokio-rustls 0.25.0",
"webpki-roots 0.26.3",
]
@@ -4875,12 +4867,6 @@ dependencies = [
"malachite-nz",
]
[[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"
@@ -4962,9 +4948,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.5"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
@@ -5196,9 +5182,9 @@ dependencies = [
[[package]]
name = "num-bigint"
version = "0.4.6"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
dependencies = [
"num-integer",
"num-traits",
@@ -5318,9 +5304,9 @@ dependencies = [
[[package]]
name = "object"
version = "0.36.1"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce"
checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
dependencies = [
"memchr",
]
@@ -5337,7 +5323,7 @@ dependencies = [
"chrono",
"futures",
"humantime",
"hyper 1.4.0",
"hyper 1.3.1",
"itertools 0.12.1",
"md-5 0.10.6",
"parking_lot",
@@ -5559,7 +5545,7 @@ dependencies = [
"libc",
"redox_syscall 0.5.2",
"smallvec",
"windows-targets 0.52.6",
"windows-targets 0.52.5",
]
[[package]]
@@ -5955,7 +5941,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d2234cdee9408b523530a9b6d2d6b373d1db34f6a8e51dc03ded1828d7fb67c"
dependencies = [
"array-init",
"bit-vec 0.6.3",
"bit-vec",
"bytes",
"chrono",
"fallible-iterator",
@@ -6270,9 +6256,9 @@ dependencies = [
[[package]]
name = "quick_cache"
version = "0.6.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e00e03be638aaab399c951dba6bea0161f99f26df0ccab4ab0fc6eb9535bd48"
checksum = "347e1a588d1de074eeb3c00eadff93db4db65aeb62aee852b1efd0949fe65b6c"
dependencies = [
"ahash 0.8.11",
"equivalent",
@@ -6671,7 +6657,7 @@ dependencies = [
"http 1.1.0",
"http-body 1.0.0",
"http-body-util",
"hyper 1.4.0",
"hyper 1.3.1",
"hyper-rustls 0.27.2",
"hyper-tls 0.6.0",
"hyper-util",
@@ -6685,7 +6671,7 @@ dependencies = [
"pin-project-lite",
"quinn",
"rustls 0.23.10",
"rustls-native-certs 0.7.1",
"rustls-native-certs 0.7.0",
"rustls-pemfile 2.1.2",
"rustls-pki-types",
"serde",
@@ -6989,7 +6975,7 @@ dependencies = [
"log",
"ring 0.17.8",
"rustls-pki-types",
"rustls-webpki 0.102.5",
"rustls-webpki 0.102.4",
"subtle",
"zeroize",
]
@@ -7003,7 +6989,7 @@ dependencies = [
"once_cell",
"ring 0.17.8",
"rustls-pki-types",
"rustls-webpki 0.102.5",
"rustls-webpki 0.102.4",
"subtle",
"zeroize",
]
@@ -7022,9 +7008,9 @@ dependencies = [
[[package]]
name = "rustls-native-certs"
version = "0.7.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba"
checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792"
dependencies = [
"openssl-probe",
"rustls-pemfile 2.1.2",
@@ -7082,9 +7068,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
version = "0.102.5"
version = "0.102.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78"
checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e"
dependencies = [
"ring 0.17.8",
"rustls-pki-types",
@@ -7376,9 +7362,9 @@ dependencies = [
[[package]]
name = "serde-wasm-bindgen"
version = "0.6.5"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
dependencies = [
"js-sys",
"serde",
@@ -7409,9 +7395,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.120"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
dependencies = [
"indexmap 2.2.6",
"itoa",
@@ -7508,9 +7494,9 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.8.3"
version = "3.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e73139bc5ec2d45e6c5fd85be5a46949c1c39a4c18e56915f5eb4c12f975e377"
checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -7526,9 +7512,9 @@ dependencies = [
[[package]]
name = "serde_with_macros"
version = "3.8.3"
version = "3.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b80d3d6b56b64335c0180e5ffde23b3c5e08c14c585b51a15bd0e95393f46703"
checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2"
dependencies = [
"darling 0.20.9",
"proc-macro2",
@@ -8995,6 +8981,17 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
dependencies = [
"rustls 0.22.4",
"rustls-pki-types",
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.26.0"
@@ -9672,7 +9669,7 @@ dependencies = [
"once_cell",
"rustls 0.22.4",
"rustls-pki-types",
"rustls-webpki 0.102.5",
"rustls-webpki 0.102.4",
"serde",
"serde_json",
"url",
@@ -9825,9 +9822,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -9835,9 +9832,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
dependencies = [
"bumpalo",
"log",
@@ -9850,9 +9847,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.42"
version = "0.4.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12"
dependencies = [
"cfg-if",
"js-sys",
@@ -9862,9 +9859,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -9872,9 +9869,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
@@ -9885,15 +9882,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
[[package]]
name = "wasm-bindgen-test"
version = "0.3.42"
version = "0.3.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b"
checksum = "2cf9242c0d27999b831eae4767b2a146feb0b27d332d553e605864acd2afd403"
dependencies = [
"console_error_panic_hook",
"js-sys",
@@ -9905,9 +9902,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.42"
version = "0.3.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0"
checksum = "794645f5408c9a039fd09f4d113cdfb2e7eba5ff1956b07bcf701cf4b394fe89"
dependencies = [
"proc-macro2",
"quote",
@@ -9951,9 +9948,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.69"
version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -10058,7 +10055,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"axum",
@@ -10097,7 +10094,7 @@ dependencies = [
[[package]]
name = "windmill-api"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"argon2",
@@ -10124,7 +10121,7 @@ dependencies = [
"hf-hub",
"hmac",
"http 1.1.0",
"hyper 1.4.0",
"hyper 1.3.1",
"itertools 0.13.0",
"jsonwebtoken",
"lazy_static",
@@ -10175,7 +10172,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"base64 0.21.7",
"chrono",
@@ -10193,7 +10190,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"chrono",
"serde",
@@ -10206,7 +10203,7 @@ dependencies = [
[[package]]
name = "windmill-common"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"aws-config",
@@ -10218,7 +10215,7 @@ dependencies = [
"git-version",
"hex",
"hmac",
"hyper 1.4.0",
"hyper 1.3.1",
"indexmap 2.2.6",
"itertools 0.13.0",
"lazy_static",
@@ -10245,7 +10242,7 @@ dependencies = [
[[package]]
name = "windmill-git-sync"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"regex",
"rsmq_async",
@@ -10260,7 +10257,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"serde",
"serde_json",
@@ -10268,7 +10265,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -10279,7 +10276,7 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"gosyn",
@@ -10291,7 +10288,7 @@ dependencies = [
[[package]]
name = "windmill-parser-graphql"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -10302,7 +10299,7 @@ dependencies = [
[[package]]
name = "windmill-parser-php"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -10316,7 +10313,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"itertools 0.13.0",
@@ -10327,7 +10324,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py-imports"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -10344,7 +10341,7 @@ dependencies = [
[[package]]
name = "windmill-parser-sql"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -10355,7 +10352,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -10374,7 +10371,7 @@ dependencies = [
[[package]]
name = "windmill-parser-wasm"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"getrandom 0.2.15",
@@ -10393,7 +10390,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -10426,7 +10423,7 @@ dependencies = [
[[package]]
name = "windmill-sql-datatype-parser-wasm"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"serde",
"wasm-bindgen",
@@ -10437,12 +10434,12 @@ dependencies = [
[[package]]
name = "windmill-worker"
version = "1.358.0"
version = "1.355.0"
dependencies = [
"anyhow",
"async-recursion",
"base64 0.21.7",
"bit-vec 0.6.3",
"bit-vec",
"bytes",
"chrono",
"const_format",
@@ -10465,7 +10462,6 @@ dependencies = [
"itertools 0.13.0",
"jsonwebtoken",
"lazy_static",
"mappable-rc",
"mysql_async",
"native-tls",
"nix",
@@ -10514,7 +10510,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.6",
"windows-targets 0.52.5",
]
[[package]]
@@ -10532,7 +10528,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
"windows-targets 0.52.5",
]
[[package]]
@@ -10552,18 +10548,18 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.5",
"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",
"windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.5",
]
[[package]]
@@ -10574,9 +10570,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
@@ -10586,9 +10582,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
@@ -10598,15 +10594,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
@@ -10616,9 +10612,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
@@ -10628,9 +10624,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
@@ -10640,9 +10636,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
@@ -10652,9 +10648,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "winnow"
@@ -10767,18 +10763,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.7.35"
version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.358.0"
version = "1.355.0"
authors.workspace = true
edition.workspace = true
@@ -24,7 +24,7 @@ members = [
]
[workspace.package]
version = "1.358.0"
version = "1.355.0"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
@@ -114,7 +114,7 @@ windmill-parser-graphql = { path = "./parsers/windmill-parser-graphql" }
windmill-parser-php = { path = "./parsers/windmill-parser-php" }
windmill-api-client = { path = "./windmill-api-client" }
axum = { version = "^0.7", features = ["multipart"] }
axum = { version = "^0.7", features = ["multipart"] }
headers = "^0"
hyper = { version = "^1", features = ["full"] }
tokio = { version = "^1", features = ["full", "tracing"] }
@@ -207,14 +207,13 @@ rsmq_async = { version = "5.1.5" }
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.2.89"
serde-wasm-bindgen = "0.4"
wasm-bindgen-test = "0.3.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"]}
bit-vec = "=0.6.3"
mappable-rc = "^0"
mysql_async = { version = "*", default-features = false, features = ["minimal", "default", "native-tls-tls"]}
postgres-native-tls = "^0"
native-tls = "^0"

View File

@@ -1 +1 @@
d414242f92cfaf9f364e9aeb321a511c3ad46fa7
e9917f62ebc48bdd93f9ddda5a58ba6ce8a74ed9

View File

@@ -1,7 +0,0 @@
-- Add down migration script here
DROP INDEX deployment_metadata_flow;
ALTER TABLE deployment_metadata DROP COLUMN flow_version;
create index if not exists deployment_metadata_flow on deployment_metadata (workspace_id, path) where script_hash is null and app_version is null;
alter table flow drop column versions;
DROP TABLE flow_version;

View File

@@ -1,55 +0,0 @@
-- Add up migration script here
-- create flow_version table with index
CREATE TABLE flow_version (
id bigserial PRIMARY KEY,
workspace_id varchar(50) NOT NULL,
path varchar(255) NOT NULL,
value jsonb,
schema json,
created_by varchar(50) NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
FOREIGN KEY (workspace_id, path) REFERENCES flow (workspace_id, path) ON DELETE CASCADE
);
CREATE INDEX index_flow_version_path_created_at ON flow_version (path, created_at);
-- add versions column to flow
ALTER TABLE flow ADD COLUMN versions bigint[] NOT NULL DEFAULT '{}'::bigint[];
-- create flow_version records for existing flows and update flow versions
INSERT INTO flow_version (workspace_id, path, value, schema, created_by, created_at) SELECT workspace_id, path, value, schema, edited_by, edited_at FROM flow;
UPDATE flow
SET versions = subquery.versions
FROM (
SELECT
path,
workspace_id,
array_agg(id) AS versions
FROM
flow_version
GROUP BY
path,
workspace_id
) subquery
WHERE
flow.path = subquery.path
AND flow.workspace_id = subquery.workspace_id;
-- add flow_version column to deployment_metadata
ALTER TABLE deployment_metadata ADD COLUMN flow_version int8;
-- populate flow_version column in deployment_metadata
UPDATE deployment_metadata
SET flow_version = fv.id
FROM flow_version fv
WHERE deployment_metadata.workspace_id = fv.workspace_id
AND deployment_metadata.path = fv.path
AND deployment_metadata.app_version IS NULL AND deployment_metadata.script_hash IS NULL;
-- update flow metadata index to include flow_verison
DROP INDEX IF EXISTS deployment_metadata_flow;
CREATE UNIQUE INDEX IF NOT EXISTS deployment_metadata_flow ON deployment_metadata (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL;
-- make sure the windmill_user and windmill_admin roles have access to the new tables
GRANT ALL ON flow_version TO windmill_user;
GRANT ALL ON flow_version_id_seq TO windmill_user;
GRANT ALL ON flow_version TO windmill_admin;
GRANT ALL ON flow_version_id_seq TO windmill_admin;

View File

@@ -17,7 +17,7 @@ pub fn parse_graphql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
}
lazy_static::lazy_static! {
static ref RE_ARG_GRAPHQL: Regex = Regex::new(r#"\$(\w+)\s*:\s*(?:(\w+)(!)?|\[(\w+)!?\])(!)?\s*(?:=\s*"?(\w+)"?\s*)?"#).unwrap();
static ref RE_ARG_GRAPHQL: Regex = Regex::new(r#"\$(\w+)\s*:\s*(?:(\w+)!?|\[(\w+)!?\])!?\s*(?:=\s*(\w+)\s*)?"#).unwrap();
}
fn parse_graphql_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
@@ -28,17 +28,16 @@ fn parse_graphql_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let mut typ = cap.get(2).map(|x| x.as_str().to_string());
let parsed_typ = if typ.is_none() {
let inner_typ = cap.get(4).map(|x| x.as_str().to_string());
let inner_typ = cap.get(3).map(|x| x.as_str().to_string());
typ = inner_typ.clone().map(|x| format!("[{}]", x.to_string()));
Typ::List(Box::new(parse_graphql_typ(inner_typ.unwrap().as_str())))
} else {
parse_graphql_typ(typ.clone().unwrap().as_str())
};
let (has_default, default) = match cap.get(6).map(|x| x.as_str().to_string()) {
Some(x) => (true, Some(x)), // default value => optional
None => (cap.get(3).is_none() && cap.get(5).is_none(), None), // optional if no !
};
let default = cap.get(4).map(|x| x.as_str().to_string());
let has_default = default.is_some();
let parsed_default = default.and_then(|x| match parsed_typ {
Typ::Int => x.parse::<i64>().ok().map(|x| json!(x)),
@@ -75,7 +74,7 @@ mod tests {
#[test]
fn test_parse_graphql_sig() -> anyhow::Result<()> {
let code = r#"
query($i: Int, $arr: [String]!, $wahoo: String = "wahoo") {
query($s: String, $arr: [String]) {
books {
title
}
@@ -89,11 +88,11 @@ query($i: Int, $arr: [String]!, $wahoo: String = "wahoo") {
star_kwargs: false,
args: vec![
Arg {
otyp: Some("Int".to_string()),
name: "i".to_string(),
typ: Typ::Int,
otyp: Some("String".to_string()),
name: "s".to_string(),
typ: Typ::Str(None),
default: None,
has_default: true
has_default: false
},
Arg {
otyp: Some("[String]".to_string()),
@@ -102,13 +101,6 @@ query($i: Int, $arr: [String]!, $wahoo: String = "wahoo") {
default: None,
has_default: false
},
Arg {
otyp: Some("String".to_string()),
name: "wahoo".to_string(),
typ: Typ::Str(None),
default: Some(json!("wahoo")),
has_default: true
}
],
no_main_func: None
}

View File

@@ -3,7 +3,7 @@
"collaborators": [
"Ruben Fiszel <ruben@windmill.dev>"
],
"version": "1.355.4",
"version": "1.347.1",
"files": [
"windmill_parser_wasm_bg.wasm",
"windmill_parser_wasm.js",

View File

@@ -6,6 +6,20 @@ heap.push(undefined, null, true, false);
function getObject(idx) { return heap[idx]; }
let heap_next = heap.length;
function dropObject(idx) {
if (idx < 132) return;
heap[idx] = heap_next;
heap_next = idx;
}
function takeObject(idx) {
const ret = getObject(idx);
dropObject(idx);
return ret;
}
let WASM_VECTOR_LEN = 0;
let cachedUint8Memory0 = null;
@@ -64,7 +78,6 @@ function passStringToWasm0(arg, malloc, realloc) {
const ret = encodeString(arg, view);
offset += ret.written;
ptr = realloc(ptr, len, offset, 1) >>> 0;
}
WASM_VECTOR_LEN = offset;
@@ -84,27 +97,13 @@ function getInt32Memory0() {
return cachedInt32Memory0;
}
let heap_next = heap.length;
let cachedFloat64Memory0 = null;
function dropObject(idx) {
if (idx < 132) return;
heap[idx] = heap_next;
heap_next = idx;
}
function takeObject(idx) {
const ret = getObject(idx);
dropObject(idx);
return ret;
}
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
function getFloat64Memory0() {
if (cachedFloat64Memory0 === null || cachedFloat64Memory0.byteLength === 0) {
cachedFloat64Memory0 = new Float64Array(wasm.memory.buffer);
}
return cachedFloat64Memory0;
}
function addHeapObject(obj) {
@@ -116,13 +115,13 @@ function addHeapObject(obj) {
return idx;
}
let cachedFloat64Memory0 = null;
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
function getFloat64Memory0() {
if (cachedFloat64Memory0 === null || cachedFloat64Memory0.byteLength === 0) {
cachedFloat64Memory0 = new Float64Array(wasm.memory.buffer);
}
return cachedFloat64Memory0;
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}
let cachedBigInt64Memory0 = null;
@@ -585,6 +584,9 @@ async function __wbg_load(module, imports) {
function __wbg_get_imports() {
const imports = {};
imports.wbg = {};
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
takeObject(arg0);
};
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
const obj = getObject(arg1);
const ret = typeof(obj) === 'string' ? obj : undefined;
@@ -593,13 +595,6 @@ function __wbg_get_imports() {
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
};
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
takeObject(arg0);
};
imports.wbg.__wbindgen_error_new = function(arg0, arg1) {
const ret = new Error(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
};
imports.wbg.__wbindgen_boolean_get = function(arg0) {
const v = getObject(arg0);
const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
@@ -636,7 +631,11 @@ function __wbg_get_imports() {
const ret = BigInt.asUintN(64, arg0);
return addHeapObject(ret);
};
imports.wbg.__wbg_eval_64ce1594989e82f8 = function(arg0, arg1) {
imports.wbg.__wbindgen_error_new = function(arg0, arg1) {
const ret = new Error(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
};
imports.wbg.__wbg_eval_83a1cdd46625140c = function(arg0, arg1) {
const ret = eval(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
};
@@ -644,11 +643,11 @@ function __wbg_get_imports() {
const ret = getObject(arg0) == getObject(arg1);
return ret;
};
imports.wbg.__wbg_get_bd8e338fbd5f5cc8 = function(arg0, arg1) {
imports.wbg.__wbg_get_f01601b5a68d10e3 = function(arg0, arg1) {
const ret = getObject(arg0)[arg1 >>> 0];
return addHeapObject(ret);
};
imports.wbg.__wbg_length_cd7af8117672b8b8 = function(arg0) {
imports.wbg.__wbg_length_1009b1af0c481d7b = function(arg0) {
const ret = getObject(arg0).length;
return ret;
};
@@ -656,39 +655,39 @@ function __wbg_get_imports() {
const ret = typeof(getObject(arg0)) === 'function';
return ret;
};
imports.wbg.__wbg_next_40fc327bfc8770e6 = function(arg0) {
imports.wbg.__wbg_next_9b877f231f476d01 = function(arg0) {
const ret = getObject(arg0).next;
return addHeapObject(ret);
};
imports.wbg.__wbg_next_196c84450b364254 = function() { return handleError(function (arg0) {
imports.wbg.__wbg_next_6529ee0cca8d57ed = function() { return handleError(function (arg0) {
const ret = getObject(arg0).next();
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_done_298b57d23c0fc80c = function(arg0) {
imports.wbg.__wbg_done_5fe336b092d60cf2 = function(arg0) {
const ret = getObject(arg0).done;
return ret;
};
imports.wbg.__wbg_value_d93c65011f51a456 = function(arg0) {
imports.wbg.__wbg_value_0c248a78fdc8e19f = function(arg0) {
const ret = getObject(arg0).value;
return addHeapObject(ret);
};
imports.wbg.__wbg_iterator_2cee6dadfd956dfa = function() {
imports.wbg.__wbg_iterator_db7ca081358d4fb2 = function() {
const ret = Symbol.iterator;
return addHeapObject(ret);
};
imports.wbg.__wbg_get_e3c254076557e348 = function() { return handleError(function (arg0, arg1) {
imports.wbg.__wbg_get_7b48513de5dc5ea4 = function() { return handleError(function (arg0, arg1) {
const ret = Reflect.get(getObject(arg0), getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_call_27c0f87801dedf93 = function() { return handleError(function (arg0, arg1) {
imports.wbg.__wbg_call_90c26b09837aba1c = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg0).call(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_isArray_2ab64d95e09ea0ae = function(arg0) {
imports.wbg.__wbg_isArray_74fb723e24f76012 = function(arg0) {
const ret = Array.isArray(getObject(arg0));
return ret;
};
imports.wbg.__wbg_instanceof_ArrayBuffer_836825be07d4c9d2 = function(arg0) {
imports.wbg.__wbg_instanceof_ArrayBuffer_e7d53d51371448e2 = function(arg0) {
let result;
try {
result = getObject(arg0) instanceof ArrayBuffer;
@@ -698,40 +697,30 @@ function __wbg_get_imports() {
const ret = result;
return ret;
};
imports.wbg.__wbg_instanceof_Map_87917e0a7aaf4012 = function(arg0) {
let result;
try {
result = getObject(arg0) instanceof Map;
} catch (_) {
result = false;
}
const ret = result;
return ret;
};
imports.wbg.__wbg_isSafeInteger_f7b04ef02296c4d2 = function(arg0) {
imports.wbg.__wbg_isSafeInteger_f93fde0dca9820f8 = function(arg0) {
const ret = Number.isSafeInteger(getObject(arg0));
return ret;
};
imports.wbg.__wbg_entries_95cc2c823b285a09 = function(arg0) {
imports.wbg.__wbg_entries_9e2e2aa45aa5094a = function(arg0) {
const ret = Object.entries(getObject(arg0));
return addHeapObject(ret);
};
imports.wbg.__wbg_buffer_12d079cc21e14bdb = function(arg0) {
imports.wbg.__wbg_buffer_a448f833075b71ba = function(arg0) {
const ret = getObject(arg0).buffer;
return addHeapObject(ret);
};
imports.wbg.__wbg_new_63b92bc8671ed464 = function(arg0) {
imports.wbg.__wbg_new_8f67e318f15d7254 = function(arg0) {
const ret = new Uint8Array(getObject(arg0));
return addHeapObject(ret);
};
imports.wbg.__wbg_set_a47bac70306a19a7 = function(arg0, arg1, arg2) {
imports.wbg.__wbg_set_2357bf09366ee480 = function(arg0, arg1, arg2) {
getObject(arg0).set(getObject(arg1), arg2 >>> 0);
};
imports.wbg.__wbg_length_c20a40f15020d68a = function(arg0) {
imports.wbg.__wbg_length_1d25fa9e4ac21ce7 = function(arg0) {
const ret = getObject(arg0).length;
return ret;
};
imports.wbg.__wbg_instanceof_Uint8Array_2b3bbecd033d19f6 = function(arg0) {
imports.wbg.__wbg_instanceof_Uint8Array_bced6f43aed8c1aa = function(arg0) {
let result;
try {
result = getObject(arg0) instanceof Uint8Array;

View File

@@ -1123,15 +1123,13 @@ async fn handle_zombie_flows(
let id = flow.id.clone();
let last_ping = flow.last_ping.clone();
let now = now_from_db(db).await?;
let reason = format!(
"{} was hanging in between 2 steps. Last ping: {last_ping:?} (now: {now})",
if flow.is_flow_step && flow.parent_job.is_some() {
format!("Flow was cancelled because subflow {id}")
} else {
format!("Flow {id} was cancelled because it")
}
);
cancel_zombie_flow_job(db, flow, &rsmq, reason).await?;
cancel_zombie_flow_job(
db,
flow,
&rsmq,
format!("Flow {} cancelled as it was hanging in between 2 steps. Last ping: {last_ping:?} (now: {now})", id),
)
.await?;
}
}
@@ -1191,7 +1189,7 @@ async fn cancel_zombie_flow_job(
tx,
db,
rsmq.clone(),
true,
false,
false,
)
.await?;

View File

@@ -71,7 +71,7 @@ if [ "$REVERT" == "YES" ]; then
ce_file="${ee_file/${EE_CODE_DIR}/.}"
ce_file="${root_dirpath}/backend/${ce_file}"
if [ "$REVERT_PREVIOUS" == "YES" ]; then
git checkout HEAD@{5} ${ce_file} || true
git checkout HEAD@{3} ${ce_file} || true
else
git restore --staged ${ce_file} || true
git restore ${ce_file} || true

View File

@@ -41,22 +41,12 @@ export async function main() {
'',
'f/system/schedule_recovery_handler', -28028598712388160, 'deno', '');
INSERT INTO public.flow(workspace_id, summary, description, path, versions, schema, value, edited_by) VALUES (
INSERT INTO public.flow(workspace_id, edited_by, value, schema, summary, description, path) VALUES (
'test-workspace',
'system',
'{"modules": [{"id": "a", "value": {"path": "f/system/failing_script", "type": "script", "input_transforms": {"fail": {"expr": "flow_input.fail", "type": "javascript"}}}}]}',
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"fail":{"default":true,"description":"","type":"boolean","format":""}},"required":[],"type":"object"}',
'',
'',
'f/system/failing_flow',
'{1}',
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"fail":{"default":true,"description":"","type":"boolean","format":""}},"required":[],"type":"object"}',
'{"modules": [{"id": "a", "value": {"path": "f/system/failing_script", "type": "script", "input_transforms": {"fail": {"expr": "flow_input.fail", "type": "javascript"}}}}]}',
'system'
);
INSERT INTO public.flow_version(id, workspace_id, path, schema, value, created_by) VALUES (
1,
'test-workspace',
'f/system/failing_flow',
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"fail":{"default":true,"description":"","type":"boolean","format":""}},"required":[],"type":"object"}',
'{"modules": [{"id": "a", "value": {"path": "f/system/failing_script", "type": "script", "input_transforms": {"fail": {"expr": "flow_input.fail", "type": "javascript"}}}}]}',
'system'
'f/system/failing_flow'
);

View File

@@ -899,7 +899,7 @@ impl RunJob {
tx,
"test-workspace",
payload,
windmill_queue::PushArgs::from(&hm_args),
hm_args.into(),
/* user */ "test-user",
/* email */ "test@windmill.dev",
/* permissioned_as */ "u/test-user".to_string(),

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.358.0
version: 1.355.0
title: Windmill API
contact:
@@ -114,16 +114,6 @@ paths:
- $ref: "#/components/parameters/After"
- $ref: "#/components/parameters/Username"
- $ref: "#/components/parameters/Operation"
- name: operations
in: query
description: comma separated list of exact operations to include
schema:
type: string
- name: exclude_operations
in: query
description: comma separated list of operations to exclude
schema:
type: string
- $ref: "#/components/parameters/ResourceName"
- $ref: "#/components/parameters/ActionKind"
@@ -3556,13 +3546,6 @@ paths:
in: query
schema:
type: boolean
- name: with_deployment_msg
description: |
(default false)
include deployment message
in: query
schema:
type: boolean
responses:
"200":
description: All scripts
@@ -4305,13 +4288,6 @@ paths:
in: query
schema:
type: boolean
- name: with_deployment_msg
description: |
(default false)
include deployment message
in: query
schema:
type: boolean
responses:
"200":
description: All flow
@@ -4329,84 +4305,6 @@ paths:
draft_only:
type: boolean
/w/{workspace}/flows/history/p/{path}:
get:
summary: get flow history by path
operationId: getFlowHistory
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
tags:
- flow
responses:
"200":
description: Flow history
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/FlowVersion"
/w/{workspace}/flows/get/v/{version}/p/{path}:
get:
summary: get flow version
operationId: getFlowVersion
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- type: string
name: version
in: path
required: true
schema:
type: number
- $ref: "#/components/parameters/ScriptPath"
tags:
- flow
responses:
"200":
description: flow details
content:
application/json:
schema:
$ref: "#/components/schemas/Flow"
/w/{workspace}/flows/history_update/v/{version}/p/{path}:
post:
summary: update flow history
operationId: updateFlowHistory
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- type: string
name: version
in: path
required: true
schema:
type: number
- $ref: "#/components/parameters/ScriptPath"
requestBody:
description: Flow deployment message
required: true
content:
application/json:
schema:
type: object
properties:
deployment_msg:
type: string
required:
- deployment_msg
tags:
- flow
responses:
"200":
description: success
content:
text/plain:
schema:
type: string
/w/{workspace}/flows/get/{path}:
get:
summary: get flow by path
@@ -4750,13 +4648,6 @@ paths:
in: query
schema:
type: boolean
- name: with_deployment_msg
description: |
(default false)
include deployment message
in: query
schema:
type: boolean
responses:
"200":
description: All apps
@@ -5711,7 +5602,6 @@ paths:
- $ref: "#/components/parameters/Running"
- $ref: "#/components/parameters/ScheduledForBeforeNow"
- $ref: "#/components/parameters/CreatedOrStartedAfter"
- $ref: "#/components/parameters/CreatedOrStartedAfterCompletedJob"
- $ref: "#/components/parameters/JobKinds"
- $ref: "#/components/parameters/ArgsFilter"
- $ref: "#/components/parameters/Tag"
@@ -5827,23 +5717,6 @@ paths:
schema:
type: string
/w/{workspace}/jobs_u/get_args/{id}:
get:
summary: get job args
operationId: getJobArgs
tags:
- job
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/JobId"
responses:
"200":
description: job args
content:
application/json:
schema: {}
/w/{workspace}/jobs_u/getupdate/{id}:
get:
summary: get job updates
@@ -8499,7 +8372,6 @@ paths:
- $ref: "#/components/parameters/Running"
- $ref: "#/components/parameters/ScheduledForBeforeNow"
- $ref: "#/components/parameters/CreatedOrStartedAfter"
- $ref: "#/components/parameters/CreatedOrStartedAfterCompletedJob"
- $ref: "#/components/parameters/JobKinds"
- $ref: "#/components/parameters/ArgsFilter"
- $ref: "#/components/parameters/Tag"
@@ -8765,15 +8637,6 @@ components:
schema:
type: string
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
after (exclusive) timestamp but only for the completed jobs
in: query
schema:
type: string
format: date-time
CreatedOrStartedBefore:
name: created_or_started_before
description:
@@ -10724,20 +10587,6 @@ components:
required:
- version
FlowVersion:
type: object
properties:
id:
type: integer
created_at:
type: string
format: date-time
deployment_msg:
type: string
required:
- id
- created_at
SlackToken:
type: object
properties:

View File

@@ -46,7 +46,7 @@ use windmill_common::{
};
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
use windmill_queue::{push, PushArgs, PushArgsOwned, PushIsolationLevel, QueueTransaction};
use windmill_queue::{push, PushArgs, PushIsolationLevel, QueueTransaction};
pub fn workspaced_service() -> Router {
Router::new()
@@ -91,9 +91,6 @@ pub struct ListableApp {
pub has_draft: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub draft_only: Option<bool>,
#[sqlx(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub deployment_msg: Option<String>,
}
#[derive(FromRow, Serialize, Deserialize)]
@@ -294,13 +291,6 @@ async fn list_apps(
sqlb.and_where("app.draft_only IS NOT TRUE");
}
if lq.with_deployment_msg.unwrap_or(false) {
sqlb.join("deployment_metadata dm")
.left()
.on("dm.app_version = app.versions[array_upper(app.versions, 1)]")
.fields(&["dm.deployment_msg"]);
}
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
let mut tx = user_db.begin(&authed).await?;
let rows = sqlx::query_as::<_, ListableApp>(&sql)
@@ -653,7 +643,7 @@ async fn create_app(
tx,
&w_id,
JobPayload::AppDependencies { path: app.path.clone(), version: v_id },
PushArgs { args: &args, extra: None },
PushArgs { args, extra: HashMap::new() },
&authed.username,
&authed.email,
windmill_common::users::username_to_permissioned_as(&authed.username),
@@ -944,7 +934,7 @@ async fn update_app(
tx,
&w_id,
JobPayload::AppDependencies { path: npath.clone(), version: v_id },
PushArgs { args: &args, extra: None },
PushArgs { args, extra: HashMap::new() },
&authed.username,
&authed.email,
windmill_common::users::username_to_permissioned_as(&authed.username),
@@ -1157,7 +1147,7 @@ async fn execute_component(
tx,
&w_id,
job_payload,
PushArgs { args: &args.args, extra: args.extra },
args,
&username,
&email,
permissioned_as,
@@ -1239,12 +1229,12 @@ async fn build_args(
policy: Policy,
component: &str,
path: String,
mut args: HashMap<String, Box<RawValue>>,
args: HashMap<String, Box<RawValue>>,
authed: Option<&ApiAuthed>,
user_db: &UserDB,
db: &DB,
w_id: &str,
) -> Result<(PushArgsOwned, Option<Uuid>)> {
) -> Result<(PushArgs, Option<Uuid>)> {
let mut job_id: Option<Uuid> = None;
let key = format!("{}:{}", component, &path);
let (static_inputs, one_of_inputs, allow_user_resources) = match policy {
@@ -1294,6 +1284,7 @@ async fn build_args(
)))?,
};
let mut args = args.clone();
let mut safe_args = HashMap::<String, Box<RawValue>>::new();
// tracing::error!("{:?}", allow_user_resources);
@@ -1412,8 +1403,5 @@ async fn build_args(
for (k, v) in static_inputs {
extra.insert(k.to_string(), v.to_owned());
}
Ok((
PushArgsOwned { extra: Some(extra), args: safe_args },
job_id,
))
Ok((PushArgs { extra, args: safe_args }, job_id))
}

View File

@@ -18,7 +18,7 @@ use windmill_common::{
error::{JsonResult, Result},
utils::{not_found_if_none, StripPath},
};
use windmill_queue::{PushArgs, PushArgsOwned};
use windmill_queue::PushArgs;
use crate::db::{ApiAuthed, DB};
@@ -86,7 +86,7 @@ pub async fn new_payload(
pub async fn update_payload(
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
args: PushArgsOwned,
args: PushArgs,
) -> Result<StatusCode> {
let mut tx = db.begin().await?;
@@ -99,7 +99,7 @@ pub async fn update_payload(
",
&w_id,
&path.to_path(),
Json(PushArgs { args: &args.args, extra: args.extra }) as Json<PushArgs>,
Json(args) as Json<PushArgs>,
)
.execute(&mut *tx)
.await?;

View File

@@ -123,10 +123,7 @@ struct ExtendedJobsParams {
row_limit: Option<i64>,
}
pub fn join_concurrency_key<'c>(
concurrency_key: Option<&String>,
mut sqlb: SqlBuilder,
) -> SqlBuilder {
pub fn join_concurrency_key<'c>(concurrency_key: Option<&String>, mut sqlb: SqlBuilder) -> SqlBuilder {
if let Some(key) = concurrency_key {
sqlb.join("concurrency_key")
.on_eq("id", "concurrency_key.job_id")
@@ -208,7 +205,6 @@ async fn get_concurrent_intervals(
created_after: _,
created_or_started_before: _,
created_or_started_after: _,
created_or_started_after_completed_jobs: _,
order_desc: _,
job_kinds: _,
is_flow_step: _,

View File

@@ -56,12 +56,6 @@ pub fn workspaced_service() -> Router {
.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(
"/history_update/v/:version/p/*path",
post(update_flow_history),
)
.route("/get/v/:version/p/*path", get(get_flow_version))
.route(
"/toggle_workspace_error_handler/*path",
post(toggle_workspace_error_handler),
@@ -92,10 +86,7 @@ async fn list_search_flows(
let mut tx = user_db.begin(&authed).await?;
let rows = sqlx::query_as::<_, SearchFlow>(
"SELECT flow.path, flow_version.value
FROM flow
LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]
WHERE flow.workspace_id = $1 LIMIT $2",
"SELECT path, value from flow WHERE workspace_id = $1 LIMIT $2",
)
.bind(&w_id)
.bind(n)
@@ -122,8 +113,8 @@ async fn list_flows(
"o.path",
"summary",
"description",
"fv.created_by as edited_by",
"fv.created_at as edited_at",
"edited_by",
"edited_at",
"archived",
"extra_perms",
"favorite.path IS NOT NULL as starred",
@@ -142,13 +133,8 @@ async fn list_flows(
.on(
"draft.path = o.path AND draft.workspace_id = o.workspace_id AND draft.typ = 'flow'"
)
.left()
.join("flow_version fv")
.on(
"fv.id = o.versions[array_upper(o.versions, 1)]"
)
.order_desc("favorite.path IS NOT NULL")
.order_by("fv.created_at", lq.order_desc.unwrap_or(true))
.order_by("edited_at", lq.order_desc.unwrap_or(true))
.and_where("o.workspace_id = ?".bind(&w_id))
.offset(offset)
.limit(per_page)
@@ -163,7 +149,7 @@ async fn list_flows(
sqlb.and_where_eq("o.path", "?".bind(p));
}
if let Some(cb) = &lq.edited_by {
sqlb.and_where_eq("fv.created_by", "?".bind(cb));
sqlb.and_where_eq("edited_by", "?".bind(cb));
}
if lq.starred_only.unwrap_or(false) {
sqlb.and_where_is_not_null("favorite.path");
@@ -173,13 +159,6 @@ async fn list_flows(
sqlb.and_where("o.draft_only IS NOT TRUE");
}
if lq.with_deployment_msg.unwrap_or(false) {
sqlb.join("deployment_metadata dm")
.left()
.on("dm.flow_version = o.versions[array_upper(o.versions, 1)]")
.fields(&["dm.deployment_msg"]);
}
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
let mut tx = user_db.begin(&authed).await?;
let rows = sqlx::query_as::<_, ListableFlow>(&sql)
@@ -345,47 +324,24 @@ async fn create_flow(
check_path_conflict(tx.transaction_mut(), &w_id, &nf.path).await?;
check_schedule_conflict(tx.transaction_mut(), &w_id, &nf.path).await?;
let schema_str = nf.schema.and_then(|x| serde_json::to_string(&x.0).ok());
sqlx::query!(
"INSERT INTO flow (workspace_id, path, summary, description, \
dependency_job, draft_only, tag, dedicated_worker, visible_to_runner_only, value, schema, edited_by, edited_at)
VALUES ($1, $2, $3, $4, NULL, $5, $6, $7, $8, $9, $10::text::json, $11, now())",
"INSERT INTO flow (workspace_id, path, summary, description, value, edited_by, edited_at, \
schema, dependency_job, draft_only, tag, dedicated_worker, visible_to_runner_only) VALUES ($1, $2, $3, $4, $5, $6, now(), $7::text::json, NULL, $8, $9, $10, $11)",
w_id,
nf.path,
nf.summary,
nf.description.unwrap_or_else(String::new),
nf.value,
&authed.username,
nf.schema.and_then(|x| serde_json::to_string(&x.0).ok()),
nf.draft_only,
nf.tag,
nf.dedicated_worker,
nf.visible_to_runner_only.unwrap_or(false),
nf.value,
schema_str,
&authed.username,
)
.execute(&mut tx)
.await?;
let version = sqlx::query_scalar!(
"INSERT INTO flow_version (workspace_id, path, value, schema, created_by)
VALUES ($1, $2, $3, $4::text::json, $5)
RETURNING id",
w_id,
nf.path,
nf.value,
schema_str,
&authed.username,
)
.fetch_one(&mut tx)
.await?;
sqlx::query!(
"UPDATE flow SET versions = array_append(versions, $1) WHERE path = $2 AND workspace_id = $3",
version,
nf.path,
w_id
).execute(&mut tx).await?;
sqlx::query!(
"DELETE FROM draft WHERE path = $1 AND workspace_id = $2 AND typ = 'flow'",
nf.path,
@@ -423,9 +379,8 @@ async fn create_flow(
JobPayload::FlowDependencies {
path: nf.path.clone(),
dedicated_worker: nf.dedicated_worker,
version: version,
},
windmill_queue::PushArgs { args: &args, extra: None },
args.into(),
&authed.username,
&authed.email,
windmill_common::users::username_to_permissioned_as(&authed.username),
@@ -499,110 +454,6 @@ pub async fn require_is_writer(authed: &ApiAuthed, path: &str, w_id: &str, db: D
.await;
}
#[derive(Serialize)]
pub struct FlowVersion {
pub id: i64,
pub created_at: chrono::DateTime<chrono::Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub deployment_msg: Option<String>,
}
async fn get_flow_history(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<Vec<FlowVersion>> {
let path = path.to_path();
let mut tx = user_db.begin(&authed).await?;
let flows = sqlx::query_as!(
FlowVersion,
"SELECT flow_version.id, flow_version.created_at, deployment_metadata.deployment_msg FROM flow_version
LEFT JOIN deployment_metadata ON flow_version.id = deployment_metadata.flow_version
WHERE flow_version.path = $1 AND flow_version.workspace_id = $2
ORDER BY flow_version.created_at DESC",
path,
w_id
)
.fetch_all(&mut *tx)
.await?;
tx.commit().await?;
Ok(Json(flows))
}
async fn get_flow_version(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, version, path)): Path<(String, i64, StripPath)>,
) -> JsonResult<Flow> {
let path = path.to_path();
let mut tx = user_db.begin(&authed).await?;
let flow = 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_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.path = flow.path AND flow_version.workspace_id = flow.workspace_id
WHERE flow.path = $1 AND flow.workspace_id = $2 AND flow_version.id = $3",
)
.bind(path)
.bind(w_id)
.bind(version)
.fetch_optional(&mut *tx)
.await?;
tx.commit().await?;
let flow = not_found_if_none(flow, "Flow version", version.to_string())?;
Ok(Json(flow))
}
#[derive(Deserialize)]
pub struct FlowHistoryUpdate {
pub deployment_msg: String,
}
async fn update_flow_history(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, version, path)): Path<(String, i64, StripPath)>,
Json(history_update): Json<FlowHistoryUpdate>,
) -> Result<()> {
let path = path.to_path();
let mut tx = user_db.begin(&authed).await?;
let path_o = sqlx::query_scalar!(
"SELECT flow.path FROM flow
LEFT JOIN flow_version
ON flow_version.path = flow.path AND flow_version.workspace_id = flow.workspace_id
WHERE flow.path = $1 AND flow.workspace_id = $2 AND flow_version.id = $3",
path,
w_id,
version
)
.fetch_optional(&mut *tx)
.await?;
if path_o.is_none() {
tx.commit().await?;
return Err(Error::NotFound(
format!("Flow version {version} for path {path} not found").to_string(),
));
}
sqlx::query!(
"INSERT INTO deployment_metadata (workspace_id, path, flow_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL DO UPDATE SET deployment_msg = $4",
w_id,
path_o.unwrap(),
version,
history_update.deployment_msg,
)
.fetch_optional(&mut *tx)
.await?;
tx.commit().await?;
return Ok(());
}
async fn update_flow(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
@@ -641,99 +492,26 @@ async fn update_flow(
.fetch_optional(&mut tx)
.await?;
let old_dep_job = not_found_if_none(old_dep_job, "Flow", flow_path)?;
let is_new_path = nf.path != flow_path;
let schema_str = schema.and_then(|x| serde_json::to_string(&x).ok());
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, \
value = $7, schema = $8::text::json, edited_by = $9, edited_at = now()
WHERE path = $10 AND workspace_id = $11",
if is_new_path { flow_path } else { &nf.path }, // if new path, do not rename directly (to avoid flow_version foreign key constraint)
"UPDATE flow SET path = $1, summary = $2, description = $3, value = $4, edited_by = $5, \
edited_at = now(), schema = $6::text::json, dependency_job = NULL, draft_only = NULL, tag = $9, dedicated_worker = $10, visible_to_runner_only = $11
WHERE path = $7 AND workspace_id = $8",
nf.path,
nf.summary,
nf.description.unwrap_or_else(String::new),
nf.value,
&authed.username,
schema.and_then(|x| serde_json::to_string(&x).ok()),
flow_path,
w_id,
nf.tag,
nf.dedicated_worker,
nf.visible_to_runner_only.unwrap_or(false),
nf.value,
schema_str,
&authed.username,
flow_path,
w_id,
)
.execute(&mut tx)
.await.map_err(|e| error::Error::InternalErr(format!("Error updating flow due to flow update: {e:#}")))?;
if is_new_path {
// if new path, must clone flow to new path and delete old flow for flow_version foreign key constraint
sqlx::query!(
"INSERT INTO flow
(workspace_id, path, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, concurrency_key, versions, value, schema, edited_by, edited_at)
SELECT workspace_id, $1, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, concurrency_key, versions, value, schema, edited_by, edited_at
FROM flow
WHERE path = $2 AND workspace_id = $3",
nf.path,
flow_path,
w_id
)
.execute(&mut tx)
.await
.map_err(|e| {
error::Error::InternalErr(format!("Error updating flow due to create new flow: {e:#}"))
})?;
sqlx::query!(
"UPDATE flow_version SET path = $1 WHERE path = $2 AND workspace_id = $3",
nf.path,
flow_path,
w_id
)
.execute(&mut tx)
.await
.map_err(|e| {
error::Error::InternalErr(format!(
"Error updating flow due to updating flow history path: {e:#}"
))
})?;
sqlx::query!(
"DELETE FROM flow WHERE path = $1 AND workspace_id = $2",
flow_path,
w_id
)
.execute(&mut tx)
.await
.map_err(|e| {
error::Error::InternalErr(format!(
"Error updating flow due to deleting old flow: {e:#}"
))
})?;
}
let version = sqlx::query_scalar!(
"INSERT INTO flow_version (workspace_id, path, value, schema, created_by) VALUES ($1, $2, $3, $4::text::json, $5) RETURNING id",
w_id,
nf.path,
nf.value,
schema_str,
&authed.username,
)
.fetch_one(&mut tx)
.await
.map_err(|e| {
error::Error::InternalErr(format!(
"Error updating flow due to flow history insert: {e:#}"
))
})?;
sqlx::query!(
"UPDATE flow SET versions = array_append(versions, $1) WHERE path = $2 AND workspace_id = $3",
version, flow_path, w_id
).execute(&mut tx).await?;
if is_new_path {
if nf.path != flow_path {
check_schedule_conflict(tx.transaction_mut(), &w_id, &nf.path).await?;
if !authed.is_admin {
@@ -818,9 +596,8 @@ async fn update_flow(
JobPayload::FlowDependencies {
path: nf.path.clone(),
dedicated_worker: nf.dedicated_worker,
version: version,
},
windmill_queue::PushArgs { args: &args, extra: None },
windmill_queue::PushArgs { args, extra: HashMap::new() },
&authed.username,
&authed.email,
windmill_common::users::username_to_permissioned_as(&authed.username),
@@ -881,12 +658,7 @@ async fn get_flow_by_path(
let mut tx = user_db.begin(&authed).await?;
let flow_o =
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_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.path = $1 AND flow.workspace_id = $2"
)
sqlx::query_as::<_, Flow>("SELECT * FROM flow WHERE path = $1 AND workspace_id = $2")
.bind(path)
.bind(w_id)
.fetch_optional(&mut *tx)
@@ -928,12 +700,10 @@ async fn get_flow_by_path_w_draft(
let mut tx = user_db.begin(&authed).await?;
let flow_o = sqlx::query_as::<_, FlowWDraft>(
"SELECT flow.path, flow.summary, flow,description, flow_version.schema, flow_version.value, flow.extra_perms, flow.draft_only, flow.ws_error_handler_muted, flow.dedicated_worker, draft.value as draft, flow.tag, flow.visible_to_runner_only
"SELECT flow.path, flow.summary, flow,description, flow.schema, flow.value, flow.extra_perms, flow.draft_only, flow.ws_error_handler_muted, flow.dedicated_worker, draft.value as draft, flow.tag, flow.visible_to_runner_only
FROM flow
LEFT JOIN draft
ON flow.path = draft.path AND draft.workspace_id = $2 AND draft.typ = 'flow'
LEFT JOIN flow_version
ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]
LEFT JOIN draft ON
flow.path = draft.path AND draft.workspace_id = $2 AND draft.typ = 'flow'
WHERE flow.path = $1 AND flow.workspace_id = $2",
)
.bind(path)
@@ -1007,11 +777,7 @@ async fn archive_flow_by_path(
&authed.username,
&db,
&w_id,
DeployedObject::Flow {
path: path.to_string(),
parent_path: Some(path.to_string()),
version: 0, // dummy version as it will not get inserted in db
},
DeployedObject::Flow { path: path.to_string(), parent_path: Some(path.to_string()) },
Some(format!(
"Flow '{}' {}",
path,
@@ -1078,11 +844,7 @@ async fn delete_flow_by_path(
&authed.username,
&db,
&w_id,
DeployedObject::Flow {
path: path.to_string(),
parent_path: Some(path.to_string()),
version: 0, // dummy version as it will not get inserted in db
},
DeployedObject::Flow { path: path.to_string(), parent_path: Some(path.to_string()) },
Some(format!("Flow '{}' deleted", path)),
rsmq,
true,

View File

@@ -76,8 +76,8 @@ use windmill_common::{METRICS_DEBUG_ENABLED, METRICS_ENABLED};
use windmill_common::{get_latest_deployed_hash_for_path, BASE_URL};
use windmill_queue::{
cancel_job, get_queued_job, get_result_by_id_from_running_flow, job_is_complete, push,
DecodeQueries, PushArgs, PushArgsOwned, PushIsolationLevel, QueueTransaction,
add_completed_job_error, append_logs, get_queued_job, get_result_by_id_from_running_flow,
job_is_complete, push, CanceledBy, DecodeQueries, PushArgs, PushIsolationLevel,
};
#[cfg(feature = "prometheus")]
@@ -266,7 +266,6 @@ pub fn global_service() -> Router {
.route("/get_root_job_id/:id", get(get_root_job))
.route("/get/:id", get(get_job))
.route("/get_logs/:id", get(get_job_logs))
.route("/get_args/:id", get(get_args))
.route("/get_flow_debug_info/:id", get(get_flow_job_debug_info))
.route("/completed/get/:id", get(get_completed_job))
.route("/completed/get_result/:id", get(get_completed_job_result))
@@ -529,8 +528,8 @@ pub async fn get_path_for_hash<'c>(
Ok(path)
}
pub async fn get_path_tag_limits_cache_for_hash<'c, R: rsmq_async::RsmqConnection + Send>(
tx: &mut QueueTransaction<'c, R>,
pub async fn get_path_tag_limits_cache_for_hash(
db: &DB,
w_id: &str,
hash: i64,
) -> error::Result<(
@@ -551,15 +550,13 @@ pub async fn get_path_tag_limits_cache_for_hash<'c, R: rsmq_async::RsmqConnectio
hash,
w_id
)
.fetch_optional(tx)
.fetch_one(db)
.await
.map_err(|e| {
Error::InternalErr(format!(
"querying getting path for hash {hash} in {w_id}: {e:#}"
))
})?.ok_or_else(|| Error::NotFound(format!(
"deployed script not found at hash {hash} in workspace {w_id}"
)))?;
})?;
Ok((
script.path,
script.tag,
@@ -588,20 +585,14 @@ async fn get_flow_job_debug_info(
"This endpoint is only for root flow jobs".to_string(),
));
}
let leaf_jobs: HashMap<String, JobResult> = job
.leaf_jobs
.clone()
.and_then(|x| serde_json::from_value(x).ok())
.unwrap_or_else(HashMap::new);
let mut jobs = HashMap::new();
let id = job.id.clone();
jobs.insert("root_job".to_string(), Job::QueuedJob(job));
jobs.insert("root_job".to_string(), Job::QueuedJob(job.clone()));
let mut job_ids = vec![];
let jobs_with_root = sqlx::query_scalar!(
"SELECT id FROM queue WHERE workspace_id = $1 and root_job = $2",
&w_id,
&id,
&job.id,
)
.fetch_all(&db)
.await?;
@@ -610,6 +601,10 @@ async fn get_flow_job_debug_info(
job_ids.push(job);
}
let leaf_jobs: HashMap<String, JobResult> = job
.leaf_jobs
.and_then(|x| serde_json::from_value(x).ok())
.unwrap_or_else(HashMap::new);
for job in leaf_jobs.iter() {
match job.1 {
JobResult::ListJob(jobs) => job_ids.extend(jobs.to_owned()),
@@ -911,54 +906,6 @@ async fn get_job_logs(
}
}
#[derive(FromRow)]
pub struct RawArgs {
pub args: Option<sqlx::types::Json<Box<RawValue>>>,
pub created_by: String,
}
async fn get_args(
OptAuthed(opt_authed): OptAuthed,
Extension(db): Extension<DB>,
Path((w_id, id)): Path<(String, Uuid)>,
) -> error::JsonResult<Box<serde_json::value::RawValue>> {
let record = sqlx::query_as::<_, RawArgs>(
"SELECT created_by, args
FROM completed_job
WHERE completed_job.id = $1 AND completed_job.workspace_id = $2",
)
.bind(&id)
.bind(&w_id)
.fetch_optional(&db)
.await?;
if let Some(record) = record {
if opt_authed.is_none() && record.created_by != "anonymous" {
return Err(Error::BadRequest(
"As a non logged in user, you can only see jobs ran by anonymous users".to_string(),
));
}
Ok(Json(record.args.map(|x| x.0).unwrap_or_default()))
} else {
let record = sqlx::query_as::<_, RawArgs>(
"SELECT created_by, args
FROM queue
WHERE queue.id = $1 AND queue.workspace_id = $2",
)
.bind(&id)
.bind(&w_id)
.fetch_optional(&db)
.await?;
let record = not_found_if_none(record, "Job Args", id.to_string())?;
if opt_authed.is_none() && record.created_by != "anonymous" {
return Err(Error::BadRequest(
"As a non logged in user, you can only see jobs ran by anonymous users".to_string(),
));
}
Ok(Json(record.args.map(|x| x.0).unwrap_or_default()))
}
}
#[derive(Debug, sqlx::FromRow, Serialize)]
pub struct ListableCompletedJob {
pub r#type: String,
@@ -1290,49 +1237,66 @@ async fn list_queue_jobs(
Ok(Json(jobs))
}
#[derive(Deserialize, FromRow)]
struct JobToCancel {
id: Uuid,
is_flow_step: Option<bool>,
running: bool,
}
async fn cancel_jobs(
jobs: Vec<Uuid>,
jobs: Vec<JobToCancel>,
db: &DB,
username: &str,
w_id: &str,
rsmq: Option<rsmq_async::MultiplexedRsmq>,
) -> error::JsonResult<Vec<Uuid>> {
let mut uuids = vec![];
for job_id in jobs.into_iter() {
let rsmq = rsmq.clone();
match tokio::time::timeout(tokio::time::Duration::from_secs(5), async move {
let tx = db.begin().await?;
let (tx, _) = windmill_queue::cancel_job(
username,
None,
job_id.clone(),
w_id,
tx,
db,
rsmq,
false,
false,
)
.await?;
tx.commit().await?;
Ok::<_, anyhow::Error>(())
})
.await
{
Ok(result) => match result {
Ok(_) => {
uuids.push(job_id);
for j in jobs.iter() {
let r = sqlx::query!(
"UPDATE queue SET canceled = true, canceled_by = $1, scheduled_for = now(), suspend = 0 WHERE id = $2 RETURNING 1 as one",
username,
j.id,
)
.fetch_optional(db)
.await;
if r.as_ref().is_ok_and(|x| x.is_some()) {
uuids.push(j.id);
if !j.running && !j.is_flow_step.unwrap_or(false) {
let e = serde_json::json!({"message": format!("Job canceled: cancel_all by {username}"), "name": "Canceled", "reason": "cancel_all", "canceler": username});
let job_running = get_queued_job(&j.id, &w_id, &db).await?;
if let Some(job_running) = job_running {
append_logs(
&j.id,
w_id,
format!("canceled by {username}: cancel_all"),
db.clone(),
)
.await;
let add_job = add_completed_job_error(
&db,
&job_running,
job_running.mem_peak.unwrap_or(0),
Some(CanceledBy {
username: Some(username.to_string()),
reason: Some("cancel_all".to_string()),
}),
e,
rsmq.clone(),
"server",
true,
)
.await;
if let Err(e) = add_job {
tracing::error!("Failed to add canceled job: {}", e);
}
}
Err(e) => {
tracing::error!("Failed to cancel job {:?}: {:?}", job_id, e);
}
},
Err(_) => {
tracing::error!(
"Timeout while trying to cancel job {:?} after 5 seconds",
job_id
);
}
} else {
tracing::error!("Failed to cancel job: {:?} {:?}", j.id, r.err());
}
}
@@ -1351,8 +1315,9 @@ async fn cancel_selection(
require_admin(authed.is_admin, &authed.username)?;
let mut tx = user_db.begin(&authed).await?;
let jobs_to_cancel = sqlx::query_scalar!(
"SELECT id FROM queue WHERE id = ANY($1) AND schedule_path IS NULL",
let jobs_to_cancel = sqlx::query_as!(
JobToCancel,
"SELECT id, is_flow_step, running FROM queue WHERE id = ANY($1) AND schedule_path IS NULL",
&jobs
)
.fetch_all(&mut *tx)
@@ -1492,11 +1457,6 @@ async fn list_jobs(
#[cfg(feature = "prometheus")]
let start = Instant::now();
#[cfg(feature = "prometheus")]
if _api_list_jobs_query_duration.is_some() {
tracing::info!("list_jobs query: {}", sql);
}
let jobs: Vec<UnifiedJob> = sqlx::query_as(&sql).fetch_all(&mut *tx).await?;
tx.commit().await?;
@@ -2634,40 +2594,34 @@ pub async fn run_flow_by_path(
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
Path((w_id, flow_path)): Path<(String, StripPath)>,
Query(run_query): Query<RunJobQuery>,
args: PushArgsOwned,
args: PushArgs,
) -> error::Result<(StatusCode, String)> {
#[cfg(feature = "enterprise")]
check_license_key_valid().await?;
let flow_path = flow_path.to_path();
check_scopes(&authed, || format!("run:flow/{flow_path}"))?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let (tag, dedicated_worker) = sqlx::query!(
"SELECT tag, dedicated_worker from flow WHERE path = $1 and workspace_id = $2",
flow_path,
w_id
)
.fetch_optional(&mut tx)
.fetch_optional(&db)
.await?
.map(|x| (x.tag, x.dedicated_worker))
.ok_or_else(|| {
Error::NotFound(format!(
"flow not found at path {flow_path} in workspace {w_id}"
))
})?;
.unwrap_or_else(|| (None, None));
let tag = run_query.tag.clone().or(tag);
check_tag_available_for_workspace(&w_id, &tag).await?;
let scheduled_for = run_query.get_scheduled_for(&db).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
tx,
&w_id,
JobPayload::Flow { path: flow_path.to_string(), dedicated_worker },
PushArgs { args: &args.args, extra: args.extra },
args,
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -2726,14 +2680,12 @@ pub async fn restart_flow(
) -> error::Result<(StatusCode, String)> {
check_license_key_valid().await?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let completed_job = sqlx::query_as::<_, CompletedJob>(
"SELECT *, result->'wm_labels' as labels from completed_job WHERE id = $1 and workspace_id = $2",
)
.bind(job_id)
.bind(&w_id)
.fetch_optional(&mut tx)
.fetch_optional(&db)
.await?
.with_context(|| "Unable to find completed job with the given job UUID")?;
@@ -2742,16 +2694,13 @@ pub async fn restart_flow(
.with_context(|| "No flow path set for completed flow job")?;
check_scopes(&authed, || format!("run:flow/{flow_path}"))?;
let ehm = HashMap::new();
let push_args = completed_job
.args
.as_ref()
.map(|json| PushArgs { args: &json.0, extra: None })
.unwrap_or_else(|| PushArgs::from(&ehm));
.map(|json| PushArgs { args: json.0, extra: HashMap::new() })
.unwrap_or_else(PushArgs::empty);
let scheduled_for = run_query.get_scheduled_for(&db).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
@@ -2793,7 +2742,7 @@ pub async fn run_script_by_path(
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
Path((w_id, script_path)): Path<(String, StripPath)>,
Query(run_query): Query<RunJobQuery>,
args: PushArgsOwned,
args: PushArgs,
) -> error::Result<(StatusCode, String)> {
#[cfg(feature = "enterprise")]
check_license_key_valid().await?;
@@ -2802,23 +2751,20 @@ pub async fn run_script_by_path(
check_scopes(&authed, || format!("run:script/{script_path}"))?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let (job_payload, tag, _delete_after_use, timeout) =
script_path_to_payload(script_path, &mut tx, &w_id).await?;
script_path_to_payload(script_path, &db, &w_id).await?;
let scheduled_for = run_query.get_scheduled_for(&db).await?;
let tag = run_query.tag.clone().or(tag);
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
tx,
&w_id,
job_payload,
PushArgs { args: &args.args, extra: args.extra },
args,
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -2855,8 +2801,6 @@ pub async fn run_workflow_as_code(
check_license_key_valid().await?;
check_tag_available_for_workspace(&w_id, &run_query.tag).await?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let job = get_queued_job(&job_id, &w_id, &db).await?;
let job = not_found_if_none(job, "Queued Job", &job_id.to_string())?;
let (job_payload, tag, _delete_after_use, timeout) = match job.job_kind {
@@ -2879,26 +2823,25 @@ pub async fn run_workflow_as_code(
None,
run_query.timeout,
),
JobKind::Script => script_path_to_payload(job.script_path(), &mut tx, &w_id).await?,
JobKind::Script => script_path_to_payload(job.script_path(), &db, &w_id).await?,
_ => return Err(anyhow::anyhow!("Not supported").into()),
};
let mut extra = HashMap::new();
extra.insert(ENTRYPOINT_OVERRIDE.to_string(), to_raw_value(&entrypoint));
let args = PushArgs { args: &task.args.unwrap_or_else(HashMap::new), extra: Some(extra) };
let args = PushArgs { args: task.args.unwrap_or_else(HashMap::new), extra };
let scheduled_for = run_query.get_scheduled_for(&db).await?;
let tag = run_query.tag.clone().or(tag).or(Some(job.tag));
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into(), rsmq);
let (uuid, mut tx) = push(
&db,
tx,
&w_id,
job_payload,
PushArgs { args: &args.args, extra: args.extra },
args,
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -2934,7 +2877,6 @@ struct Guard {
id: Uuid,
w_id: String,
db: DB,
username: String,
}
impl Drop for Guard {
@@ -2943,33 +2885,16 @@ impl Drop for Guard {
let id = self.id;
let w_id = self.w_id.clone();
let db = self.db.clone();
let username = self.username.clone();
tracing::info!("http connection broke, marking job {id} as canceled");
tokio::spawn(async move {
let cancel_f = async {
let tx = db.begin().await?;
let (tx, _) = cancel_job(
&username,
Some("http connection broke".to_string()),
id,
&w_id,
tx,
&db,
None,
false,
false,
)
.await?;
tx.commit().await?;
Ok::<_, anyhow::Error>(())
};
if let Err(e) = cancel_f.await {
tracing::error!(
"Error marking job as canceled after http connection broke: {e}"
);
}
let _ = sqlx::query!(
"UPDATE queue SET canceled = true, canceled_reason = 'http connection broke', canceled_by = queue.created_by WHERE id = $1 AND workspace_id = $2",
id,
w_id
)
.execute(&db)
.await;
});
}
}
@@ -2986,7 +2911,6 @@ async fn run_wait_result(
uuid: Uuid,
w_id: String,
node_id_for_empty_return: Option<String>,
username: &str,
) -> error::Result<Response> {
let mut result = None;
let timeout = SERVER_CONFIG.read().await.timeout_wait_result.clone();
@@ -2996,13 +2920,7 @@ async fn run_wait_result(
(timeout * 1000) as u64
};
let mut g = Guard {
done: false,
id: uuid,
w_id: w_id.clone(),
db: db.clone(),
username: username.to_string(),
};
let mut g = Guard { done: false, id: uuid, w_id: w_id.clone(), db: db.clone() };
let fast_poll_duration = *WAIT_RESULT_FAST_POLL_DURATION_SECS as u64 * 1000;
let mut accumulated_delay = 0 as u64;
@@ -3021,14 +2939,15 @@ async fn run_wait_result(
}
if result.is_none() {
let row = sqlx::query_as::<_, RawResult>(
let row = sqlx::query(
"SELECT null as created_by, result, language, flow_status FROM completed_job WHERE id = $1 AND workspace_id = $2",
)
.bind(uuid)
.bind(&w_id)
.fetch_optional(db)
.await?;
if let Some(raw_result) = row {
if let Some(row) = row {
let raw_result = RawResult::from_row(&row)?;
result = match format_result(
raw_result.language.as_ref(),
raw_result.flow_status.map(|x| x.0),
@@ -3202,28 +3121,25 @@ pub async fn run_wait_result_job_by_path_get(
});
let inner_args: HashMap<String, Box<RawValue>> = HashMap::new();
let args = PushArgs { extra: Some(payload_args), args: &inner_args };
let args = PushArgs { extra: payload_args, args: inner_args };
check_queue_too_long(&db, QUEUE_LIMIT_WAIT_RESULT.or(run_query.queue_limit)).await?;
let script_path = script_path.to_path();
check_scopes(&authed, || format!("run:script/{script_path}"))?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let (job_payload, tag, delete_after_use, timeout) =
script_path_to_payload(script_path, &mut tx, &w_id).await?;
script_path_to_payload(script_path, &db, &w_id).await?;
let tag = run_query.tag.clone().or(tag);
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
tx,
&w_id,
job_payload,
PushArgs { args: &args.args, extra: args.extra },
args,
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -3245,7 +3161,7 @@ pub async fn run_wait_result_job_by_path_get(
.await?;
tx.commit().await?;
let wait_result = run_wait_result(&db, uuid, w_id, None, &authed.username).await;
let wait_result = run_wait_result(&db, uuid, w_id, None).await;
if delete_after_use.unwrap_or(false) {
delete_job_metadata_after_use(&db, uuid).await?;
}
@@ -3284,7 +3200,7 @@ pub async fn run_wait_result_flow_by_path_get(
payload_args.insert(k.to_string(), v.clone());
});
let args = PushArgsOwned { extra: Some(payload_args), args: HashMap::new() };
let args = PushArgs { extra: payload_args, args: HashMap::new() };
run_wait_result_flow_by_path_internal(
db, run_query, flow_path, authed, rsmq, user_db, args, w_id,
@@ -3299,7 +3215,7 @@ pub async fn run_wait_result_script_by_path(
Extension(db): Extension<DB>,
Path((w_id, script_path)): Path<(String, StripPath)>,
Query(run_query): Query<RunJobQuery>,
args: PushArgsOwned,
args: PushArgs,
) -> error::Result<Response> {
#[cfg(feature = "enterprise")]
check_license_key_valid().await?;
@@ -3325,28 +3241,25 @@ async fn run_wait_result_script_by_path_internal(
rsmq: Option<rsmq_async::MultiplexedRsmq>,
user_db: UserDB,
w_id: String,
args: PushArgsOwned,
args: PushArgs,
) -> error::Result<Response> {
check_queue_too_long(&db, QUEUE_LIMIT_WAIT_RESULT.or(run_query.queue_limit)).await?;
let script_path = script_path.to_path();
check_scopes(&authed, || format!("run:script/{script_path}"))?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let (job_payload, tag, delete_after_use, timeout) =
script_path_to_payload(script_path, &mut tx, &w_id).await?;
script_path_to_payload(script_path, &db, &w_id).await?;
let tag = run_query.tag.clone().or(tag);
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
tx,
&w_id,
job_payload,
PushArgs { args: &args.args, extra: args.extra },
args,
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -3368,7 +3281,7 @@ async fn run_wait_result_script_by_path_internal(
.await?;
tx.commit().await?;
let wait_result = run_wait_result(&db, uuid, w_id, None, &authed.username).await;
let wait_result = run_wait_result(&db, uuid, w_id, None).await;
if delete_after_use.unwrap_or(false) {
delete_job_metadata_after_use(&db, uuid).await?;
}
@@ -3382,15 +3295,13 @@ pub async fn run_wait_result_script_by_hash(
Extension(db): Extension<DB>,
Path((w_id, script_hash)): Path<(String, ScriptHash)>,
Query(run_query): Query<RunJobQuery>,
args: PushArgsOwned,
args: PushArgs,
) -> error::Result<Response> {
#[cfg(feature = "enterprise")]
check_license_key_valid().await?;
check_queue_too_long(&db, run_query.queue_limit).await?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let hash = script_hash.0;
let (
path,
@@ -3404,7 +3315,7 @@ pub async fn run_wait_result_script_by_hash(
priority,
delete_after_use,
timeout,
) = get_path_tag_limits_cache_for_hash(&mut tx, &w_id, hash).await?;
) = get_path_tag_limits_cache_for_hash(&db, &w_id, hash).await?;
if let Some(run_query_cache_ttl) = run_query.cache_ttl {
cache_ttl = Some(run_query_cache_ttl);
}
@@ -3412,8 +3323,7 @@ pub async fn run_wait_result_script_by_hash(
let tag = run_query.tag.clone().or(tag);
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
@@ -3430,7 +3340,7 @@ pub async fn run_wait_result_script_by_hash(
dedicated_worker,
priority,
},
PushArgs { args: &args.args, extra: args.extra },
args,
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -3452,7 +3362,7 @@ pub async fn run_wait_result_script_by_hash(
.await?;
tx.commit().await?;
let wait_result = run_wait_result(&db, uuid, w_id, None, &authed.username).await;
let wait_result = run_wait_result(&db, uuid, w_id, None).await;
if delete_after_use.unwrap_or(false) {
delete_job_metadata_after_use(&db, uuid).await?;
}
@@ -3466,7 +3376,7 @@ pub async fn run_wait_result_flow_by_path(
Extension(db): Extension<DB>,
Path((w_id, flow_path)): Path<(String, StripPath)>,
Query(run_query): Query<RunJobQuery>,
args: PushArgsOwned,
args: PushArgs,
) -> error::Result<Response> {
#[cfg(feature = "enterprise")]
check_license_key_valid().await?;
@@ -3484,7 +3394,7 @@ async fn run_wait_result_flow_by_path_internal(
authed: ApiAuthed,
rsmq: Option<rsmq_async::MultiplexedRsmq>,
user_db: UserDB,
args: PushArgsOwned,
args: PushArgs,
w_id: String,
) -> error::Result<Response> {
check_queue_too_long(&db, run_query.queue_limit).await?;
@@ -3492,39 +3402,28 @@ async fn run_wait_result_flow_by_path_internal(
let flow_path = flow_path.to_path();
check_scopes(&authed, || format!("run:flow/{flow_path}"))?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let scheduled_for = run_query.get_scheduled_for(&db).await?;
let (tag, dedicated_worker, early_return) = sqlx::query!(
"SELECT tag, dedicated_worker, flow_version.value->>'early_return' as early_return
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",
"SELECT tag, dedicated_worker, value->>'early_return' as early_return from flow WHERE path = $1 and workspace_id = $2",
flow_path,
w_id
)
.fetch_optional(&mut tx)
.fetch_optional(&db)
.await?
.map(|x| (x.tag, x.dedicated_worker, x.early_return))
.ok_or_else(|| {
Error::NotFound(format!(
"flow not found at path {flow_path} in workspace {w_id}"
))
})?;
.unwrap_or_else(|| (None, None, None));
let tag = run_query.tag.clone().or(tag);
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
tx,
&w_id,
JobPayload::Flow { path: flow_path.to_string(), dedicated_worker },
PushArgs { args: &args.args, extra: args.extra },
args,
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -3546,7 +3445,7 @@ async fn run_wait_result_flow_by_path_internal(
.await?;
tx.commit().await?;
run_wait_result(&db, uuid, w_id, early_return, &authed.username).await
run_wait_result(&db, uuid, w_id, early_return).await
}
async fn run_preview_script(
@@ -3592,7 +3491,7 @@ async fn run_preview_script(
dedicated_worker: preview.dedicated_worker,
}),
},
PushArgs::from(&preview.args.unwrap_or_default()),
preview.args.unwrap_or_default().into(),
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -3674,7 +3573,7 @@ async fn run_bundle_preview_script(
custom_concurrency_key: None,
}),
},
PushArgs::from(&args),
args.into(),
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -3788,17 +3687,16 @@ async fn run_dependencies_job(
}
let raw_script = req.raw_scripts[0].clone();
let script_path = raw_script.script_path;
let ehm = HashMap::new();
let (args, raw_code) = if let Some(deps) = req.raw_deps {
let mut hm = HashMap::new();
hm.insert(
"raw_deps".to_string(),
JsonRawValue::from_string("true".to_string()).unwrap(),
);
(PushArgs { extra: Some(hm), args: &ehm }, deps)
(PushArgs { extra: hm, args: HashMap::new() }, deps)
} else {
(
PushArgs::from(&ehm),
PushArgs::empty(),
raw_script.raw_code.unwrap_or_else(|| "".to_string()),
)
};
@@ -3836,7 +3734,7 @@ async fn run_dependencies_job(
.await?;
tx.commit().await?;
let wait_result = run_wait_result(&db, uuid, w_id, None, &authed.username).await;
let wait_result = run_wait_result(&db, uuid, w_id, None).await;
wait_result
}
@@ -3869,10 +3767,7 @@ async fn run_flow_dependencies_job(
PushIsolationLevel::IsolatedRoot(db.clone(), rsmq),
&w_id,
JobPayload::RawFlowDependencies { path: req.path, flow_value: req.flow_value },
PushArgs::from(&HashMap::from([(
"skip_flow_update".to_string(),
to_raw_value(&true),
)])),
HashMap::from([("skip_flow_update".to_string(), to_raw_value(&true))]).into(),
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -3894,7 +3789,7 @@ async fn run_flow_dependencies_job(
.await?;
tx.commit().await?;
let wait_result = run_wait_result(&db, uuid, w_id, None, &authed.username).await;
let wait_result = run_wait_result(&db, uuid, w_id, None).await;
wait_result
}
@@ -3974,13 +3869,12 @@ async fn add_batch_jobs(
}
};
for _ in 0..n {
let ehm = HashMap::new();
let (uuid, ntx) = push(
&db,
tx,
&w_id,
payload.clone(),
PushArgs::from(&ehm),
PushArgs::empty(),
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -4110,7 +4004,7 @@ async fn run_preview_flow_job(
path: raw_flow.path,
restarted_from: raw_flow.restarted_from,
},
PushArgs::from(&raw_flow.args.unwrap_or_default()),
raw_flow.args.unwrap_or_default().into(),
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -4143,13 +4037,11 @@ pub async fn run_job_by_hash(
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
Path((w_id, script_hash)): Path<(String, ScriptHash)>,
Query(run_query): Query<RunJobQuery>,
args: PushArgsOwned,
args: PushArgs,
) -> error::Result<(StatusCode, String)> {
#[cfg(feature = "enterprise")]
check_license_key_valid().await?;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
let hash = script_hash.0;
let (
path,
@@ -4163,7 +4055,7 @@ pub async fn run_job_by_hash(
priority,
_delete_after_use, // not taken into account in async endpoints
timeout,
) = get_path_tag_limits_cache_for_hash(&mut tx, &w_id, hash).await?;
) = get_path_tag_limits_cache_for_hash(&db, &w_id, hash).await?;
check_scopes(&authed, || format!("run:script/{path}"))?;
if let Some(run_query_cache_ttl) = run_query.cache_ttl {
cache_ttl = Some(run_query_cache_ttl);
@@ -4172,7 +4064,7 @@ pub async fn run_job_by_hash(
let tag = run_query.tag.clone().or(tag);
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Transaction(tx);
let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into(), rsmq);
let (uuid, tx) = push(
&db,
@@ -4189,7 +4081,7 @@ pub async fn run_job_by_hash(
dedicated_worker,
priority,
},
PushArgs { args: &args.args, extra: args.extra },
args,
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -4434,13 +4326,6 @@ pub fn filter_list_completed_query(
);
}
if let Some(dt) = &lq.created_or_started_after_completed_jobs {
sqlb.and_where_ge(
"started_at",
format!("to_timestamp({} / 1000.0)", dt.timestamp_millis()),
);
}
if let Some(sk) = &lq.is_skipped {
sqlb.and_where_eq("is_skipped", sk);
}
@@ -4510,7 +4395,6 @@ pub struct ListCompletedQuery {
pub created_after: Option<chrono::DateTime<chrono::Utc>>,
pub created_or_started_before: Option<chrono::DateTime<chrono::Utc>>,
pub created_or_started_after: Option<chrono::DateTime<chrono::Utc>>,
pub created_or_started_after_completed_jobs: Option<chrono::DateTime<chrono::Utc>>,
pub success: Option<bool>,
pub running: Option<bool>,
pub parent_job: Option<String>,
@@ -4597,7 +4481,7 @@ async fn get_completed_job<'a>(
Extension(db): Extension<DB>,
Path((w_id, id)): Path<(String, Uuid)>,
) -> error::Result<Response> {
let job_o = sqlx::query_as::<_, CompletedJob>("SELECT id, workspace_id, parent_job, created_by, created_at, duration_ms, success, script_hash, script_path,
let job_o = sqlx::query("SELECT id, workspace_id, parent_job, created_by, created_at, duration_ms, success, script_hash, script_path,
CASE WHEN args is null or pg_column_size(args) < 90000 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args, CASE WHEN result is null or pg_column_size(result) < 90000 THEN result ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as result, logs, deleted, raw_code, canceled, canceled_by, canceled_reason, job_kind, env_id,
schedule_path, permissioned_as, flow_status, raw_flow, is_flow_step, language, started_at, is_skipped,
raw_lock, email, visible_to_owner, mem_peak, tag, priority, result->'wm_labels' as labels FROM completed_job WHERE id = $1 AND workspace_id = $2")
@@ -4606,7 +4490,9 @@ async fn get_completed_job<'a>(
.fetch_optional(&db)
.await?;
let cj = not_found_if_none(job_o, "Completed Job", id.to_string())?;
let job = not_found_if_none(job_o, "Completed Job", id.to_string())?;
let cj = CompletedJob::from_row(&job)?;
if opt_authed.is_none() && cj.created_by != "anonymous" {
return Err(Error::BadRequest(
@@ -4651,7 +4537,7 @@ async fn get_completed_job_result(
Query(JsonPath { json_path, suspended_job, approver, resume_id, secret }): Query<JsonPath>,
) -> error::Result<Response> {
let result_o = if let Some(json_path) = json_path {
sqlx::query_as::<_, RawResult>(
sqlx::query(
"SELECT result #> $3 as result, flow_status, language, created_by FROM completed_job WHERE id = $1 AND workspace_id = $2",
)
.bind(id)
@@ -4665,14 +4551,16 @@ async fn get_completed_job_result(
.fetch_optional(&db)
.await?
} else {
sqlx::query_as::<_, RawResult>("SELECT result, flow_status, language, created_by FROM completed_job WHERE id = $1 AND workspace_id = $2")
sqlx::query("SELECT result, flow_status, language, created_by FROM completed_job WHERE id = $1 AND workspace_id = $2")
.bind(id)
.bind(&w_id)
.fetch_optional(&db)
.await?
};
let raw_result = not_found_if_none(result_o, "Completed Job", id.to_string())?;
let result = not_found_if_none(result_o, "Completed Job", id.to_string())?;
let raw_result = RawResult::from_row(&result)?;
if opt_authed.is_none() && raw_result.created_by.unwrap_or_default() != "anonymous" {
match (suspended_job, resume_id, approver, secret) {
@@ -4740,7 +4628,7 @@ async fn get_completed_job_result_maybe(
Path((w_id, id)): Path<(String, Uuid)>,
Query(GetCompletedJobQuery { get_started }): Query<GetCompletedJobQuery>,
) -> error::Result<Response> {
let result_o = sqlx::query_as::<_, RawResultWithSuccess>(
let result_o = sqlx::query(
"SELECT result, success, language, flow_status, created_by FROM completed_job WHERE id = $1 AND workspace_id = $2",
)
.bind(id)
@@ -4748,7 +4636,8 @@ async fn get_completed_job_result_maybe(
.fetch_optional(&db)
.await?;
if let Some(res) = result_o {
if let Some(result) = result_o {
let res = RawResultWithSuccess::from_row(&result)?;
let result = format_result(
res.language.as_ref(),
res.flow_status.map(|x| x.0),
@@ -4803,7 +4692,7 @@ async fn delete_completed_job<'a>(
let mut tx = user_db.begin(&authed).await?;
require_admin(authed.is_admin, &authed.username)?;
let job_o = sqlx::query_as::<_, CompletedJob>(
let job_o = sqlx::query(
"UPDATE completed_job SET args = null, logs = '', result = null, deleted = true WHERE id = $1 AND workspace_id = $2 \
RETURNING *",
)
@@ -4816,7 +4705,7 @@ async fn delete_completed_job<'a>(
.execute(&mut *tx)
.await?;
let cj = not_found_if_none(job_o, "Completed Job", id.to_string())?;
let job = not_found_if_none(job_o, "Completed Job", id.to_string())?;
audit_log(
&mut *tx,
@@ -4830,6 +4719,7 @@ async fn delete_completed_job<'a>(
.await?;
tx.commit().await?;
let cj = CompletedJob::from_row(&job)?;
let cj = format_completed_job_result(cj);

View File

@@ -280,13 +280,6 @@ async fn list_scripts(
sqlb.and_where_is_not_null("favorite.path");
}
if lq.with_deployment_msg.unwrap_or(false) {
sqlb.join("deployment_metadata dm")
.left()
.on("dm.script_hash = o.hash")
.fields(&["dm.deployment_msg"]);
}
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
let mut tx = user_db.begin(&authed).await?;
let rows = sqlx::query_as::<_, ListableScript>(&sql)
@@ -745,7 +738,7 @@ async fn create_script_internal<'c>(
path: ns.path,
dedicated_worker: ns.dedicated_worker,
},
windmill_queue::PushArgs::from(&args),
args.into(),
&authed.username,
&authed.email,
permissioned_as,

View File

@@ -119,18 +119,20 @@ pub fn make_unauthed_service() -> Router {
}
fn username_override_from_label(label: Option<String>) -> Option<String> {
match label {
Some(label) if label.starts_with("webhook-") => Some(label),
Some(label) if label.starts_with("ephemeral-script-end-user-") => Some(
if label.as_ref().is_some_and(|x| x.starts_with("webhook-")) {
label
} else if label
.as_ref()
.is_some_and(|x| x.starts_with("ephemeral-script-end-user-"))
{
Some(
label
.unwrap()
.trim_start_matches("ephemeral-script-end-user-")
.to_string(),
),
Some(label) if label == "Ephemeral lsp token" => Some("lsp".to_string()),
Some(label) if label != "ephemeral-script" && label != "session" && !label.is_empty() => {
Some(format!("label-{label}"))
}
_ => None,
)
} else {
None
}
}
@@ -2476,11 +2478,7 @@ async fn get_all_runnables(
})?;
let mut tx = db.clone().begin(&nauthed).await?;
let flows = sqlx::query!(
"SELECT flow.workspace_id as workspace, flow.path, summary, description, 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",
workspace
"SELECT workspace_id as workspace, path, summary, description, schema FROM flow WHERE workspace_id = $1", workspace
)
.fetch_all(&mut *tx)
.await?;
@@ -2920,19 +2918,7 @@ async fn update_username_in_workpsace<'c>(
// ---- flows ----
sqlx::query!(
r#"INSERT INTO flow
(workspace_id, path, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, concurrency_key, versions, value, schema, edited_by, edited_at)
SELECT workspace_id, REGEXP_REPLACE(path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1'), summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, concurrency_key, versions, value, schema, edited_by, edited_at
FROM flow
WHERE path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#,
new_username,
old_username,
w_id
).execute(&mut **tx)
.await?;
sqlx::query!(
r#"UPDATE flow_version SET path = REGEXP_REPLACE(path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#,
r#"UPDATE flow SET path = REGEXP_REPLACE(path,'u/' || $2 || '/(.*)','u/' || $1 || '/\1') WHERE path LIKE ('u/' || $2 || '/%') AND workspace_id = $3"#,
new_username,
old_username,
w_id
@@ -2941,12 +2927,14 @@ async fn update_username_in_workpsace<'c>(
.await?;
sqlx::query!(
"DELETE FROM flow WHERE path LIKE ('u/' || $1 || '/%') AND workspace_id = $2",
"UPDATE flow SET edited_by = $1 WHERE edited_by = $2 AND workspace_id = $3",
new_username,
old_username,
w_id
)
.execute(&mut **tx)
.await?;
.await
.unwrap();
sqlx::query!(
"UPDATE flow SET extra_perms = extra_perms - ('u/' || $2) || jsonb_build_object(('u/' || $1), extra_perms->('u/' || $2)) WHERE extra_perms ? ('u/' || $2) AND workspace_id = $3",

View File

@@ -2572,10 +2572,7 @@ 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_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",
"SELECT * FROM flow WHERE workspace_id = $1 AND archived = false",
)
.bind(&w_id)
.fetch_all(&mut *tx)
@@ -2973,10 +2970,7 @@ async fn change_workspace_id(
.await?;
sqlx::query!(
"INSERT INTO flow
(workspace_id, path, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, concurrency_key, versions, value, schema, edited_by, edited_at)
SELECT $1, path, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, concurrency_key, versions, value, schema, edited_by, edited_at
FROM flow WHERE workspace_id = $2",
"UPDATE flow SET workspace_id = $1 WHERE workspace_id = $2",
&rw.new_id,
&old_id
)
@@ -2984,17 +2978,13 @@ async fn change_workspace_id(
.await?;
sqlx::query!(
"UPDATE flow_version SET workspace_id = $1 WHERE workspace_id = $2",
"UPDATE folder SET workspace_id = $1 WHERE workspace_id = $2",
&rw.new_id,
&old_id
)
.execute(&mut *tx)
.await?;
sqlx::query!("DELETE FROM flow WHERE workspace_id = $1", &old_id)
.execute(&mut *tx)
.await?;
// have to duplicate group_ with new workspace id because of foreign key constraint
sqlx::query!(
"INSERT INTO group_ SELECT $1, name, summary, extra_perms FROM group_ WHERE workspace_id = $2",
@@ -3017,14 +3007,6 @@ async fn change_workspace_id(
.execute(&mut *tx)
.await?;
sqlx::query!(
"UPDATE folder SET workspace_id = $1 WHERE workspace_id = $2",
&rw.new_id,
&old_id
)
.execute(&mut *tx)
.await?;
sqlx::query!(
"UPDATE input SET workspace_id = $1 WHERE workspace_id = $2",
&rw.new_id,

View File

@@ -28,8 +28,6 @@ pub struct AuditLog {
pub struct ListAuditLogQuery {
pub username: Option<String>,
pub operation: Option<String>,
pub operations: Option<String>,
pub exclude_operations: Option<String>,
pub action_kind: Option<String>,
pub resource: Option<String>,
pub before: Option<chrono::DateTime<chrono::Utc>>,

View File

@@ -14,5 +14,4 @@ pub struct ListAppQuery {
pub path_exact: Option<String>,
pub path_start: Option<String>,
pub include_draft_only: Option<bool>,
pub with_deployment_msg: Option<bool>,
}

View File

@@ -24,7 +24,6 @@ pub struct JWTAuthClaims {
pub label: Option<String>,
pub workspace_id: String,
pub exp: usize,
pub job_id: Option<String>,
}
#[derive(Deserialize)]

View File

@@ -20,7 +20,7 @@ use crate::{
scripts::{Schema, ScriptHash, ScriptLang},
};
#[derive(Serialize, Deserialize, sqlx::FromRow)]
#[derive(Serialize, sqlx::FromRow)]
pub struct Flow {
pub workspace_id: String,
pub path: String,
@@ -62,9 +62,6 @@ pub struct ListableFlow {
pub draft_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ws_error_handler_muted: Option<bool>,
#[sqlx(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub deployment_msg: Option<String>,
}
#[derive(Deserialize, sqlx::FromRow)]
@@ -76,6 +73,7 @@ pub struct NewFlow {
pub schema: Option<Schema>,
pub draft_only: Option<bool>,
pub tag: Option<String>,
pub ws_error_handler_muted: Option<bool>,
pub dedicated_worker: Option<bool>,
pub timeout: Option<i32>,
pub deployment_message: Option<String>,
@@ -554,7 +552,6 @@ pub struct ListFlowQuery {
pub order_desc: Option<bool>,
pub starred_only: Option<bool>,
pub include_draft_only: Option<bool>,
pub with_deployment_msg: Option<bool>,
}
pub fn add_virtual_items_if_necessary(modules: &mut Vec<FlowModule>) {

View File

@@ -113,6 +113,14 @@ pub struct QueuedJob {
}
impl QueuedJob {
pub fn get_args(&self) -> HashMap<String, Box<RawValue>> {
if let Some(args) = self.args.as_ref() {
args.0.clone()
} else {
HashMap::new()
}
}
pub fn script_path(&self) -> &str {
self.script_path
.as_ref()
@@ -279,8 +287,8 @@ impl CompletedJob {
}
#[derive(sqlx::FromRow)]
pub struct BranchResults {
pub result: sqlx::types::Json<Box<RawValue>>,
pub struct BranchResults<'a> {
pub result: &'a RawValue,
pub id: Uuid,
}
@@ -310,7 +318,6 @@ pub enum JobPayload {
FlowDependencies {
path: String,
dedicated_worker: Option<bool>,
version: i64,
},
AppDependencies {
path: String,
@@ -376,9 +383,9 @@ type Tag = String;
pub type DB = Pool<Postgres>;
pub async fn script_path_to_payload<'e, E: sqlx::Executor<'e, Database = Postgres>>(
pub async fn script_path_to_payload(
script_path: &str,
db: E,
db: &DB,
w_id: &str,
) -> error::Result<(JobPayload, Option<Tag>, Option<bool>, Option<i32>)> {
let (job_payload, tag, delete_after_use, script_timeout) = if script_path.starts_with("hub/") {
@@ -464,13 +471,13 @@ pub async fn script_hash_to_tag_and_limits<'c>(
))
}
pub async fn get_payload_tag_from_prefixed_path<'e, E: sqlx::Executor<'e, Database = Postgres>>(
pub async fn get_payload_tag_from_prefixed_path(
path: &str,
db: E,
db: &DB,
w_id: &str,
) -> Result<(JobPayload, Option<String>), Error> {
let (payload, tag, _, _) = if path.starts_with("script/") {
script_path_to_payload(path.strip_prefix("script/").unwrap(), db, w_id).await?
script_path_to_payload(path.strip_prefix("script/").unwrap(), &db, w_id).await?
} else if path.starts_with("flow/") {
let path = path.strip_prefix("flow/").unwrap().to_string();
let r = sqlx::query!(

View File

@@ -243,8 +243,8 @@ type Tag = String;
pub type DB = Pool<Postgres>;
pub async fn get_latest_deployed_hash_for_path<'e, E: sqlx::Executor<'e, Database = Postgres>>(
db: E,
pub async fn get_latest_deployed_hash_for_path(
db: &DB,
w_id: &str,
script_path: &str,
) -> error::Result<(

View File

@@ -206,9 +206,6 @@ pub struct ListableScript {
pub no_main_func: Option<bool>,
#[serde(skip_serializing_if = "is_false")]
pub use_codebase: bool,
#[sqlx(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub deployment_msg: Option<String>,
}
fn is_false(x: &bool) -> bool {
@@ -341,7 +338,6 @@ pub struct ListScriptQuery {
pub starred_only: Option<bool>,
pub include_without_main: Option<bool>,
pub include_draft_only: Option<bool>,
pub with_deployment_msg: Option<bool>,
}
pub fn to_i64(s: &str) -> crate::error::Result<i64> {

View File

@@ -18,7 +18,7 @@ pub type DB = Pool<Postgres>;
#[derive(Clone, Debug)]
pub enum DeployedObject {
Script { hash: ScriptHash, path: String, parent_path: Option<String> },
Flow { path: String, parent_path: Option<String>, version: i64 },
Flow { path: String, parent_path: Option<String> },
App { path: String, version: i64, parent_path: Option<String> },
Folder { path: String },
Resource { path: String, parent_path: Option<String> },

View File

@@ -31,7 +31,7 @@ use reqwest::{
Client, StatusCode,
};
use rsmq_async::RsmqConnection;
use serde::{ser::SerializeMap, Deserialize, Serialize};
use serde::{Deserialize, Serialize};
use serde_json::{json, value::RawValue};
use sqlx::{types::Json, FromRow, Pool, Postgres, Transaction};
#[cfg(feature = "benchmark")]
@@ -137,19 +137,34 @@ pub struct CanceledBy {
pub async fn cancel_single_job<'c>(
username: &str,
reason: Option<String>,
job_running: Arc<QueuedJob>,
job_running: &QueuedJob,
w_id: &str,
mut tx: Transaction<'c, Postgres>,
db: &Pool<Postgres>,
rsmq: Option<rsmq_async::MultiplexedRsmq>,
force_cancel: bool,
) -> error::Result<(Transaction<'c, Postgres>, Option<Uuid>)> {
if force_cancel || (job_running.parent_job.is_none() && !job_running.running) {
if ((job_running.running || job_running.root_job.is_some()) || (job_running.is_flow()))
&& !force_cancel
{
let id = sqlx::query_scalar!(
"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 RETURNING id",
username,
reason,
job_running.id,
w_id
)
.fetch_optional(&mut *tx)
.await?;
if let Some(id) = id {
tracing::info!("Soft cancelling job {}", id);
}
} else {
let username = username.to_string();
let job_running = job_running.clone();
let w_id = w_id.to_string();
let db = db.clone();
let rsmq = rsmq.clone();
let job_running = job_running.clone();
tokio::task::spawn(async move {
let reason: String = reason
.clone()
@@ -178,19 +193,6 @@ pub async fn cancel_single_job<'c>(
tracing::error!("Failed to add canceled job: {}", e);
}
});
} else {
let id: Option<Uuid> = sqlx::query_scalar!(
"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 RETURNING id",
username,
reason,
job_running.id,
w_id
)
.fetch_optional(&mut *tx)
.await?;
if let Some(id) = id {
tracing::info!("Soft cancelling job {}", id);
}
}
if let Some(mut rsmq) = rsmq.clone() {
rsmq.change_message_visibility(&job_running.tag, &job_running.id.to_string(), 0)
@@ -223,27 +225,10 @@ pub async fn cancel_job<'c>(
"You are not logged in and this job was not created by an anonymous user like you so you cannot cancel it".to_string(),
));
}
let mut job = job.unwrap();
if force_cancel {
// if force canceling a flow step, make sure we force cancel from the highest parent
loop {
if job.parent_job.is_none() {
break;
}
match get_queued_job_tx(job.parent_job.unwrap(), &w_id, &mut tx).await? {
Some(j) => {
job = j;
}
None => break,
}
}
}
let job = Arc::new(job);
let job = job.unwrap();
// get all children
let mut jobs = vec![job.id];
let mut jobs = vec![id];
let mut jobs_to_cancel = vec![];
while !jobs.is_empty() {
let p_job = jobs.pop();
@@ -262,7 +247,7 @@ pub async fn cancel_job<'c>(
let (ntx, _) = cancel_single_job(
username,
reason.clone(),
job.clone(),
&job,
w_id,
tx,
db,
@@ -272,6 +257,27 @@ pub async fn cancel_job<'c>(
.await?;
tx = ntx;
// soft cancel parent if force cancel and job is a flow step
if force_cancel && job.is_flow_step {
if let Some(parent_job_id) = job.parent_job {
let job = get_queued_job_tx(parent_job_id, &w_id, &mut tx).await?;
if let Some(job) = job {
let (ntx, _) = cancel_single_job(
username,
reason.clone(),
&job,
w_id,
tx,
db,
rsmq.clone(),
false,
)
.await?;
tx = ntx;
}
}
}
// cancel children
for job_id in jobs_to_cancel {
let job = get_queued_job_tx(job_id, &w_id, &mut tx).await?;
@@ -280,7 +286,7 @@ pub async fn cancel_job<'c>(
let (ntx, _) = cancel_single_job(
username,
reason.clone(),
Arc::new(job),
&job,
w_id,
tx,
db,
@@ -306,11 +312,6 @@ pub async fn append_logs(
return;
}
if job_id.is_nil() {
tracing::info!("local job: {}", logs.as_ref());
return;
}
if *NO_LOGS {
tracing::info!("NO LOGS [{job_id}]: {}", logs.as_ref());
return;
@@ -424,12 +425,6 @@ impl ValidableJson for Box<RawValue> {
}
}
impl ValidableJson for Arc<Box<RawValue>> {
fn is_valid_json(&self) -> bool {
!self.get().is_empty()
}
}
impl ValidableJson for serde_json::Value {
fn is_valid_json(&self) -> bool {
true
@@ -997,7 +992,6 @@ pub async fn add_completed_job<
None
};
let ehm = HashMap::new();
let (_uuid, tx) = push(
db,
tx,
@@ -1019,8 +1013,8 @@ pub async fn add_completed_job<
queued_job
.args
.as_ref()
.map(|x| PushArgs::from(&x.0))
.unwrap_or_else(|| PushArgs::from(&ehm)),
.map(|x| PushArgs { args: x.0.clone(), extra: HashMap::new() })
.unwrap_or_else(PushArgs::empty),
&queued_job.created_by,
&queued_job.email,
queued_job.permissioned_as.clone(),
@@ -1371,7 +1365,7 @@ async fn apply_schedule_handlers<
} else {
#[cfg(feature = "enterprise")]
if let Some(on_recovery_path) = schedule.on_recovery.clone() {
let tx: QueueTransaction<'_, R> = (rsmq.clone(), db.begin().await?).into();
let mut tx: QueueTransaction<'_, R> = (rsmq.clone(), db.begin().await?).into();
let times = schedule.on_recovery_times.unwrap_or(1).max(1);
let past_jobs = sqlx::query_as::<_, CompletedJobSubset>(
"SELECT success, result, started_at FROM completed_job WHERE workspace_id = $1 AND schedule_path = $2 AND script_path = $3 AND id != $4 ORDER BY created_at DESC LIMIT $5",
@@ -1396,7 +1390,7 @@ async fn apply_schedule_handlers<
let failed_job = past_jobs[past_jobs.len() - 1].clone();
if !failed_job.success {
handle_recovered_schedule(
tx = handle_recovered_schedule(
db,
tx,
job_id,
@@ -1412,9 +1406,9 @@ async fn apply_schedule_handlers<
schedule.on_recovery_extra_args.clone(),
)
.await?;
} else {
tx.commit().await?;
}
tx.commit().await?;
}
}
@@ -1494,7 +1488,7 @@ pub async fn push_error_handler<
tx,
handler_w_id,
payload,
PushArgs { extra: Some(extra), args: &result },
PushArgs { extra, args: result },
if is_global_error_handler {
"global"
} else if is_schedule_error_handler {
@@ -1567,7 +1561,7 @@ async fn handle_recovered_schedule<
successful_times: i32,
successful_job_started_at: DateTime<Utc>,
extra_args: Option<Json<Box<RawValue>>>,
) -> windmill_common::error::Result<()> {
) -> windmill_common::error::Result<QueueTransaction<'c, R>> {
let (payload, tag) = get_payload_tag_from_prefixed_path(on_recovery_path, db, w_id).await?;
let mut extra = HashMap::new();
@@ -1620,7 +1614,7 @@ async fn handle_recovered_schedule<
tx,
w_id,
payload,
PushArgs { extra: Some(extra), args: &args },
PushArgs { extra: extra, args: args },
SCHEDULE_RECOVERY_HANDLER_USERNAME,
SCHEDULE_RECOVERY_HANDLER_USER_EMAIL,
ERROR_HANDLER_USER_GROUP.to_string(),
@@ -1645,8 +1639,7 @@ async fn handle_recovered_schedule<
uuid,
schedule_path
);
tx.commit().await?;
Ok(())
return Ok(tx);
}
pub async fn pull<R: rsmq_async::RsmqConnection + Send + Clone>(
@@ -1945,7 +1938,7 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit<
.map_err(|_| anyhow::anyhow!("Failed to parsed Redis message"))?,
);
let m2r = sqlx::query_as::<_, QueuedJob>(
let m2r = sqlx::query(
"UPDATE queue
SET running = true
, started_at = coalesce(started_at, now())
@@ -1963,6 +1956,11 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit<
.bind(uuid)
.fetch_optional(db)
.await?;
let m2 = if let Some(row) = m2r {
Some(QueuedJob::from_row(&row)?)
} else {
None
};
rsmq.delete_message(&tag.unwrap(), &msg.id)
.await
@@ -1971,7 +1969,7 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit<
#[cfg(feature = "benchmark")]
println!("rsmq 2: {:?}", instant.elapsed());
m2r
m2
} else {
None
}
@@ -1991,7 +1989,7 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit<
let priority_tags_sorted = config.priority_tags_sorted.clone();
drop(config);
let r = if suspend_first {
sqlx::query_as::<_, QueuedJob>("UPDATE queue
sqlx::query("UPDATE queue
SET running = true
, started_at = coalesce(started_at, now())
, last_ping = now()
@@ -2017,13 +2015,18 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit<
} else {
None
};
let r = if let Some(row) = r {
Some(QueuedJob::from_row(&row)?)
} else {
None
};
if r.is_none() {
// #[cfg(feature = "benchmark")]
// let instant = Instant::now();
let mut highest_priority_job: Option<QueuedJob> = None;
for priority_tags in priority_tags_sorted {
let r = sqlx::query_as::<_, QueuedJob>(
let r = sqlx::query(
"UPDATE queue
SET running = true
, started_at = coalesce(started_at, now())
@@ -2049,11 +2052,12 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit<
.fetch_optional(db)
.await?;
if let Some(pulled_job) = r {
let id = pulled_job.id;
highest_priority_job = Some(pulled_job);
if let Some(pulled_row) = r {
let pulled_job = QueuedJob::from_row(&pulled_row)?;
highest_priority_job = Some(pulled_job.clone());
tracing::debug!(
"Pulling for job {id} with tags {:?} with priority {}",
"Pulling for job {} with tags {:?} with priority {}",
pulled_job.id,
priority_tags.tags,
priority_tags.priority
);
@@ -2084,11 +2088,7 @@ pub async fn custom_concurrency_key(
async fn legacy_concurrency_key(db: &Pool<Postgres>, queued_job: &QueuedJob) -> Option<String> {
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",
"SELECT value->>'concurrency_key' FROM flow WHERE path = $1 AND workspace_id = $2",
queued_job.script_path,
queued_job.workspace_id
)
@@ -2107,13 +2107,18 @@ async fn legacy_concurrency_key(db: &Pool<Postgres>, queued_job: &QueuedJob) ->
.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))
r.map(|x| {
interpolate_args(
x,
&queued_job
.args
.clone()
.map(|x| x.0)
.unwrap_or_default()
.into(),
&queued_job.workspace_id,
)
})
}
async fn concurrency_key(
@@ -2137,7 +2142,7 @@ fn interpolate_args(x: String, args: &PushArgs, workspace_id: &str) -> String {
let arg_value = args
.args
.get(arg_name)
.or(args.extra.as_ref().and_then(|x| x.get(arg_name)))
.or(args.extra.get(arg_name))
.map(|x| x.get())
.unwrap_or_default()
.trim_matches('"');
@@ -2426,7 +2431,7 @@ async fn extract_result_from_job_result(
) -> error::Result<Box<RawValue>> {
match job_result {
JobResult::ListJob(job_ids) => {
let rows = sqlx::query_as::<_, ResultWithId>(
let rows = sqlx::query(
"SELECT id, result FROM completed_job WHERE id = ANY($1) AND workspace_id = $2",
)
.bind(job_ids.as_slice())
@@ -2434,7 +2439,11 @@ async fn extract_result_from_job_result(
.fetch_all(db)
.await?
.into_iter()
.filter_map(|x| x.result.map(|y| (x.id, y)))
.filter_map(|x| {
ResultWithId::from_row(&x)
.ok()
.and_then(|x| x.result.map(|y| (x.id, y)))
})
.collect::<HashMap<Uuid, Json<Box<RawValue>>>>();
let result = job_ids
.into_iter()
@@ -2446,7 +2455,7 @@ async fn extract_result_from_job_result(
.collect::<Vec<_>>();
Ok(to_raw_value(&result))
}
JobResult::SingleJob(x) => Ok(sqlx::query_as::<_, ResultR>(
JobResult::SingleJob(x) => Ok(sqlx::query(
"SELECT result #> $3 as result FROM completed_job WHERE id = $1 AND workspace_id = $2",
)
.bind(x)
@@ -2458,7 +2467,11 @@ async fn extract_result_from_job_result(
)
.fetch_optional(db)
.await?
.map(|r| r.result.map(|x| x.0))
.map(|r| {
ResultR::from_row(&r)
.ok()
.and_then(|x| x.result.map(|x| x.0))
})
.flatten()
.unwrap_or_else(|| to_raw_value(&serde_json::Value::Null))),
}
@@ -2514,27 +2527,35 @@ pub async fn get_queued_job_tx<'c>(
w_id: &str,
tx: &mut Transaction<'c, Postgres>,
) -> error::Result<Option<QueuedJob>> {
sqlx::query_as::<_, QueuedJob>(
let r = sqlx::query(
"SELECT *
FROM queue WHERE id = $1 AND workspace_id = $2",
)
.bind(id)
.bind(w_id)
.fetch_optional(&mut **tx)
.await
.map_err(Into::into)
.await?;
if let Some(row) = r {
Ok(Some(QueuedJob::from_row(&row)?.to_owned()))
} else {
Ok(None)
}
}
pub async fn get_queued_job(id: &Uuid, w_id: &str, db: &DB) -> error::Result<Option<QueuedJob>> {
sqlx::query_as::<_, QueuedJob>(
let r = sqlx::query(
"SELECT *
FROM queue WHERE id = $1 AND workspace_id = $2",
)
.bind(id)
.bind(w_id)
.fetch_optional(db)
.await
.map_err(Into::into)
.await?;
if let Some(row) = r {
Ok(Some(QueuedJob::from_row(&row)?.to_owned()))
} else {
Ok(None)
}
}
pub enum PushIsolationLevel<'c, R: rsmq_async::RsmqConnection + Send + 'c> {
@@ -2569,48 +2590,14 @@ macro_rules! fetch_scalar_isolated {
use sqlx::types::JsonRawValue;
#[derive(Debug)]
pub struct PushArgsOwned {
pub extra: Option<HashMap<String, Box<RawValue>>>,
#[derive(Serialize, Debug)]
pub struct PushArgs {
#[serde(flatten)]
pub extra: HashMap<String, Box<RawValue>>,
#[serde(flatten)]
pub args: HashMap<String, Box<RawValue>>,
}
#[derive(Debug)]
pub struct PushArgs<'c> {
pub extra: Option<HashMap<String, Box<RawValue>>>,
pub args: &'c HashMap<String, Box<RawValue>>,
}
impl<'c> From<&'c HashMap<String, Box<RawValue>>> for PushArgs<'c> {
fn from(args: &'c HashMap<String, Box<RawValue>>) -> Self {
PushArgs { extra: None, args }
}
}
impl<'c> Serialize for PushArgs<'c> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut map = serializer.serialize_map(Some(
self.args.len() + self.extra.as_ref().map(|x| x.len()).unwrap_or_default(),
))?;
let mut in_extra = vec![];
if let Some(extra) = &self.extra {
for (k, v) in extra {
map.serialize_entry(k, v)?;
in_extra.push(k);
}
}
for (k, v) in self.args {
if !in_extra.contains(&k) {
map.serialize_entry(k, v)?;
}
}
map.end()
}
}
#[derive(Deserialize)]
pub struct DecodeQuery {
pub include_query: Option<String>,
@@ -2665,11 +2652,11 @@ impl DecodeQueries {
}
}
// impl<'c> PushArgs<'c> {
// pub fn insert<K: Into<String>, V: Into<Box<RawValue>>>(&mut self, k: K, v: V) {
// self.extra.insert(k.into(), v.into());
// }
// }
impl PushArgs {
pub fn insert<K: Into<String>, V: Into<Box<RawValue>>>(&mut self, k: K, v: V) {
self.extra.insert(k.into(), v.into());
}
}
#[derive(Deserialize)]
pub struct RequestQuery {
@@ -2704,7 +2691,7 @@ fn restructure_cloudevents_metadata(
}
}
impl PushArgsOwned {
impl PushArgs {
async fn from_json(
mut extra: HashMap<String, Box<RawValue>>,
use_raw: bool,
@@ -2722,12 +2709,12 @@ impl PushArgsOwned {
.unwrap_or_else(|| to_raw_value(&serde_json::Value::Null));
let mut hm = HashMap::new();
hm.insert("body".to_string(), args);
Ok(PushArgsOwned { extra: Some(extra), args: hm })
Ok(PushArgs { extra, args: hm })
} else {
let hm = serde_json::from_str::<Option<HashMap<String, Box<JsonRawValue>>>>(&str)
.map_err(|e| Error::BadRequest(format!("invalid json: {}", e)).into_response())?
.unwrap_or_else(HashMap::new);
Ok(PushArgsOwned { extra: Some(extra), args: hm })
Ok(PushArgs { extra, args: hm })
}
}
@@ -2744,7 +2731,7 @@ impl PushArgsOwned {
Error::BadRequest(format!("invalid cloudevents+json: {}", e)).into_response()
})?;
let hm = restructure_cloudevents_metadata(hm).map_err(|e| e.into_response())?;
Ok(PushArgsOwned { extra: Some(extra), args: hm })
Ok(PushArgs { extra, args: hm })
}
}
@@ -2788,10 +2775,10 @@ mod tests {
"#;
let extra = HashMap::new();
let a1 = PushArgsOwned::from_ce_json(extra.clone(), false, r1.to_string())
let a1 = PushArgs::from_ce_json(extra.clone(), false, r1.to_string())
.await
.expect("Failed to parse the cloudevent");
let a2 = PushArgsOwned::from_ce_json(extra.clone(), false, r2.to_string())
let a2 = PushArgs::from_ce_json(extra.clone(), false, r2.to_string())
.await
.expect("Failed to parse the cloudevent");
@@ -2809,7 +2796,7 @@ mod tests {
}
#[axum::async_trait]
impl<S> FromRequest<S, axum::body::Body> for PushArgsOwned
impl<S> FromRequest<S, axum::body::Body> for PushArgs
where
S: Send + Sync,
{
@@ -2841,7 +2828,7 @@ where
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, str).await
PushArgs::from_json(extra, use_raw, str).await
} else if content_type
.unwrap()
.starts_with("application/cloudevents+json")
@@ -2852,7 +2839,7 @@ where
let str = String::from_utf8(bytes.to_vec())
.map_err(|e| Error::BadRequest(format!("invalid utf8: {}", e)).into_response())?;
PushArgsOwned::from_ce_json(extra, use_raw, str).await
PushArgs::from_ce_json(extra, use_raw, str).await
} else if content_type
.unwrap()
.starts_with("application/cloudevents-batch+json")
@@ -2885,7 +2872,7 @@ where
.map(|(k, v)| (k, to_raw_value(&v)))
.collect::<HashMap<_, _>>();
return Ok(PushArgsOwned { extra: Some(extra), args: payload });
return Ok(PushArgs { extra, args: payload });
} else {
Err(StatusCode::UNSUPPORTED_MEDIA_TYPE.into_response())
}
@@ -2923,9 +2910,9 @@ pub fn build_extra(
args
}
impl PushArgsOwned {
impl PushArgs {
pub fn empty() -> Self {
PushArgsOwned { extra: None, args: HashMap::new() }
PushArgs { extra: HashMap::new(), args: HashMap::new() }
}
}
@@ -2933,6 +2920,12 @@ pub fn empty_result() -> Box<RawValue> {
return JsonRawValue::from_string("{}".to_string()).unwrap();
}
impl From<HashMap<String, Box<JsonRawValue>>> for PushArgs {
fn from(value: HashMap<String, Box<JsonRawValue>>) -> Self {
PushArgs { extra: HashMap::new(), args: value }
}
}
// impl<T> From<PushArgsInner<T>> for PushArgs<T> {
// fn from(value: PushArgsInner<T>) -> Self {
// PushArgs::Unwrapped(value)
@@ -2949,12 +2942,12 @@ struct FlowRawValue {
}
// #[instrument(level = "trace", skip_all)]
pub async fn push<'c, 'd, R: rsmq_async::RsmqConnection + Send + 'c>(
pub async fn push<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
_db: &Pool<Postgres>,
mut tx: PushIsolationLevel<'c, R>,
workspace_id: &str,
job_payload: JobPayload,
args: PushArgs<'d>,
args: PushArgs,
user: &str,
mut email: &str,
mut permissioned_as: String,
@@ -3285,10 +3278,13 @@ pub async fn push<'c, 'd, R: rsmq_async::RsmqConnection + Send + 'c>(
None,
None,
),
JobPayload::FlowDependencies { path, dedicated_worker, version } => {
JobPayload::FlowDependencies { path, dedicated_worker } => {
let value_json = fetch_scalar_isolated!(
sqlx::query_as::<_, FlowRawValue>("SELECT value FROM flow_version WHERE id = $1",)
.bind(&version),
sqlx::query_as::<_, FlowRawValue>(
"SELECT value FROM flow WHERE path = $1 AND workspace_id = $2",
)
.bind(&path)
.bind(&workspace_id),
tx
)?
.ok_or_else(|| Error::InternalErr(format!("not found flow at path {:?}", path)))?;
@@ -3299,7 +3295,7 @@ pub async fn push<'c, 'd, R: rsmq_async::RsmqConnection + Send + 'c>(
))
})?;
(
Some(version),
None,
Some(path),
None,
JobKind::FlowDependencies,
@@ -3453,10 +3449,7 @@ pub async fn push<'c, 'd, R: rsmq_async::RsmqConnection + Send + 'c>(
JobPayload::Flow { path, dedicated_worker } => {
let value_json = fetch_scalar_isolated!(
sqlx::query_as::<_, FlowRawValue>(
"SELECT flow_version.value 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",
"SELECT value FROM flow WHERE path = $1 AND workspace_id = $2",
)
.bind(&path)
.bind(&workspace_id),
@@ -3742,7 +3735,6 @@ pub async fn push<'c, 'd, R: rsmq_async::RsmqConnection + Send + 'c>(
.map_err(|e| Error::InternalErr(format!("Could not insert concurrency_key={concurrency_key} for job_id={job_id} script_path={script_path:?} 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 queue
(workspace_id, id, running, parent_job, created_by, permissioned_as, scheduled_for,
@@ -3787,7 +3779,6 @@ pub async fn push<'c, 'd, R: rsmq_async::RsmqConnection + Send + 'c>(
.await
.map_err(|e| Error::InternalErr(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!("Pushed {job_id}");
// TODO: technically the job isn't queued yet, as the transaction can be rolled back. Should be solved when moving these metrics to the queue abstraction.
#[cfg(feature = "prometheus")]
if METRICS_ENABLED.load(std::sync::atomic::Ordering::Relaxed) {

View File

@@ -208,7 +208,7 @@ pub async fn push_scheduled_job<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
tx,
&schedule.workspace_id,
payload,
crate::PushArgs { args: &args, extra: None },
crate::PushArgs { args, extra: HashMap::new() },
&schedule_to_user(&schedule.path),
&schedule.email,
username_to_permissioned_as(&schedule.edited_by),
@@ -228,7 +228,6 @@ pub async fn push_scheduled_job<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
authed,
)
.await?;
Ok(tx) // TODO: Bubble up pushed UUID from here
}

View File

@@ -49,7 +49,6 @@ chrono.workspace = true
dotenv.workspace = true
rand.workspace = true # TODO: Remove. only used by token creation hack.
const_format.workspace = true
mappable-rc.workspace = true
git-version.workspace = true
dyn-iter.workspace = true
once_cell.workspace = true

View File

@@ -37,7 +37,6 @@ mount {
src: "/lib64"
dst: "/lib64"
is_bind: true
mandatory: false
}
mount {

View File

@@ -38,7 +38,6 @@ mount {
src: "/lib64"
dst: "/lib64"
is_bind: true
mandatory: false
}

View File

@@ -39,7 +39,6 @@ mount {
src: "/lib64"
dst: "/lib64"
is_bind: true
mandatory: false
}

View File

@@ -31,7 +31,6 @@ mount {
src: "/lib64"
dst: "/lib64"
is_bind: true
mandatory: false
}

View File

@@ -32,7 +32,6 @@ mount {
src: "/lib64"
dst: "/lib64"
is_bind: true
mandatory: false
}

View File

@@ -38,7 +38,6 @@ mount {
src: "/lib64"
dst: "/lib64"
is_bind: true
mandatory: false
}

View File

@@ -34,7 +34,6 @@ mount {
src: "/lib64"
dst: "/lib64"
is_bind: true
mandatory: false
}

View File

@@ -379,21 +379,6 @@ $env:PSModulePath = \"{}:$PSModulePathBackup\"",
)
.await?;
let result_json_path = format!("{job_dir}/result.json");
if let Ok(metadata) = tokio::fs::metadata(&result_json_path).await {
if metadata.len() > 0 {
return Ok(read_file(&result_json_path).await?);
}
}
let result_out_path = format!("{job_dir}/result.out");
if let Ok(metadata) = tokio::fs::metadata(&result_out_path).await {
if metadata.len() > 0 {
let result = read_file_content(&result_out_path).await?;
return Ok(to_raw_value(&json!(result)));
}
}
let result_out_path2 = format!("{job_dir}/result2.out");
if tokio::fs::metadata(&result_out_path2).await.is_ok() {
let result = read_file_content(&result_out_path2)

View File

@@ -894,6 +894,8 @@ pub async fn get_common_bun_proc_envs(base_internal_url: &str) -> HashMap<String
#[cfg(feature = "enterprise")]
use crate::{dedicated_worker::handle_dedicated_process, JobCompletedSender};
#[cfg(feature = "enterprise")]
use std::sync::Arc;
#[cfg(feature = "enterprise")]
pub async fn start_worker(
@@ -909,7 +911,7 @@ pub async fn start_worker(
script_path: &str,
token: &str,
job_completed_tx: JobCompletedSender,
jobs_rx: Receiver<std::sync::Arc<QueuedJob>>,
jobs_rx: Receiver<Arc<QueuedJob>>,
killpill_rx: tokio::sync::broadcast::Receiver<()>,
) -> Result<()> {
let mut logs = "".to_string();

View File

@@ -561,7 +561,9 @@ where
Fut: Future<Output = i32>,
{
let update_job_interval = Duration::from_millis(500);
if job_id == Uuid::nil() {
return UpdateJobPollingExit::Done;
}
let db = db.clone();
let mut interval = interval(update_job_interval);
@@ -583,19 +585,17 @@ where
let memory_usage = get_worker_memory_usage();
let wm_memory_usage = get_windmill_memory_usage();
tracing::info!("{worker_name}/{job_id} in {w_id} worker memory snapshot {}kB/{}kB", memory_usage.unwrap_or_default()/1024, wm_memory_usage.unwrap_or_default()/1024);
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 WHERE worker = $5",
&job_id,
&w_id,
memory_usage,
wm_memory_usage,
&worker_name
)
.execute(&db)
.await
.expect("update worker ping");
}
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 WHERE worker = $5",
&job_id,
&w_id,
memory_usage,
wm_memory_usage,
&worker_name
)
.execute(&db)
.await
.expect("update worker ping");
}
let current_mem = get_mem().await;
if current_mem > *mem_peak {
@@ -608,51 +608,47 @@ where
if update_job_row {
#[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);
}
// 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, canceled_by, canceled_reason, already_completed) = sqlx::query_as::<_, (bool, Option<String>, Option<String>, bool)>("UPDATE queue SET mem_peak = $1, last_ping = now() WHERE id = $2 RETURNING canceled, canceled_by, canceled_reason, false")
.bind(*mem_peak)
.bind(job_id)
.fetch_optional(&db)
.await
.unwrap_or_else(|e| {
tracing::error!(%e, "error updating job {job_id}: {e:#}");
Some((false, 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
(false, None, None, true)
});
if already_completed {
return UpdateJobPollingExit::AlreadyCompleted
}
if canceled {
canceled_by_ref.replace(CanceledBy {
username: canceled_by.clone(),
reason: canceled_reason.clone(),
});
break
}
let (canceled, canceled_by, canceled_reason, already_completed) = sqlx::query_as::<_, (bool, Option<String>, Option<String>, bool)>("UPDATE queue SET mem_peak = $1, last_ping = now() WHERE id = $2 RETURNING canceled, canceled_by, canceled_reason, false")
.bind(*mem_peak)
.bind(job_id)
.fetch_optional(&db)
.await
.unwrap_or_else(|e| {
tracing::error!(%e, "error updating job {job_id}: {e:#}");
Some((false, 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
(false, None, None, true)
});
if already_completed {
return UpdateJobPollingExit::AlreadyCompleted
}
if canceled {
canceled_by_ref.replace(CanceledBy {
username: canceled_by.clone(),
reason: canceled_reason.clone(),
});
break
}
}
},
@@ -895,7 +891,7 @@ pub async fn handle_child(
canceled_by_ref: &mut Option<CanceledBy>,
mut child: Child,
nsjail: bool,
worker: &str,
worker_name: &str,
w_id: &str,
child_name: &str,
custom_timeout: Option<i32>,
@@ -937,7 +933,7 @@ pub async fn handle_child(
mem_peak,
canceled_by_ref,
|| get_mem_peak(pid, nsjail),
worker,
worker_name,
w_id,
rx,
);
@@ -1138,14 +1134,14 @@ pub async fn handle_child(
log_total_size = 0;
}
let worker_name = worker.to_string();
let worker_name = worker_name.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 Err(err) = result {
tracing::error!(%job_id, %err, "error reading output for job {job_id} '{child_name}': {err}");
tracing::error!(%job_id, %err, "error reading output for job {job_id}: {err}");
break;
}
@@ -1169,11 +1165,7 @@ pub async fn handle_child(
let (wait_result, _) = tokio::join!(wait_on_child, lines);
let success = wait_result.is_ok()
&& wait_result.as_ref().unwrap().is_ok()
&& wait_result.as_ref().unwrap().as_ref().unwrap().success();
tracing::info!(%job_id, %success, %mem_peak, %worker, "child process '{child_name}' took {}ms", start.elapsed().as_millis());
tracing::info!(%job_id, "child process '{child_name}' for {worker_name}/{job_id} took {}ms, mem_peak: {:?}", start.elapsed().as_millis(), mem_peak);
match wait_result {
_ if *too_many_logs.borrow() => Err(Error::ExecutionErr(format!(
"logs or result reached limit. (current max size: {MAX_RESULT_SIZE} characters)"

View File

@@ -70,14 +70,13 @@ pub async fn handle_dedicated_process(
mut killpill_rx: tokio::sync::broadcast::Receiver<()>,
job_completed_tx: JobCompletedSender,
token: &str,
mut jobs_rx: Receiver<std::sync::Arc<QueuedJob>>,
mut jobs_rx: Receiver<Arc<QueuedJob>>,
worker_name: &str,
db: &DB,
script_path: &str,
mode: &str,
) -> std::result::Result<(), error::Error> {
//do not cache local dependencies
let mut child = {
let mut cmd = Command::new(command_path);
cmd.current_dir(job_dir)
@@ -131,7 +130,7 @@ pub async fn handle_dedicated_process(
}
});
let mut jobs: VecDeque<Arc<QueuedJob>> = VecDeque::with_capacity(MAX_BUFFERED_DEDICATED_JOBS);
let mut jobs = VecDeque::with_capacity(MAX_BUFFERED_DEDICATED_JOBS);
// let mut i = 0;
// let mut j = 0;
let mut alive = true;
@@ -174,7 +173,6 @@ pub async fn handle_dedicated_process(
tracing::info!("job completed on dedicated worker {script_path}: {}", job.id);
match serde_json::from_str::<Box<serde_json::value::RawValue>>(&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;
if line.starts_with("wm_res[success]:") {
job_completed_tx.send(JobCompleted { job , result, mem_peak: 0, canceled_by: None, success: true, cached_res_path: None, token: token.to_string() }).await.unwrap()
@@ -184,7 +182,7 @@ pub async fn handle_dedicated_process(
},
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:?}")}))), mem_peak: 0, canceled_by: None, success: false, cached_res_path: None, token: token.to_string() }).await.unwrap();
job_completed_tx.send(JobCompleted { job , result: to_raw_value(&serde_json::json!({"error": format!("Could not deserialize job result `{line}`: {e:?}")})), mem_peak: 0, canceled_by: None, success: false, cached_res_path: None, token: token.to_string() }).await.unwrap();
},
};
logs = init_log.clone();
@@ -224,11 +222,7 @@ pub async fn handle_dedicated_process(
Ok(())
}
type DedicatedWorker = (
String,
Sender<std::sync::Arc<QueuedJob>>,
Option<JoinHandle<()>>,
);
type DedicatedWorker = (String, Sender<Arc<QueuedJob>>, Option<JoinHandle<()>>);
// spawn one dedicated worker per compatible steps of the flow, associating the node id to the dedicated worker channel send
#[async_recursion]
@@ -246,8 +240,7 @@ async fn spawn_dedicated_workers_for_flow(
job_completed_tx: &JobCompletedSender,
) -> Vec<DedicatedWorker> {
let mut workers = vec![];
let mut script_path_to_worker: HashMap<String, Sender<std::sync::Arc<QueuedJob>>> =
HashMap::new();
let mut script_path_to_worker: HashMap<String, Sender<Arc<QueuedJob>>> = HashMap::new();
for module in modules.iter() {
let value = module.get_value();
if let Ok(value) = value {
@@ -285,8 +278,8 @@ async fn spawn_dedicated_workers_for_flow(
FlowModuleValue::ForloopFlow { modules, .. } => {
let w = spawn_dedicated_workers_for_flow(
&modules,
w_id,
path,
w_id,
killpill_tx.clone(),
killpill_rx,
db,
@@ -301,8 +294,8 @@ async fn spawn_dedicated_workers_for_flow(
FlowModuleValue::WhileloopFlow { modules, .. } => {
let w = spawn_dedicated_workers_for_flow(
&modules,
w_id,
path,
w_id,
killpill_tx.clone(),
killpill_rx,
db,
@@ -322,8 +315,8 @@ async fn spawn_dedicated_workers_for_flow(
{
let w = spawn_dedicated_workers_for_flow(
&modules,
w_id,
path,
w_id,
killpill_tx.clone(),
killpill_rx,
db,
@@ -340,8 +333,8 @@ async fn spawn_dedicated_workers_for_flow(
for branch in branches {
let w = spawn_dedicated_workers_for_flow(
&branch.modules,
w_id,
path,
w_id,
killpill_tx.clone(),
killpill_rx,
db,
@@ -396,7 +389,7 @@ pub async fn create_dedicated_worker_map(
worker_name: &str,
job_completed_tx: &JobCompletedSender,
) -> (
HashMap<String, Sender<std::sync::Arc<QueuedJob>>>,
HashMap<String, Sender<Arc<QueuedJob>>>,
bool,
Vec<JoinHandle<()>>,
) {
@@ -407,15 +400,11 @@ pub async fn create_dedicated_worker_map(
if let Some(flow_path) = _wp.path.strip_prefix("flow/") {
is_flow_worker = true;
let value = sqlx::query_scalar!(
"SELECT flow_version.value
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",
"SELECT value FROM flow WHERE path = $1 AND workspace_id = $2",
flow_path,
_wp.workspace_id
)
.fetch_one(db)
.fetch_optional(db)
.await;
if let Ok(v) = value {
if let Some(v) = v {
@@ -487,8 +476,6 @@ pub async fn create_dedicated_worker_map(
(HashMap::new(), false, dedicated_handles)
}
}
#[derive(Debug, Clone)]
pub enum SpawnWorker {
Script { path: String, hash: Option<ScriptHash> },
RawScript { path: String, content: String, lock: Option<String>, lang: ScriptLang },
@@ -528,20 +515,13 @@ async fn spawn_dedicated_worker(
#[cfg(feature = "enterprise")]
{
let (dedicated_worker_tx, dedicated_worker_rx) =
tokio::sync::mpsc::channel::<std::sync::Arc<QueuedJob>>(MAX_BUFFERED_DEDICATED_JOBS);
tokio::sync::mpsc::channel::<Arc<QueuedJob>>(MAX_BUFFERED_DEDICATED_JOBS);
let killpill_rx = killpill_rx.resubscribe();
let db = 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();
let job_dir = format!(
"{}/dedicated{}",
worker_dir,
node_id
.as_ref()
.map(|x| format!("-{x}"))
.unwrap_or_else(|| "".to_string())
);
let job_dir = format!("{}/dedicated", worker_dir);
tokio::fs::create_dir_all(&job_dir)
.await
.expect("create dir");
@@ -554,7 +534,7 @@ async fn spawn_dedicated_worker(
let path2 = path.clone();
let w_id = w_id.to_string();
let (content, lock, language, envs, codebase) = match sw.clone() {
let (content, lock, language, envs, codebase) = match sw {
SpawnWorker::Script { path, hash } => {
let q = if let Some(hash) = hash {
get_script_content_by_hash(&hash, &w_id, &db).await.map(
@@ -684,7 +664,7 @@ async fn spawn_dedicated_worker(
}
_ => unreachable!("Non supported language for dedicated worker"),
} {
tracing::error!("error in dedicated worker for {sw:#?}: {:?}", e);
tracing::error!("error in dedicated worker: {:?}", e);
};
if let Err(e) = killpill_tx.clone().send(()) {
tracing::error!("failed to send final killpill to dedicated worker: {:?}", e);

View File

@@ -394,7 +394,8 @@ async fn build_import_map(
#[cfg(feature = "enterprise")]
use crate::{dedicated_worker::handle_dedicated_process, JobCompletedSender};
#[cfg(feature = "enterprise")]
use std::sync::Arc;
#[cfg(feature = "enterprise")]
use tokio::sync::mpsc::Receiver;
@@ -409,7 +410,7 @@ pub async fn start_worker(
script_path: &str,
token: &str,
job_completed_tx: JobCompletedSender,
jobs_rx: Receiver<std::sync::Arc<QueuedJob>>,
jobs_rx: Receiver<Arc<QueuedJob>>,
killpill_rx: tokio::sync::broadcast::Receiver<()>,
db: &sqlx::Pool<sqlx::Postgres>,
) -> Result<()> {

View File

@@ -5,7 +5,6 @@ use futures::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::{error::Error, worker::CLOUD_HOSTED};
use windmill_parser_graphql::parse_graphql_sig;
use windmill_queue::{CanceledBy, HTTP_CLIENT};
@@ -65,18 +64,13 @@ pub async fn do_graphql(
.args;
if let Some(job_args) = job_args {
for arg in &sig {
match job_args.get(&arg.name) {
Some(x) => {
variables.insert(arg.name.clone(), x.to_owned());
}
None if arg.default.is_some() => {
variables.insert(
arg.name.clone(),
to_raw_value(arg.default.as_ref().unwrap()),
);
}
_ => {}
}
variables.insert(
arg.name.clone(),
job_args
.get(&arg.name)
.map(|x| x.to_owned())
.unwrap_or_default(),
);
}
}

View File

@@ -138,7 +138,7 @@ pub struct OptAuthedClient(Option<AuthedClient>);
pub async fn eval_timeout(
expr: String,
transform_context: HashMap<String, Arc<Box<RawValue>>>,
flow_input: Option<mappable_rc::Marc<HashMap<String, Box<RawValue>>>>,
flow_input: Option<Arc<HashMap<String, Box<RawValue>>>>,
authed_client: Option<&AuthedClient>,
by_id: Option<IdContext>,
) -> anyhow::Result<Box<RawValue>> {
@@ -563,7 +563,7 @@ async fn op_resource(
pub struct TransformContext {
pub envs: HashMap<String, Arc<Box<RawValue>>>,
pub flow_input: Option<mappable_rc::Marc<HashMap<String, Box<RawValue>>>>,
pub flow_input: Option<Arc<HashMap<String, Box<RawValue>>>>,
}
#[op2]
@@ -575,7 +575,7 @@ fn op_get_context(op_state: Rc<RefCell<OpState>>, #[string] id: &str) -> String
client
.flow_input
.as_ref()
.and_then(|x| serde_json::to_string(x.as_ref()).ok())
.and_then(|x| serde_json::to_string(&x).ok())
.unwrap_or_else(|| "null".to_string())
} else {
client

View File

@@ -1007,6 +1007,9 @@ pub async fn handle_python_reqs(
Ok(req_paths)
}
#[cfg(feature = "enterprise")]
use std::sync::Arc;
#[cfg(feature = "enterprise")]
use crate::JobCompletedSender;
#[cfg(feature = "enterprise")]
@@ -1029,7 +1032,7 @@ pub async fn start_worker(
script_path: &str,
token: &str,
job_completed_tx: JobCompletedSender,
jobs_rx: Receiver<std::sync::Arc<QueuedJob>>,
jobs_rx: Receiver<Arc<QueuedJob>>,
killpill_rx: tokio::sync::broadcast::Receiver<()>,
) -> error::Result<()> {
let mut mem_peak: i32 = 0;

View File

@@ -1,5 +1,4 @@
use base64::{engine, Engine as _};
use chrono::Datelike;
use core::fmt::Write;
use futures::TryFutureExt;
use jsonwebtoken::{encode, Algorithm, EncodingKey, Header};
@@ -37,21 +36,21 @@ struct SnowflakeDatabase {
role: Option<String>,
}
#[derive(Deserialize, Debug)]
#[derive(Deserialize)]
#[allow(non_snake_case)]
struct SnowflakeResponse {
data: Vec<Vec<Value>>,
resultSetMetaData: SnowflakeResultSetMetaData,
}
#[derive(Deserialize, Debug)]
#[derive(Deserialize)]
#[allow(non_snake_case)]
struct SnowflakeResultSetMetaData {
numRows: i64,
rowType: Vec<SnowflakeRowType>,
}
#[derive(Deserialize, Debug)]
#[derive(Deserialize)]
struct SnowflakeRowType {
name: String,
r#type: String,
@@ -199,8 +198,6 @@ pub async fn do_snowflake(
.await
.map_err(|e| Error::ExecutionErr(e.to_string()))?;
tracing::info!("Snowflake response: {:?}", result);
if result.resultSetMetaData.numRows > 10000 {
return Err(Error::ExecutionErr(
"More than 10000 rows were requested, use LIMIT 10000 to limit the number of rows".to_string(),
@@ -333,36 +330,10 @@ fn parse_val(value: &Value, typ: &str) -> Value {
let str_value = value.as_str().unwrap_or("").to_string();
let val = match typ.to_lowercase().as_str() {
"boolean" => str_value.parse::<bool>().ok().map(|v| json!(v)),
"real" => str_value.parse::<f64>().ok().map(|v| json!(v)),
"timestamp_ltz" | "timestamp_ntz" => str_value
.parse::<f64>()
.ok()
.map(|v| {
chrono::DateTime::from_timestamp(v.round() as i64, 0)
.map(|d| json!(d.format("%Y-%m-%d %H:%M:%S").to_string()))
})
.flatten(),
"time" => str_value
.parse::<f64>()
.ok()
.map(|v| {
chrono::NaiveTime::from_num_seconds_from_midnight_opt(v.round() as u32, 0)
.map(|d| json!(d.format("%H:%M:%S").to_string()))
})
.flatten(),
"date" => str_value
.parse::<i32>()
.ok()
.map(|v| {
chrono::NaiveDate::from_num_days_from_ce_opt(
v + chrono::NaiveDate::from_ymd_opt(1970, 1, 1)
.unwrap()
.num_days_from_ce(),
)
.map(|d| json!(d.format("%Y-%m-%d").to_string()))
})
.flatten(),
"fixed" | "number" => str_value
"real" | "time" | "timestamp_ltz" | "timestamp_ntz" => {
str_value.parse::<f64>().ok().map(|v| json!(v))
}
"fixed" | "date" | "number" => str_value
.parse::<i64>()
.ok()
.map(|v| json!(v))

View File

@@ -206,7 +206,6 @@ pub async fn create_token_for_owner(
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()),
};
let token = jsonwebtoken::encode(
@@ -1344,7 +1343,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
&Uuid::nil(),
&w_id,
success,
Arc::new(result),
&result,
true,
same_worker_tx2.clone(),
&worker_dir,
@@ -1676,7 +1675,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
.send(JobCompleted {
job: Arc::new(job),
success: true,
result: Arc::new(empty_result()),
result: empty_result(),
mem_peak: 0,
cached_res_path: None,
token: "".to_string(),
@@ -1936,7 +1935,6 @@ async fn queue_init_bash_maybe<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
) -> error::Result<()> {
if let Some(content) = WORKER_CONFIG.read().await.init_bash.clone() {
let tx = PushIsolationLevel::IsolatedRoot(db.clone(), rsmq);
let ehm = HashMap::new();
let (uuid, inner_tx) = push(
&db,
tx,
@@ -1953,7 +1951,7 @@ async fn queue_init_bash_maybe<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
cache_ttl: None,
dedicated_worker: None,
}),
PushArgs::from(&ehm),
PushArgs::empty(),
worker_name,
"worker@windmill.dev",
SUPERADMIN_SECRET_EMAIL.to_string(),
@@ -2018,10 +2016,6 @@ pub async fn process_completed_job<R: rsmq_async::RsmqConnection + Send + Sync +
save_in_cache(db, client, &job, cached_path.to_string(), &result).await;
}
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();
#[cfg(feature = "prometheus")]
let timer = _worker_save_completed_job_duration
.as_ref()
@@ -2038,26 +2032,25 @@ pub async fn process_completed_job<R: rsmq_async::RsmqConnection + Send + Sync +
false,
)
.await?;
drop(job);
#[cfg(feature = "prometheus")]
timer.map(|x| x.stop_and_record());
if is_flow_step {
if let Some(parent_job) = parent_job {
if job.is_flow_step {
if let Some(parent_job) = job.parent_job {
#[cfg(feature = "prometheus")]
let timer = _worker_flow_transition_duration
.as_ref()
.map(|x| x.start_timer());
tracing::info!(parent_flow = %parent_job, subflow = %job_id, "updating flow status (2)");
tracing::info!(parent_flow = %parent_job, subflow = %job.id, "updating flow status (2)");
update_flow_status_after_job_completion(
db,
client,
parent_job,
&job_id,
&workspace_id,
&job.id,
&job.workspace_id,
true,
result,
&result,
false,
same_worker_tx.clone(),
&worker_dir,
@@ -2095,7 +2088,7 @@ pub async fn process_completed_job<R: rsmq_async::RsmqConnection + Send + Sync +
&job.id,
&job.workspace_id,
false,
Arc::new(serde_json::value::to_raw_value(&result).unwrap()),
&serde_json::value::to_raw_value(&result).unwrap(),
false,
same_worker_tx,
&worker_dir,
@@ -2211,7 +2204,7 @@ pub async fn handle_job_error<R: rsmq_async::RsmqConnection + Send + Sync + Clon
&job_status_to_update,
&job.workspace_id,
false,
Arc::new(serde_json::value::to_raw_value(&wrapped_error).unwrap()),
&serde_json::value::to_raw_value(&wrapped_error).unwrap(),
unrecoverable,
same_worker_tx,
worker_dir,
@@ -2297,7 +2290,7 @@ pub enum SendResult {
#[derive(Debug, Clone)]
pub struct JobCompleted {
pub job: Arc<QueuedJob>,
pub result: Arc<Box<RawValue>>,
pub result: Box<RawValue>,
pub mem_peak: i32,
pub success: bool,
pub cached_res_path: Option<String>,
@@ -2464,7 +2457,7 @@ async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
job_completed_tx
.send(JobCompleted {
job: job,
result: Arc::new(cached_resource_value),
result: cached_resource_value,
mem_peak: 0,
canceled_by: None,
success: true,
@@ -2481,7 +2474,7 @@ async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
#[cfg(feature = "prometheus")]
let timer = _worker_flow_initial_transition_duration.map(|x| x.start_timer());
handle_flow(
job,
&job,
db,
&client.get_authed().await,
None,
@@ -2621,7 +2614,7 @@ async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
process_result(
job,
result.map(|x| Arc::new(x)),
result,
job_dir,
job_completed_tx,
mem_peak,
@@ -2638,7 +2631,7 @@ async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
async fn process_result(
job: Arc<QueuedJob>,
result: error::Result<Arc<Box<RawValue>>>,
result: error::Result<Box<RawValue>>,
job_dir: &str,
job_completed_tx: JobCompletedSender,
mem_peak: i32,
@@ -2713,7 +2706,7 @@ async fn process_result(
job_completed_tx
.send(JobCompleted {
job: job,
result: Arc::new(to_raw_value(&error_value)),
result: to_raw_value(&error_value),
mem_peak,
canceled_by,
success: false,

View File

@@ -17,7 +17,6 @@ use crate::common::{hash_args, save_in_cache};
use crate::js_eval::{eval_timeout, IdContext};
use crate::{AuthedClient, PreviousResult, SameWorkerPayload, SendResult, JOB_TOKEN, KEEP_JOB_DIR};
use anyhow::Context;
use mappable_rc::Marc;
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use serde_json::{json, Value};
@@ -48,7 +47,7 @@ use windmill_common::{
use windmill_queue::schedule::get_schedule_opt;
use windmill_queue::{
add_completed_job, add_completed_job_error, append_logs, get_queued_job,
handle_maybe_scheduled_job, CanceledBy, PushArgs, PushIsolationLevel, WrappedError,
handle_maybe_scheduled_job, CanceledBy, PushIsolationLevel, WrappedError,
};
type DB = sqlx::Pool<sqlx::Postgres>;
@@ -57,6 +56,7 @@ use windmill_queue::{canceled_job_to_result, get_queued_job_tx, push, QueueTrans
// #[instrument(level = "trace", skip_all)]
pub async fn update_flow_status_after_job_completion<
'a,
R: rsmq_async::RsmqConnection + Send + Sync + Clone,
>(
db: &DB,
@@ -65,7 +65,7 @@ pub async fn update_flow_status_after_job_completion<
job_id_for_status: &Uuid,
w_id: &str,
success: bool,
result: Arc<Box<RawValue>>,
result: &'a RawValue,
unrecoverable: bool,
same_worker_tx: Sender<SameWorkerPayload>,
worker_dir: &str,
@@ -105,7 +105,7 @@ pub async fn update_flow_status_after_job_completion<
&nrec.job_id_for_status,
w_id,
nrec.success,
nrec.result,
nrec.result.as_ref(),
false,
same_worker_tx.clone(),
worker_dir,
@@ -127,9 +127,7 @@ pub async fn update_flow_status_after_job_completion<
&nrec.job_id_for_status,
w_id,
false,
Arc::new(to_raw_value(&Json(&WrappedError {
error: json!(e.to_string()),
}))),
&to_raw_value(&Json(&WrappedError { error: json!(e.to_string()) })),
true,
same_worker_tx.clone(),
worker_dir,
@@ -149,7 +147,7 @@ pub struct RecUpdateFlowStatusAfterJobCompletion {
flow: uuid::Uuid,
job_id_for_status: Uuid,
success: bool,
result: Arc<Box<RawValue>>,
result: Box<RawValue>,
stop_early_override: Option<bool>,
skip_error_handler: bool,
}
@@ -168,6 +166,7 @@ pub struct RowFlowStatus {
}
// #[instrument(level = "trace", skip_all)]
pub async fn update_flow_status_after_job_completion_internal<
'a,
R: rsmq_async::RsmqConnection + Send + Sync + Clone,
>(
db: &DB,
@@ -176,7 +175,7 @@ pub async fn update_flow_status_after_job_completion_internal<
job_id_for_status: &Uuid,
w_id: &str,
mut success: bool,
result: Arc<Box<RawValue>>,
result: &'a RawValue,
unrecoverable: bool,
same_worker_tx: Sender<SameWorkerPayload>,
worker_dir: &str,
@@ -259,7 +258,7 @@ pub async fn update_flow_status_after_job_completion_internal<
} else if is_failure_step {
(false, false, false)
} else {
let r = sqlx::query_as::<_, SkipIfStopped>(
let row = sqlx::query(
"SELECT
raw_flow->'modules'->$1::int->'stop_after_if'->>'expr' as stop_early_expr,
(raw_flow->'modules'->$1::int->'stop_after_if'->>'skip_if_stopped')::bool as skip_if_stopped,
@@ -273,18 +272,19 @@ pub async fn update_flow_status_after_job_completion_internal<
.fetch_one(db)
.await
.map_err(|e| Error::InternalErr(format!("retrieval of stop_early_expr from state: {e:#}")))?;
let r = SkipIfStopped::from_row(&row)?;
let stop_early = success
&& if let Some(expr) = r.stop_early_expr.clone() {
compute_bool_from_expr(
expr,
Marc::new(
Arc::new(
r.args
.map(|x| x.0)
.unwrap_or_else(|| serde_json::from_str("{}").unwrap())
.to_owned(),
),
result.clone(),
Arc::new(result.to_owned()),
None,
Some(client),
None,
@@ -434,7 +434,7 @@ pub async fn update_flow_status_after_job_completion_internal<
if parallelism.is_some() {
sqlx::query!(
"UPDATE queue SET suspend = 0 WHERE parent_job = $1 AND suspend = $2 AND (flow_status->'step')::int = 0",
"UPDATE queue SET suspend = 0 WHERE parent_job = $1 AND suspend = $2",
flow,
nindex
)
@@ -654,9 +654,9 @@ pub async fn update_flow_status_after_job_completion_internal<
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?)
retrieve_flow_jobs_results(db, w_id, jobs).await?
}
_ => result.clone(),
_ => result.to_owned(),
};
if old_status.retry.fail_count > 0
@@ -730,8 +730,6 @@ pub async fn update_flow_status_after_job_completion_internal<
)
};
let flow_job = Arc::new(flow_job);
let done = if !should_continue_flow {
{
let logs = if flow_job.canceled {
@@ -839,10 +837,10 @@ pub async fn update_flow_status_after_job_completion_internal<
} else {
tracing::debug!(id = %flow_job.id, "start handle flow");
match handle_flow(
flow_job.clone(),
&flow_job,
db,
client,
Some(nresult.clone()),
Some(nresult.to_owned()),
same_worker_tx.clone(),
worker_dir,
rsmq.clone(),
@@ -888,7 +886,7 @@ pub async fn update_flow_status_after_job_completion_internal<
flow: parent_job,
job_id_for_status: flow,
success: success && !is_failure_step,
result: nresult.clone(),
result: nresult,
stop_early_override: if stop_early {
Some(skip_if_stop_early)
} else {
@@ -908,7 +906,7 @@ async fn retrieve_flow_jobs_results(
w_id: &str,
job_uuids: &Vec<Uuid>,
) -> error::Result<Box<RawValue>> {
let results = sqlx::query_as::<_, BranchResults>(
let results = sqlx::query(
"SELECT result, id
FROM completed_job
WHERE id = ANY($1) AND workspace_id = $2",
@@ -918,7 +916,10 @@ async fn retrieve_flow_jobs_results(
.fetch_all(db)
.await?
.into_iter()
.map(|br| (br.id, br.result))
.map(|r| {
let br = BranchResults::from_row(&r).unwrap();
(br.id, br.result.to_owned())
})
.collect::<HashMap<_, _>>();
let results = job_uuids
@@ -1067,7 +1068,7 @@ fn next_retry(retry: &Retry, status: &RetryStatus) -> Option<(u16, Duration)> {
async fn compute_bool_from_expr(
expr: String,
flow_args: Marc<HashMap<String, Box<RawValue>>>,
flow_args: Arc<HashMap<String, Box<RawValue>>>,
result: Arc<Box<RawValue>>,
by_id: Option<IdContext>,
client: Option<&AuthedClient>,
@@ -1162,7 +1163,7 @@ pub async fn get_step_of_flow_status(db: &DB, id: Uuid) -> error::Result<Step> {
/// 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(
flow_args: Marc<HashMap<String, Box<RawValue>>>,
flow_args: Arc<HashMap<String, Box<RawValue>>>,
last_result: Arc<Box<RawValue>>,
input_transforms: &HashMap<String, InputTransform>,
resumes: Arc<Box<RawValue>>,
@@ -1215,10 +1216,10 @@ async fn transform_input(
#[instrument(level = "trace", skip_all)]
pub async fn handle_flow<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
flow_job: Arc<QueuedJob>,
flow_job: &QueuedJob,
db: &sqlx::Pool<sqlx::Postgres>,
client: &AuthedClient,
last_result: Option<Arc<Box<RawValue>>>,
last_result: Option<Box<RawValue>>,
same_worker_tx: Sender<SameWorkerPayload>,
worker_dir: &str,
rsmq: Option<R>,
@@ -1250,7 +1251,7 @@ pub async fn handle_flow<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
if let Err(err) = handle_maybe_scheduled_job(
rsmq.clone(),
db,
&flow_job,
flow_job,
&schedule,
flow_job.script_path.as_ref().unwrap(),
&flow_job.workspace_id,
@@ -1277,7 +1278,7 @@ pub async fn handle_flow<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
flow,
db,
client,
last_result,
last_result.to_owned(),
same_worker_tx,
worker_dir,
rsmq,
@@ -1327,19 +1328,15 @@ fn potentially_crash_for_testing() {
}
}
// static
lazy_static::lazy_static! {
pub static ref EHM: HashMap<String, Box<RawValue>> = HashMap::new();
}
// #[async_recursion]
// #[instrument(level = "trace", skip_all)]
async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
flow_job: Arc<QueuedJob>,
flow_job: &QueuedJob,
mut status: FlowStatus,
flow: FlowValue,
db: &sqlx::Pool<sqlx::Postgres>,
client: &AuthedClient,
last_job_result: Option<Arc<Box<RawValue>>>,
last_job_result: Option<Box<RawValue>>,
same_worker_tx: Sender<SameWorkerPayload>,
worker_dir: &str,
rsmq: Option<R>,
@@ -1360,14 +1357,7 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
.cloned()
.unwrap_or_else(|| status.failure_module.module_status.clone());
let fj: mappable_rc::Marc<QueuedJob> = flow_job.clone().into();
let arc_flow_job_args: Marc<HashMap<String, Box<RawValue>>> = Marc::map(fj, |x| {
if let Some(args) = &x.args {
&args.0
} else {
&EHM
}
});
let flow_job_args = flow_job.get_args();
// if this is an empty module of if the module has already been completed, successfully, update the parent flow
if flow.modules.is_empty() || matches!(status_module, FlowStatusModule::Success { .. }) {
@@ -1376,7 +1366,7 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
flow: flow_job.id,
success: true,
result: if flow.modules.is_empty() {
to_raw_value(arc_flow_job_args.as_ref())
to_raw_value(&flow_job_args)
} else {
// it has to be an empty for loop event
serde_json::from_str("[]").unwrap()
@@ -1396,6 +1386,8 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
return Ok(());
}
let arc_flow_job_args = Arc::new(flow_job_args.clone());
if i == 0 {
if !flow_job.is_flow_step && flow_job.schedule_path.is_some() {
let no_flow_overlap = sqlx::query_scalar!(
@@ -1478,7 +1470,7 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
// Compute and initialize last_job_result
let arc_last_job_result = if status_module.is_failure() {
// if job is being retried, pass the result of its previous failure
last_job_result.unwrap_or_else(|| Arc::new(to_raw_value(&json!("{}"))))
Arc::new(last_job_result.unwrap_or(to_raw_value(&json!("{}"))))
} else if i == 0 {
// if it's the first job executed in the flow, pass the flow args
Arc::new(to_raw_value(&flow_job.args))
@@ -1487,7 +1479,7 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
// having last_job_result empty can happen either when the job was suspended and is being restarted, or if it's a
// flow restart from a specific step
if last_job_result.is_some() {
last_job_result.unwrap()
Arc::new(last_job_result.unwrap())
} else {
match get_previous_job_result(db, flow_job.workspace_id.as_str(), &status).await? {
None => Arc::new(to_raw_value(&json!("{}"))),
@@ -1522,19 +1514,25 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
.await
.context("lock flow in queue")?;
let resumes = sqlx::query_as::<_, ResumeRow>(
let resumes = sqlx::query(
"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()
.map(|x| ResumeRow::from_row(&x))
.collect::<Vec<_>>();
resume_messages.extend(resumes.iter().map(|r| to_raw_value(&r.value)));
resume_messages.extend(
resumes
.iter()
.map(|r| to_raw_value(&r.as_ref().map(|x| x.value.clone()).ok())),
);
approvers.extend(resumes.iter().map(|r| {
r.approver
.clone()
r.as_ref()
.ok()
.and_then(|x| x.approver.clone())
.as_deref()
.unwrap_or_else(|| "anonymous")
.to_string()
@@ -1601,7 +1599,9 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
.await?;
}
let is_disapproved = resumes.iter().find(|x| !x.approved);
let is_disapproved = resumes
.iter()
.find(|x| x.as_ref().is_ok_and(|x| !x.approved));
if is_disapproved.is_none() && resume_messages.len() >= required_events as usize {
sqlx::query(
"UPDATE queue
@@ -1612,9 +1612,11 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
.bind(json!(resumes
.into_iter()
.map(|r| Approval {
resume_id: r.resume_id as u16,
resume_id: r.as_ref().map(|x| x.resume_id).unwrap_or_default() as u16,
approver: r
.approver.clone()
.as_ref()
.ok()
.and_then(|x| x.approver.clone())
.unwrap_or_else(|| "unknown".to_string())
})
.collect::<Vec<_>>()))
@@ -1674,7 +1676,10 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
let (logs, error_name) = if let Some(disapprover) = is_disapproved {
(
format!("Disapproved by {:?}", disapprover.approver),
format!(
"Disapproved by {:?}",
disapprover.as_ref().unwrap().approver
),
"SuspendedDisapproved",
)
} else {
@@ -1870,83 +1875,80 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
drop(resume_messages);
let args: windmill_common::error::Result<_> =
if module.mock.is_some() && module.mock.as_ref().unwrap().enabled {
let mut hm = HashMap::new();
hm.insert(
"previous_result".to_string(),
to_raw_value(
&module
.mock
.as_ref()
.unwrap()
.return_value
.clone()
.unwrap_or_else(|| serde_json::from_str("null").unwrap()),
),
);
Ok(Marc::new(hm))
} else if let Some(id) = get_args_from_id {
let row = sqlx::query_as::<_, RawArgs>(
"SELECT args FROM completed_job WHERE id = $1 AND workspace_id = $2",
)
let args: windmill_common::error::Result<_> = if module.mock.is_some()
&& module.mock.as_ref().unwrap().enabled
{
let mut hm = HashMap::new();
hm.insert(
"previous_result".to_string(),
to_raw_value(
&module
.mock
.as_ref()
.unwrap()
.return_value
.clone()
.unwrap_or_else(|| serde_json::from_str("null").unwrap()),
),
);
Ok(hm)
} else if let Some(id) = get_args_from_id {
let row = sqlx::query("SELECT args FROM completed_job WHERE id = $1 AND workspace_id = $2")
.bind(id)
.bind(&flow_job.workspace_id)
.fetch_optional(db)
.await?;
if let Some(raw_args) = row {
Ok(Marc::new(
raw_args.args.map(|x| x.0).unwrap_or_else(HashMap::new),
))
} else {
Ok(Marc::new(HashMap::new()))
}
if let Some(row) = row {
RawArgs::from_row(&row)
.map(|x| x.args.map(|x| x.0).unwrap_or_else(HashMap::new))
.map_err(|e| error::Error::InternalErr(format!("Impossible to build args: {e:#}")))
} else {
match &module.get_value() {
Ok(
FlowModuleValue::Script { input_transforms, .. }
| FlowModuleValue::RawScript { input_transforms, .. }
| FlowModuleValue::Flow { input_transforms, .. },
) => {
let ctx = get_transform_context(&flow_job, &previous_id, &status).await?;
transform_context = Some(ctx);
let by_id = transform_context.as_ref().unwrap();
transform_input(
arc_flow_job_args.clone(),
arc_last_job_result.clone(),
input_transforms,
resumes.clone(),
resume.clone(),
approvers.clone(),
by_id,
client,
)
.await
.map(Marc::new)
}
Ok(FlowModuleValue::Identity) => serde_json::from_str(
&serde_json::to_string(&PreviousResult {
previous_result: Some(&arc_last_job_result),
})
.unwrap(),
Ok(HashMap::new())
}
} else {
match &module.get_value() {
Ok(
FlowModuleValue::Script { input_transforms, .. }
| FlowModuleValue::RawScript { input_transforms, .. }
| FlowModuleValue::Flow { input_transforms, .. },
) => {
let ctx = get_transform_context(&flow_job, &previous_id, &status).await?;
transform_context = Some(ctx);
let by_id = transform_context.as_ref().unwrap();
transform_input(
arc_flow_job_args.clone(),
arc_last_job_result.clone(),
input_transforms,
resumes.clone(),
resume.clone(),
approvers.clone(),
by_id,
client,
)
.map(Marc::new)
.map_err(|e| error::Error::InternalErr(format!("identity: {e:#}"))),
Ok(_) => Ok(arc_flow_job_args.clone()),
Err(e) => {
return Err(error::Error::InternalErr(format!(
"module was not convertible to acceptable value {e:?}"
)))
}
.await
}
};
Ok(FlowModuleValue::Identity) => serde_json::from_str(
&serde_json::to_string(&PreviousResult {
previous_result: Some(&arc_last_job_result),
})
.unwrap(),
)
.map_err(|e| error::Error::InternalErr(format!("identity: {e:#}"))),
Ok(_) => Ok(flow_job_args),
Err(e) => {
return Err(error::Error::InternalErr(format!(
"module was not convertible to acceptable value {e:?}"
)))
}
}
};
tracing::debug!(id = %flow_job.id, root_id = %job_root, "flow job args computed");
let next_flow_transform = compute_next_flow_transform(
arc_flow_job_args.clone(),
arc_last_job_result.clone(),
&flow_job,
flow_job,
&flow,
transform_context,
db,
@@ -2005,7 +2007,7 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
};
let mut tx: QueueTransaction<'_, R> = (rsmq.clone(), db.begin().await?).into();
let nargs = args.as_ref();
for i in (0..len).into_iter() {
if i % 100 == 0 && i != 0 {
tracing::info!(id = %flow_job.id, root_id = %job_root, "pushed (non-commited yet) first {i} subflows of {len}");
@@ -2037,20 +2039,19 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
None
};
let marc;
let me;
let transform_inp;
let args = match &next_status {
NextStatus::AllFlowJobs {
branchall: Some(BranchAllStatus { .. }),
iterator: None,
..
} => nargs,
} => args.as_ref().map(|args| args.clone()),
NextStatus::NextLoopIteration {
next: ForloopNextIteration { new_args, .. },
simple_input_transforms,
} => {
let mut args = if let Ok(args) = nargs {
args.as_ref().clone()
let mut args = if let Ok(args) = args.as_ref() {
args.clone()
} else {
HashMap::new()
};
@@ -2060,8 +2061,8 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
if let Some(input_transforms) = simple_input_transforms {
//previous id is none because we do not want to use previous id if we are in a for loop
let ctx = get_transform_context(&flow_job, "", &status).await?;
let ti = transform_input(
Marc::new(args),
transform_inp = transform_input(
Arc::new(args),
arc_last_job_result.clone(),
input_transforms,
resumes.clone(),
@@ -2070,23 +2071,10 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
&ctx,
client,
)
.await
.map_err(|e| {
Error::ExecutionErr(
format!("could not transform input using an expr: {e}",),
)
})
.map(Marc::new);
if let Ok(ti) = ti {
marc = ti;
Ok(&marc)
} else {
me = ti.unwrap_err();
Err(&me)
}
.await;
transform_inp.as_ref().map(|args| args.clone())
} else {
marc = Marc::new(args);
Ok(&marc)
Ok(args)
}
}
NextStatus::AllFlowJobs {
@@ -2096,7 +2084,7 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
} => {
if let Ok(args) = args.as_ref() {
let mut hm = HashMap::new();
for (k, v) in args.iter() {
for (k, v) in args {
hm.insert(k.to_string(), v.to_owned());
}
insert_iter_arg(
@@ -2106,8 +2094,8 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
);
if let Some(input_transforms) = simple_input_transforms {
let ctx = get_transform_context(&flow_job, &previous_id, &status).await?;
let ti = transform_input(
Marc::new(hm),
transform_inp = transform_input(
Arc::new(hm),
arc_last_job_result.clone(),
input_transforms,
resumes.clone(),
@@ -2116,45 +2104,20 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
&ctx,
client,
)
.await
.map_err(|e| {
Error::ExecutionErr(format!(
"could not transform input using an expr: {e}"
))
})
.map(Marc::new);
if let Ok(ti) = ti {
marc = ti;
Ok(&marc)
} else {
me = ti.unwrap_err();
Err(&me)
}
.await;
transform_inp.as_ref().map(|args| args.clone())
} else {
marc = Marc::new(hm);
Ok(&marc)
Ok(hm)
}
} else {
nargs
args.as_ref().map(|args| args.clone())
}
}
_ => nargs,
_ => args.as_ref().map(|args| args.clone()),
};
let push_args;
let err;
let ov;
match args {
Ok(v) => {
ov = v;
push_args = PushArgs::from(ov.as_ref());
err = None;
}
Err(e) => {
push_args = PushArgs::from(&*EHM);
err = Some(e);
}
let (ok, err) = match args {
Ok(v) => (Some(v), None),
Err(e) => (None, Some(e)),
};
tracing::debug!(id = %flow_job.id, root_id = %job_root, "computed args for job {i} of {len}");
@@ -2198,7 +2161,10 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
tx2,
&flow_job.workspace_id,
payload_tag.payload,
push_args,
windmill_queue::PushArgs {
args: ok.unwrap_or_else(|| serde_json::from_str("{}").unwrap()),
extra: HashMap::new(),
},
&flow_job.created_by,
&flow_job.email,
flow_job.permissioned_as.to_owned(),
@@ -2548,7 +2514,7 @@ fn insert_iter_arg(
}
async fn compute_next_flow_transform(
arc_flow_job_args: Marc<HashMap<String, Box<RawValue>>>,
arc_flow_job_args: Arc<HashMap<String, Box<RawValue>>>,
arc_last_job_result: Arc<Box<RawValue>>,
flow_job: &QueuedJob,
flow: &FlowValue,
@@ -3075,7 +3041,7 @@ async fn next_forloop_status(
resumes: Arc<Box<RawValue>>,
resume: Arc<Box<RawValue>>,
approvers: Arc<Box<RawValue>>,
arc_flow_job_args: Marc<HashMap<String, Box<RawValue>>>,
arc_flow_job_args: Arc<HashMap<String, Box<RawValue>>>,
client: &AuthedClient,
parallel: &bool,
) -> Result<ForLoopStatus, Error> {
@@ -3282,7 +3248,7 @@ async fn script_to_payload(
tag_override: &Option<String>,
) -> Result<JobPayloadWithTag, Error> {
let (payload, tag, delete_after_use, script_timeout) = if script_hash.is_none() {
script_path_to_payload(script_path, db, &flow_job.workspace_id).await?
script_path_to_payload(script_path, &db, &flow_job.workspace_id).await?
} else {
let hash = script_hash.clone().unwrap();
let mut tx: sqlx::Transaction<'_, sqlx::Postgres> = db.begin().await?;

View File

@@ -409,34 +409,7 @@ async fn trigger_dependents_to_recompute_dependencies<
"nodes_to_relock".to_string(),
to_raw_value(&s.importer_node_ids),
);
let r = sqlx::query_scalar!(
"SELECT versions[array_upper(versions, 1)] FROM flow WHERE path = $1 AND workspace_id = $2",
s.importer_path,
w_id,
).fetch_one(db)
.await
.map_err(to_anyhow);
match r {
Ok(Some(version)) => JobPayload::FlowDependencies {
path: s.importer_path.clone(),
dedicated_worker: None,
version: version,
},
Ok(None) => {
tracing::error!(
"no flow version found for path {path}",
path = s.importer_path
);
continue;
}
Err(err) => {
tracing::error!(
"error getting latest deployed flow version for path {path}: {err}",
path = s.importer_path,
);
continue;
}
}
JobPayload::FlowDependencies { path: s.importer_path.clone(), dedicated_worker: None }
} else {
tracing::error!(
"unexpected importer kind: {kind} for path {path}",
@@ -451,7 +424,7 @@ async fn trigger_dependents_to_recompute_dependencies<
tx,
&w_id,
job_payload,
windmill_queue::PushArgs { args: &args, extra: None },
windmill_queue::PushArgs { args, extra: HashMap::new() },
&created_by,
email,
permissioned_as.to_string(),
@@ -497,15 +470,6 @@ pub async fn handle_flow_dependency_job<R: rsmq_async::RsmqConnection + Send + S
"Cannot resolve flow dependencies for flow without path".to_string(),
)
})?;
let version = job
.script_hash
.clone()
.ok_or_else(|| {
Error::InternalErr("Flow Dependency requires script hash (flow version)".to_owned())
})?
.0;
let raw_flow = job.raw_flow.clone().map(|v| Ok(v)).unwrap_or_else(|| {
Err(Error::InternalErr(
"Flow Dependency requires raw flow".to_owned(),
@@ -583,13 +547,6 @@ pub async fn handle_flow_dependency_job<R: rsmq_async::RsmqConnection + Send + S
)
.execute(db)
.await?;
sqlx::query!(
"UPDATE flow_version SET value = $1 WHERE id = $2",
new_flow_value,
version
)
.execute(db)
.await?;
}
tx.commit().await?;
@@ -598,7 +555,7 @@ pub async fn handle_flow_dependency_job<R: rsmq_async::RsmqConnection + Send + S
&job.created_by,
&db,
&job.workspace_id,
DeployedObject::Flow { path: job_path, parent_path, version },
DeployedObject::Flow { path: job_path, parent_path },
deployment_message,
rsmq.clone(),
false,
@@ -1059,14 +1016,14 @@ pub async fn handle_app_dependency_job<R: rsmq_async::RsmqConnection + Send + Sy
) -> error::Result<()> {
let job_path = job.script_path.clone().ok_or_else(|| {
error::Error::InternalErr(
"Cannot resolve app dependencies for app without path".to_string(),
"Cannot resolve flow dependencies for flow without path".to_string(),
)
})?;
let id = job
.script_hash
.clone()
.ok_or_else(|| Error::InternalErr("App Dependency requires script hash".to_owned()))?
.ok_or_else(|| Error::InternalErr("Flow Dependency requires script hash".to_owned()))?
.0;
let value = sqlx::query_scalar!("SELECT value FROM app_version WHERE id = $1", id)
.fetch_optional(db)
@@ -1088,7 +1045,7 @@ pub async fn handle_app_dependency_job<R: rsmq_async::RsmqConnection + Send + Sy
)
.await?;
// Re-check cancelation to ensure we don't accidentially override an app.
// Re-check cancelation to ensure we don't accidentially override a flow.
if sqlx::query_scalar!("SELECT canceled FROM queue WHERE id = $1", job.id)
.fetch_optional(db)
.await

View File

@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
export const VERSION = "v1.358.0";
export const VERSION = "v1.355.0";
export async function login(email: string, password: string): Promise<string> {
return await windmill.UserService.login({

View File

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

View File

@@ -21,7 +21,7 @@ RUN apt-get -y update \
RUN git clone -b master --single-branch https://github.com/google/nsjail.git . && git checkout dccf911fd2659e7b08ce9507c25b2b38ec2c5800
RUN make
FROM ghcr.io/windmill-labs/windmill-ee:dev
FROM ghcr.io/windmill-labs/windmill-ee:dev as downloader
RUN apt-get update && apt-get install -y libprotobuf-dev libnl-route-3-dev

View File

@@ -1,34 +0,0 @@
ARG PYTHON_IMAGE=python:3.11.8-slim-bookworm
FROM ${PYTHON_IMAGE}
ARG APP=/usr/src/app
RUN apt-get update \
&& apt-get install -y ca-certificates wget curl git jq unzip build-essential unixodbc xmlsec1 software-properties-common \
&& rm -rf /var/lib/apt/lists/*
RUN curl -sL https://deb.nodesource.com/setup_20.x | bash -
RUN apt-get -y update && apt-get install -y curl nodejs awscli
ENV TZ=Etc/UTC
RUN /usr/local/bin/python3 -m pip install pip-tools
COPY --from=oven/bun:1.1.8 /usr/local/bin/bun /usr/bin/bun
# add the docker client to call docker from a worker if enabled
COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/
WORKDIR ${APP}
COPY --from=ghcr.io/windmill-labs/windmill-ee:dev --chmod=755 ${APP}/windmill ${APP}/windmill
COPY --from=denoland/deno:1.44.4 --chmod=755 /usr/bin/deno /usr/bin/deno
RUN ln -s ${APP}/windmill /usr/local/bin/windmill
RUN windmill cache
EXPOSE 8000
CMD ["windmill"]

View File

@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.358.0",
"version": "1.355.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.358.0",
"version": "1.355.0",
"license": "AGPL-3.0",
"dependencies": {
"@aws-crypto/sha256-js": "^4.0.0",
@@ -52,7 +52,7 @@
"vscode-languageclient": "~9.0.1",
"vscode-uri": "~3.0.8",
"vscode-ws-jsonrpc": "~3.1.0",
"windmill-parser-wasm": "^1.355.4",
"windmill-parser-wasm": "^1.347.1",
"windmill-sql-datatype-parser-wasm": "^1.318.0",
"y-monaco": "^0.1.4",
"y-websocket": "^1.5.0",
@@ -10261,9 +10261,9 @@
}
},
"node_modules/windmill-parser-wasm": {
"version": "1.355.4",
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.355.4.tgz",
"integrity": "sha512-7Usvb55qAULgGg5ULoMm4iWviAl0fQMNxoDQXx4lrPASpXSSV0BS9anP2jF/gse5HQ1usKj63TNtCFYedMThHg=="
"version": "1.347.1",
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.347.1.tgz",
"integrity": "sha512-0ErA/2Wz+KLEDf4iQqBvtp5FDz2LU2oO3W0MYli67x0OhLVRh2PVr4G39rn2lCRNc3n5RU1HW6qMgBoBy+4yig=="
},
"node_modules/windmill-sql-datatype-parser-wasm": {
"version": "1.318.0",

View File

@@ -1,6 +1,6 @@
{
"name": "windmill-components",
"version": "1.358.0",
"version": "1.355.0",
"scripts": {
"dev": "vite dev",
"build": "vite build",
@@ -132,7 +132,7 @@
"vscode-languageclient": "~9.0.1",
"vscode-uri": "~3.0.8",
"vscode-ws-jsonrpc": "~3.1.0",
"windmill-parser-wasm": "^1.355.4",
"windmill-parser-wasm": "^1.347.1",
"windmill-sql-datatype-parser-wasm": "^1.318.0",
"y-monaco": "^0.1.4",
"y-websocket": "^1.5.0",

View File

@@ -714,11 +714,7 @@
multiple={false}
/>
{#if value?.length}
<div class="text-2xs text-tertiary mt-1"
>File length: {value.length} base64 chars ({(value.length / 1024 / 1024).toFixed(
2
)}MB)</div
>
<div class="text-2xs text-tertiary mt-1">File length: {value.length} base64 chars</div>
{/if}
</div>
{:else if inputCat == 'resource-string'}

View File

@@ -10,8 +10,6 @@
export let username: string
export let isConflict = false
let loading = false
let usernameInfo:
| {
username: string
@@ -44,31 +42,26 @@
const dispatch = createEventDispatcher()
async function renameUser() {
loading = true
try {
const automateUsernameCreation =
(await SettingService.getGlobal({ key: 'automate_username_creation' })) ?? false
const automateUsernameCreation =
(await SettingService.getGlobal({ key: 'automate_username_creation' })) ?? false
if (!automateUsernameCreation) {
sendUserToast(
'Modifying the username is only possible when the creation of usernames is automated and defined at instance level..'
)
return
}
await UserService.globalUserRename({
email,
requestBody: {
new_username: username
}
})
sendUserToast(`Renamed user ${email} to ${username}`)
dispatch('renamed')
} finally {
loading = false
if (!automateUsernameCreation) {
sendUserToast(
'Modifying the username is only possible when the creation of usernames is automated and defined at instance level..'
)
return
}
await UserService.globalUserRename({
email,
requestBody: {
new_username: username
}
})
sendUserToast(`Rename user ${email} to ${username}`)
dispatch('renamed')
}
</script>
@@ -146,7 +139,6 @@
})
}}
disabled={email === undefined || !username}
{loading}
>
Confirm username change
</Button>

View File

@@ -1,81 +0,0 @@
<script lang="ts">
import { createPopperActions, type PopperOptions } from 'svelte-popperjs'
import type { PopoverPlacement } from './Popover.model'
import Portal from 'svelte-portal'
export let placement: PopoverPlacement = 'bottom-end'
export let notClickable = false
export let disablePopup = false
export let disappearTimeout = 100
export let appearTimeout = 300
export let style: string | undefined = undefined
export let focusEl: HTMLElement | undefined = undefined
const [popperRef, popperContent] = createPopperActions({ placement })
const popperOptions: PopperOptions<{}> = {
placement,
strategy: 'fixed',
modifiers: [
{ name: 'offset', options: { offset: [8, 8] } },
{
name: 'arrow',
options: {
padding: 10
}
}
]
}
let showTooltip = false
let timeout: NodeJS.Timeout | undefined = undefined
let inTimeout: NodeJS.Timeout | undefined = undefined
function open() {
clearTimeout(timeout)
if (appearTimeout == 0) {
showTooltip = true
} else {
inTimeout = setTimeout(() => (showTooltip = true), appearTimeout)
}
}
function close() {
inTimeout && clearTimeout(inTimeout)
inTimeout = undefined
timeout = setTimeout(() => (showTooltip = false), disappearTimeout)
}
$: focusEl && focusEl?.focus()
</script>
{#if notClickable}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<span {style} use:popperRef on:mouseenter={open} on:mouseleave={close} class={$$props.class}>
<slot />
</span>
{:else}
<button
{style}
use:popperRef
on:mouseenter={open}
on:mouseleave={close}
on:click
class={$$props.class}
>
<slot />
</button>
{/if}
{#if showTooltip && !disablePopup}
<Portal>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
use:popperContent={popperOptions}
on:mouseenter={open}
on:mouseleave={close}
class="z-[5001] border rounded-lg shadow-lg p-4 bg-surface"
>
<slot name="overlay" />
</div>
</Portal>
{/if}

View File

@@ -1,14 +1,13 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { Button } from './common'
import { Clock, X } from 'lucide-svelte'
import { Clock } from 'lucide-svelte'
import { twMerge } from 'tailwind-merge'
// import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
// import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte'
export let value: string | undefined = undefined
export let clearable: boolean = false
export let autofocus: boolean | null = false
export let useDropdown: boolean = false
export let minDate: string | undefined = undefined
@@ -131,20 +130,6 @@
>
Now
</Button>
{#if clearable}
<Button
variant="border"
color="light"
wrapperClasses="h-8"
{disabled}
on:click={() => {
value = undefined
dispatch('clear')
}}
>
<X size={14} />
</Button>
{/if}
<!-- <div>
<ToggleButtonGroup bind:selected={format}>
<ToggleButton light small value={'local'} label="local" />

View File

@@ -27,9 +27,9 @@
sleep
} from '$lib/utils'
import { sendUserToast } from '$lib/toast'
import { Drawer } from '$lib/components/common'
import type { Drawer } from '$lib/components/common'
import { setContext, tick, type ComponentType } from 'svelte'
import { setContext, tick } from 'svelte'
import { writable, type Writable } from 'svelte/store'
import CenteredPage from './CenteredPage.svelte'
import { Badge, Button, UndoRedo } from './common'
@@ -43,17 +43,7 @@
import { loadFlowSchedule, type Schedule } from './flows/scheduleUtils'
import type { FlowEditorContext, FlowInput } from './flows/types'
import { cleanInputs, emptyFlowModuleState } from './flows/utils'
import {
Calendar,
Pen,
Save,
DiffIcon,
MoreVertical,
HistoryIcon,
FileJson,
type Icon,
CornerDownLeft
} from 'lucide-svelte'
import { Calendar, Pen, Save, DiffIcon } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
import Awareness from './Awareness.svelte'
import { getAllModules } from './flows/flowExplorer'
@@ -74,12 +64,6 @@
import FlowTutorials from './FlowTutorials.svelte'
import { ignoredTutorials } from './tutorials/ignoredTutorials'
import type DiffDrawer from './DiffDrawer.svelte'
import FlowHistory from './flows/FlowHistory.svelte'
import ButtonDropdown from './common/button/ButtonDropdown.svelte'
import { MenuItem } from '@rgossiaux/svelte-headlessui'
import { twMerge } from 'tailwind-merge'
import CustomPopover from './CustomPopover.svelte'
import Summary from './Summary.svelte'
export let initialPath: string = ''
export let pathStoreInit: string | undefined = undefined
@@ -223,7 +207,7 @@
)
}
async function saveFlow(deploymentMsg?: string): Promise<void> {
async function saveFlow(): Promise<void> {
loadingSave = true
try {
const flow = cleanInputs($flowStore)
@@ -249,8 +233,7 @@
ws_error_handler_muted: flow.ws_error_handler_muted,
tag: flow.tag,
dedicated_worker: flow.dedicated_worker,
visible_to_runner_only: flow.visible_to_runner_only,
deployment_message: deploymentMsg || undefined
visible_to_runner_only: flow.visible_to_runner_only
}
})
if (enabled) {
@@ -313,8 +296,7 @@
tag: flow.tag,
dedicated_worker: flow.dedicated_worker,
ws_error_handler_muted: flow.ws_error_handler_muted,
visible_to_runner_only: flow.visible_to_runner_only,
deployment_message: deploymentMsg || undefined
visible_to_runner_only: flow.visible_to_runner_only
}
})
}
@@ -996,9 +978,6 @@
let renderCount = 0
let flowTutorials: FlowTutorials | undefined = undefined
let jsonViewerDrawer: Drawer | undefined = undefined
let flowHistory: FlowHistory | undefined = undefined
export function triggerTutorial() {
const urlParams = new URLSearchParams(window.location.search)
const tutorial = urlParams.get('tutorial')
@@ -1009,30 +988,6 @@
flowTutorials?.runTutorialById('action')
}
}
const moreItems: {
displayName: string
icon: ComponentType<Icon>
action: () => void
disabled?: boolean
}[] = [
{
displayName: 'Deployment History',
icon: HistoryIcon,
action: () => {
flowHistory?.open()
},
disabled: newFlow
},
{
displayName: 'Export',
icon: FileJson,
action: () => jsonViewerDrawer?.openDrawer()
}
]
let deploymentMsg = ''
let msgInput: HTMLInputElement | undefined = undefined
</script>
<svelte:window on:keydown={onKeyDown} />
@@ -1042,10 +997,6 @@
{#key renderCount}
{#if !$userStore?.operator}
<FlowCopilotDrawer {getHubCompletions} {genFlow} bind:flowCopilotMode />
{#if $pathStore}
<FlowHistory bind:this={flowHistory} path={$pathStore} on:historyRestore />
{/if}
<FlowImportExportMenu bind:drawer={jsonViewerDrawer} />
<FlowCopilotInputsModal
on:confirmed={async () => {
applyCopilotFlowInputs()
@@ -1069,8 +1020,14 @@
<div transition:fade class="absolute inset-0 bg-gray-500 bg-opacity-75 z-[900] !m-0" />
{/if}
<div class="flex w-full max-w-md gap-4 items-center">
<Summary bind:value={$flowStore.summary} />
<div class="min-w-64 w-full">
<input
type="text"
placeholder="Flow summary"
class="text-sm w-full font-semibold"
bind:value={$flowStore.summary}
/>
</div>
<UndoRedo
undoProps={{ disabled: $history.index === 0 }}
redoProps={{ disabled: $history.index === $history.history.length - 1 }}
@@ -1126,40 +1083,10 @@
/>
</div>
</div>
<div class="flex flex-row gap-2 items-center">
<div class="flex flex-row space-x-2">
{#if $enterpriseLicense && !newFlow}
<Awareness />
{/if}
<div>
<ButtonDropdown hasPadding={false}>
<svelte:fragment slot="buttonReplacement">
<Button nonCaptureEvent size="xs" color="light">
<div class="flex flex-row items-center">
<MoreVertical size={14} />
</div>
</Button>
</svelte:fragment>
<svelte:fragment slot="items">
{#each moreItems as item}
<MenuItem
on:click={item.action}
disabled={item.disabled}
class={item.disabled ? 'opacity-50' : ''}
>
<div
class={twMerge(
'text-primary flex flex-row items-center text-left px-4 py-2 gap-2 cursor-pointer hover:bg-surface-hover !text-xs font-semibold'
)}
>
<svelte:component this={item.icon} size={14} />
{item.displayName}
</div>
</MenuItem>
{/each}
</svelte:fragment>
</ButtonDropdown>
</div>
<FlowBuilderTutorials
on:reload={() => {
renderCount += 1
@@ -1197,6 +1124,8 @@
{abortController}
/>
<FlowImportExportMenu />
<FlowPreviewButtons />
<Button
loading={loadingDraft}
@@ -1210,41 +1139,15 @@
>
Draft
</Button>
<CustomPopover appearTimeout={0} focusEl={msgInput}>
<Button
loading={loadingSave}
size="xs"
startIcon={{ icon: Save }}
on:click={() => saveFlow()}
dropdownItems={!newFlow ? dropdownItems : undefined}
>
Deploy
</Button>
<svelte:fragment slot="overlay">
<div class="flex flex-row gap-2 w-80">
<input
type="text"
placeholder="Deployment message"
bind:value={deploymentMsg}
on:keydown={(e) => {
if (e.key === 'Enter') {
saveFlow(deploymentMsg)
}
}}
bind:this={msgInput}
/>
<Button
size="xs"
on:click={() => saveFlow(deploymentMsg)}
endIcon={{ icon: CornerDownLeft }}
loading={loadingSave}
>
Deploy
</Button>
</div>
</svelte:fragment>
</CustomPopover>
<Button
loading={loadingSave}
size="xs"
startIcon={{ icon: Save }}
on:click={() => saveFlow()}
dropdownItems={!newFlow ? dropdownItems : undefined}
>
Deploy
</Button>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More