Compare commits
3 Commits
v1.358.0
...
rf/extraDo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c90d0965a5 | ||
|
|
2c4c443f75 | ||
|
|
afa73ad6ea |
23
.github/workflows/build_ws.yml
vendored
23
.github/workflows/build_ws.yml
vendored
@@ -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
|
||||
|
||||
1
.github/workflows/change-versions.yml
vendored
1
.github/workflows/change-versions.yml
vendored
@@ -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
|
||||
|
||||
91
.github/workflows/docker-image.yml
vendored
91
.github/workflows/docker-image.yml
vendored
@@ -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
|
||||
|
||||
23
.github/workflows/publish_lsp.yml
vendored
23
.github/workflows/publish_lsp.yml
vendored
@@ -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
|
||||
|
||||
94
CHANGELOG.md
94
CHANGELOG.md
@@ -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)
|
||||
|
||||
|
||||
|
||||
41
Dockerfile
41
Dockerfile
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
34
backend/.sqlx/query-4c529e29a0eb084a92f4e952506d701592bc707fd8a8cb3311bf146be6078f26.json
generated
Normal file
34
backend/.sqlx/query-4c529e29a0eb084a92f4e952506d701592bc707fd8a8cb3311bf146be6078f26.json
generated
Normal 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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"name": "bool",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
|
||||
23
backend/.sqlx/query-6b1ea6f39c6f41a093112418ac4c1b69a57de50fdeb4bdcd8d4cab0553af42ea.json
generated
Normal file
23
backend/.sqlx/query-6b1ea6f39c6f41a093112418ac4c1b69a57de50fdeb4bdcd8d4cab0553af42ea.json
generated
Normal 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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
16
backend/.sqlx/query-a459d973c6392af6af1af8b17bc735128fa06ee80e867e2421e70df7a722081a.json
generated
Normal file
16
backend/.sqlx/query-a459d973c6392af6af1af8b17bc735128fa06ee80e867e2421e70df7a722081a.json
generated
Normal 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"
|
||||
}
|
||||
23
backend/.sqlx/query-a875cb56485b812e9d4739afd0915067f7e5abe0ca0adf264b792fccf21e005b.json
generated
Normal file
23
backend/.sqlx/query-a875cb56485b812e9d4739afd0915067f7e5abe0ca0adf264b792fccf21e005b.json
generated
Normal 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"
|
||||
}
|
||||
15
backend/.sqlx/query-a96dd57b127a1adbdca13867a76d7fd9f9a9c76b02bcebd2830d928821f3abc0.json
generated
Normal file
15
backend/.sqlx/query-a96dd57b127a1adbdca13867a76d7fd9f9a9c76b02bcebd2830d928821f3abc0.json
generated
Normal 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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
23
backend/.sqlx/query-d4fb94ee8198592c24e85d29078feb5220ab367e339510ee0e83bb7b5abfd184.json
generated
Normal file
23
backend/.sqlx/query-d4fb94ee8198592c24e85d29078feb5220ab367e339510ee0e83bb7b5abfd184.json
generated
Normal 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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
318
backend/Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1 +1 @@
|
||||
d414242f92cfaf9f364e9aeb321a511c3ad46fa7
|
||||
e9917f62ebc48bdd93f9ddda5a58ba6ce8a74ed9
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
Binary file not shown.
@@ -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?;
|
||||
|
||||
@@ -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
|
||||
|
||||
20
backend/tests/fixtures/schedule.sql
vendored
20
backend/tests/fixtures/schedule.sql
vendored
@@ -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'
|
||||
);
|
||||
@@ -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(),
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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: _,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>>,
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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>) {
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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<(
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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> },
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -37,7 +37,6 @@ mount {
|
||||
src: "/lib64"
|
||||
dst: "/lib64"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
mount {
|
||||
|
||||
@@ -38,7 +38,6 @@ mount {
|
||||
src: "/lib64"
|
||||
dst: "/lib64"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ mount {
|
||||
src: "/lib64"
|
||||
dst: "/lib64"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ mount {
|
||||
src: "/lib64"
|
||||
dst: "/lib64"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ mount {
|
||||
src: "/lib64"
|
||||
dst: "/lib64"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ mount {
|
||||
src: "/lib64"
|
||||
dst: "/lib64"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ mount {
|
||||
src: "/lib64"
|
||||
dst: "/lib64"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"]
|
||||
12
frontend/package-lock.json
generated
12
frontend/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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'}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user