Compare commits
19 Commits
rf/orderBy
...
hc/signed-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24bf1f1493 | ||
|
|
a34ed9a24d | ||
|
|
fc261b4ac1 | ||
|
|
ed6627032e | ||
|
|
577c748b55 | ||
|
|
62b0925db9 | ||
|
|
5409b280c3 | ||
|
|
dea74c0846 | ||
|
|
31d15d6d33 | ||
|
|
5c0da7f7bd | ||
|
|
0c5abc1f2f | ||
|
|
c8cc52d840 | ||
|
|
4b9cc2ec28 | ||
|
|
091a4717f4 | ||
|
|
cb7c83c374 | ||
|
|
51eb0be80a | ||
|
|
6fcdf2af2e | ||
|
|
38d8cf842a | ||
|
|
0eff6b139d |
2
.env
2
.env
@@ -10,4 +10,4 @@ WM_IMAGE=ghcr.io/windmill-labs/windmill:main
|
||||
|
||||
# To rotate logs, set the following variables:
|
||||
#LOG_MAX_SIZE=10m
|
||||
#LOG_MAX_FILE=3
|
||||
#LOG_MAX_FILE=3
|
||||
|
||||
2
.github/change-versions.sh
vendored
2
.github/change-versions.sh
vendored
@@ -24,4 +24,4 @@ sed -i -e "/^wmill_pg =/s/= .*/= \">=$VERSION\"/" ${root_dirpath}/lsp/Pipfile
|
||||
|
||||
sed -i -zE "s/name = \"windmill\"\nversion = \"[^\"]*\"\\n(.*)/name = \"windmill\"\nversion = \"$VERSION\"\\n\\1/" ${root_dirpath}/backend/Cargo.lock
|
||||
|
||||
cd ${root_dirpath}/frontend && npm i --package-lock-only --ignore-scripts
|
||||
cd ${root_dirpath}/frontend && npm i --package-lock-only
|
||||
|
||||
7
.github/workflows/build-publish-rh-image.yml
vendored
7
.github/workflows/build-publish-rh-image.yml
vendored
@@ -64,7 +64,7 @@ jobs:
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
@@ -111,7 +111,8 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: RHEL9-amd64 build
|
||||
path: ${{ steps.extract-ee-amd64.outputs.destination
|
||||
path:
|
||||
${{ steps.extract-ee-amd64.outputs.destination
|
||||
}}/windmill-ee-amd64-rhel9
|
||||
|
||||
# - uses: actions/upload-artifact@v4
|
||||
|
||||
2
.github/workflows/build_windows_worker_.yml
vendored
2
.github/workflows/build_windows_worker_.yml
vendored
@@ -51,7 +51,7 @@ jobs:
|
||||
$env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static"
|
||||
mkdir frontend/build && cd backend
|
||||
New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force
|
||||
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,nats,sqs_trigger,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,mcp
|
||||
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,nats,sqs_trigger,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
- name: Rename binary with corresponding architecture
|
||||
run: |
|
||||
Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe"
|
||||
|
||||
2
.github/workflows/docker-image-rpi4.yml
vendored
2
.github/workflows/docker-image-rpi4.yml
vendored
@@ -67,7 +67,7 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=embedding,parquet,openidconnect,license,http_trigger,zip,oauth2,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp
|
||||
features=embedding,parquet,openidconnect,license,http_trigger,zip,oauth2,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
|
||||
${{ steps.meta-public.outputs.tags }}
|
||||
|
||||
26
.github/workflows/docker-image.yml
vendored
26
.github/workflows/docker-image.yml
vendored
@@ -1,8 +1,10 @@
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.event_name != 'pull_request' && github.event_name !=
|
||||
IMAGE_NAME:
|
||||
${{ github.event_name != 'pull_request' && github.event_name !=
|
||||
'workflow_dispatch' && github.repository || 'windmill-labs/windmill-test' }}
|
||||
DEV_SHA: ${{ github.event_name != 'pull_request' && github.event_name !=
|
||||
DEV_SHA:
|
||||
${{ github.event_name != 'pull_request' && github.event_name !=
|
||||
'workflow_dispatch' && 'dev' || github.event.inputs.tag || github.sha }}
|
||||
name: Build windmill:main
|
||||
on:
|
||||
@@ -38,7 +40,8 @@ permissions: write-all
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubicloud
|
||||
if: (github.event_name != 'workflow_dispatch') || (github.event.inputs &&
|
||||
if:
|
||||
(github.event_name != 'workflow_dispatch') || (github.event.inputs &&
|
||||
!github.event.inputs.ee)
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -92,7 +95,7 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=embedding,parquet,openidconnect,jemalloc,license,http_trigger,zip,oauth2,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,agent_worker_server,all_languages,deno_core,mcp
|
||||
features=embedding,parquet,openidconnect,jemalloc,license,http_trigger,zip,oauth2,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,agent_worker_server,all_languages
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
|
||||
${{ steps.meta-public.outputs.tags }}
|
||||
@@ -154,7 +157,7 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,agent_worker_server,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,agent_worker_server,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
|
||||
${{ steps.meta-ee-public.outputs.tags }}
|
||||
@@ -312,7 +315,7 @@ jobs:
|
||||
needs: [run_integration_test, build]
|
||||
if:
|
||||
github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' ||
|
||||
startsWith(github.ref, 'refs/tags/v')) && (github.event_name != 'workflow_dispatch')
|
||||
startsWith(github.ref, 'refs/tags/v')) && (github.event_name != 'workflow_dispatch')
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -352,7 +355,7 @@ jobs:
|
||||
verify_ee_image_vulnerabilities:
|
||||
runs-on: ubicloud
|
||||
needs: [tag_latest_ee]
|
||||
if: startsWith(github.ref, 'refs/tags/v') && (github.event_name != 'workflow_dispatch')
|
||||
if: startsWith(github.ref, 'refs/tags/v') && (github.event_name != 'workflow_dispatch')
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@@ -394,7 +397,8 @@ jobs:
|
||||
build_ee_nsjail:
|
||||
needs: [build_ee]
|
||||
runs-on: ubicloud
|
||||
if: (github.event_name != 'pull_request') && ((github.event_name != 'workflow_dispatch') || (github.event.inputs.ee || github.event.inputs.nsjail))
|
||||
if:
|
||||
(github.event_name != 'pull_request') && ((github.event_name != 'workflow_dispatch') || (github.event.inputs.ee || github.event.inputs.nsjail))
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -433,7 +437,7 @@ jobs:
|
||||
run: |
|
||||
sed -i 's|FROM ghcr.io/windmill-labs/windmill-ee:dev|FROM ghcr.io/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}|' ./docker/DockerfileNsjail
|
||||
cat ./docker/DockerfileNsjail | grep "FROM"
|
||||
|
||||
|
||||
- name: Build and push publicly ee
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
@@ -447,10 +451,12 @@ jobs:
|
||||
${{ steps.meta-ee-public.outputs.labels }}
|
||||
org.opencontainers.image.licenses=Windmill-Enterprise-License
|
||||
|
||||
|
||||
publish_ecr_s3:
|
||||
needs: [build_ee_nsjail]
|
||||
runs-on: ubicloud-standard-2-arm
|
||||
if: (github.event_name != 'pull_request') && (github.event_name !=
|
||||
if:
|
||||
(github.event_name != 'pull_request') && (github.event_name !=
|
||||
'workflow_dispatch')
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
|
||||
2
.github/workflows/publish_windows_worker.yml
vendored
2
.github/workflows/publish_windows_worker.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
||||
$env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static"
|
||||
mkdir frontend/build && cd backend
|
||||
New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force
|
||||
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages,mcp
|
||||
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages
|
||||
- name: Rename binary with corresponding architecture
|
||||
run: |
|
||||
Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe"
|
||||
|
||||
167
CHANGELOG.md
167
CHANGELOG.md
@@ -1,172 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [1.486.1](https://github.com/windmill-labs/windmill/compare/v1.486.0...v1.486.1) (2025-05-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve MultiSelectWrapper behavior ([36da8ae](https://github.com/windmill-labs/windmill/commit/36da8aec080742e13f23e1dee12b3954947f53dd))
|
||||
|
||||
## [1.486.0](https://github.com/windmill-labs/windmill/compare/v1.485.3...v1.486.0) (2025-05-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add run now directly on schedule drawer and duplicate schedule option ([#5674](https://github.com/windmill-labs/windmill/issues/5674)) ([dfb947f](https://github.com/windmill-labs/windmill/commit/dfb947ff37c688f54a32de5aa3c5c3d142cb80f4))
|
||||
* Database Manager ([#5586](https://github.com/windmill-labs/windmill/issues/5586)) ([41c15fc](https://github.com/windmill-labs/windmill/commit/41c15fc78aaf844c559d3d6c772e04ecce436e9d))
|
||||
* Integrate MCP with hub ([#5685](https://github.com/windmill-labs/windmill/issues/5685)) ([ec701a9](https://github.com/windmill-labs/windmill/commit/ec701a9ee74c9d890b54234362392deca63a77c7))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Ai Chat: do not send tools if empty + respond even if tool fails ([#5692](https://github.com/windmill-labs/windmill/issues/5692)) ([9c55040](https://github.com/windmill-labs/windmill/commit/9c55040e47e76af8b7e2864b82fa30505545dcb5))
|
||||
* do not track relative deps for scripts with raw defined deps from CLI ([#5696](https://github.com/windmill-labs/windmill/issues/5696)) ([7eb9d7d](https://github.com/windmill-labs/windmill/commit/7eb9d7d46cb48ae69a3fd3ff852a57abae450a3b))
|
||||
* improve CLI file scanning performances ([0916978](https://github.com/windmill-labs/windmill/commit/09169784bd2d0ab7acf5f40dc86f36f1cae967b7))
|
||||
|
||||
## [1.485.3](https://github.com/windmill-labs/windmill/compare/v1.485.2...v1.485.3) (2025-04-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve performance of background cleanup monitoring operations ([18dced3](https://github.com/windmill-labs/windmill/commit/18dced3c748cd5305f0934b26e50d69899563723))
|
||||
|
||||
## [1.485.2](https://github.com/windmill-labs/windmill/compare/v1.485.1...v1.485.2) (2025-04-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve agent workers for deployed scripts ([60018aa](https://github.com/windmill-labs/windmill/commit/60018aadf62cecadf111e019d3600513a89810f1))
|
||||
* make `#(extra_)requirements:` work better with pins ([#5680](https://github.com/windmill-labs/windmill/issues/5680)) ([1ab4160](https://github.com/windmill-labs/windmill/commit/1ab41603f4fd1526d0c944396ef250b184aed1f4))
|
||||
* **python:** handle better relative imports with requirements or extra_requirements ([f662cf5](https://github.com/windmill-labs/windmill/commit/f662cf5d75beed8fd114ba171cbe0fa8e4b2773f))
|
||||
|
||||
## [1.485.1](https://github.com/windmill-labs/windmill/compare/v1.485.0...v1.485.1) (2025-04-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve mcp mode api ([cf77ff0](https://github.com/windmill-labs/windmill/commit/cf77ff088b8382b861113120589de58f7cf241d0))
|
||||
* MCP handle long names + invalid char in prop key + fix for not found resource type ([#5668](https://github.com/windmill-labs/windmill/issues/5668)) ([eadae95](https://github.com/windmill-labs/windmill/commit/eadae95a42d679bf8792bdefd8b9d19dbcbc4b57))
|
||||
* skip_flow_update for dependency tracking table ([#5670](https://github.com/windmill-labs/windmill/issues/5670)) ([35b69da](https://github.com/windmill-labs/windmill/commit/35b69da25c5bd17deff5a54b635e9150cb865cc0))
|
||||
|
||||
## [1.485.0](https://github.com/windmill-labs/windmill/compare/v1.484.0...v1.485.0) (2025-04-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add universal search to object viewer ([7254743](https://github.com/windmill-labs/windmill/commit/72547437fead0a071fceac27dae8628cdcae6a3e))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add svelte 5 boundaries to app components to contain errors ([1b16918](https://github.com/windmill-labs/windmill/commit/1b1691837a7e6b88afbacf7d88c14ca5e475b493))
|
||||
* Fix object handling on some MCP clients + better frontend for MCP ([#5663](https://github.com/windmill-labs/windmill/issues/5663)) ([12c3202](https://github.com/windmill-labs/windmill/commit/12c32026e5879a65fc0f1cc9f2481087c4b95111))
|
||||
|
||||
## [1.484.0](https://github.com/windmill-labs/windmill/compare/v1.483.2...v1.484.0) (2025-04-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add MCP endpoints ([#5639](https://github.com/windmill-labs/windmill/issues/5639)) ([a34ac4f](https://github.com/windmill-labs/windmill/commit/a34ac4fa24c2a5482e45724e76316d57f64f7040))
|
||||
* Add MCP only mode ([#5661](https://github.com/windmill-labs/windmill/issues/5661)) ([1625524](https://github.com/windmill-labs/windmill/commit/162552431138d68002c7060cad4ae31f1ec4c69c))
|
||||
* Ansible improvements (vault, roles and git repos) ([#5655](https://github.com/windmill-labs/windmill/issues/5655)) ([fdd1642](https://github.com/windmill-labs/windmill/commit/fdd1642ce10866da1d8d373bda44f050e2e0f403))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* check for valid teams_channel config when saving critical alerts settings ([#5660](https://github.com/windmill-labs/windmill/issues/5660)) ([dc5c8d8](https://github.com/windmill-labs/windmill/commit/dc5c8d8c5f8577b7ded3da1d684cdb735fa7a936))
|
||||
* Fix CI for MCP + optimization ([#5657](https://github.com/windmill-labs/windmill/issues/5657)) ([b199a77](https://github.com/windmill-labs/windmill/commit/b199a77d486c5bfd086ca73a58a14bb747e386b5))
|
||||
* fix token creation after mcp mode change to make it non workspace specific ([2b5dfcf](https://github.com/windmill-labs/windmill/commit/2b5dfcfb251471dcc39b04c54e25008d617cc34f))
|
||||
* improve full-scaleout of autoscaling event logging ([8435eb3](https://github.com/windmill-labs/windmill/commit/8435eb3adff8429a73db88b12204f7cf8f14d3d2))
|
||||
* improve skip failure on parallel branchall ([a7b2b51](https://github.com/windmill-labs/windmill/commit/a7b2b51444d757964560de3a89024b1c9b0fefe9))
|
||||
|
||||
## [1.483.2](https://github.com/windmill-labs/windmill/compare/v1.483.1...v1.483.2) (2025-04-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* batch reruns query missing workspace_id check in subquery ([#5652](https://github.com/windmill-labs/windmill/issues/5652)) ([444a6ab](https://github.com/windmill-labs/windmill/commit/444a6abad670114c52e44f3606bf6fefc5d3fd98))
|
||||
* **frontend:** fix validity check ([#5654](https://github.com/windmill-labs/windmill/issues/5654)) ([c41c1eb](https://github.com/windmill-labs/windmill/commit/c41c1eb587bf22364f1202310a0c64b6040ab968))
|
||||
* improve MySQL datetime parser timezone handling (WIN-1155) ([#5645](https://github.com/windmill-labs/windmill/issues/5645)) ([5bca8f6](https://github.com/windmill-labs/windmill/commit/5bca8f60e970cc67839edb5dc491685f36cf0499))
|
||||
* track relative imports in python and ts even if lockfile is provided ([e316dbd](https://github.com/windmill-labs/windmill/commit/e316dbd9bdd5c59e9aaba6a4472bb7d832834e84))
|
||||
|
||||
## [1.483.1](https://github.com/windmill-labs/windmill/compare/v1.483.0...v1.483.1) (2025-04-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* pin libxml to 0.3.3 ([e5595e4](https://github.com/windmill-labs/windmill/commit/e5595e41b5c87704d814eff95bc00b82195728ba))
|
||||
|
||||
## [1.483.0](https://github.com/windmill-labs/windmill/compare/v1.482.1...v1.483.0) (2025-04-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* handle different aws auth resource type ([#5637](https://github.com/windmill-labs/windmill/issues/5637)) ([5b123b0](https://github.com/windmill-labs/windmill/commit/5b123b01a1318208450789b5bcade447a0b331c7))
|
||||
* oidc support for sqs trigger ([#5614](https://github.com/windmill-labs/windmill/issues/5614)) ([34b307b](https://github.com/windmill-labs/windmill/commit/34b307b2be1f6cf92a81694325f4c333bdd7b055))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix click outside popover fullscreen ([#5631](https://github.com/windmill-labs/windmill/issues/5631)) ([0811457](https://github.com/windmill-labs/windmill/commit/081145726a5d4ab81510a7637126a036823a1565))
|
||||
* improve flow editor step switch performance ([58fa4c8](https://github.com/windmill-labs/windmill/commit/58fa4c80062a5704bbd13ddda1b2f00c7c9e40dd))
|
||||
* linter in early stop doesn't include flow_input ([#5638](https://github.com/windmill-labs/windmill/issues/5638)) ([6a9bdfd](https://github.com/windmill-labs/windmill/commit/6a9bdfd3bd52ff802b6a71c3ae9504bfe7d0421f))
|
||||
* output picker output opening doesn't change id ([#5641](https://github.com/windmill-labs/windmill/issues/5641)) ([64c72b6](https://github.com/windmill-labs/windmill/commit/64c72b6fce669e47f04dc620750840857cbe66cf))
|
||||
|
||||
## [1.482.1](https://github.com/windmill-labs/windmill/compare/v1.482.0...v1.482.1) (2025-04-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* flow editor workspace script test use actual workspace script hash ([24e893b](https://github.com/windmill-labs/windmill/commit/24e893b8c50fafdb41f4b6e1777cb34aceafc466))
|
||||
* **frontend:** postgres remove selectedTable ([#5386](https://github.com/windmill-labs/windmill/issues/5386)) ([bd7c6a2](https://github.com/windmill-labs/windmill/commit/bd7c6a2a46047de5fe89753decdfdf1f4851ee3f))
|
||||
* **openapi:** fix openapi def of batch re-run jobs ([cb8731e](https://github.com/windmill-labs/windmill/commit/cb8731e7e37fb6cd052f5dae6fdce46e6ca2409c))
|
||||
* show workspace color if superadmin and not in workspace + change workspace name when switching workspace ([#5625](https://github.com/windmill-labs/windmill/issues/5625)) ([cc4384f](https://github.com/windmill-labs/windmill/commit/cc4384f48cc89f883237a2082d854d69a7b5dc56))
|
||||
|
||||
## [1.482.0](https://github.com/windmill-labs/windmill/compare/v1.481.0...v1.482.0) (2025-04-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add diff toggle to flow inline scripts ([#5550](https://github.com/windmill-labs/windmill/issues/5550)) ([b3ecde3](https://github.com/windmill-labs/windmill/commit/b3ecde3316252bcd7323de98149786349019ba7e))
|
||||
* add gcp trigger ([#5501](https://github.com/windmill-labs/windmill/issues/5501)) ([6339775](https://github.com/windmill-labs/windmill/commit/63397754046eed41d32e28d4698db37b4c9b9710))
|
||||
* add wildcards filter for worker/label/tags ([62f14d1](https://github.com/windmill-labs/windmill/commit/62f14d1cb95e3f1c7de85c46e1c6bb092247656c))
|
||||
* add windmill context to autocomplete ([#5548](https://github.com/windmill-labs/windmill/issues/5548)) ([b47c151](https://github.com/windmill-labs/windmill/commit/b47c15165f93ca68a58f81cf2b86fc9467155482))
|
||||
* agent workers v2 using http ([#5588](https://github.com/windmill-labs/windmill/issues/5588)) ([63fa499](https://github.com/windmill-labs/windmill/commit/63fa4990153f33434b49269922f7803d04e407cd))
|
||||
* Batch re-run ([#5553](https://github.com/windmill-labs/windmill/issues/5553)) ([26b5ea5](https://github.com/windmill-labs/windmill/commit/26b5ea5023a100c57d077910c99a5e5703edf1c1))
|
||||
* **frontend:** app editor code input component (monaco) ([#5566](https://github.com/windmill-labs/windmill/issues/5566)) ([177e16b](https://github.com/windmill-labs/windmill/commit/177e16bb18eed0d1c454b967aaa59547f61e8d26))
|
||||
* handle sending selected lines to ai context ([#5527](https://github.com/windmill-labs/windmill/issues/5527)) ([5abdc3e](https://github.com/windmill-labs/windmill/commit/5abdc3e4403b5c604309bd99a24d7a2847a17b9b))
|
||||
* Implement sending diff to ai ([#5510](https://github.com/windmill-labs/windmill/issues/5510)) ([e118d2c](https://github.com/windmill-labs/windmill/commit/e118d2cd5f9c641884a76229802a5228ef41f1a5))
|
||||
* make azure a standalone AI provider ([#5558](https://github.com/windmill-labs/windmill/issues/5558)) ([2c5e58c](https://github.com/windmill-labs/windmill/commit/2c5e58cf1ab9225d516540b38d9e4dde482a3a7f))
|
||||
* migrate to svelte5 + vite6 ([#4813](https://github.com/windmill-labs/windmill/issues/4813)) ([3c99b3f](https://github.com/windmill-labs/windmill/commit/3c99b3fdc7b78b1cdc7d8fb21d999296695f7889))
|
||||
* **postgres-trigger:** postgres trigger fix circular dependencies and add remove associate resource ([#5606](https://github.com/windmill-labs/windmill/issues/5606)) ([1daeb2f](https://github.com/windmill-labs/windmill/commit/1daeb2f48f3026621b3ffc58e10f048d5911906c))
|
||||
* **python:** per import requirement pin ([#5520](https://github.com/windmill-labs/windmill/issues/5520)) ([0b6d017](https://github.com/windmill-labs/windmill/commit/0b6d017fedc31e790a76cf29a1adaaf2a72acc61))
|
||||
* signed s3 objects ([#5593](https://github.com/windmill-labs/windmill/issues/5593)) ([b9e8796](https://github.com/windmill-labs/windmill/commit/b9e879618bc223ce17effde8bb4c5d1df2ad6df5))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add support for ${} syntax without default in bash ([#5594](https://github.com/windmill-labs/windmill/issues/5594)) ([3950cfd](https://github.com/windmill-labs/windmill/commit/3950cfd7e3297d7f8ec56430d6462f6b67ecd3c2))
|
||||
* app editor svelte 5 fixes ([#5570](https://github.com/windmill-labs/windmill/issues/5570)) ([b926076](https://github.com/windmill-labs/windmill/commit/b9260769883348ecd5aeb5684f527a8bf0073928))
|
||||
* binding not working in nested array script arg ([#5585](https://github.com/windmill-labs/windmill/issues/5585)) ([f5d46d5](https://github.com/windmill-labs/windmill/commit/f5d46d5751bc875b7f4da1db06be40571ac55ab8))
|
||||
* **cli:** properly handle enabled/disabled updates of schedules ([2629458](https://github.com/windmill-labs/windmill/commit/26294584d6c2ca02bbc4fc5f28cb8df6a5fb3790))
|
||||
* **cli:** wmill-locks improvement ([8d062c4](https://github.com/windmill-labs/windmill/commit/8d062c47ecd9e84a81140d5c59814da9217dd434))
|
||||
* Dynamic select does not work with tag //native ([#5576](https://github.com/windmill-labs/windmill/issues/5576)) ([1f3e7d9](https://github.com/windmill-labs/windmill/commit/1f3e7d9029051832db6ab1755b3cad38176a9e96)), closes [#5490](https://github.com/windmill-labs/windmill/issues/5490)
|
||||
* fix list jobs by tag ([0c3cb37](https://github.com/windmill-labs/windmill/commit/0c3cb3700a3fb9b69e396487bd7491dbbd8861c0))
|
||||
* flow editor svelte 5 issues ([#5567](https://github.com/windmill-labs/windmill/issues/5567)) ([4f6be6e](https://github.com/windmill-labs/windmill/commit/4f6be6ed340e26bf1ed95398a9dc9f1eb41b33dd))
|
||||
* freeze when clicking script history diff button ([#5581](https://github.com/windmill-labs/windmill/issues/5581)) ([07094b6](https://github.com/windmill-labs/windmill/commit/07094b6aa21f10688b138d2a81d4fd5833f003fc))
|
||||
* **frontend:** app builder - force json configuration in rich result ([#5565](https://github.com/windmill-labs/windmill/issues/5565)) ([6fae3a5](https://github.com/windmill-labs/windmill/commit/6fae3a566be06dae88ece8ec23f5723cd8f3f2b9))
|
||||
* **frontend:** load all step jobs ([#5617](https://github.com/windmill-labs/windmill/issues/5617)) ([16bed59](https://github.com/windmill-labs/windmill/commit/16bed593dfd0b735a92d0928df5091547b98ae79))
|
||||
* **frontend:** prevent deploy popover to show if deploy dropdown is open ([#5542](https://github.com/windmill-labs/windmill/issues/5542)) ([c2180c6](https://github.com/windmill-labs/windmill/commit/c2180c6eb34e14fe2292ff40aa6a99c627698d5e))
|
||||
* **frontend:** proper each block binding + better app settings reactivity ([#5568](https://github.com/windmill-labs/windmill/issues/5568)) ([4c71af8](https://github.com/windmill-labs/windmill/commit/4c71af8a74627d0ba76917e0dac0ac9e5e984cca))
|
||||
* improve app image picker UX ([#5589](https://github.com/windmill-labs/windmill/issues/5589)) ([f497a4b](https://github.com/windmill-labs/windmill/commit/f497a4bfae8d1bff097e0c2c9df8381a531dfeb9))
|
||||
* legacy script gen model selection ([#5574](https://github.com/windmill-labs/windmill/issues/5574)) ([3507925](https://github.com/windmill-labs/windmill/commit/3507925624a43804a3be463b6f7913cea5821384))
|
||||
* mssql ca_cert deserializing ([#5587](https://github.com/windmill-labs/windmill/issues/5587)) ([b4f8c88](https://github.com/windmill-labs/windmill/commit/b4f8c88c19bd4f844c3ecb53ececc340ee326b0e))
|
||||
* number input in app multiselect yields NOT_NUMBER ([#5616](https://github.com/windmill-labs/windmill/issues/5616)) ([4aae6ab](https://github.com/windmill-labs/windmill/commit/4aae6ab634280adc1de9abd890100b7c12c89158))
|
||||
* prevent invalid returned ai completion object errors ([#5564](https://github.com/windmill-labs/windmill/issues/5564)) ([9276c71](https://github.com/windmill-labs/windmill/commit/9276c717a21aaee3241845a9cc00d3fb6bce9eb9))
|
||||
* Remaining svelte 5 bugs ([#5563](https://github.com/windmill-labs/windmill/issues/5563)) ([6e9ec63](https://github.com/windmill-labs/windmill/commit/6e9ec6323c265a747ef8696865297e6d47abb016))
|
||||
* tenant id to never be undefined on teams ([#5572](https://github.com/windmill-labs/windmill/issues/5572)) ([102b58a](https://github.com/windmill-labs/windmill/commit/102b58a5f40dde22f15700d4b6c11eb7f3fbf4bb))
|
||||
* validate saved module before passing to flow module editor ([#5580](https://github.com/windmill-labs/windmill/issues/5580)) ([2eb1a16](https://github.com/windmill-labs/windmill/commit/2eb1a161d15627b440195b65eec54998561f4ef6))
|
||||
|
||||
## [1.481.0](https://github.com/windmill-labs/windmill/compare/v1.480.1...v1.481.0) (2025-04-02)
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
bind {$ADDRESS}
|
||||
reverse_proxy /ws/* http://lsp:3001
|
||||
# reverse_proxy /ws_mp/* http://multiplayer:3002
|
||||
# reverse_proxy /api/srch/* http://windmill_indexer:8002
|
||||
# reverse_proxy /api/srch/* http://windmill_indexer:8001
|
||||
reverse_proxy /* http://windmill_server:8000
|
||||
# tls /certs/cert.pem /certs/key.pem
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ FROM node:20-alpine as frontend
|
||||
# install dependencies
|
||||
WORKDIR /frontend
|
||||
COPY ./frontend/package.json ./frontend/package-lock.json ./
|
||||
COPY ./frontend/scripts/ ./scripts/
|
||||
RUN npm ci
|
||||
|
||||
# Copy all local files into the image.
|
||||
|
||||
@@ -363,7 +363,6 @@ you to have it being synced automatically everyday.
|
||||
| DENO_AUTH_TOKENS | None | Custom DENO_AUTH_TOKENS to pass to worker to allow the use of private modules | Worker |
|
||||
| DISABLE_RESPONSE_LOGS | false | Disable response logs | Server |
|
||||
| CREATE_WORKSPACE_REQUIRE_SUPERADMIN | true | If true, only superadmins can create new workspaces | Server |
|
||||
| MIN_FREE_DISK_SPACE_MB | 15000 | Minimum amount of free space on worker. Sends critical alert if worker has less free space. | Worker |
|
||||
|
||||
## Run a local dev setup
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n subscription_mode AS \"subscription_mode: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1\n ",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -45,71 +45,56 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "subscription_mode: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "gcp_subscription_mode",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"create_update",
|
||||
"existing"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 10,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 11,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 13,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 14,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 15,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 16,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -132,7 +117,6 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
@@ -140,5 +124,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "c2925a28212265bd9ac8e2d498b3588fc040c7a29ae6d9bab1f05af0b535e2ac"
|
||||
"hash": "0102308ffa1c0dbfba54d29246535bb81146a4cfae0ec408435570e0813a3bef"
|
||||
}
|
||||
16
backend/.sqlx/query-1182fe055306d7ea435d76b74d781e066915c8397e6bbc9e408ff3dda9fec27f.json
generated
Normal file
16
backend/.sqlx/query-1182fe055306d7ea435d76b74d781e066915c8397e6bbc9e408ff3dda9fec27f.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE script SET ws_error_handler_muted = $3 WHERE workspace_id = $2 AND path = $1 AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1182fe055306d7ea435d76b74d781e066915c8397e6bbc9e408ff3dda9fec27f"
|
||||
}
|
||||
22
backend/.sqlx/query-122090a0f89e5248a0a0f199ebd24582fdb302883aebd2da187ac0084e767ea3.json
generated
Normal file
22
backend/.sqlx/query-122090a0f89e5248a0a0f199ebd24582fdb302883aebd2da187ac0084e767ea3.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT set_config('session.pgroups', $1, true)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "set_config",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "122090a0f89e5248a0a0f199ebd24582fdb302883aebd2da187ac0084e767ea3"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n subscription_mode AS \"subscription_mode: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n gcp_trigger\n WHERE\n delivery_type != 'push'::DELIVERY_MODE AND\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n gcp_trigger\n WHERE\n delivery_type != 'push'::DELIVERY_MODE AND\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -45,71 +45,56 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "subscription_mode: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "gcp_subscription_mode",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"create_update",
|
||||
"existing"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 10,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 11,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 13,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 14,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 15,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 16,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -130,7 +115,6 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
@@ -138,5 +122,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2dcff8b16da75740c362015b2293f578a21813f038d75184a5034c37d4daf36e"
|
||||
"hash": "15fbe481789a7817bf37415fb935f9ed537fd7d3b266d928af4d5d3dd8bb5c18"
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT set_session_context($1, $2, $3, $4, $5, $6)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "set_session_context",
|
||||
"type_info": "Void"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Bool",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "18ddbb9eb4c0ec03e6170f8c70634546cddaf1029618ed14015fd7b0a7017441"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT \n workspace_id,\n path,\n url,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n filters AS \"filters: _\",\n initial_messages AS \"initial_messages: _\",\n url_runnable_args AS \"url_runnable_args: _\",\n can_return_message\n FROM \n websocket_trigger\n WHERE \n workspace_id = $1\n ",
|
||||
"query": "SELECT workspace_id, path, url, script_path, is_flow, edited_by, email, edited_at, server_id, last_server_ping, extra_perms, error, enabled, filters as \"filters: _\", initial_messages as \"initial_messages: _\", url_runnable_args as \"url_runnable_args: _\", can_return_message FROM websocket_trigger\n WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -114,5 +114,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "8eabeee5527da4aad3f46ad185015262b4304667449f3e1a71efd7477a39f7fc"
|
||||
"hash": "1aa8ead10f8d994f6685d266fcbd409b0fff43111d9600e64b2348401ed8929d"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT jsonb_build_object(\n 'kind', jb.kind,\n 'script_path', jb.runnable_path,\n 'latest_schema', COALESCE(\n (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.workspace_id = $1 AND s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),\n (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.workspace_id = $1 AND flow.path = jb.runnable_path AND jb.kind = 'flow')\n ),\n 'schemas', ARRAY(\n SELECT jsonb_build_object(\n 'script_hash', LPAD(TO_HEX(COALESCE(s.hash, f.id)), 16, '0'),\n 'job_ids', ARRAY_AGG(DISTINCT j.id),\n 'schema', (ARRAY_AGG(COALESCE(s.schema, f.schema)))[1]\n ) FROM v2_job j\n LEFT JOIN script s ON s.hash = j.runnable_id AND j.kind = 'script'\n LEFT JOIN flow_version f ON f.id = j.runnable_id AND j.kind = 'flow'\n WHERE j.id = ANY(ARRAY_AGG(jb.id))\n GROUP BY COALESCE(s.hash, f.id)\n )\n ) FROM v2_job jb\n WHERE (jb.kind = 'flow' OR jb.kind = 'script')\n AND jb.workspace_id = $1 AND jb.id = ANY($2)\n GROUP BY jb.kind, jb.runnable_path",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "jsonb_build_object",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "26761fbd7953416eb391de47b1694e0f4ab2bb96a6d838f1b1fdce4b58a8f5d4"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH inserted_job AS (\n INSERT INTO v2_job (id, workspace_id, raw_code, raw_lock, raw_flow, tag, parent_job,\n created_by, permissioned_as, runnable_id, runnable_path, args, kind, trigger,\n script_lang, same_worker, pre_run_error, permissioned_as_email, visible_to_owner,\n flow_innermost_root_job, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id,\n cache_ttl, priority, trigger_kind, script_entrypoint_override, preprocessed)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18,\n $19, $20, $21, $22, $23, $24, $25, $26,\n CASE WHEN $14::VARCHAR IS NOT NULL THEN 'schedule'::job_trigger_kind END,\n ($12::JSONB)->>'_ENTRYPOINT_OVERRIDE', $27)\n ),\n inserted_runtime AS (\n INSERT INTO v2_job_runtime (id, ping) VALUES ($1, null)\n ),\n inserted_job_perms AS (\n INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) \n values ($1, $32, $33, $34, $35, $36, $37, $2) \n ON CONFLICT (job_id) DO UPDATE SET email = $32, username = $33, is_admin = $34, is_operator = $35, folders = $36, groups = $37, workspace_id = $2\n )\n INSERT INTO v2_job_queue\n (workspace_id, id, running, scheduled_for, started_at, tag, priority)\n VALUES ($2, $1, $28, COALESCE($29, now()), CASE WHEN $27 THEN now() END, $30, $31)",
|
||||
"query": "INSERT INTO v2_job (id, workspace_id, raw_code, raw_lock, raw_flow, tag, parent_job,\n created_by, permissioned_as, runnable_id, runnable_path, args, kind, trigger,\n script_lang, same_worker, pre_run_error, permissioned_as_email, visible_to_owner,\n flow_innermost_root_job, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id,\n cache_ttl, priority, trigger_kind, script_entrypoint_override, preprocessed)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18,\n $19, $20, $21, $22, $23, $24, $25, $26,\n CASE WHEN $14::VARCHAR IS NOT NULL THEN 'schedule'::job_trigger_kind END,\n ($12::JSONB)->>'_ENTRYPOINT_OVERRIDE', $27)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -86,20 +86,10 @@
|
||||
"Varchar",
|
||||
"Int4",
|
||||
"Int2",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"Timestamptz",
|
||||
"Varchar",
|
||||
"Int2",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"JsonbArray",
|
||||
"TextArray"
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "cccdcb7fe7968eadfc04d8957a8e98b2f2d92a6d7f687a9dd5a70edb3d5a63e6"
|
||||
"hash": "29624682d687790dd199c4af759132d79fdb2982de111cb5fd43e3d9ecd0f15e"
|
||||
}
|
||||
22
backend/.sqlx/query-29fbc3a8c35845a997cd548ed417b9cc3c82d815d99b3d435adcfbb5a9246124.json
generated
Normal file
22
backend/.sqlx/query-29fbc3a8c35845a997cd548ed417b9cc3c82d815d99b3d435adcfbb5a9246124.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT set_config('session.folders_read', $1, true)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "set_config",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "29fbc3a8c35845a997cd548ed417b9cc3c82d815d99b3d435adcfbb5a9246124"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO app_version\n (app_id, value, created_by, raw_app)\n VALUES ($1, $2::text::json, $3, $4) RETURNING id",
|
||||
"query": "INSERT INTO app_version\n (app_id, value, created_by)\n VALUES ($1, $2::text::json, $3) RETURNING id",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -13,13 +13,12 @@
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Bool"
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "56264b88a9f428e79c6e531a0011e0dbed634f3b12dedaa5fee0362d670887f6"
|
||||
"hash": "2a3992b5e9abcfbb032d10e142d98efa969dae26a7242eb7ac12593ed5421ef3"
|
||||
}
|
||||
@@ -1,95 +1,80 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n aws_auth_resource_type AS \"aws_auth_resource_type: _\",\n aws_resource_path,\n message_attributes,\n queue_url,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n sqs_trigger\n WHERE \n workspace_id = $1\n ",
|
||||
"query": "SELECT * FROM sqs_trigger\n WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "aws_auth_resource_type: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "aws_auth_resource_type",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"oidc",
|
||||
"credentials"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "aws_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "message_attributes",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "queue_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"ordinal": 1,
|
||||
"name": "queue_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "aws_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "message_attributes",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 5,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 6,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 10,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 11,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 12,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -100,6 +85,7 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
@@ -109,8 +95,6 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
@@ -118,5 +102,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "a7df493316f632fc636e4c3c90bef4c98035b5dc808bb8379251c0b35d945ba0"
|
||||
"hash": "2b6c13191484b0c664f35e2c811082b00c44fd4a7e98a11b714133674a7b6da7"
|
||||
}
|
||||
@@ -1,95 +1,80 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n aws_auth_resource_type AS \"aws_auth_resource_type: _\",\n aws_resource_path,\n message_attributes,\n queue_url,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n sqs_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ",
|
||||
"query": "\n SELECT\n aws_resource_path,\n message_attributes,\n queue_url,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n sqs_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "aws_auth_resource_type: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "aws_auth_resource_type",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"oidc",
|
||||
"credentials"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "aws_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"ordinal": 1,
|
||||
"name": "message_attributes",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"ordinal": 2,
|
||||
"name": "queue_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"ordinal": 3,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"ordinal": 4,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"ordinal": 5,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 6,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 10,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 11,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 13,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 14,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -101,7 +86,6 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
@@ -119,5 +103,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "5237f9d3f57f4e799968a4a9630c984775d23c7a4dc1a7ad37528a46ab89d6bd"
|
||||
"hash": "2ef82fad8a6ccdc66228cfbce5393de351653ab9ac171fa0eea447c905440867"
|
||||
}
|
||||
27
backend/.sqlx/query-31df83e9eb6078e93ec5fe4168306caccb849db9e0f71d86da655b01c6a3e8d0.json
generated
Normal file
27
backend/.sqlx/query-31df83e9eb6078e93ec5fe4168306caccb849db9e0f71d86da655b01c6a3e8d0.json
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job_queue\n (workspace_id, id, running, scheduled_for, started_at, tag, priority)\n VALUES ($1, $2, $3, COALESCE($4, now()), CASE WHEN $3 THEN now() END, $5, $6) RETURNING id AS \"id!\"",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id!",
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Uuid",
|
||||
"Bool",
|
||||
"Timestamptz",
|
||||
"Varchar",
|
||||
"Int2"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "31df83e9eb6078e93ec5fe4168306caccb849db9e0f71d86da655b01c6a3e8d0"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt)\n VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)\n ON CONFLICT (hostname, log_ts) DO UPDATE SET ok_lines = log_file.ok_lines + $6, err_lines = log_file.err_lines + $7",
|
||||
"query": "INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -17,5 +17,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "92faee8714a45a403b623e04d789f7f99067a05e9dfe270223164db8a1df2e4b"
|
||||
"hash": "33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT s.hash as hash, dm.deployment_msg as deployment_msg \n FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash\n WHERE s.workspace_id = $1 AND s.path = $2\n ORDER by s.created_at DESC",
|
||||
"query": "SELECT s.hash as hash, dm.deployment_msg as deployment_msg \n FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash\n WHERE s.workspace_id = $1 AND s.path = $2\n ORDER by created_at DESC",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -25,5 +25,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "726e956cfcd3ac7c07abeecdf92cf0996efe7fa7b671ac2b3b000ead0ea307de"
|
||||
"hash": "362419eb262c83d6a98a0200b116e831ada60399fe5f55a56d930cc69aff2675"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT content FROM script WHERE path = $1 AND workspace_id = $2\n AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND\n workspace_id = $2)\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "content",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "443bd83bcea1d37c79cb080095343c98104529879f991c49585cd181e34aa827"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM metrics WHERE created_at < NOW() - INTERVAL '180 day'",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "45dde0dc2da12fe46b5975ed53b520fc4160c008c3d29fe7d99e609c0b6e3e6d"
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH worker_ids AS (SELECT unnest($1::text[]) as worker)\n SELECT worker_ids.worker FROM worker_ids\n LEFT JOIN worker_ping ON worker_ids.worker = worker_ping.worker\n WHERE worker_ping.worker IS NULL OR worker_ping.ping_at < now() - ('60 seconds')::interval",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "worker",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55"
|
||||
}
|
||||
22
backend/.sqlx/query-6199e8be5cb13db71108e555ea20f0b76dc38476670f9fc0667b057d2766d42e.json
generated
Normal file
22
backend/.sqlx/query-6199e8be5cb13db71108e555ea20f0b76dc38476670f9fc0667b057d2766d42e.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT set_config('session.groups', $1, true)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "set_config",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "6199e8be5cb13db71108e555ea20f0b76dc38476670f9fc0667b057d2766d42e"
|
||||
}
|
||||
22
backend/.sqlx/query-6c90d5ea2a09b47b81fdab465062c25f8768b220e53bf469550a3a3697ab756a.json
generated
Normal file
22
backend/.sqlx/query-6c90d5ea2a09b47b81fdab465062c25f8768b220e53bf469550a3a3697ab756a.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT set_config('session.user', $1, true)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "set_config",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "6c90d5ea2a09b47b81fdab465062c25f8768b220e53bf469550a3a3697ab756a"
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO sqs_trigger (\n aws_auth_resource_type,\n aws_resource_path,\n queue_url,\n message_attributes,\n workspace_id, \n path, \n script_path, \n is_flow, \n email, \n enabled, \n edited_by\n ) \n VALUES (\n $1, \n $2, \n $3, \n $4, \n $5, \n $6, \n $7,\n $8,\n $9,\n $10,\n $11\n )",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
{
|
||||
"Custom": {
|
||||
"name": "aws_auth_resource_type",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"oidc",
|
||||
"credentials"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"TextArray",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "776909d3452aaf9e0ed41a5ec314b7bfc5bc8b6a76c98bf4daf7d715d18c52e6"
|
||||
}
|
||||
21
backend/.sqlx/query-7d78efab0a588f56a13a7b5251f0a72f5d341b053218e7aec83a834cf7ccc98f.json
generated
Normal file
21
backend/.sqlx/query-7d78efab0a588f56a13a7b5251f0a72f5d341b053218e7aec83a834cf7ccc98f.json
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) \n values ($1, $2, $3, $4, $5, $6, $7, $8) \n ON CONFLICT (job_id) DO UPDATE SET email = $2, username = $3, is_admin = $4, is_operator = $5, folders = $6, groups = $7, workspace_id = $8",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"JsonbArray",
|
||||
"TextArray",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7d78efab0a588f56a13a7b5251f0a72f5d341b053218e7aec83a834cf7ccc98f"
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT \n active_pid \n FROM \n pg_replication_slots \n WHERE \n slot_name = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "active_pid",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Name"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "7e64ba7e2362cc19d2aed9f34c9879983922e96a9baab7c1a2b09ed2b1c261e2"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO app_version\n (app_id, value, created_by, raw_app)\n VALUES ($1, $2::text::json, $3, $4) RETURNING id",
|
||||
"query": "INSERT INTO app_version\n (app_id, value, created_by)\n VALUES ($1, $2::text::json, $3) RETURNING id",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -13,13 +13,12 @@
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Bool"
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "a9f6e5720f748418ae52cf0e31778b9bbef26008803f2dd2a043bdb091b2ff20"
|
||||
"hash": "83d18f4e1cda2c1867168551d855d5626e766b5469c6976c986bdafc8b9407c5"
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE script \n SET ws_error_handler_muted = $3 \n WHERE ctid = (\n SELECT ctid FROM script\n WHERE path = $1 AND workspace_id = $2\n ORDER BY created_at DESC\n LIMIT 1\n )\n",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "848c8371eeb17ebd4b36a33f7d8a61eb8f07c54d291bb857ddd41a549cbc88dd"
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n sqs_trigger \n SET \n aws_auth_resource_type = $1,\n aws_resource_path = $2,\n queue_url = $3,\n message_attributes = $4, \n is_flow = $5, \n edited_by = $6, \n email = $7,\n script_path = $8,\n path = $9,\n edited_at = now(), \n error = NULL,\n server_id = NULL\n WHERE \n workspace_id = $10 AND \n path = $11\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
{
|
||||
"Custom": {
|
||||
"name": "aws_auth_resource_type",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"oidc",
|
||||
"credentials"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"TextArray",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8490024b96aa689d3c1bc5cbf94fcd8c5491732818404fee2397f00cccee0ad3"
|
||||
}
|
||||
22
backend/.sqlx/query-9897705b7c265333559df757a725711a40924ec551a7589a64ee96f8aa7f6a21.json
generated
Normal file
22
backend/.sqlx/query-9897705b7c265333559df757a725711a40924ec551a7589a64ee96f8aa7f6a21.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT set_config('session.folders_write', $1, true)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "set_config",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "9897705b7c265333559df757a725711a40924ec551a7589a64ee96f8aa7f6a21"
|
||||
}
|
||||
23
backend/.sqlx/query-9b9bc21023f10a0b4bb45879c7da8e119bffd2982b97fc900358506a0a14bbb8.json
generated
Normal file
23
backend/.sqlx/query-9b9bc21023f10a0b4bb45879c7da8e119bffd2982b97fc900358506a0a14bbb8.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO sqs_trigger (\n aws_resource_path,\n queue_url,\n message_attributes,\n workspace_id, \n path, \n script_path, \n is_flow, \n email, \n enabled, \n edited_by\n ) \n VALUES (\n $1, \n $2, \n $3, \n $4, \n $5, \n $6, \n $7,\n $8,\n $9,\n $10\n )",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"TextArray",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "9b9bc21023f10a0b4bb45879c7da8e119bffd2982b97fc900358506a0a14bbb8"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT content FROM script WHERE path = $1 AND workspace_id = $2 AND archived = false ORDER BY created_at DESC LIMIT 1",
|
||||
"query": "SELECT content FROM script WHERE path = $1 AND workspace_id = $2 AND\n created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND archived = false AND workspace_id = $2)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -19,5 +19,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "b5860f6a7672a368d740dcd367a8d5ab98fa93e0382a57a698564695db6c40ac"
|
||||
"hash": "a17260a1f1ee02e786690994d98c84ddf81e2eeb883f895c9cfc47e144d422cb"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH to_update AS (\n SELECT q.id, q.workspace_id, r.ping, COALESCE(zjc.counter, 0) as counter\n FROM v2_job_queue q\n JOIN v2_job j ON j.id = q.id\n JOIN v2_job_runtime r ON r.id = j.id\n LEFT JOIN zombie_job_counter zjc ON zjc.job_id = q.id\n WHERE ping < now() - ($1 || ' seconds')::interval\n AND running = true\n AND kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow')\n AND same_worker = false\n AND (zjc.counter IS NULL OR zjc.counter <= $2)\n FOR UPDATE of q SKIP LOCKED\n ),\n zombie_jobs AS (\n UPDATE v2_job_queue q\n SET running = false, started_at = null\n FROM to_update tu\n WHERE q.id = tu.id AND (tu.counter IS NULL OR tu.counter < $2)\n RETURNING q.id, q.workspace_id, ping, tu.counter\n ),\n update_ping AS (\n UPDATE v2_job_runtime r\n SET ping = null\n FROM zombie_jobs zj\n WHERE r.id = zj.id\n ),\n increment_counter AS (\n INSERT INTO zombie_job_counter (job_id, counter)\n SELECT id, 1 FROM to_update WHERE counter < $2\n ON CONFLICT (job_id) DO UPDATE\n SET counter = zombie_job_counter.counter + 1\n ),\n update_concurrency AS (\n UPDATE concurrency_counter cc\n SET job_uuids = job_uuids - zj.id::text\n FROM zombie_jobs zj\n INNER JOIN concurrency_key ck ON ck.job_id = zj.id\n WHERE cc.concurrency_id = ck.key\n )\n SELECT id AS \"id!\", workspace_id AS \"workspace_id!\", ping, counter + 1 AS counter FROM to_update",
|
||||
"query": "WITH to_update AS (\n SELECT q.id, q.workspace_id, r.ping, COALESCE(zjc.counter, 0) as counter\n FROM v2_job_queue q\n JOIN v2_job j ON j.id = q.id\n JOIN v2_job_runtime r ON r.id = j.id\n LEFT JOIN zombie_job_counter zjc ON zjc.job_id = q.id\n WHERE ping < now() - ($1 || ' seconds')::interval\n AND running = true\n AND kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow')\n AND same_worker = false\n AND (zjc.counter IS NULL OR zjc.counter <= $2)\n FOR UPDATE of q SKIP LOCKED\n ),\n zombie_jobs AS (\n UPDATE v2_job_queue q\n SET running = false, started_at = null\n FROM to_update tu\n WHERE q.id = tu.id AND (tu.counter IS NULL OR tu.counter < $2)\n RETURNING q.id, q.workspace_id, ping, tu.counter\n ),\n update_ping AS (\n UPDATE v2_job_runtime r\n SET ping = null\n FROM zombie_jobs zj\n WHERE r.id = zj.id\n ),\n increment_counter AS (\n INSERT INTO zombie_job_counter (job_id, counter)\n SELECT id, 1 FROM to_update WHERE counter < $2\n ON CONFLICT (job_id) DO UPDATE \n SET counter = zombie_job_counter.counter + 1\n ),\n update_concurrency AS (\n UPDATE concurrency_counter cc\n SET job_uuids = job_uuids - zj.id::text\n FROM zombie_jobs zj\n INNER JOIN concurrency_key ck ON ck.job_id = zj.id\n WHERE cc.concurrency_id = ck.key\n )\n SELECT id AS \"id!\", workspace_id AS \"workspace_id!\", ping, counter + 1 AS counter FROM to_update",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -37,5 +37,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "daf9674838fb3e3653a356c7434c719616a614d77e726433737e5f5d9bd60134"
|
||||
"hash": "abfc6b530565f125bd0b8ac99cd67fd22f14f8fce89e59890ba21e87fe323af5"
|
||||
}
|
||||
23
backend/.sqlx/query-ac01e45d3335015f53f3d63fe159e631efb65c3d326b6b6ae8361a2116bff145.json
generated
Normal file
23
backend/.sqlx/query-ac01e45d3335015f53f3d63fe159e631efb65c3d326b6b6ae8361a2116bff145.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT content FROM script WHERE path = $1 AND workspace_id = $2\n AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND\n workspace_id = $2)\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "content",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "ac01e45d3335015f53f3d63fe159e631efb65c3d326b6b6ae8361a2116bff145"
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT \n j.id,\n j.kind AS \"kind: _\",\n COALESCE(s.path, f.path) AS \"script_path!\",\n COALESCE(s.hash, f.id) AS \"script_hash!: _\",\n COALESCE(jc.started_at, jq.scheduled_for, make_date(1970, 1, 1)) AS \"scheduled_for!: _\",\n args AS input,\n COALESCE(s.schema, f.schema) AS \"schema: _\"\n FROM v2_job j\n LEFT JOIN script s ON j.runnable_id = s.hash AND j.kind = 'script'\n LEFT JOIN flow_version f ON j.runnable_id = f.id AND j.runnable_path = f.path AND j.kind = 'flow'\n LEFT JOIN v2_job_completed jc ON jc.id = j.id\n LEFT JOIN v2_job_queue jq ON jq.id = j.id\n WHERE j.id = ANY($1)\n AND j.workspace_id = $2\n AND COALESCE(s.hash, f.id) IS NOT NULL\n AND COALESCE(s.path, f.path) IS NOT NULL",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "kind: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "job_kind",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"script",
|
||||
"preview",
|
||||
"flow",
|
||||
"dependencies",
|
||||
"flowpreview",
|
||||
"script_hub",
|
||||
"identity",
|
||||
"flowdependencies",
|
||||
"http",
|
||||
"graphql",
|
||||
"postgresql",
|
||||
"noop",
|
||||
"appdependencies",
|
||||
"deploymentcallback",
|
||||
"singlescriptflow",
|
||||
"flowscript",
|
||||
"flownode",
|
||||
"appscript"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "script_path!",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "script_hash!: _",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "scheduled_for!: _",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "input",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "schema: _",
|
||||
"type_info": "Json"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "ad88831c9021b79c9037a925c2b53be3a93dbe2a67bdc4d4342c84f439b5eaf9"
|
||||
}
|
||||
14
backend/.sqlx/query-ca8bf3dda133556ee7051d5436a5d76ae2223d59e9f321a8b6d0c27adc09f741.json
generated
Normal file
14
backend/.sqlx/query-ca8bf3dda133556ee7051d5436a5d76ae2223d59e9f321a8b6d0c27adc09f741.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job_runtime (id, ping) VALUES ($1, null)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ca8bf3dda133556ee7051d5436a5d76ae2223d59e9f321a8b6d0c27adc09f741"
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT s.hash as hash, dm.deployment_msg as deployment_msg \n FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash\n WHERE s.workspace_id = $1 AND s.path = $2\n ORDER by s.created_at DESC LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "hash",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "deployment_msg",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "cf2a6ad6471a40b6298775cda9300aeecdd75503bed59d80cd62091d1642d1ec"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COALESCE(\n (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),\n (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.path = jb.runnable_path AND jb.kind = 'flow')\n ) FROM v2_job jb\n WHERE jb.id = $1 AND jb.workspace_id = $2\n GROUP BY jb.kind, jb.runnable_path",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "coalesce",
|
||||
"type_info": "Json"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "d1fdfabd4f3bbe93cf9667e8e76bd8f537e68fabceb094fe535693fbd146eff2"
|
||||
}
|
||||
22
backend/.sqlx/query-ddf2eccb78a310ed00c7d8b9c3f05d394a7cbcf0038c72a78add5c7b02ef5927.json
generated
Normal file
22
backend/.sqlx/query-ddf2eccb78a310ed00c7d8b9c3f05d394a7cbcf0038c72a78add5c7b02ef5927.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH worker_ids AS (SELECT unnest($1::text[]) as worker) \n SELECT worker_ids.worker FROM worker_ids \n LEFT JOIN worker_ping ON worker_ids.worker = worker_ping.worker \n WHERE worker_ping.worker IS NULL OR worker_ping.ping_at < now() - ('60 seconds')::interval",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "worker",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "ddf2eccb78a310ed00c7d8b9c3f05d394a7cbcf0038c72a78add5c7b02ef5927"
|
||||
}
|
||||
@@ -1,95 +1,80 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n aws_auth_resource_type AS \"aws_auth_resource_type: _\",\n queue_url,\n aws_resource_path,\n message_attributes,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n sqs_trigger\n WHERE\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ",
|
||||
"query": "\n SELECT\n queue_url,\n aws_resource_path,\n message_attributes,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n sqs_trigger\n WHERE\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "aws_auth_resource_type: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "aws_auth_resource_type",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"oidc",
|
||||
"credentials"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "queue_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"ordinal": 1,
|
||||
"name": "aws_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"ordinal": 2,
|
||||
"name": "message_attributes",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"ordinal": 3,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"ordinal": 4,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"ordinal": 5,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 6,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 10,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 11,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 13,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 14,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -98,7 +83,6 @@
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
@@ -116,5 +100,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "bc0bedddcafad216c30c5061eef3f4ed8573cda89f655d6490d22f182e3f2f36"
|
||||
"hash": "e6adaebcade2e25be800e8b888b23c94caae2421f3cae7c06d6346bd6de1d94a"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1)",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 AND\n created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2))",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -19,5 +19,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "2a49e5b5486b650d96f3e9038cba8a5f2e75d3b12ee4718452e82c7318b1bcf4"
|
||||
"hash": "ea2b88dc050aec038641ea37399d68d4385c5bdc721d1351609f27ca45e4dbdc"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT worker, array_agg(v2_job_queue.id) as ids FROM v2_job_queue LEFT JOIN v2_job ON v2_job_queue.id = v2_job.id LEFT JOIN v2_job_runtime ON v2_job_queue.id = v2_job_runtime.id WHERE v2_job_queue.created_at < now() - ('60 seconds')::interval\n AND running = true AND ping IS NULL AND same_worker = true AND worker IS NOT NULL GROUP BY worker",
|
||||
"query": "SELECT worker, array_agg(v2_job_queue.id) as ids FROM v2_job_queue LEFT JOIN v2_job ON v2_job_queue.id = v2_job.id LEFT JOIN v2_job_runtime ON v2_job_queue.id = v2_job_runtime.id WHERE v2_job_queue.created_at < now() - ('60 seconds')::interval \n AND running = true AND ping IS NULL AND same_worker = true AND worker IS NOT NULL GROUP BY worker",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -22,5 +22,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "3e261911cc4c5289da49865f54350613f9e651540a279bd7d75e5e7d79f676a8"
|
||||
"hash": "ebd25047d14bec8457465ba6ed9b6b15b8f2303157b4c6e80ac9e3d84a57d8b1"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n subscription_mode AS \"subscription_mode: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -45,71 +45,56 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "subscription_mode: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "gcp_subscription_mode",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"create_update",
|
||||
"existing"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 10,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 11,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 13,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 14,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 15,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 16,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -133,7 +118,6 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
@@ -141,5 +125,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "1312b7fd622cc814a406c85dbbff61f003c29185c267642cfd898075ebda855d"
|
||||
"hash": "f4f6336fc671b00bed7835124892f7a4d3bbe673f7c48153819dab385a5cb357"
|
||||
}
|
||||
23
backend/.sqlx/query-f68d23841e0e31cdf8633aaf0f32777e04e4965682e5b37afbe84194b756d5f5.json
generated
Normal file
23
backend/.sqlx/query-f68d23841e0e31cdf8633aaf0f32777e04e4965682e5b37afbe84194b756d5f5.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n sqs_trigger \n SET \n aws_resource_path = $1,\n queue_url = $2,\n message_attributes = $3, \n is_flow = $4, \n edited_by = $5, \n email = $6,\n script_path = $7,\n path = $8,\n edited_at = now(), \n error = NULL,\n server_id = NULL\n WHERE \n workspace_id = $9 AND \n path = $10\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"TextArray",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f68d23841e0e31cdf8633aaf0f32777e04e4965682e5b37afbe84194b756d5f5"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n DELETE\n FROM parallel_monitor_lock\n WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval\n RETURNING parent_flow_id, job_id, last_ping, (SELECT workspace_id FROM v2_job_queue q\n WHERE q.id = parent_flow_id AND q.running = true AND q.canceled_by IS NULL\n ) AS workspace_id\n ",
|
||||
"query": "\n DELETE\n FROM parallel_monitor_lock\n WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval \n RETURNING parent_flow_id, job_id, last_ping, (SELECT workspace_id FROM v2_job_queue q\n WHERE q.id = parent_flow_id AND q.running = true AND q.canceled_by IS NULL\n ) AS workspace_id\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -36,5 +36,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "00c4a602aa6a50f2f922851ce63b5216e915c7649698687a00d47da55c70349f"
|
||||
"hash": "f822df86bf511fe0d3044b69525a6ff2524167877929886adca9b0fa3d3fee45"
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) \n SELECT unnest($1::uuid[]), $2, $3, $4, $5, $6, $7, $8",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"JsonbArray",
|
||||
"TextArray",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "fe7221651a982861dede4116bc71fe2dce615ff76a53f72cb5386dc17e4e07aa"
|
||||
}
|
||||
2
backend/.vscode/settings.json
vendored
2
backend/.vscode/settings.json
vendored
@@ -11,5 +11,5 @@
|
||||
"remote.autoForwardPorts": true,
|
||||
"conventionalCommits.scopes": [
|
||||
"restructring triggers, decoding trigger message on work"
|
||||
]
|
||||
],
|
||||
}
|
||||
|
||||
2128
backend/Cargo.lock
generated
2128
backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.486.1"
|
||||
version = "1.481.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -32,7 +32,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.486.1"
|
||||
version = "1.481.0"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -64,7 +64,6 @@ cloud = ["windmill-queue/cloud", "windmill-worker/cloud", "windmill-common/cloud
|
||||
jemalloc = ["windmill-common/jemalloc", "dep:tikv-jemallocator", "dep:tikv-jemalloc-sys", "dep:tikv-jemalloc-ctl"]
|
||||
tantivy = ["dep:windmill-indexer", "windmill-api/tantivy", "windmill-indexer/enterprise", "windmill-indexer/parquet", "windmill-common/tantivy", "enterprise", "parquet"]
|
||||
sqlx = ["windmill-worker/sqlx"]
|
||||
deno_core = ["windmill-worker/deno_core", "windmill-api/deno_core", "dep:deno_core", "dep:v8"]
|
||||
kafka = ["windmill-api/kafka"]
|
||||
nats = ["windmill-api/nats"]
|
||||
otel = ["windmill-common/otel", "windmill-worker/otel"]
|
||||
@@ -72,9 +71,8 @@ dind = ["windmill-worker/dind"]
|
||||
websocket = ["windmill-api/websocket"]
|
||||
http_trigger = ["windmill-api/http_trigger"]
|
||||
postgres_trigger = ["windmill-api/postgres_trigger"]
|
||||
mcp = ["windmill-api/mcp"]
|
||||
mqtt_trigger = ["windmill-api/mqtt_trigger"]
|
||||
sqs_trigger = ["windmill-api/sqs_trigger", "windmill-common/aws_auth", "windmill-api/openidconnect"]
|
||||
sqs_trigger = ["windmill-api/sqs_trigger"]
|
||||
gcp_trigger = ["windmill-api/gcp_trigger"]
|
||||
smtp = ["windmill-api/smtp", "windmill-common/smtp"]
|
||||
license = ["windmill-api/license"]
|
||||
@@ -84,6 +82,7 @@ static_frontend = ["windmill-api/static_frontend"]
|
||||
scoped_cache = ["windmill-common/scoped_cache"]
|
||||
# Languages
|
||||
python = ["windmill-worker/python"]
|
||||
deno_core = ["windmill-worker/deno_core", "dep:deno_core", "dep:v8"]
|
||||
rust = ["windmill-worker/rust"]
|
||||
mysql = ["windmill-worker/mysql"]
|
||||
oracledb = ["windmill-worker/oracledb"]
|
||||
@@ -99,7 +98,6 @@ all_languages = [ "python", "deno_core", "rust", "mysql", "oracledb", "mssql", "
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
tokio.workspace = true
|
||||
tokio-stream.workspace = true
|
||||
dotenv.workspace = true
|
||||
windmill-queue.workspace = true
|
||||
windmill-common = { workspace = true, default-features = false }
|
||||
@@ -132,8 +130,6 @@ quote.workspace = true
|
||||
memchr.workspace = true
|
||||
v8 = { workspace = true, optional = true }
|
||||
rustls.workspace = true
|
||||
systemstat.workspace = true
|
||||
size.workspace = true
|
||||
|
||||
[target.'cfg(not(target_env = "msvc"))'.dependencies]
|
||||
tikv-jemallocator = { optional = true, workspace = true }
|
||||
@@ -185,7 +181,6 @@ axum = { version = "^0.7", features = ["multipart"] }
|
||||
headers = "^0"
|
||||
hyper = { version = "^1", features = ["full"] }
|
||||
tokio = { version = "^1.42.0", features = ["full", "tracing", "time"] }
|
||||
tokio-stream = { version = "0.1.17" }
|
||||
tower = "^0"
|
||||
tower-http = { version = "^0.6", features = ["trace", "cors"] }
|
||||
tower-cookies = "^0.10"
|
||||
@@ -194,7 +189,7 @@ serde_json = { version = "^1", features = ["preserve_order", "raw_value"] }
|
||||
uuid = { version = "^1", features = ["serde", "v4"] }
|
||||
thiserror = "^2"
|
||||
anyhow = "^1"
|
||||
chrono = { version = "^0.4", features = ["serde"] }
|
||||
chrono = { version = "=0.4.39", features = ["serde"] }
|
||||
chrono-tz = "^0.10.1"
|
||||
tracing = "^0"
|
||||
tracing-subscriber = { version = "^0", features = ["env-filter", "json"] }
|
||||
@@ -307,7 +302,6 @@ postgres-native-tls = "^0"
|
||||
native-tls = "^0"
|
||||
# samael will break compilation on MacOS. Use this fork instead to make it work
|
||||
# samael = { git="https://github.com/njaremko/samael", rev="464d015e3ae393e4b5dd00b4d6baa1b617de0dd6", features = ["xmlsec"] }
|
||||
libxml = { version = "=0.3.3" }
|
||||
samael = { version="0.0.14", features = ["xmlsec"] }
|
||||
gcp_auth = "0.9.0"
|
||||
rust_decimal = { version = "^1", features = ["db-postgres", "serde-float"]}
|
||||
@@ -317,9 +311,9 @@ nix = { version = "0.27.1", features = ["process", "signal"] }
|
||||
tinyvector = { git = "https://github.com/windmill-labs/tinyvector", rev = "20823b94c20f2b9093f318badd24026cf54dcc85" }
|
||||
hf-hub = "0.3.2"
|
||||
tokenizers = "0.14.1"
|
||||
candle-core = "0.9.1"
|
||||
candle-transformers = "0.9.1"
|
||||
candle-nn = "0.9.1"
|
||||
candle-core = "0.3.0"
|
||||
candle-transformers = "0.3.0"
|
||||
candle-nn = "0.3.0"
|
||||
tiberius = { version = "0.12.3", default-features = false, features = ["rustls", "tds73", "chrono", "sql-browser-tokio"]}
|
||||
pin-project = "1"
|
||||
indexmap = { version = "2.2.5", features = ["serde"]}
|
||||
@@ -333,8 +327,8 @@ async-nats = "0.38.0"
|
||||
nkeys = "0.4.4"
|
||||
nu-parser = { version = "0.101.0", default-features = false }
|
||||
|
||||
datafusion = "47.0.0"
|
||||
object_store = { version = "0.12.0", features = ["aws", "azure"] }
|
||||
datafusion = "39.0.0"
|
||||
object_store = { version = "0.10.0", features = ["aws", "azure"] }
|
||||
openidconnect = { version = "4.0.0-rc.1" }
|
||||
aws-config = "^1"
|
||||
aws-sdk-sqs = "1.57.0"
|
||||
@@ -357,6 +351,9 @@ bollard = "0.18.1"
|
||||
tonic = { version = "=0.12.3", features = ["tls-native-roots"] }
|
||||
byteorder = "1.5.0"
|
||||
|
||||
# todo remove
|
||||
half = "=2.4.1"
|
||||
|
||||
tikv-jemallocator = { version = "0.5" }
|
||||
tikv-jemalloc-sys = { version = "^0.5" }
|
||||
tikv-jemalloc-ctl = { version = "^0.5" }
|
||||
@@ -367,10 +364,6 @@ pin-project-lite = "^0"
|
||||
tantivy = "0.22.0"
|
||||
|
||||
backon = "1.3.0"
|
||||
systemstat = "0.2.4"
|
||||
size = "0.5.0"
|
||||
|
||||
flume = { version = "0.11.1", features = ["async"] }
|
||||
|
||||
# Macro-related
|
||||
proc-macro2 = "1.0"
|
||||
|
||||
@@ -1 +1 @@
|
||||
868ccad87afb804fe22818ecd3d5a091199bcdbf
|
||||
85c37983ffb8f622458425c182613206625c6cee
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE app_version DROP COLUMN IF EXISTS raw_app;
|
||||
@@ -1,2 +0,0 @@
|
||||
-- Add up migration script here
|
||||
ALTER TABLE app_version ADD COLUMN IF NOT EXISTS raw_app BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
@@ -1 +0,0 @@
|
||||
-- Add down migration script here
|
||||
@@ -1,22 +0,0 @@
|
||||
-- Add up migration script here
|
||||
CREATE OR REPLACE FUNCTION set_session_context(
|
||||
admin BOOLEAN,
|
||||
username TEXT,
|
||||
groups TEXT,
|
||||
pgroups TEXT,
|
||||
folders_read TEXT,
|
||||
folders_write TEXT
|
||||
) RETURNS void AS $$
|
||||
BEGIN
|
||||
IF admin THEN
|
||||
SET LOCAL ROLE windmill_admin;
|
||||
ELSE
|
||||
SET LOCAL ROLE windmill_user;
|
||||
END IF;
|
||||
PERFORM set_config('session.user', username, true);
|
||||
PERFORM set_config('session.groups', groups, true);
|
||||
PERFORM set_config('session.pgroups', pgroups, true);
|
||||
PERFORM set_config('session.folders_read', folders_read, true);
|
||||
PERFORM set_config('session.folders_write', folders_write, true);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
@@ -1,3 +0,0 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE sqs_trigger DROP COLUMN aws_auth_resource_type;
|
||||
DROP TYPE IF EXISTS AWS_AUTH_RESOURCE_TYPE;
|
||||
@@ -1,4 +0,0 @@
|
||||
-- Add up migration script here
|
||||
CREATE TYPE AWS_AUTH_RESOURCE_TYPE AS ENUM ('oidc', 'credentials');
|
||||
ALTER TABLE sqs_trigger
|
||||
ADD COLUMN aws_auth_resource_type AWS_AUTH_RESOURCE_TYPE DEFAULT 'credentials'::AWS_AUTH_RESOURCE_TYPE NOT NULL;
|
||||
@@ -1,3 +0,0 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE gcp_trigger DROP COLUMN subscription_mode;
|
||||
DROP TYPE GCP_SUBSCRIPTION_MODE;
|
||||
@@ -1,3 +0,0 @@
|
||||
-- Add up migration script here
|
||||
CREATE TYPE GCP_SUBSCRIPTION_MODE AS ENUM ('create_update', 'existing');
|
||||
ALTER TABLE gcp_trigger ADD COLUMN subscription_mode GCP_SUBSCRIPTION_MODE NOT NULL DEFAULT 'create_update'::GCP_SUBSCRIPTION_MODE;
|
||||
@@ -1,3 +0,0 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE websocket_trigger
|
||||
ALTER COLUMN url TYPE VARCHAR(255);
|
||||
@@ -1,3 +0,0 @@
|
||||
-- Add up migration script here
|
||||
ALTER TABLE websocket_trigger
|
||||
ALTER COLUMN url TYPE VARCHAR(1000);
|
||||
@@ -1 +0,0 @@
|
||||
DELETE FROM global_settings WHERE name = 'critical_alerts_on_db_oversize';
|
||||
@@ -1,3 +0,0 @@
|
||||
INSERT INTO global_settings (name, value)
|
||||
VALUES ('critical_alerts_on_db_oversize', '{}')
|
||||
ON CONFLICT (name) DO NOTHING;
|
||||
@@ -1 +0,0 @@
|
||||
-- Add down migration script here
|
||||
@@ -1,2 +0,0 @@
|
||||
-- Add up migration script here
|
||||
ALTER TYPE log_mode ADD VALUE 'mcp';
|
||||
@@ -1 +0,0 @@
|
||||
-- Add down migration script here
|
||||
@@ -1,2 +0,0 @@
|
||||
-- Add up migration script here
|
||||
CREATE INDEX IF NOT EXISTS idx_metrics_id_created_at ON public.metrics (id, created_at DESC) WHERE id LIKE 'queue_%';
|
||||
@@ -1 +0,0 @@
|
||||
-- Add down migration script here
|
||||
@@ -1,2 +0,0 @@
|
||||
-- Add up migration script here
|
||||
CREATE INDEX IF NOT EXISTS job_stats_id ON job_stats (job_id);
|
||||
@@ -1 +0,0 @@
|
||||
-- Add down migration script here
|
||||
@@ -1,3 +0,0 @@
|
||||
-- Add up migration script here
|
||||
DROP INDEX IF EXISTS index_script_on_path_created_at;
|
||||
CREATE INDEX IF NOT EXISTS index_script_on_path_created_at ON script (workspace_id, path, created_at DESC);
|
||||
@@ -11,7 +11,6 @@ mod mapping;
|
||||
use async_recursion::async_recursion;
|
||||
use itertools::Itertools;
|
||||
use lazy_static::lazy_static;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use mapping::{FULL_IMPORTS_MAP, SHORT_IMPORTS_MAP};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -21,7 +20,6 @@ use regex_lite::Regex;
|
||||
|
||||
use rustpython_parser::{
|
||||
ast::{Stmt, StmtImport, StmtImportFrom, Suite},
|
||||
text_size::TextRange,
|
||||
Parse,
|
||||
};
|
||||
use sqlx::{Pool, Postgres};
|
||||
@@ -43,11 +41,9 @@ fn replace_full_import(x: &str) -> Option<String> {
|
||||
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new(r"^\#\s?(\S+)\s*$").unwrap();
|
||||
static ref PIN_RE: Regex = Regex::new(r"(?:\s*#\s*(pin|repin):\s*)(\S*)").unwrap();
|
||||
static ref PKG_RE: Regex = Regex::new(r"^([^!=<>]+)(?:[!=<>]|$)").unwrap();
|
||||
}
|
||||
|
||||
fn process_import(module: Option<String>, path: &str, level: usize) -> Vec<NImport> {
|
||||
fn process_import(module: Option<String>, path: &str, level: usize) -> Vec<String> {
|
||||
if level > 0 {
|
||||
let mut imports = vec![];
|
||||
let splitted_path = path.split("/");
|
||||
@@ -56,18 +52,17 @@ fn process_import(module: Option<String>, path: &str, level: usize) -> Vec<NImpo
|
||||
.take(splitted_path.count() - level)
|
||||
.join("/");
|
||||
if let Some(m) = module {
|
||||
imports.push(NImport::Relative(format!("{base}/{}", m.replace(".", "/"))));
|
||||
imports.push(format!("relative:{base}/{}", m.replace(".", "/")));
|
||||
} else {
|
||||
imports.push(NImport::Relative(format!("{base}")));
|
||||
imports.push(format!("relative:{base}"));
|
||||
}
|
||||
imports
|
||||
} else if let Some(module) = module {
|
||||
let imprt = module.split('.').next().unwrap_or("").replace("_", "-");
|
||||
if imprt == "u" || imprt == "f" {
|
||||
vec![NImport::Relative(module.replace(".", "/"))]
|
||||
vec![format!("relative:{}", module.replace(".", "/"))]
|
||||
} else {
|
||||
let pkg = replace_full_import(&module).unwrap_or(replace_import(imprt));
|
||||
vec![NImport::Auto { key: if module == pkg { None } else { Some(module) }, pkg }]
|
||||
vec![replace_full_import(&module).unwrap_or(replace_import(imprt))]
|
||||
}
|
||||
} else {
|
||||
vec![]
|
||||
@@ -78,68 +73,17 @@ pub fn parse_relative_imports(code: &str, path: &str) -> error::Result<Vec<Strin
|
||||
let nimports = parse_code_for_imports(code, path)?;
|
||||
return Ok(nimports
|
||||
.into_iter()
|
||||
.filter_map(|x| match x {
|
||||
NImport::Relative(path) => Some(path),
|
||||
_ => None,
|
||||
.filter_map(|x| {
|
||||
if x.starts_with("relative:") {
|
||||
Some(x.replace("relative:", ""))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect());
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
enum NImport {
|
||||
// Order matters! First we want to resolve all repins
|
||||
|
||||
// manually repinned requirement
|
||||
// e.g.:
|
||||
// import pandas # repin: pandas==x.y.z
|
||||
Repin {
|
||||
pin: ImportPin,
|
||||
key: String,
|
||||
},
|
||||
// manually pinned requirements
|
||||
// e.g.:
|
||||
// import pandas # pin: pandas>=x.y.z
|
||||
// import pandas # pin: pandas<=x.y.z
|
||||
//
|
||||
// NOTE: It is possible for multiple pins exist on same import
|
||||
// That's why we store vector of pins
|
||||
Pin {
|
||||
pins: Vec<ImportPin>,
|
||||
key: String,
|
||||
},
|
||||
// Automatically inferred requirement
|
||||
// e.g.:
|
||||
// import pandas
|
||||
Auto {
|
||||
// Take `x.y.z` for example
|
||||
// x is going to be the `root`
|
||||
// and x.y.z is `full`
|
||||
//
|
||||
// `full` will be None if it is equal to root
|
||||
//
|
||||
// We will use `root` as a requirement name and pass to `uv pip compile` if it was not replaced with any pin
|
||||
pkg: String,
|
||||
|
||||
// However we still need full, since all pins pin against full import names
|
||||
key: Option<String>,
|
||||
},
|
||||
// Relative imports
|
||||
Relative(String),
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
enum NImportResolved {
|
||||
Repin { pin: ImportPin, key: String },
|
||||
Pin { pins: Vec<ImportPin>, key: String },
|
||||
Auto { pkg: String, key: Option<String> },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
struct ImportPin {
|
||||
pkg: String,
|
||||
path: String,
|
||||
}
|
||||
|
||||
fn parse_code_for_imports(code: &str, path: &str) -> error::Result<Vec<NImport>> {
|
||||
fn parse_code_for_imports(code: &str, path: &str) -> error::Result<Vec<String>> {
|
||||
let mut code = code.split(DEF_MAIN).next().unwrap_or("").to_string();
|
||||
|
||||
// remove main function decorator from end of file if it exists
|
||||
@@ -160,54 +104,19 @@ fn parse_code_for_imports(code: &str, path: &str) -> error::Result<Vec<NImport>>
|
||||
let ast = Suite::parse(&code, "main.py").map_err(|e| {
|
||||
error::Error::ExecutionErr(format!("Error parsing code for imports: {}", e.to_string()))
|
||||
})?;
|
||||
|
||||
let find_pin = |range: TextRange, key: String| {
|
||||
let hs = code
|
||||
.chars()
|
||||
.skip(range.end().to_usize())
|
||||
.take_while(|e| *e != '\n')
|
||||
.collect::<String>();
|
||||
|
||||
if hs.trim_start().is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
PIN_RE.captures(&hs).and_then(|x| {
|
||||
x.get(1).zip(x.get(2)).and_then(|(ty_m, pkg_m)| {
|
||||
let pkg = pkg_m.as_str().to_owned();
|
||||
if ty_m.as_str() == "pin" {
|
||||
Some(vec![NImport::Pin {
|
||||
pins: vec![ImportPin { pkg, path: path.to_owned() }],
|
||||
key,
|
||||
}])
|
||||
} else if ty_m.as_str() == "repin" {
|
||||
Some(vec![NImport::Repin {
|
||||
pin: ImportPin { pkg, path: path.to_owned() },
|
||||
key,
|
||||
}])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
let mut nimports: Vec<NImport> = ast
|
||||
let nimports: Vec<String> = ast
|
||||
.into_iter()
|
||||
.filter_map(|x| match x {
|
||||
Stmt::Import(StmtImport { names, range }) => names
|
||||
.get(0)
|
||||
.and_then(|al| find_pin(range, al.name.to_string()))
|
||||
.or(Some(
|
||||
names
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
let name = x.name.to_string();
|
||||
process_import(Some(name), path, 0)
|
||||
})
|
||||
.flatten()
|
||||
.collect::<Vec<NImport>>(),
|
||||
)),
|
||||
Stmt::Import(StmtImport { names, .. }) => Some(
|
||||
names
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
let name = x.name.to_string();
|
||||
process_import(Some(name), path, 0)
|
||||
})
|
||||
.flatten()
|
||||
.collect::<Vec<String>>(),
|
||||
),
|
||||
Stmt::ImportFrom(StmtImportFrom { level: Some(i), module, .. }) if i.to_u32() > 0 => {
|
||||
Some(process_import(
|
||||
module.map(|x| x.to_string()),
|
||||
@@ -215,25 +124,15 @@ fn parse_code_for_imports(code: &str, path: &str) -> error::Result<Vec<NImport>>
|
||||
i.to_usize(),
|
||||
))
|
||||
}
|
||||
Stmt::ImportFrom(StmtImportFrom { level: _, module, range, .. }) => find_pin(
|
||||
range,
|
||||
module.clone().map(|x| x.to_string()).unwrap_or_default(),
|
||||
)
|
||||
.or(Some(process_import(module.map(|x| x.to_string()), path, 0))),
|
||||
Stmt::ImportFrom(StmtImportFrom { level: _, module, .. }) => {
|
||||
Some(process_import(module.map(|x| x.to_string()), path, 0))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.flatten()
|
||||
.filter(|x| {
|
||||
if let NImport::Auto { ref pkg, .. } = x {
|
||||
!STDIMPORTS.contains(&(*pkg).as_str())
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.filter(|x| !STDIMPORTS.contains(&x.as_str()))
|
||||
.unique()
|
||||
.collect();
|
||||
|
||||
nimports.sort();
|
||||
return Ok(nimports);
|
||||
}
|
||||
|
||||
@@ -244,9 +143,8 @@ pub async fn parse_python_imports(
|
||||
db: &Pool<Postgres>,
|
||||
already_visited: &mut Vec<String>,
|
||||
annotated_pyv_numeric: &mut Option<u32>,
|
||||
) -> error::Result<(Vec<String>, Option<String>)> {
|
||||
let mut compile_error_hint: Option<String> = None;
|
||||
let mut imports = parse_python_imports_inner(
|
||||
) -> error::Result<Vec<String>> {
|
||||
parse_python_imports_inner(
|
||||
code,
|
||||
w_id,
|
||||
path,
|
||||
@@ -255,46 +153,7 @@ pub async fn parse_python_imports(
|
||||
annotated_pyv_numeric,
|
||||
&mut annotated_pyv_numeric.and_then(|_| Some(path.to_owned())),
|
||||
)
|
||||
.await?
|
||||
.into_values()
|
||||
.map(|nimport| match nimport {
|
||||
NImportResolved::Pin { pins, .. } => pins.into_iter().map(|p| {
|
||||
if let Some(hint) = &mut compile_error_hint{
|
||||
hint.push_str(&format!("\n - pin to {} in {}", p.pkg, p.path));
|
||||
} else {
|
||||
compile_error_hint = Some("\n\nMultiple pins can cause problems during lockfile resolution.\nMake sure you checked every pin for conflicts:\n".into())
|
||||
};
|
||||
Ok(p.pkg)
|
||||
}).collect_vec(),
|
||||
NImportResolved::Repin { pin: ImportPin { pkg, .. }, .. } => vec![Ok(pkg)],
|
||||
NImportResolved::Auto { pkg, key } => vec![
|
||||
|
||||
if let Some(key) = key {
|
||||
Ok(format!("{pkg} # (mapped from {key})"))
|
||||
} else {
|
||||
Ok(pkg)
|
||||
}
|
||||
],
|
||||
})
|
||||
.flatten()
|
||||
.collect::<error::Result<Vec<String>>>()?
|
||||
.into_iter()
|
||||
.unique()
|
||||
.collect_vec();
|
||||
|
||||
imports.sort();
|
||||
|
||||
compile_error_hint
|
||||
.as_mut()
|
||||
.map(|e| e.push_str("\n\nNOTE: You can also `repin` to override all pins"));
|
||||
Ok((imports, compile_error_hint))
|
||||
}
|
||||
|
||||
fn extract_pkg_name(requirement: &str) -> String {
|
||||
PKG_RE
|
||||
.captures(requirement)
|
||||
.map(|x| x.get(1).map(|m| m.as_str().to_string()).unwrap_or_default())
|
||||
.unwrap_or_default()
|
||||
.await
|
||||
}
|
||||
|
||||
#[async_recursion]
|
||||
@@ -306,7 +165,7 @@ async fn parse_python_imports_inner(
|
||||
already_visited: &mut Vec<String>,
|
||||
annotated_pyv_numeric: &mut Option<u32>,
|
||||
path_where_annotated_pyv: &mut Option<String>,
|
||||
) -> error::Result<HashMap<String, NImportResolved>> {
|
||||
) -> error::Result<Vec<String>> {
|
||||
let PythonAnnotations { py310, py311, py312, py313, .. } = PythonAnnotations::parse(&code);
|
||||
|
||||
// we pass only if there is none or only one annotation
|
||||
@@ -335,6 +194,7 @@ async fn parse_python_imports_inner(
|
||||
} else {
|
||||
*annotated_pyv_numeric = Some(numeric);
|
||||
}
|
||||
|
||||
*path_where_annotated_pyv = Some(path.to_owned());
|
||||
}
|
||||
Ok(())
|
||||
@@ -349,216 +209,74 @@ async fn parse_python_imports_inner(
|
||||
.lines()
|
||||
.find_position(|x| x.starts_with("#requirements:") || x.starts_with("# requirements:"));
|
||||
if let Some((pos, _)) = find_requirements {
|
||||
let mut requirements = HashMap::new();
|
||||
code.lines()
|
||||
let lines = code
|
||||
.lines()
|
||||
.skip(pos + 1)
|
||||
.map_while(|x| {
|
||||
RE.captures(x).and_then(|x| {
|
||||
x.get(1).map(|m| {
|
||||
let requirement = m.as_str().to_string();
|
||||
let key = extract_pkg_name(&requirement);
|
||||
requirements.insert(
|
||||
key.clone(),
|
||||
NImportResolved::Pin {
|
||||
pins: vec![ImportPin {
|
||||
pkg: requirement.clone(),
|
||||
path: Default::default(),
|
||||
}],
|
||||
key,
|
||||
},
|
||||
);
|
||||
})
|
||||
})
|
||||
RE.captures(x)
|
||||
.map(|x| x.get(1).unwrap().as_str().to_string())
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
Ok(requirements)
|
||||
.collect();
|
||||
Ok(lines)
|
||||
} else {
|
||||
let find_extra_requirements = code.lines().find_position(|x| {
|
||||
x.starts_with("#extra_requirements:") || x.starts_with("# extra_requirements:")
|
||||
});
|
||||
let mut imports: HashMap<String, NImportResolved> = HashMap::new();
|
||||
let mut imports: Vec<String> = vec![];
|
||||
if let Some((pos, _)) = find_extra_requirements {
|
||||
code.lines()
|
||||
let lines: Vec<String> = code
|
||||
.lines()
|
||||
.skip(pos + 1)
|
||||
.map_while(|x| {
|
||||
RE.captures(x).and_then(|x| {
|
||||
x.get(1).map(|m| {
|
||||
let requirement = m.as_str().to_string();
|
||||
let key = extract_pkg_name(&requirement);
|
||||
imports.insert(
|
||||
key.clone(),
|
||||
NImportResolved::Pin {
|
||||
pins: vec![ImportPin {
|
||||
pkg: requirement,
|
||||
path: Default::default(),
|
||||
}],
|
||||
key,
|
||||
},
|
||||
);
|
||||
})
|
||||
})
|
||||
RE.captures(x)
|
||||
.map(|x| x.get(1).unwrap().as_str().to_string())
|
||||
})
|
||||
.collect_vec();
|
||||
.collect();
|
||||
imports.extend(lines);
|
||||
}
|
||||
|
||||
// Will get unsorted vector of imports found in current script
|
||||
let mut nimports = parse_code_for_imports(code, path)?;
|
||||
let nimports = parse_code_for_imports(code, path)?;
|
||||
for n in nimports.iter() {
|
||||
let nested = if n.starts_with("relative:") {
|
||||
let rpath = n.replace("relative:", "");
|
||||
let code = sqlx::query_scalar!(
|
||||
r#"
|
||||
SELECT content FROM script WHERE path = $1 AND workspace_id = $2
|
||||
AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND
|
||||
workspace_id = $2)
|
||||
"#,
|
||||
&rpath,
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await?
|
||||
.unwrap_or_else(|| "".to_string());
|
||||
|
||||
// It is important to note, that sorting is important and will always result in this pattern:
|
||||
// 1. All Repins go first
|
||||
// 2. All Pins go second
|
||||
// 3. All Auto go third
|
||||
// 4. All relative imports go the last
|
||||
//
|
||||
// This way we make sure all repins are resolved before (re)pins inside imported relative scripts.
|
||||
nimports.sort();
|
||||
|
||||
for n in nimports.into_iter() {
|
||||
let mut nested = match n {
|
||||
NImport::Relative(rpath) => {
|
||||
let code = sqlx::query_scalar!(
|
||||
r#"
|
||||
SELECT content FROM script WHERE path = $1 AND workspace_id = $2
|
||||
AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND
|
||||
workspace_id = $2)
|
||||
"#,
|
||||
if already_visited.contains(&rpath) {
|
||||
vec![]
|
||||
} else {
|
||||
already_visited.push(rpath.clone());
|
||||
parse_python_imports_inner(
|
||||
&code,
|
||||
w_id,
|
||||
&rpath,
|
||||
w_id
|
||||
db,
|
||||
already_visited,
|
||||
annotated_pyv_numeric,
|
||||
path_where_annotated_pyv,
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await?
|
||||
.unwrap_or_else(|| "".to_string());
|
||||
|
||||
if already_visited.contains(&rpath) {
|
||||
vec![]
|
||||
} else {
|
||||
already_visited.push(rpath.clone());
|
||||
// Because the algo goes depth first, this function will never return relative import
|
||||
// This why we can safely assume later, that there is no relative imports
|
||||
parse_python_imports_inner(
|
||||
&code,
|
||||
w_id,
|
||||
&rpath,
|
||||
db,
|
||||
already_visited,
|
||||
annotated_pyv_numeric,
|
||||
path_where_annotated_pyv,
|
||||
)
|
||||
.await?
|
||||
.into_values()
|
||||
.collect_vec()
|
||||
}
|
||||
}
|
||||
NImport::Repin { pin, key } => vec![NImportResolved::Repin { pin, key }],
|
||||
NImport::Pin { pins, key } => vec![NImportResolved::Pin { pins, key }],
|
||||
NImport::Auto { pkg, key } => vec![NImportResolved::Auto { pkg, key }],
|
||||
} else {
|
||||
vec![n.to_string()]
|
||||
};
|
||||
|
||||
// Nested should also be sorted for the same reason
|
||||
nested.sort();
|
||||
|
||||
// At this point there should be no NImport::Relative in `nested`
|
||||
for imp in nested {
|
||||
let key = match imp.clone() {
|
||||
NImportResolved::Pin { key, .. } => key,
|
||||
NImportResolved::Repin { key, .. } => key,
|
||||
NImportResolved::Auto { key, pkg } => key.unwrap_or(pkg),
|
||||
};
|
||||
// Handled cases:
|
||||
//
|
||||
// 1.
|
||||
// Error: Imported windmill scripts have different pins
|
||||
//
|
||||
// auto
|
||||
// ├── pin:2
|
||||
// └── pin:1
|
||||
//
|
||||
// Fix 1:
|
||||
//
|
||||
// auto
|
||||
// ├── pin:1
|
||||
// └── pin:1
|
||||
//
|
||||
// Fix 2:
|
||||
//
|
||||
// repin:1
|
||||
// ├── pin:2
|
||||
// └── pin:1
|
||||
//
|
||||
// 2.
|
||||
// Error: Imported windmill scripts have different pins
|
||||
//
|
||||
// pin:2
|
||||
// └── pin:1
|
||||
//
|
||||
// Fix 1:
|
||||
//
|
||||
// auto
|
||||
// └── pin:1
|
||||
//
|
||||
// Fix 2:
|
||||
//
|
||||
// repin:2
|
||||
// └── pin:1
|
||||
//
|
||||
// 3. repins allowed to be repinned again
|
||||
//
|
||||
// repin:2
|
||||
// └── repin:1
|
||||
//
|
||||
match imp.clone() {
|
||||
NImportResolved::Repin { .. } => {
|
||||
if let Some(existing_import) = imports.get(&key) {
|
||||
match existing_import {
|
||||
// replace
|
||||
p if matches!(
|
||||
p,
|
||||
NImportResolved::Pin { .. } | NImportResolved::Auto { .. }
|
||||
) =>
|
||||
{
|
||||
imports.insert(key, imp);
|
||||
}
|
||||
// do nothing (older repins have greater precedence)
|
||||
NImportResolved::Repin { .. } => {}
|
||||
// Should not be possible
|
||||
_ => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Internal error: cannot resolve requirement pins",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
imports.insert(key, imp.clone());
|
||||
}
|
||||
}
|
||||
NImportResolved::Pin { pins: new_pins, .. } => {
|
||||
if let Some(existing_import) = imports.get_mut(&key) {
|
||||
match existing_import {
|
||||
// Check if pin is the same version, if same, do nothing, if not error
|
||||
NImportResolved::Pin { pins: existing_pins, .. } => {
|
||||
existing_pins.extend(new_pins)
|
||||
}
|
||||
// do nothing
|
||||
NImportResolved::Repin { .. } => {}
|
||||
// Replace with new pin
|
||||
NImportResolved::Auto { .. } => {
|
||||
imports.insert(key, imp);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
imports.insert(key, imp.clone());
|
||||
}
|
||||
}
|
||||
NImportResolved::Auto { .. } => {
|
||||
if !imports.contains_key(&key) {
|
||||
imports.insert(key, imp);
|
||||
}
|
||||
}
|
||||
if !imports.contains(&imp) {
|
||||
imports.push(imp);
|
||||
}
|
||||
}
|
||||
}
|
||||
imports.sort();
|
||||
Ok(imports)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,8 +142,6 @@ pub static FULL_IMPORTS_MAP: PyMap = phf_map! {
|
||||
"google.cloud.dns" => "google-cloud-dns",
|
||||
"google.cloud.runtimeconfig" => "google-cloud-runtimeconfig",
|
||||
"google.cloud.iot" => "google-cloud-iot",
|
||||
"google.generativeai" => "google-generativeai",
|
||||
"google.genai" => "google-genai",
|
||||
// Azure
|
||||
"azure.mgmt.hybridkubernetes" => "azure-mgmt-hybridkubernetes",
|
||||
"azure.mgmt.sql" => "azure-mgmt-sql",
|
||||
@@ -327,7 +325,6 @@ pub static FULL_IMPORTS_MAP: PyMap = phf_map! {
|
||||
"azure.mgmt.nspkg" => "azure-mgmt-nspkg",
|
||||
"azure.keyvault.secrets" => "azure-keyvault-secrets",
|
||||
"azure.storage.blob" => "azure-storage-blob",
|
||||
"azure.storage.filedatalake" => "azure-storage-file-datalake",
|
||||
// Add new entry here ^
|
||||
};
|
||||
|
||||
@@ -379,6 +376,5 @@ pub static SHORT_IMPORTS_MAP: PyMap = phf_map! {
|
||||
"socks" => "PySocks",
|
||||
"taiga" => "python-taiga",
|
||||
"docx" => "python-docx",
|
||||
"vt" => "vt-py",
|
||||
// Add new entry here ^
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ def main():
|
||||
|
||||
";
|
||||
let mut already_visited = vec![];
|
||||
let (r, ..) = parse_python_imports(
|
||||
let r = parse_python_imports(
|
||||
code,
|
||||
"test-workspace",
|
||||
"f/foo/bar",
|
||||
@@ -29,15 +29,7 @@ def main():
|
||||
)
|
||||
.await?;
|
||||
// println!("{}", serde_json::to_string(&r)?);
|
||||
assert_eq!(
|
||||
r,
|
||||
vec![
|
||||
"matplotlib # (mapped from matplotlib.pyplot)",
|
||||
"wmill",
|
||||
"zanzibar # (mapped from zanzibar.estonie)"
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(r, vec!["matplotlib", "wmill", "zanzibar"]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -60,7 +52,7 @@ def main():
|
||||
|
||||
";
|
||||
let mut already_visited = vec![];
|
||||
let (r, ..) = parse_python_imports(
|
||||
let r = parse_python_imports(
|
||||
code,
|
||||
"test-workspace",
|
||||
"f/foo/bar",
|
||||
@@ -91,7 +83,7 @@ def main():
|
||||
";
|
||||
let mut already_visited = vec![];
|
||||
|
||||
let (r, ..) = parse_python_imports(
|
||||
let r = parse_python_imports(
|
||||
code,
|
||||
"test-workspace",
|
||||
"f/foo/bar",
|
||||
|
||||
@@ -49,12 +49,8 @@ impl Visit for ImportsFinder {
|
||||
pub fn parse_expr_for_imports(code: &str) -> anyhow::Result<Vec<String>> {
|
||||
let cm: Lrc<SourceMap> = Default::default();
|
||||
let fm = cm.new_source_file(FileName::Custom("main.d.ts".into()).into(), code.into());
|
||||
let mut tss = TsSyntax::default();
|
||||
tss.disallow_ambiguous_jsx_like;
|
||||
tss.tsx = true;
|
||||
tss.no_early_errors = true;
|
||||
let lexer = Lexer::new(
|
||||
Syntax::Typescript(tss),
|
||||
Syntax::Typescript(TsSyntax::default()),
|
||||
// EsVersion defaults to es5
|
||||
Default::default(),
|
||||
StringInput::from(&*fm),
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use serde_json::json;
|
||||
use windmill_parser::{Arg, MainArgSignature, ObjectProperty, Typ};
|
||||
@@ -210,52 +208,15 @@ pub struct AnsibleInventory {
|
||||
resource_type: Option<String>,
|
||||
pub pinned_resource: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GitRepo {
|
||||
pub url: String,
|
||||
pub commit: Option<String>,
|
||||
pub branch: Option<String>,
|
||||
pub target_path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AnsibleRequirements {
|
||||
pub python_reqs: Vec<String>,
|
||||
pub roles_and_collections: Option<String>,
|
||||
pub collections: Option<String>,
|
||||
pub file_resources: Vec<FileResource>,
|
||||
pub inventories: Vec<AnsibleInventory>,
|
||||
pub vars: Vec<(String, String)>,
|
||||
pub resources: Vec<(String, String)>,
|
||||
pub options: AnsiblePlaybookOptions,
|
||||
pub vault_password: Option<String>,
|
||||
pub vault_id: Vec<String>,
|
||||
pub git_repos: Vec<GitRepo>,
|
||||
pub git_ssh_identity: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for AnsibleRequirements {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
python_reqs: vec![],
|
||||
roles_and_collections: None,
|
||||
file_resources: vec![],
|
||||
inventories: vec![],
|
||||
vars: vec![],
|
||||
resources: vec![],
|
||||
options: AnsiblePlaybookOptions {
|
||||
verbosity: None,
|
||||
forks: None,
|
||||
timeout: None,
|
||||
flush_cache: None,
|
||||
force_handlers: None,
|
||||
},
|
||||
vault_password: None,
|
||||
vault_id: vec![],
|
||||
git_repos: vec![],
|
||||
git_ssh_identity: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_inventories(inventory_yaml: &Yaml) -> anyhow::Result<Vec<AnsibleInventory>> {
|
||||
@@ -314,7 +275,22 @@ pub fn parse_ansible_reqs(
|
||||
return Ok((logs, None, inner_content.to_string()));
|
||||
}
|
||||
|
||||
let mut ret = AnsibleRequirements::default();
|
||||
let opts = AnsiblePlaybookOptions {
|
||||
verbosity: None,
|
||||
forks: None,
|
||||
timeout: None,
|
||||
flush_cache: None,
|
||||
force_handlers: None,
|
||||
};
|
||||
let mut ret = AnsibleRequirements {
|
||||
python_reqs: vec![],
|
||||
collections: None,
|
||||
file_resources: vec![],
|
||||
inventories: vec![],
|
||||
vars: vec![],
|
||||
resources: vec![],
|
||||
options: opts,
|
||||
};
|
||||
|
||||
if let Yaml::Hash(doc) = &docs[0] {
|
||||
for (key, value) in doc {
|
||||
@@ -327,7 +303,7 @@ pub fn parse_ansible_reqs(
|
||||
let mut out_str = String::new();
|
||||
let mut emitter = YamlEmitter::new(&mut out_str);
|
||||
emitter.dump(galaxy_requirements)?;
|
||||
ret.roles_and_collections = Some(out_str);
|
||||
ret.collections = Some(out_str);
|
||||
}
|
||||
if let Some(Yaml::Array(py_reqs)) =
|
||||
deps.get(&Yaml::String("python".to_string()))
|
||||
@@ -369,60 +345,11 @@ pub fn parse_ansible_reqs(
|
||||
Yaml::String(key) if key == "inventory" => {
|
||||
ret.inventories = parse_inventories(value)?;
|
||||
}
|
||||
Yaml::String(key) if key == "vault_password" => {
|
||||
let Yaml::String(filename) = value else {
|
||||
return Err(anyhow!(
|
||||
"Vault Password File expects a String containing the file name"
|
||||
));
|
||||
};
|
||||
ret.vault_password = Some(filename.to_string());
|
||||
}
|
||||
Yaml::String(key) if key == "vault_id" => {
|
||||
let Yaml::Array(filenames) = value else {
|
||||
return Err(anyhow!("Vault ID field expects an array of strings in the format: `label@filename`"));
|
||||
};
|
||||
|
||||
for f in filenames {
|
||||
let Yaml::String(filename) = f else {
|
||||
return Err(anyhow!("The elements of the vault_id field should be strings in the format: `label@filename`"));
|
||||
};
|
||||
ret.vault_id.push(filename.to_string());
|
||||
}
|
||||
}
|
||||
Yaml::String(key) if key == "options" => {
|
||||
if let Yaml::Array(opts) = &value {
|
||||
ret.options = parse_ansible_options(opts);
|
||||
}
|
||||
}
|
||||
Yaml::String(key) if key == "git_repos" => {
|
||||
let Yaml::Array(repos) = &value else {
|
||||
return Err(anyhow!("git_repos field expects an array of repos"));
|
||||
};
|
||||
|
||||
for r in repos {
|
||||
ret.git_repos.push(
|
||||
parse_git_repo(r)
|
||||
.map_err(|e| anyhow!("Failed to parse git repo: {e}"))?,
|
||||
);
|
||||
}
|
||||
}
|
||||
Yaml::String(key) if key == "git_ssh_identity" => {
|
||||
let Yaml::Array(indentities) = &value else {
|
||||
return Err(anyhow!(
|
||||
"git_ssh_identity expects an array of windmill variables (or secrets) containing ssh IDs"
|
||||
));
|
||||
};
|
||||
|
||||
for r in indentities {
|
||||
let Yaml::String(file_name) = r else {
|
||||
return Err(anyhow!(
|
||||
"Git ssh identity file must be a string path to a Windmill variable/secret"
|
||||
));
|
||||
};
|
||||
|
||||
ret.git_ssh_identity.push(file_name.clone());
|
||||
}
|
||||
}
|
||||
Yaml::String(key) => logs.push_str(&format!("\nUnknown field `{}`. Ignoring", key)),
|
||||
_ => (),
|
||||
}
|
||||
@@ -437,38 +364,6 @@ pub fn parse_ansible_reqs(
|
||||
Ok((logs, Some(ret), out_str))
|
||||
}
|
||||
|
||||
fn parse_git_repo(r: &Yaml) -> anyhow::Result<GitRepo> {
|
||||
let Yaml::Hash(repo) = r else {
|
||||
return Err(anyhow!("Should be a Map"));
|
||||
};
|
||||
|
||||
let url = repo
|
||||
.get(&Yaml::String("url".to_string()))
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string())
|
||||
.ok_or(anyhow!("Expected `url` field"))?;
|
||||
|
||||
let target_path = repo
|
||||
.get(&Yaml::String("target".to_string()))
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string())
|
||||
.ok_or(anyhow!(
|
||||
"Expected `target` field (target directory for cloning the repo)"
|
||||
))?;
|
||||
|
||||
let branch = repo
|
||||
.get(&Yaml::String("branch".to_string()))
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
let commit = repo
|
||||
.get(&Yaml::String("commit".to_string()))
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
Ok(GitRepo { url, commit, branch, target_path })
|
||||
}
|
||||
|
||||
fn parse_ansible_options(opts: &Vec<Yaml>) -> AnsiblePlaybookOptions {
|
||||
let mut ret = AnsiblePlaybookOptions {
|
||||
verbosity: None,
|
||||
@@ -634,78 +529,3 @@ fn yaml_to_json(yaml: &Yaml) -> serde_json::Value {
|
||||
_ => serde_json::Value::Null,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_versions(
|
||||
section: &str,
|
||||
yaml: &mut Yaml,
|
||||
versions: &HashMap<String, String>,
|
||||
) -> anyhow::Result<String> {
|
||||
let mut logs = String::new();
|
||||
|
||||
let Yaml::Hash(ref mut m) = yaml else {
|
||||
return Err(anyhow!("{section} dependency should be a map"));
|
||||
};
|
||||
|
||||
if let Some(Yaml::Array(elements)) = m.get_mut(&Yaml::String(section.to_string())) {
|
||||
for el in elements {
|
||||
let Yaml::Hash(ref mut h) = el else {
|
||||
return Err(anyhow!("{section} dependency element should be a map"));
|
||||
};
|
||||
|
||||
if let Some(name) = h
|
||||
.get(&Yaml::String("name".to_string()))
|
||||
.and_then(|n| n.as_str())
|
||||
{
|
||||
if let Some(version) = versions.get(name) {
|
||||
h.insert(
|
||||
Yaml::String("version".to_string()),
|
||||
Yaml::String(version.to_string()),
|
||||
);
|
||||
} else {
|
||||
logs.push_str(&format!("WARNING: {section} dependency `{name}` has no locked version, using the latest or system installed version.\n"));
|
||||
}
|
||||
} else {
|
||||
return Err(anyhow!(
|
||||
"{section} dependency element: missing or invalid `name` field"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(logs)
|
||||
}
|
||||
|
||||
pub fn add_versions_to_requirements_yaml(
|
||||
input: &str,
|
||||
role_versions: &HashMap<String, String>,
|
||||
collection_versions: &HashMap<String, String>,
|
||||
) -> anyhow::Result<(String,String)> {
|
||||
let mut docs =
|
||||
YamlLoader::load_from_str(input).map_err(|e| anyhow!("YAML parse error: {}", e))?;
|
||||
let doc = &mut docs[0];
|
||||
|
||||
let mut logs = String::new();
|
||||
|
||||
logs.push_str(
|
||||
&update_versions("roles", doc, role_versions)
|
||||
.map_err(|e| anyhow!("Error updating role versions: {e}"))?,
|
||||
);
|
||||
logs.push_str(
|
||||
&update_versions("collections", doc, collection_versions)
|
||||
.map_err(|e| anyhow!("Error updating collection versions: {e}"))?,
|
||||
);
|
||||
|
||||
if !logs.is_empty() {
|
||||
logs.push_str("WARNING: You might want to try adding manual versions for these, otherwise there could be breaking changes on deployed scripts\n");
|
||||
}
|
||||
|
||||
let mut out_str = String::new();
|
||||
{
|
||||
let mut emitter = YamlEmitter::new(&mut out_str);
|
||||
emitter
|
||||
.dump(doc)
|
||||
.map_err(|e| anyhow!("YAML emit error: {}", e))?;
|
||||
}
|
||||
|
||||
Ok((out_str, logs))
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import json
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Path to the profiling JSON file
|
||||
# file_path = "/tmp/windmill/profiling_main.json"
|
||||
file_path = "/tmp/profiling.json"
|
||||
|
||||
# Load the JSON data
|
||||
with open(file_path, "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Extract timings for "pre pull->post pull"
|
||||
pre_post_pull_timings = [
|
||||
timing / 1000000.0 for entry in data["timings"]
|
||||
for step, timing in entry["timings"]
|
||||
# if step == "pre pull->post pull"
|
||||
if step == "->job pulled from DB"
|
||||
]
|
||||
|
||||
# Plotting the distribution
|
||||
plt.figure(figsize=(10, 6))
|
||||
# plt.hist(pre_post_pull_timings, bins=10, edgecolor='black')
|
||||
plt.scatter(range(len(pre_post_pull_timings)), pre_post_pull_timings,
|
||||
alpha=1.0, # Transparency level
|
||||
s=40) # Size of the dots`)
|
||||
plt.title("Distribution of 'pre pull->post pull' timings")
|
||||
# plt.xlabel("Time (ms)")
|
||||
# plt.ylabel("Frequency")
|
||||
plt.xlabel("Sample Index")
|
||||
plt.ylabel("Time (ms)")
|
||||
plt.grid(True)
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
@@ -8,12 +8,11 @@
|
||||
|
||||
use anyhow::Context;
|
||||
use monitor::{
|
||||
load_base_url, load_otel, reload_critical_alerts_on_db_oversize,
|
||||
reload_delete_logs_periodically_setting, reload_indexer_config,
|
||||
load_base_url, load_otel, reload_delete_logs_periodically_setting, reload_indexer_config,
|
||||
reload_instance_python_version_setting, reload_maven_repos_setting,
|
||||
reload_no_default_maven_setting, reload_nuget_config_setting,
|
||||
reload_timeout_wait_result_setting, send_current_log_file_to_object_store,
|
||||
send_logs_to_object_store, WORKERS_NAMES,
|
||||
send_logs_to_object_store,
|
||||
};
|
||||
use rand::Rng;
|
||||
use sqlx::postgres::PgListener;
|
||||
@@ -34,16 +33,15 @@ use windmill_common::{
|
||||
agent_workers::build_agent_http_client,
|
||||
get_database_url,
|
||||
global_settings::{
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING,
|
||||
CRITICAL_ALERT_MUTE_UI_SETTING, CRITICAL_ERROR_CHANNELS_SETTING, CUSTOM_TAGS_SETTING,
|
||||
DEFAULT_TAGS_PER_WORKSPACE_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING, EMAIL_DOMAIN_SETTING,
|
||||
ENV_SETTINGS, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING,
|
||||
EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, INDEXER_SETTING,
|
||||
INSTANCE_PYTHON_VERSION_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING,
|
||||
KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, MAVEN_REPOS_SETTING,
|
||||
MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NO_DEFAULT_MAVEN_SETTING,
|
||||
NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, OAUTH_SETTING, OTEL_SETTING,
|
||||
PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING,
|
||||
CRITICAL_ERROR_CHANNELS_SETTING, CUSTOM_TAGS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING,
|
||||
DEFAULT_TAGS_WORKSPACES_SETTING, EMAIL_DOMAIN_SETTING, ENV_SETTINGS,
|
||||
EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING,
|
||||
HUB_BASE_URL_SETTING, INDEXER_SETTING, INSTANCE_PYTHON_VERSION_SETTING,
|
||||
JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING,
|
||||
LICENSE_KEY_SETTING, MAVEN_REPOS_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING,
|
||||
NO_DEFAULT_MAVEN_SETTING, NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, OAUTH_SETTING,
|
||||
OTEL_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING,
|
||||
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, SMTP_SETTING, TEAMS_SETTING,
|
||||
TIMEOUT_WAIT_RESULT_SETTING,
|
||||
@@ -267,8 +265,6 @@ async fn windmill_main() -> anyhow::Result<()> {
|
||||
|
||||
if mode == Mode::Standalone {
|
||||
println!("Running in standalone mode");
|
||||
} else if mode == Mode::MCP {
|
||||
println!("Running in MCP mode");
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
|
||||
@@ -301,7 +297,7 @@ async fn windmill_main() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut num_workers = if mode == Mode::Server || mode == Mode::Indexer || mode == Mode::MCP {
|
||||
let mut num_workers = if mode == Mode::Server || mode == Mode::Indexer {
|
||||
0
|
||||
} else {
|
||||
std::env::var("NUM_WORKERS")
|
||||
@@ -323,9 +319,8 @@ async fn windmill_main() -> anyhow::Result<()> {
|
||||
&& (mode == Mode::Server || mode == Mode::Standalone);
|
||||
|
||||
let indexer_mode = mode == Mode::Indexer;
|
||||
let mcp_mode = mode == Mode::MCP;
|
||||
|
||||
let server_bind_address: IpAddr = if server_mode || indexer_mode || mcp_mode {
|
||||
let server_bind_address: IpAddr = if server_mode || indexer_mode {
|
||||
std::env::var("SERVER_BIND_ADDR")
|
||||
.ok()
|
||||
.and_then(|x| x.parse().ok())
|
||||
@@ -334,16 +329,26 @@ async fn windmill_main() -> anyhow::Result<()> {
|
||||
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
|
||||
};
|
||||
|
||||
let (conn, first_suffix) = if mode == Mode::Agent {
|
||||
tracing::info!(
|
||||
"Creating http client for cluster using base internal url {}",
|
||||
std::env::var("BASE_INTERNAL_URL").unwrap_or_default()
|
||||
);
|
||||
let mut first_worker_suffix = None;
|
||||
let mut worker_names = vec![];
|
||||
|
||||
for _ in 0..num_workers {
|
||||
let suffix = windmill_common::utils::worker_suffix(&hostname, &rd_string(5));
|
||||
(
|
||||
Connection::Http(build_agent_http_client(&suffix)),
|
||||
Some(suffix),
|
||||
)
|
||||
worker_names.push(windmill_common::utils::worker_name_with_suffix(
|
||||
mode == Mode::Agent,
|
||||
WORKER_GROUP.as_str(),
|
||||
&suffix,
|
||||
));
|
||||
if first_worker_suffix.is_none() {
|
||||
first_worker_suffix = Some(suffix);
|
||||
}
|
||||
}
|
||||
|
||||
let conn = if mode == Mode::Agent {
|
||||
let worker_suffix = first_worker_suffix.unwrap_or_else(|| {
|
||||
panic!("there must be at least one worker in agent mode");
|
||||
});
|
||||
Connection::Http(build_agent_http_client(&worker_suffix))
|
||||
} else {
|
||||
println!("Connecting to database...");
|
||||
|
||||
@@ -361,7 +366,7 @@ async fn windmill_main() -> anyhow::Result<()> {
|
||||
load_otel(&db).await;
|
||||
|
||||
tracing::info!("Database connected");
|
||||
(Connection::Sql(db), None)
|
||||
Connection::Sql(db)
|
||||
};
|
||||
|
||||
let environment = load_base_url(&conn)
|
||||
@@ -385,7 +390,7 @@ async fn windmill_main() -> anyhow::Result<()> {
|
||||
.is_some_and(|x| x == "1" || x == "true");
|
||||
|
||||
if let Some(db) = conn.as_sql() {
|
||||
if !is_agent && !indexer_mode && !mcp_mode {
|
||||
if !is_agent && !indexer_mode {
|
||||
let skip_migration = std::env::var("SKIP_MIGRATION")
|
||||
.map(|val| val == "true")
|
||||
.unwrap_or(false);
|
||||
@@ -404,7 +409,6 @@ async fn windmill_main() -> anyhow::Result<()> {
|
||||
let conn = if mode == Mode::Agent {
|
||||
conn
|
||||
} else {
|
||||
// This time we use a pool of connections
|
||||
let db = windmill_common::connect_db(server_mode, indexer_mode, worker_mode).await?;
|
||||
Connection::Sql(db)
|
||||
};
|
||||
@@ -435,6 +439,16 @@ Windmill Community Edition {GIT_VERSION}
|
||||
|
||||
display_config(&ENV_SETTINGS);
|
||||
|
||||
if let Err(e) = reload_base_url_setting(&conn).await {
|
||||
tracing::error!("Error loading base url: {:?}", e)
|
||||
}
|
||||
|
||||
if let Some(db) = conn.as_sql() {
|
||||
if let Err(e) = reload_critical_error_channels_setting(&db).await {
|
||||
tracing::error!("Could loading critical error emails setting: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
{
|
||||
// load the license key and check if it's valid
|
||||
@@ -448,7 +462,7 @@ Windmill Community Edition {GIT_VERSION}
|
||||
if !valid_key && !server_mode {
|
||||
tracing::error!("Invalid license key, workers require a valid license key");
|
||||
}
|
||||
if server_mode || mcp_mode {
|
||||
if server_mode {
|
||||
if let Some(db) = conn.as_sql() {
|
||||
// only force renewal if invalid but not empty (= expired)
|
||||
let renewed_now = maybe_renew_license_key_on_start(
|
||||
@@ -468,10 +482,10 @@ Windmill Community Edition {GIT_VERSION}
|
||||
}
|
||||
}
|
||||
|
||||
if server_mode || worker_mode || indexer_mode || mcp_mode {
|
||||
if server_mode || worker_mode || indexer_mode {
|
||||
let port_var = std::env::var("PORT").ok().and_then(|x| x.parse().ok());
|
||||
|
||||
let port = if server_mode || indexer_mode || mcp_mode {
|
||||
let port = if server_mode || indexer_mode {
|
||||
port_var.unwrap_or(DEFAULT_PORT as u16)
|
||||
} else {
|
||||
port_var.unwrap_or(0)
|
||||
@@ -652,7 +666,6 @@ Windmill Community Edition {GIT_VERSION}
|
||||
server_killpill_rx,
|
||||
base_internal_tx,
|
||||
server_mode,
|
||||
mcp_mode,
|
||||
base_internal_url.clone(),
|
||||
)
|
||||
.await?;
|
||||
@@ -673,34 +686,14 @@ Windmill Community Edition {GIT_VERSION}
|
||||
if !killpill_rx.try_recv().is_ok() {
|
||||
let base_internal_url = base_internal_rx.await?;
|
||||
if worker_mode {
|
||||
let mut workers = vec![];
|
||||
for i in 0..num_workers {
|
||||
let suffix: String = if i == 0 && first_suffix.as_ref().is_some() {
|
||||
first_suffix.as_ref().unwrap().clone()
|
||||
} else {
|
||||
windmill_common::utils::worker_suffix(&hostname, &rd_string(5))
|
||||
};
|
||||
let worker_conn = WorkerConn {
|
||||
conn: if i == 0 || mode != Mode::Agent {
|
||||
conn.clone()
|
||||
} else {
|
||||
Connection::Http(build_agent_http_client(&suffix))
|
||||
},
|
||||
worker_name: windmill_common::utils::worker_name_with_suffix(
|
||||
mode == Mode::Agent,
|
||||
WORKER_GROUP.as_str(),
|
||||
&suffix,
|
||||
),
|
||||
};
|
||||
workers.push(worker_conn);
|
||||
}
|
||||
|
||||
run_workers(
|
||||
conn.clone(),
|
||||
rx,
|
||||
killpill_tx.clone(),
|
||||
num_workers,
|
||||
base_internal_url.clone(),
|
||||
hostname.clone(),
|
||||
&workers,
|
||||
&worker_names,
|
||||
)
|
||||
.await?;
|
||||
tracing::info!("All workers exited.");
|
||||
@@ -919,12 +912,6 @@ Windmill Community Edition {GIT_VERSION}
|
||||
tracing::error!(error = %e, "Could not reload critical error emails setting");
|
||||
}
|
||||
},
|
||||
CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING => {
|
||||
if let Err(e) = reload_critical_alerts_on_db_oversize(&db).await {
|
||||
tracing::error!(error = %e, "Could not reload critical alerts on db oversize setting");
|
||||
}
|
||||
|
||||
},
|
||||
JWT_SECRET_SETTING => {
|
||||
if let Err(e) = reload_jwt_secret_setting(&db).await {
|
||||
tracing::error!(error = %e, "Could not reload jwt secret setting");
|
||||
@@ -1061,19 +1048,15 @@ Windmill Community Edition {GIT_VERSION}
|
||||
}
|
||||
}
|
||||
|
||||
if mcp_mode {
|
||||
futures::try_join!(shutdown_signal, workers_f, server_f)?;
|
||||
} else {
|
||||
futures::try_join!(
|
||||
shutdown_signal,
|
||||
workers_f,
|
||||
monitor_f,
|
||||
server_f,
|
||||
metrics_f,
|
||||
indexer_f,
|
||||
log_indexer_f
|
||||
)?;
|
||||
}
|
||||
futures::try_join!(
|
||||
shutdown_signal,
|
||||
workers_f,
|
||||
monitor_f,
|
||||
server_f,
|
||||
metrics_f,
|
||||
indexer_f,
|
||||
log_indexer_f
|
||||
)?;
|
||||
} else {
|
||||
tracing::info!("Nothing to do, exiting.");
|
||||
}
|
||||
@@ -1151,20 +1134,16 @@ fn display_config(envs: &[&str]) {
|
||||
)
|
||||
}
|
||||
|
||||
pub struct WorkerConn {
|
||||
conn: Connection,
|
||||
worker_name: String,
|
||||
}
|
||||
|
||||
pub async fn run_workers(
|
||||
db: Connection,
|
||||
mut rx: tokio::sync::broadcast::Receiver<()>,
|
||||
tx: KillpillSender,
|
||||
num_workers: i32,
|
||||
base_internal_url: String,
|
||||
hostname: String,
|
||||
workers: &[WorkerConn],
|
||||
worker_names: &[String],
|
||||
) -> anyhow::Result<()> {
|
||||
let mut killpill_rxs = vec![];
|
||||
let num_workers = workers.len();
|
||||
for _ in 0..num_workers {
|
||||
killpill_rxs.push(rx.resubscribe());
|
||||
}
|
||||
@@ -1222,12 +1201,9 @@ pub async fn run_workers(
|
||||
"Starting {num_workers} workers and SLEEP_QUEUE={}ms",
|
||||
*windmill_worker::SLEEP_QUEUE
|
||||
);
|
||||
|
||||
for i in 1..(num_workers + 1) {
|
||||
let wk_conf = &workers[i as usize - 1];
|
||||
let conn1 = wk_conf.conn.clone();
|
||||
let worker_name = wk_conf.worker_name.clone();
|
||||
WORKERS_NAMES.write().await.push(worker_name.clone());
|
||||
let db1 = db.clone();
|
||||
let worker_name = worker_names[i as usize - 1].clone();
|
||||
let ip = ip.clone();
|
||||
let rx = killpill_rxs.pop().unwrap();
|
||||
let tx = tx.clone();
|
||||
@@ -1240,7 +1216,7 @@ pub async fn run_workers(
|
||||
}
|
||||
|
||||
let f = windmill_worker::run_worker(
|
||||
&conn1,
|
||||
&db1,
|
||||
&hostname,
|
||||
worker_name,
|
||||
i as u64,
|
||||
|
||||
@@ -13,7 +13,7 @@ use std::{
|
||||
|
||||
use chrono::{NaiveDateTime, Utc};
|
||||
use futures::{stream::FuturesUnordered, StreamExt};
|
||||
use serde::{de::DeserializeOwned, Deserialize};
|
||||
use serde::de::DeserializeOwned;
|
||||
use sqlx::{Pool, Postgres};
|
||||
use tokio::{
|
||||
join,
|
||||
@@ -30,54 +30,28 @@ use windmill_api::{
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
use windmill_common::ee::{jobs_waiting_alerts, worker_groups_alerts};
|
||||
#[cfg(feature = "enterprise")]
|
||||
use windmill_common::ee::low_disk_alerts;
|
||||
|
||||
#[cfg(feature = "oauth2")]
|
||||
use windmill_common::global_settings::OAUTH_SETTING;
|
||||
use windmill_common::{
|
||||
agent_workers::DECODED_AGENT_TOKEN,
|
||||
auth::create_token_for_owner,
|
||||
ee::CriticalErrorChannel,
|
||||
error,
|
||||
flow_status::{FlowStatus, FlowStatusModule},
|
||||
global_settings::{
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING,
|
||||
CRITICAL_ALERT_MUTE_UI_SETTING, CRITICAL_ERROR_CHANNELS_SETTING,
|
||||
DEFAULT_TAGS_PER_WORKSPACE_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING,
|
||||
EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING,
|
||||
HUB_BASE_URL_SETTING, INSTANCE_PYTHON_VERSION_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING,
|
||||
JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING,
|
||||
MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING,
|
||||
OTEL_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
utils::empty_string_as_none,
|
||||
agent_workers::DECODED_AGENT_TOKEN, auth::create_token_for_owner, ee::CriticalErrorChannel, error, flow_status::{FlowStatus, FlowStatusModule}, global_settings::{
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING,
|
||||
CRITICAL_ERROR_CHANNELS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING,
|
||||
DEFAULT_TAGS_WORKSPACES_SETTING, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING,
|
||||
EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, INSTANCE_PYTHON_VERSION_SETTING,
|
||||
JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING,
|
||||
LICENSE_KEY_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NPM_CONFIG_REGISTRY_SETTING,
|
||||
NUGET_CONFIG_SETTING, OTEL_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING,
|
||||
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, TIMEOUT_WAIT_RESULT_SETTING,
|
||||
},
|
||||
indexer::load_indexer_config,
|
||||
jobs::QueuedJob,
|
||||
jwt::JWT_SECRET,
|
||||
oauth2::REQUIRE_PREEXISTING_USER_FOR_OAUTH,
|
||||
server::load_smtp_config,
|
||||
tracing_init::JSON_FMT,
|
||||
users::truncate_token,
|
||||
utils::empty_string_as_none,
|
||||
utils::{now_from_db, rd_string, report_critical_error, Mode},
|
||||
worker::{
|
||||
load_worker_config, reload_custom_tags_setting, store_pull_query,
|
||||
store_suspended_pull_query, update_min_version, Connection, DEFAULT_TAGS_PER_WORKSPACE,
|
||||
DEFAULT_TAGS_WORKSPACES, INDEXER_CONFIG, SCRIPT_TOKEN_EXPIRY, SMTP_CONFIG, TMP_DIR,
|
||||
WORKER_CONFIG, WORKER_GROUP,
|
||||
},
|
||||
KillpillSender, BASE_URL, CRITICAL_ALERTS_ON_DB_OVERSIZE, CRITICAL_ALERT_MUTE_UI_ENABLED,
|
||||
CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, JOB_RETENTION_SECS,
|
||||
METRICS_DEBUG_ENABLED, METRICS_ENABLED, MONITOR_LOGS_ON_OBJECT_STORE, OTEL_LOGS_ENABLED,
|
||||
OTEL_METRICS_ENABLED, OTEL_TRACING_ENABLED, SERVICE_LOG_RETENTION_SECS,
|
||||
}, indexer::load_indexer_config, jobs::QueuedJob, jwt::JWT_SECRET, oauth2::REQUIRE_PREEXISTING_USER_FOR_OAUTH, server::load_smtp_config, tracing_init::JSON_FMT, users::truncate_token, utils::{now_from_db, rd_string, report_critical_error, Mode}, worker::{
|
||||
load_worker_config, reload_custom_tags_setting, store_pull_query, store_suspended_pull_query, update_min_version, Connection, DEFAULT_TAGS_PER_WORKSPACE, DEFAULT_TAGS_WORKSPACES, INDEXER_CONFIG, SCRIPT_TOKEN_EXPIRY, SMTP_CONFIG, TMP_DIR, WORKER_CONFIG, WORKER_GROUP
|
||||
}, KillpillSender, BASE_URL, CRITICAL_ALERT_MUTE_UI_ENABLED, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, JOB_RETENTION_SECS, METRICS_DEBUG_ENABLED, METRICS_ENABLED, MONITOR_LOGS_ON_OBJECT_STORE, OTEL_LOGS_ENABLED, OTEL_METRICS_ENABLED, OTEL_TRACING_ENABLED, SERVICE_LOG_RETENTION_SECS,
|
||||
};
|
||||
use windmill_queue::{cancel_job, MiniPulledJob, SameWorkerPayload};
|
||||
use windmill_worker::{
|
||||
handle_job_error, AuthedClient, JobCompletedSender, SameWorkerSender, BUNFIG_INSTALL_SCOPES,
|
||||
INSTANCE_PYTHON_VERSION, JOB_DEFAULT_TIMEOUT, KEEP_JOB_DIR, MAVEN_REPOS, NO_DEFAULT_MAVEN,
|
||||
NPM_CONFIG_REGISTRY, NUGET_CONFIG, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL,
|
||||
handle_job_error, AuthedClient, JobCompletedSender, SameWorkerSender, BUNFIG_INSTALL_SCOPES, INSTANCE_PYTHON_VERSION, JOB_DEFAULT_TIMEOUT, KEEP_JOB_DIR, MAVEN_REPOS, NO_DEFAULT_MAVEN, NPM_CONFIG_REGISTRY, NUGET_CONFIG, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL
|
||||
};
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
@@ -137,7 +111,7 @@ lazy_static::lazy_static! {
|
||||
.and_then(|x| x.parse::<bool>().ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
pub static ref WORKERS_NAMES: Arc<RwLock<Vec<String>>> = Arc::new(RwLock::new(Vec::new()));
|
||||
|
||||
|
||||
static ref QUEUE_COUNT_TAGS: Arc<RwLock<Vec<String>>> = Arc::new(RwLock::new(Vec::new()));
|
||||
static ref DISABLE_CONCURRENCY_LIMIT: bool = std::env::var("DISABLE_CONCURRENCY_LIMIT").is_ok_and(|s| s == "true");
|
||||
@@ -161,6 +135,7 @@ pub async fn initial_load(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let Err(e) = load_metrics_enabled(conn).await {
|
||||
tracing::error!("Error loading expose metrics: {e:#}");
|
||||
}
|
||||
@@ -186,12 +161,6 @@ pub async fn initial_load(
|
||||
if server_mode {
|
||||
if let Some(db) = conn.as_sql() {
|
||||
load_require_preexisting_user(db).await;
|
||||
if let Err(e) = reload_critical_alerts_on_db_oversize(db).await {
|
||||
tracing::error!(
|
||||
"Error reloading critical alerts on db oversize setting: {:?}",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,13 +172,11 @@ pub async fn initial_load(
|
||||
}
|
||||
Connection::Http(_) => {
|
||||
// TODO: reload worker config from http
|
||||
WORKER_CONFIG.write().await.worker_tags = DECODED_AGENT_TOKEN
|
||||
.as_ref()
|
||||
.map(|x| x.tags.clone())
|
||||
.unwrap_or_default();
|
||||
WORKER_CONFIG.write().await.worker_tags = DECODED_AGENT_TOKEN.as_ref().map(|x| x.tags.clone()).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let Err(e) = reload_hub_base_url_setting(conn, server_mode).await {
|
||||
tracing::error!("Error reloading hub base url: {:?}", e)
|
||||
@@ -223,6 +190,7 @@ pub async fn initial_load(
|
||||
if let Err(e) = reload_custom_tags_setting(db).await {
|
||||
tracing::error!("Error reloading custom tags: {:?}", e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
@@ -257,8 +225,7 @@ pub async fn initial_load(
|
||||
}
|
||||
|
||||
pub async fn load_metrics_enabled(conn: &Connection) -> error::Result<()> {
|
||||
let metrics_enabled =
|
||||
load_value_from_global_settings_with_conn(conn, EXPOSE_METRICS_SETTING, true).await;
|
||||
let metrics_enabled = load_value_from_global_settings_with_conn(conn, EXPOSE_METRICS_SETTING, true).await;
|
||||
match metrics_enabled {
|
||||
Ok(Some(serde_json::Value::Bool(t))) => METRICS_ENABLED.store(t, Ordering::Relaxed),
|
||||
_ => (),
|
||||
@@ -378,13 +345,13 @@ pub async fn reload_critical_alert_mute_ui_setting(conn: &Connection) -> error::
|
||||
load_value_from_global_settings_with_conn(conn, CRITICAL_ALERT_MUTE_UI_SETTING, true).await
|
||||
{
|
||||
CRITICAL_ALERT_MUTE_UI_ENABLED.store(t, Ordering::Relaxed);
|
||||
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn load_metrics_debug_enabled(conn: &Connection) -> error::Result<()> {
|
||||
let metrics_enabled =
|
||||
load_value_from_global_settings_with_conn(conn, EXPOSE_DEBUG_METRICS_SETTING, true).await;
|
||||
let metrics_enabled = load_value_from_global_settings_with_conn(conn, EXPOSE_DEBUG_METRICS_SETTING, true).await;
|
||||
match metrics_enabled {
|
||||
Ok(Some(serde_json::Value::Bool(t))) => {
|
||||
METRICS_DEBUG_ENABLED.store(t, Ordering::Relaxed);
|
||||
@@ -608,9 +575,7 @@ async fn send_log_file_to_object_store(
|
||||
};
|
||||
|
||||
let exists = LAST_LOG_FILE_SENT.lock().map(|last_log_file_sent| {
|
||||
last_log_file_sent
|
||||
.map(|last_log_file_sent| last_log_file_sent >= ts)
|
||||
.unwrap_or(false)
|
||||
last_log_file_sent.map(|last_log_file_sent| last_log_file_sent >= ts).unwrap_or(false)
|
||||
});
|
||||
|
||||
if exists.unwrap_or(false) {
|
||||
@@ -647,9 +612,7 @@ async fn send_log_file_to_object_store(
|
||||
let (ok_lines, err_lines) = read_log_counters(ts_str);
|
||||
|
||||
if let Some(db) = conn.as_sql() {
|
||||
if let Err(e) = sqlx::query!("INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt)
|
||||
VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)
|
||||
ON CONFLICT (hostname, log_ts) DO UPDATE SET ok_lines = log_file.ok_lines + $6, err_lines = log_file.err_lines + $7",
|
||||
if let Err(e) = sqlx::query!("INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)",
|
||||
hostname, mode.to_string(), worker_group.clone(), ts, highest_file, ok_lines as i64, err_lines as i64, *JSON_FMT)
|
||||
.execute(db)
|
||||
.await {
|
||||
@@ -1039,21 +1002,11 @@ pub async fn reload_nuget_config_setting(conn: &Connection) {
|
||||
.await;
|
||||
}
|
||||
pub async fn reload_maven_repos_setting(conn: &Connection) {
|
||||
reload_option_setting_with_tracing(
|
||||
conn,
|
||||
windmill_common::global_settings::MAVEN_REPOS_SETTING,
|
||||
"MAVEN_REPOS",
|
||||
MAVEN_REPOS.clone(),
|
||||
)
|
||||
.await;
|
||||
reload_option_setting_with_tracing(conn, windmill_common::global_settings::MAVEN_REPOS_SETTING, "MAVEN_REPOS", MAVEN_REPOS.clone())
|
||||
.await;
|
||||
}
|
||||
pub async fn reload_no_default_maven_setting(conn: &Connection) {
|
||||
let value = load_value_from_global_settings_with_conn(
|
||||
conn,
|
||||
windmill_common::global_settings::NO_DEFAULT_MAVEN_SETTING,
|
||||
true,
|
||||
)
|
||||
.await;
|
||||
let value = load_value_from_global_settings_with_conn(conn, windmill_common::global_settings::NO_DEFAULT_MAVEN_SETTING, true).await;
|
||||
match value {
|
||||
Ok(Some(serde_json::Value::Bool(t))) => NO_DEFAULT_MAVEN.store(t, Ordering::Relaxed),
|
||||
Err(e) => {
|
||||
@@ -1222,6 +1175,7 @@ pub async fn load_value_from_global_settings(
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
|
||||
pub async fn load_value_from_global_settings_with_conn(
|
||||
conn: &Connection,
|
||||
setting_name: &str,
|
||||
@@ -1231,18 +1185,14 @@ pub async fn load_value_from_global_settings_with_conn(
|
||||
Connection::Sql(db) => Ok(load_value_from_global_settings(db, setting_name).await?),
|
||||
Connection::Http(client) => {
|
||||
if load_from_http {
|
||||
client
|
||||
.get::<Option<serde_json::Value>>(&format!(
|
||||
"/api/agent_workers/get_global_setting/{}",
|
||||
setting_name
|
||||
))
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Error loading setting {}: {}", setting_name, e))
|
||||
client.get::<Option<serde_json::Value>>(&format!("/api/agent_workers/get_global_setting/{}", setting_name)).await
|
||||
.map_err(|e| anyhow::anyhow!("Error loading setting {}: {}", setting_name, e))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub async fn reload_option_setting<T: FromStr + DeserializeOwned>(
|
||||
@@ -1364,12 +1314,12 @@ pub async fn monitor_db(
|
||||
let zombie_jobs_f = async {
|
||||
if server_mode && !initial_load && !*DISABLE_ZOMBIE_JOBS_MONITORING {
|
||||
if let Some(db) = conn.as_sql() {
|
||||
handle_zombie_jobs(db, base_internal_url, "server").await;
|
||||
match handle_zombie_flows(db).await {
|
||||
Err(err) => {
|
||||
tracing::error!("Error handling zombie flows: {:?}", err);
|
||||
}
|
||||
_ => {}
|
||||
handle_zombie_jobs(db, base_internal_url, "server").await;
|
||||
match handle_zombie_flows(db).await {
|
||||
Err(err) => {
|
||||
tracing::error!("Error handling zombie flows: {:?}", err);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1377,7 +1327,7 @@ pub async fn monitor_db(
|
||||
let expired_items_f = async {
|
||||
if server_mode && !initial_load {
|
||||
if let Some(db) = conn.as_sql() {
|
||||
delete_expired_items(&db).await;
|
||||
delete_expired_items(&db).await;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1415,23 +1365,6 @@ pub async fn monitor_db(
|
||||
}
|
||||
};
|
||||
|
||||
let low_disk_alerts_f = async {
|
||||
#[cfg(feature = "enterprise")]
|
||||
if let Some(db) = conn.as_sql() {
|
||||
low_disk_alerts(
|
||||
&db,
|
||||
server_mode,
|
||||
_worker_mode,
|
||||
WORKERS_NAMES.read().await.clone(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
{
|
||||
()
|
||||
}
|
||||
};
|
||||
|
||||
let apply_autoscaling_f = async {
|
||||
#[cfg(feature = "enterprise")]
|
||||
if server_mode && !initial_load {
|
||||
@@ -1454,7 +1387,6 @@ pub async fn monitor_db(
|
||||
verify_license_key_f,
|
||||
worker_groups_alerts_f,
|
||||
jobs_waiting_alerts_f,
|
||||
low_disk_alerts_f,
|
||||
apply_autoscaling_f,
|
||||
update_min_worker_version_f,
|
||||
);
|
||||
@@ -1564,7 +1496,11 @@ pub async fn reload_indexer_config(db: &Pool<Postgres>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn reload_worker_config(db: &DB, tx: KillpillSender, kill_if_change: bool) {
|
||||
pub async fn reload_worker_config(
|
||||
db: &DB,
|
||||
tx: KillpillSender,
|
||||
kill_if_change: bool,
|
||||
) {
|
||||
let config = load_worker_config(db, tx.clone()).await;
|
||||
if let Err(e) = config {
|
||||
tracing::error!("Error reloading worker config: {:?}", e)
|
||||
@@ -1607,8 +1543,7 @@ pub async fn reload_worker_config(db: &DB, tx: KillpillSender, kill_if_change: b
|
||||
}
|
||||
|
||||
pub async fn load_base_url(conn: &Connection) -> error::Result<String> {
|
||||
let q_base_url =
|
||||
load_value_from_global_settings_with_conn(conn, BASE_URL_SETTING, false).await?;
|
||||
let q_base_url = load_value_from_global_settings_with_conn(conn, BASE_URL_SETTING, false).await?;
|
||||
|
||||
let std_base_url = std::env::var("BASE_URL")
|
||||
.ok()
|
||||
@@ -1639,9 +1574,10 @@ pub async fn load_base_url(conn: &Connection) -> error::Result<String> {
|
||||
}
|
||||
|
||||
pub async fn reload_base_url_setting(conn: &Connection) -> error::Result<()> {
|
||||
|
||||
#[cfg(feature = "oauth2")]
|
||||
let oauths = if let Some(db) = conn.as_sql() {
|
||||
let q_oauth = load_value_from_global_settings(db, OAUTH_SETTING).await?;
|
||||
let q_oauth = load_value_from_global_settings (db, OAUTH_SETTING).await?;
|
||||
|
||||
if let Some(q) = q_oauth {
|
||||
if let Ok(v) = serde_json::from_value::<
|
||||
@@ -1716,7 +1652,7 @@ async fn handle_zombie_jobs(db: &Pool<Postgres>, base_internal_url: &str, worker
|
||||
increment_counter AS (
|
||||
INSERT INTO zombie_job_counter (job_id, counter)
|
||||
SELECT id, 1 FROM to_update WHERE counter < $2
|
||||
ON CONFLICT (job_id) DO UPDATE
|
||||
ON CONFLICT (job_id) DO UPDATE
|
||||
SET counter = zombie_job_counter.counter + 1
|
||||
),
|
||||
update_concurrency AS (
|
||||
@@ -1808,7 +1744,7 @@ async fn handle_zombie_jobs(db: &Pool<Postgres>, base_internal_url: &str, worker
|
||||
|
||||
let same_worker_timeout_jobs = {
|
||||
let long_same_worker_jobs = sqlx::query!(
|
||||
"SELECT worker, array_agg(v2_job_queue.id) as ids FROM v2_job_queue LEFT JOIN v2_job ON v2_job_queue.id = v2_job.id LEFT JOIN v2_job_runtime ON v2_job_queue.id = v2_job_runtime.id WHERE v2_job_queue.created_at < now() - ('60 seconds')::interval
|
||||
"SELECT worker, array_agg(v2_job_queue.id) as ids FROM v2_job_queue LEFT JOIN v2_job ON v2_job_queue.id = v2_job.id LEFT JOIN v2_job_runtime ON v2_job_queue.id = v2_job_runtime.id WHERE v2_job_queue.created_at < now() - ('60 seconds')::interval
|
||||
AND running = true AND ping IS NULL AND same_worker = true AND worker IS NOT NULL GROUP BY worker",
|
||||
)
|
||||
.fetch_all(db)
|
||||
@@ -1822,9 +1758,9 @@ async fn handle_zombie_jobs(db: &Pool<Postgres>, base_internal_url: &str, worker
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let long_dead_workers: std::collections::HashSet<String> = sqlx::query_scalar!(
|
||||
"WITH worker_ids AS (SELECT unnest($1::text[]) as worker)
|
||||
SELECT worker_ids.worker FROM worker_ids
|
||||
LEFT JOIN worker_ping ON worker_ids.worker = worker_ping.worker
|
||||
"WITH worker_ids AS (SELECT unnest($1::text[]) as worker)
|
||||
SELECT worker_ids.worker FROM worker_ids
|
||||
LEFT JOIN worker_ping ON worker_ids.worker = worker_ping.worker
|
||||
WHERE worker_ping.worker IS NULL OR worker_ping.ping_at < now() - ('60 seconds')::interval",
|
||||
&worker_ids[..]
|
||||
)
|
||||
@@ -1868,7 +1804,7 @@ async fn handle_zombie_jobs(db: &Pool<Postgres>, base_internal_url: &str, worker
|
||||
let non_restartable_jobs = if *RESTART_ZOMBIE_JOBS {
|
||||
vec![]
|
||||
} else {
|
||||
sqlx::query_as::<_, QueuedJob>("SELECT * FROM v2_as_queue WHERE last_ping < now() - ($1 || ' seconds')::interval
|
||||
sqlx::query_as::<_, QueuedJob>("SELECT * FROM v2_as_queue WHERE last_ping < now() - ($1 || ' seconds')::interval
|
||||
AND running = true AND job_kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow') AND same_worker = false")
|
||||
.bind(ZOMBIE_JOB_TIMEOUT.as_str())
|
||||
.fetch_all(db)
|
||||
@@ -1927,8 +1863,7 @@ async fn handle_zombie_jobs(db: &Pool<Postgres>, base_internal_url: &str, worker
|
||||
mpsc::channel::<SameWorkerPayload>(1);
|
||||
let same_worker_tx_never_used =
|
||||
SameWorkerSender(same_worker_tx_never_used, Arc::new(AtomicU16::new(0)));
|
||||
let (send_result_never_used, _send_result_rx_never_used) =
|
||||
JobCompletedSender::new_never_used();
|
||||
let (send_result_never_used, _send_result_rx_never_used) = JobCompletedSender::new_never_used();
|
||||
|
||||
let label = if job.permissioned_as != format!("u/{}", job.created_by)
|
||||
&& job.permissioned_as != job.created_by
|
||||
@@ -1979,7 +1914,7 @@ async fn handle_zombie_jobs(db: &Pool<Postgres>, base_internal_url: &str, worker
|
||||
worker_name,
|
||||
send_result_never_used,
|
||||
#[cfg(feature = "benchmark")]
|
||||
&mut windmill_common::bench::BenchmarkIter::new(),
|
||||
&mut windmill_worker::bench::BenchmarkIter::new(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -2066,7 +2001,7 @@ async fn handle_zombie_flows(db: &DB) -> error::Result<()> {
|
||||
}
|
||||
);
|
||||
report_critical_error(reason.clone(), db.clone(), Some(&flow.workspace_id), None).await;
|
||||
cancel_zombie_flow_job(db, flow.id, &flow.workspace_id,
|
||||
cancel_zombie_flow_job(db, flow.id, &flow.workspace_id,
|
||||
format!(r#"{reason}
|
||||
This would happen if a worker was interrupted, killed or crashed while doing a state transition at the end of a job which is always an unexpected behavior that should never happen.
|
||||
Please check your worker logs for more details and feel free to report it to the Windmill team on our Discord or support@windmill.dev (response for non EE customers will be best effort) with as much context as possible, ideally:
|
||||
@@ -2083,7 +2018,7 @@ Please check your worker logs for more details and feel free to report it to the
|
||||
r#"
|
||||
DELETE
|
||||
FROM parallel_monitor_lock
|
||||
WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval
|
||||
WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval
|
||||
RETURNING parent_flow_id, job_id, last_ping, (SELECT workspace_id FROM v2_job_queue q
|
||||
WHERE q.id = parent_flow_id AND q.running = true AND q.canceled_by IS NULL
|
||||
) AS workspace_id
|
||||
@@ -2138,12 +2073,8 @@ async fn cancel_zombie_flow_job(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn reload_hub_base_url_setting(
|
||||
conn: &Connection,
|
||||
server_mode: bool,
|
||||
) -> error::Result<()> {
|
||||
let hub_base_url =
|
||||
load_value_from_global_settings_with_conn(conn, HUB_BASE_URL_SETTING, true).await?;
|
||||
pub async fn reload_hub_base_url_setting(conn: &Connection, server_mode: bool) -> error::Result<()> {
|
||||
let hub_base_url = load_value_from_global_settings_with_conn(conn, HUB_BASE_URL_SETTING, true).await?;
|
||||
|
||||
let base_url = if let Some(q) = hub_base_url {
|
||||
if let Ok(v) = serde_json::from_value::<String>(q.clone()) {
|
||||
@@ -2195,7 +2126,7 @@ pub async fn reload_critical_error_channels_setting(conn: &DB) -> error::Result<
|
||||
v
|
||||
} else {
|
||||
tracing::error!(
|
||||
"Could not parse critical_error_channels setting as an array of channels, found: {:#?}",
|
||||
"Could not parse critical_error_emails setting as an array of channels, found: {:#?}",
|
||||
&q
|
||||
);
|
||||
vec![]
|
||||
@@ -2210,37 +2141,6 @@ pub async fn reload_critical_error_channels_setting(conn: &DB) -> error::Result<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn reload_critical_alerts_on_db_oversize(conn: &DB) -> error::Result<()> {
|
||||
#[derive(Deserialize)]
|
||||
struct DBOversize {
|
||||
enabled: bool,
|
||||
value: f32,
|
||||
}
|
||||
let db_oversize_value =
|
||||
load_value_from_global_settings(conn, CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING).await?;
|
||||
|
||||
let db_oversize = if let Some(q) = db_oversize_value {
|
||||
match serde_json::from_value::<DBOversize>(q.clone()) {
|
||||
Ok(DBOversize { enabled: true, value }) => Some(value),
|
||||
Ok(_) => None,
|
||||
Err(q) => {
|
||||
tracing::error!(
|
||||
"Could not parse critical_alerts_on_db_oversize setting, found: {:#?}",
|
||||
&q
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut l = CRITICAL_ALERTS_ON_DB_OVERSIZE.write().await;
|
||||
*l = db_oversize;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn generate_and_save_jwt_secret(db: &DB) -> error::Result<String> {
|
||||
let secret = rd_string(32);
|
||||
sqlx::query!(
|
||||
|
||||
51
backend/tests/fixtures/lockfile_python.sql
vendored
51
backend/tests/fixtures/lockfile_python.sql
vendored
@@ -1,51 +0,0 @@
|
||||
INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
|
||||
'test-workspace',
|
||||
'test-user',
|
||||
'
|
||||
# requirements:
|
||||
# microdot==2.2.0
|
||||
|
||||
import pandas
|
||||
import requests
|
||||
import tiny # pin: tiny==0.1.2
|
||||
|
||||
def main():
|
||||
pass
|
||||
',
|
||||
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
|
||||
'',
|
||||
'',
|
||||
'f/system/requirements', 12346, 'python3', '');
|
||||
|
||||
INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
|
||||
'test-workspace',
|
||||
'test-user',
|
||||
'
|
||||
# extra_requirements:
|
||||
# bottle==0.13.2
|
||||
|
||||
import tiny
|
||||
|
||||
def main():
|
||||
pass
|
||||
',
|
||||
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
|
||||
'',
|
||||
'',
|
||||
'f/system/extra_requirements', 12347, 'python3', '');
|
||||
|
||||
|
||||
INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
|
||||
'test-workspace',
|
||||
'test-user',
|
||||
'
|
||||
import tiny # pin: bottle==0.13.2
|
||||
import simplejson # pin: simplejson==3.19.3
|
||||
|
||||
def main():
|
||||
return [test1(), test2(), test3(), test4()]
|
||||
',
|
||||
'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
|
||||
'',
|
||||
'',
|
||||
'f/system/pins', 12348, 'python3', '');
|
||||
@@ -16,20 +16,16 @@ use tokio::sync::RwLock;
|
||||
#[cfg(feature = "enterprise")]
|
||||
use tokio::time::{timeout, Duration};
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
use windmill_api_client::types::{CreateFlowBody, RawScript};
|
||||
#[cfg(feature = "enterprise")]
|
||||
use windmill_api_client::types::{EditSchedule, NewSchedule, ScriptArgs};
|
||||
use windmill_api_client::types::{NewScript, ScriptLang as NewScriptLanguage};
|
||||
|
||||
use serde::Serialize;
|
||||
#[cfg(feature = "deno_core")]
|
||||
use windmill_common::flows::InputTransform;
|
||||
use windmill_common::worker::WORKER_CONFIG;
|
||||
|
||||
use windmill_common::{
|
||||
flow_status::{FlowStatus, FlowStatusModule, RestartedFrom},
|
||||
flows::{FlowModule, FlowModuleValue, FlowValue},
|
||||
flows::{FlowModule, FlowModuleValue, FlowValue, InputTransform},
|
||||
jobs::{JobKind, JobPayload, RawCode},
|
||||
jwt::JWT_SECRET,
|
||||
scripts::{ScriptHash, ScriptLang},
|
||||
@@ -141,11 +137,10 @@ impl ApiServer {
|
||||
rx,
|
||||
port_tx,
|
||||
false,
|
||||
false,
|
||||
format!("http://localhost:{}", addr.port()),
|
||||
));
|
||||
|
||||
_port_rx.await.expect("failed to receive port");
|
||||
_port_rx.await.unwrap();
|
||||
|
||||
// clear the cache between tests
|
||||
windmill_common::cache::clear();
|
||||
@@ -171,7 +166,6 @@ impl ApiServer {
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
fn get_module(cjob: &CompletedJob, id: &str) -> Option<FlowStatusModule> {
|
||||
cjob.flow_status.clone().and_then(|fs| {
|
||||
find_module_in_vec(
|
||||
@@ -181,7 +175,6 @@ fn get_module(cjob: &CompletedJob, id: &str) -> Option<FlowStatusModule> {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
fn find_module_in_vec(modules: Vec<FlowStatusModule>, id: &str) -> Option<FlowStatusModule> {
|
||||
modules.into_iter().find(|s| s.id() == id)
|
||||
}
|
||||
@@ -290,7 +283,6 @@ mod suspend_resume {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -373,7 +365,6 @@ mod suspend_resume {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn cancel_from_job(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -399,7 +390,6 @@ mod suspend_resume {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn cancel_after_suspend(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -573,7 +563,6 @@ def main(last, port):
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_pass(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -619,7 +608,6 @@ def main(last, port):
|
||||
assert_eq!(json!([3, 5, 7, 9]), result);
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_fail_step_zero(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -663,7 +651,6 @@ def main(last, port):
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_fail_step_one(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -705,7 +692,6 @@ def main(last, port):
|
||||
.contains("index out of range"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_with_failure_module(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -782,7 +768,6 @@ def main(error, port):
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_iteration(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -841,7 +826,6 @@ async fn test_iteration(db: Pool<Postgres>) {
|
||||
.contains("2"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_iteration_parallel(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -1120,7 +1104,6 @@ trait StreamFind: futures::Stream + Unpin + Sized {
|
||||
|
||||
impl<T: futures::Stream + Unpin + Sized> StreamFind for T {}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_deno_flow(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -1239,7 +1222,6 @@ async fn test_deno_flow(db: Pool<Postgres>) {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_identity(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -1277,7 +1259,6 @@ async fn test_identity(db: Pool<Postgres>) {
|
||||
assert_eq!(result, serde_json::json!(42));
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_deno_flow_same_worker(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -1553,7 +1534,6 @@ async fn test_flow_result_by_id(db: Pool<Postgres>) {
|
||||
assert_eq!(result, serde_json::json!([[42]]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_stop_after_if(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -1607,7 +1587,6 @@ async fn test_stop_after_if(db: Pool<Postgres>) {
|
||||
assert_eq!(json!(-123), result);
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_stop_after_if_nested(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -1666,7 +1645,6 @@ async fn test_stop_after_if_nested(db: Pool<Postgres>) {
|
||||
assert_eq!(json!([-123]), result);
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "deno_core", feature = "python"))]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_python_flow(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -1724,7 +1702,6 @@ async fn test_python_flow(db: Pool<Postgres>) {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_python_flow_2(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -1799,7 +1776,6 @@ func main(derp string) (string, error) {
|
||||
assert_eq!(result, serde_json::json!("hello world"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "rust")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_rust_job(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -2055,7 +2031,6 @@ public class Main {
|
||||
assert_eq!(job.json_result(), Some(json!("hello world")));
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_python_job(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -2089,7 +2064,6 @@ def main():
|
||||
assert_eq!(result, serde_json::json!("hello world"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_python_job_heavy_dep(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -2126,7 +2100,6 @@ def main():
|
||||
assert_eq!(result, serde_json::json!(3));
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_python_job_with_imports(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -2230,7 +2203,6 @@ export async function main(a: Date) {
|
||||
assert_eq!(result, serde_json::json!("object"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_python_job_datetime_and_bytes(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -2266,7 +2238,6 @@ def main(a: datetime, b: bytes):
|
||||
assert_eq!(result, serde_json::json!([true, true]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_empty_loop_1(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -2323,7 +2294,6 @@ async fn test_empty_loop_1(db: Pool<Postgres>) {
|
||||
assert_eq!(result, serde_json::json!(0));
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_invalid_first_step(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -2404,7 +2374,6 @@ async fn test_empty_loop_2(db: Pool<Postgres>) {
|
||||
assert_eq!(result, serde_json::json!([]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_step_after_loop(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -2528,7 +2497,6 @@ async fn test_branchone_simple(db: Pool<Postgres>) {
|
||||
assert_eq!(result, serde_json::json!([1, 2]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_branchone_with_cond(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -2565,7 +2533,6 @@ async fn test_branchone_with_cond(db: Pool<Postgres>) {
|
||||
assert_eq!(result, serde_json::json!([1, 3]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_branchall_sequential(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -2604,7 +2571,6 @@ async fn test_branchall_sequential(db: Pool<Postgres>) {
|
||||
assert_eq!(result, serde_json::json!([[1, 2], [1, 3]]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_branchall_simple(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -2732,7 +2698,6 @@ async fn test_branchall_skip_failure(db: Pool<Postgres>) {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_branchone_nested(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -2852,7 +2817,6 @@ async fn test_branchall_nested(db: Pool<Postgres>) {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_failure_module(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -2965,7 +2929,6 @@ async fn test_failure_module(db: Pool<Postgres>) {
|
||||
assert_eq!(json!({ "l": [0, 1, 2] }), result);
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_flow_lock_all(db: Pool<Postgres>) {
|
||||
use futures::StreamExt;
|
||||
@@ -3104,7 +3067,6 @@ async fn test_flow_lock_all(db: Pool<Postgres>) {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
|
||||
async fn test_complex_flow_restart(db: Pool<Postgres>) {
|
||||
@@ -3789,7 +3751,6 @@ export async function main() {
|
||||
run_preview_relative_imports(&db, content, ScriptLang::Bun).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base", "relative_bun"))]
|
||||
async fn test_nested_imports_bun(db: Pool<Postgres>) {
|
||||
let content = r#"
|
||||
@@ -3838,7 +3799,6 @@ export async function main() {
|
||||
run_preview_relative_imports(&db, content, ScriptLang::Deno).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base", "relative_python"))]
|
||||
async fn test_relative_imports_python(db: Pool<Postgres>) {
|
||||
let content = r#"
|
||||
@@ -3856,7 +3816,6 @@ def main():
|
||||
run_preview_relative_imports(&db, content, ScriptLang::Python3).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base", "relative_python"))]
|
||||
async fn test_nested_imports_python(db: Pool<Postgres>) {
|
||||
let content = r#"
|
||||
@@ -3872,212 +3831,6 @@ def main():
|
||||
run_preview_relative_imports(&db, content, ScriptLang::Python3).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
async fn assert_lockfile(
|
||||
db: &Pool<Postgres>,
|
||||
script_content: String,
|
||||
language: ScriptLang,
|
||||
expected_lines: Vec<&str>,
|
||||
) {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await;
|
||||
let port = server.addr.port();
|
||||
let client = windmill_api_client::create_client(
|
||||
&format!("http://localhost:{port}"),
|
||||
"SECRET_TOKEN".to_string(),
|
||||
);
|
||||
|
||||
client
|
||||
.create_script(
|
||||
"test-workspace",
|
||||
&NewScript {
|
||||
language: NewScriptLanguage::from_str(language.as_str()).unwrap(),
|
||||
content: script_content,
|
||||
path: "f/system/test_import".to_string(),
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None,
|
||||
description: "".to_string(),
|
||||
draft_only: None,
|
||||
envs: vec![],
|
||||
is_template: None,
|
||||
kind: None,
|
||||
parent_hash: None,
|
||||
lock: None,
|
||||
summary: "".to_string(),
|
||||
tag: None,
|
||||
schema: std::collections::HashMap::new(),
|
||||
ws_error_handler_muted: Some(false),
|
||||
priority: None,
|
||||
delete_after_use: None,
|
||||
timeout: None,
|
||||
restart_unless_cancelled: None,
|
||||
deployment_message: None,
|
||||
concurrency_key: None,
|
||||
visible_to_runner_only: None,
|
||||
no_main_func: None,
|
||||
codebase: None,
|
||||
has_preprocessor: None,
|
||||
on_behalf_of_email: None,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut completed = listen_for_completed_jobs(&db).await;
|
||||
let db2 = db.clone();
|
||||
in_test_worker(
|
||||
&db,
|
||||
async move {
|
||||
completed.next().await; // deployed script
|
||||
|
||||
let script = sqlx::query!(
|
||||
"SELECT hash FROM script WHERE path = $1",
|
||||
"f/system/test_import".to_string()
|
||||
)
|
||||
.fetch_one(&db2)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let job = RunJob::from(JobPayload::Dependencies {
|
||||
path: "f/system/test_import".to_string(),
|
||||
hash: ScriptHash(script.hash),
|
||||
dedicated_worker: None,
|
||||
language,
|
||||
})
|
||||
.push(&db2)
|
||||
.await;
|
||||
|
||||
completed.next().await; // completed job
|
||||
|
||||
let result = completed_job(job, &db2).await.json_result().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
json!({
|
||||
"lock": expected_lines.join("\n"),
|
||||
"status": "Successful lock file generation"
|
||||
})
|
||||
);
|
||||
},
|
||||
port,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base", "lockfile_python"))]
|
||||
async fn test_requirements_python(db: Pool<Postgres>) {
|
||||
let content = r#"
|
||||
# py311
|
||||
# requirements:
|
||||
# tiny==0.1.3
|
||||
|
||||
import bar
|
||||
import baz # pin: foo
|
||||
import baz # repin: fee
|
||||
import bug # repin: free
|
||||
|
||||
def main():
|
||||
pass
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
assert_lockfile(
|
||||
&db,
|
||||
content,
|
||||
ScriptLang::Python3,
|
||||
vec!["# py311", "tiny==0.1.3"],
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base", "lockfile_python"))]
|
||||
async fn test_extra_requirements_python(db: Pool<Postgres>) {
|
||||
{
|
||||
let content = r#"
|
||||
# py311
|
||||
# extra_requirements:
|
||||
# tiny
|
||||
|
||||
import f.system.extra_requirements
|
||||
import tiny # pin: tiny==0.1.0
|
||||
import tiny # pin: tiny==0.1.1
|
||||
import tiny # repin: tiny==0.1.2
|
||||
|
||||
def main():
|
||||
pass
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
assert_lockfile(
|
||||
&db,
|
||||
content,
|
||||
ScriptLang::Python3,
|
||||
vec!["# py311", "bottle==0.13.2", "tiny==0.1.2"],
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base", "lockfile_python"))]
|
||||
async fn test_extra_requirements_python2(db: Pool<Postgres>) {
|
||||
let content = r#"
|
||||
# py311
|
||||
# extra_requirements:
|
||||
# tiny==0.1.3
|
||||
|
||||
import simplejson # pin: simplejson==3.20.1
|
||||
def main():
|
||||
pass
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
assert_lockfile(
|
||||
&db,
|
||||
content,
|
||||
ScriptLang::Python3,
|
||||
vec!["# py311", "simplejson==3.20.1", "tiny==0.1.3"],
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base", "lockfile_python"))]
|
||||
async fn test_pins_python(db: Pool<Postgres>) {
|
||||
let content = r#"
|
||||
# py311
|
||||
# extra_requirements:
|
||||
# tiny==0.1.3
|
||||
# bottle==0.13.2
|
||||
|
||||
import f.system.requirements
|
||||
import f.system.pins
|
||||
import tiny # repin: tiny==0.1.3
|
||||
import simplejson
|
||||
|
||||
def main():
|
||||
pass
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
assert_lockfile(
|
||||
&db,
|
||||
content,
|
||||
ScriptLang::Python3,
|
||||
vec![
|
||||
"# py311",
|
||||
"bottle==0.13.2",
|
||||
"microdot==2.2.0",
|
||||
"simplejson==3.19.3",
|
||||
"tiny==0.1.3",
|
||||
],
|
||||
)
|
||||
.await;
|
||||
}
|
||||
#[sqlx::test(fixtures("base", "result_format"))]
|
||||
async fn test_result_format(db: Pool<Postgres>) {
|
||||
let ordered_result_job_id = "1eecb96a-c8b0-4a3d-b1b6-087878c55e41";
|
||||
@@ -4329,7 +4082,6 @@ mod job_payload {
|
||||
];
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base", "hello"))]
|
||||
async fn test_script_hash_payload(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -4490,7 +4242,6 @@ mod job_payload {
|
||||
test_for_versions(VERSION_FLAGS.iter().cloned(), test).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base", "hello"))]
|
||||
async fn test_flow_node_payload(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -4675,7 +4426,6 @@ mod job_payload {
|
||||
test_for_versions(VERSION_FLAGS.iter().cloned(), test).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base", "hello"))]
|
||||
async fn test_flow_payload(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -4718,7 +4468,6 @@ mod job_payload {
|
||||
test_for_versions(VERSION_FLAGS.iter().cloned(), test).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base", "hello"))]
|
||||
async fn test_flow_payload_with_preprocessor(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -4786,7 +4535,6 @@ mod job_payload {
|
||||
test_for_versions(VERSION_FLAGS.iter().cloned(), test).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base", "hello"))]
|
||||
async fn test_restarted_flow_payload(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -4839,7 +4587,6 @@ mod job_payload {
|
||||
test_for_versions(VERSION_FLAGS.iter().cloned(), test).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base", "hello"))]
|
||||
async fn test_raw_flow_payload(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -4886,7 +4633,6 @@ mod job_payload {
|
||||
test_for_versions(VERSION_FLAGS.iter().cloned(), test).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[sqlx::test(fixtures("base", "hello"))]
|
||||
async fn test_raw_flow_payload_with_restarted_from(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
|
||||
@@ -13,9 +13,9 @@ default = []
|
||||
enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker/enterprise"]
|
||||
stripe = []
|
||||
agent_worker_server = []
|
||||
enterprise_saml = ["dep:samael", "dep:libxml"]
|
||||
enterprise_saml = ["dep:samael"]
|
||||
benchmark = []
|
||||
embedding = ["dep:tinyvector", "dep:hf-hub", "dep:tokenizers", "dep:candle-core", "dep:candle-transformers", "dep:candle-nn"]
|
||||
embedding = ["dep:tinyvector", "dep:hf-hub", "dep:tokenizers", "dep:candle-core", "dep:candle-transformers", "dep:candle-nn", "dep:half"]
|
||||
parquet = ["dep:datafusion", "dep:object_store", "dep:url", "windmill-common/parquet", "windmill-worker/parquet"]
|
||||
prometheus = ["windmill-common/prometheus", "windmill-queue/prometheus", "dep:prometheus", "windmill-worker/prometheus"]
|
||||
openidconnect = ["dep:openidconnect"]
|
||||
@@ -32,13 +32,10 @@ static_frontend = ["dep:rust-embed"]
|
||||
postgres_trigger = ["dep:rust-postgres", "dep:pg_escape", "dep:byteorder", "dep:thiserror", "dep:rust_decimal", "dep:rust-postgres-native-tls"]
|
||||
mqtt_trigger = ["dep:thiserror", "dep:rumqttc"]
|
||||
sqs_trigger = ["dep:aws-sdk-sqs", "dep:thiserror", "dep:aws-config"]
|
||||
deno_core = ["dep:deno_core", "dep:deno_error"]
|
||||
gcp_trigger = ["dep:thiserror", "dep:google-cloud-pubsub", "dep:google-cloud-googleapis", "dep:tonic"]
|
||||
cloud = ["windmill-common/cloud"]
|
||||
mcp = ["dep:rmcp"]
|
||||
|
||||
[dependencies]
|
||||
rmcp = { git = "https://github.com/windmill-labs/rust-sdk", features = ["transport-sse-server"], optional = true }
|
||||
windmill-queue.workspace = true
|
||||
windmill-common = { workspace = true, default-features = false }
|
||||
windmill-audit.workspace = true
|
||||
@@ -50,10 +47,10 @@ windmill-git-sync.workspace = true
|
||||
windmill-indexer = { workspace = true, optional = true }
|
||||
windmill-worker.workspace = true
|
||||
tokio.workspace = true
|
||||
tokio-stream.workspace = true
|
||||
anyhow.workspace = true
|
||||
argon2.workspace = true
|
||||
axum.workspace = true
|
||||
half = { workspace = true, optional = true}
|
||||
futures.workspace = true
|
||||
git-version.workspace = true
|
||||
tower.workspace = true
|
||||
@@ -101,7 +98,6 @@ async_zip = { workspace = true, optional = true }
|
||||
regex.workspace = true
|
||||
bytes.workspace = true
|
||||
samael = { workspace = true, optional = true }
|
||||
libxml = { workspace = true, optional = true }
|
||||
async-recursion.workspace = true
|
||||
rsa = { workspace = true, optional = true}
|
||||
uuid.workspace = true
|
||||
@@ -135,12 +131,6 @@ rust-postgres-native-tls = { workspace = true, optional = true}
|
||||
rumqttc = { workspace = true, optional = true }
|
||||
aws-sdk-sqs = { workspace = true, optional = true }
|
||||
aws-config = { workspace = true, optional = true }
|
||||
aws-sdk-sts = { workspace = true, optional = true }
|
||||
google-cloud-pubsub = { workspace = true, optional = true }
|
||||
google-cloud-googleapis = { workspace = true , optional = true }
|
||||
tonic = { workspace = true, optional = true }
|
||||
deno_error = { workspace = true, optional = true }
|
||||
deno_core = { workspace = true, optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
deno_core = { workspace = true, optional = true }
|
||||
tonic = { workspace = true, optional = true }
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.486.1
|
||||
version: 1.481.0
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -1378,7 +1378,7 @@ paths:
|
||||
format: int64
|
||||
description: The ID of the GitHub installation to delete
|
||||
responses:
|
||||
"200":
|
||||
'200':
|
||||
description: Installation successfully deleted
|
||||
|
||||
/w/{workspace}/github_app/export/{installationId}:
|
||||
@@ -1400,7 +1400,7 @@ paths:
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
'200':
|
||||
description: Successfully exported the JWT token
|
||||
content:
|
||||
application/json:
|
||||
@@ -1435,7 +1435,7 @@ paths:
|
||||
jwt_token:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
'200':
|
||||
description: Successfully imported the installation
|
||||
|
||||
/users/accept_invite:
|
||||
@@ -1791,7 +1791,7 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/OperatorSettings"
|
||||
responses:
|
||||
"200":
|
||||
'200':
|
||||
description: Operator settings updated successfully
|
||||
content:
|
||||
text/plain:
|
||||
@@ -1997,7 +1997,7 @@ paths:
|
||||
required:
|
||||
- premium
|
||||
- owner
|
||||
|
||||
|
||||
/w/{workspace}/workspaces/threshold_alert:
|
||||
get:
|
||||
summary: get threshold alert info
|
||||
@@ -2368,6 +2368,7 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/AIConfig"
|
||||
|
||||
|
||||
/w/{workspace}/workspaces/edit_error_handler:
|
||||
post:
|
||||
@@ -3537,14 +3538,14 @@ paths:
|
||||
tags:
|
||||
- teams
|
||||
responses:
|
||||
"200":
|
||||
'200':
|
||||
description: Teams information successfully synchronized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/TeamInfo"
|
||||
$ref: '#/components/schemas/TeamInfo'
|
||||
|
||||
/teams/activities:
|
||||
post:
|
||||
@@ -3578,7 +3579,7 @@ paths:
|
||||
description: The card block to be sent in the Teams card
|
||||
|
||||
responses:
|
||||
"200":
|
||||
'200':
|
||||
description: Activity processed successfully
|
||||
|
||||
/w/{workspace}/resources/create:
|
||||
@@ -4945,7 +4946,8 @@ paths:
|
||||
|
||||
/scripts_u/tokened_raw/{workspace}/{token}/{path}:
|
||||
get:
|
||||
summary: raw script by path with a token (mostly used by lsp to be used with
|
||||
summary:
|
||||
raw script by path with a token (mostly used by lsp to be used with
|
||||
import maps to resolve scripts)
|
||||
operationId: rawScriptByPathTokened
|
||||
tags:
|
||||
@@ -5039,63 +5041,6 @@ paths:
|
||||
lock_error_logs:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/jobs/list_selected_job_groups:
|
||||
# We use post because sending a huge array as a query param can produce
|
||||
# URLs that may be too long
|
||||
post:
|
||||
summary: list selected jobs script/flow schemas grouped by (kind, path)
|
||||
operationId: listSelectedJobGroups
|
||||
tags:
|
||||
- job
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
requestBody:
|
||||
description: script args
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: result
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
kind:
|
||||
type: string
|
||||
enum: ["script", "flow"]
|
||||
script_path:
|
||||
type: string
|
||||
latest_schema:
|
||||
type: object
|
||||
schemas:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
schema:
|
||||
type: object
|
||||
script_hash:
|
||||
type: string
|
||||
job_ids:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
required: [schema, script_hash, job_ids]
|
||||
required:
|
||||
- kind
|
||||
- script_path
|
||||
- latest_schema
|
||||
- schemas
|
||||
|
||||
/w/{workspace}/jobs/run/p/{path}:
|
||||
post:
|
||||
summary: run script by path
|
||||
@@ -5580,6 +5525,7 @@ paths:
|
||||
lock_error_logs:
|
||||
type: string
|
||||
|
||||
|
||||
/w/{workspace}/flows/get_triggers_count/{path}:
|
||||
get:
|
||||
summary: get triggers count of flow
|
||||
@@ -5985,55 +5931,6 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/apps/create_raw:
|
||||
post:
|
||||
summary: create app raw
|
||||
operationId: createAppRaw
|
||||
tags:
|
||||
- app
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
requestBody:
|
||||
description: new app
|
||||
required: true
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
app:
|
||||
type: object
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
value: {}
|
||||
summary:
|
||||
type: string
|
||||
policy:
|
||||
$ref: "#/components/schemas/Policy"
|
||||
draft_only:
|
||||
type: boolean
|
||||
deployment_message:
|
||||
type: string
|
||||
custom_path:
|
||||
type: string
|
||||
required:
|
||||
- path
|
||||
- value
|
||||
- summary
|
||||
- policy
|
||||
js:
|
||||
type: string
|
||||
css:
|
||||
type: string
|
||||
responses:
|
||||
"201":
|
||||
description: app created
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/apps/exists/{path}:
|
||||
get:
|
||||
summary: does an app exisst at path
|
||||
@@ -6393,49 +6290,6 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/apps/update_raw/{path}:
|
||||
post:
|
||||
summary: update app
|
||||
operationId: updateAppRaw
|
||||
tags:
|
||||
- app
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
requestBody:
|
||||
description: update app
|
||||
required: true
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
app:
|
||||
type: object
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
summary:
|
||||
type: string
|
||||
value: {}
|
||||
policy:
|
||||
$ref: "#/components/schemas/Policy"
|
||||
deployment_message:
|
||||
type: string
|
||||
custom_path:
|
||||
type: string
|
||||
js:
|
||||
type: string
|
||||
css:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: app updated
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/apps/custom_path_exists/{custom_path}:
|
||||
get:
|
||||
summary: check if custom path exists
|
||||
@@ -6669,6 +6523,7 @@ paths:
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
|
||||
requestBody:
|
||||
description: flow args
|
||||
required: true
|
||||
@@ -6676,6 +6531,7 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
|
||||
responses:
|
||||
"201":
|
||||
description: job created
|
||||
@@ -6685,62 +6541,6 @@ paths:
|
||||
type: string
|
||||
format: uuid
|
||||
|
||||
/w/{workspace}/jobs/run/batch_rerun_jobs:
|
||||
post:
|
||||
summary: re-run multiple jobs
|
||||
operationId: batchReRunJobs
|
||||
tags:
|
||||
- job
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
requestBody:
|
||||
description: list of job ids to re run and arg tranforms
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [job_ids, script_options_by_path, flow_options_by_path]
|
||||
properties:
|
||||
job_ids:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
script_options_by_path:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: object
|
||||
properties:
|
||||
input_transforms:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/InputTransform"
|
||||
use_latest_version:
|
||||
type: boolean
|
||||
flow_options_by_path:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: object
|
||||
properties:
|
||||
input_transforms:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/InputTransform"
|
||||
use_latest_version:
|
||||
type: boolean
|
||||
responses:
|
||||
"201":
|
||||
description: stream of created job uuids separated by \n. Lines may start with 'Error:'
|
||||
example: |
|
||||
a1a74c0d-708e-4539-9768-e8b3d37996bd
|
||||
f0949132-5b30-48fe-bac8-873f047df810
|
||||
Error: Could not re-run 0b885808-ae89-4458-af95-c1ca3a13b0a5
|
||||
52b9c01d-1125-4bbb-8bee-d41f26b70066
|
||||
content:
|
||||
text/event-stream:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/jobs/restart/f/{id}/from/{step_id}/{branch_or_iteration_n}:
|
||||
post:
|
||||
summary: restart a completed flow at a given step
|
||||
@@ -6757,7 +6557,8 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
- name: branch_or_iteration_n
|
||||
description: for branchall or loop, the iteration at which the flow should
|
||||
description:
|
||||
for branchall or loop, the iteration at which the flow should
|
||||
restart
|
||||
required: true
|
||||
in: path
|
||||
@@ -7125,82 +6926,10 @@ paths:
|
||||
schema:
|
||||
type: integer
|
||||
|
||||
/w/{workspace}/jobs/list_filtered_uuids:
|
||||
get:
|
||||
summary: get the ids of all jobs matching the given filters
|
||||
operationId: listFilteredJobsUuids
|
||||
tags:
|
||||
- job
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/CreatedBy"
|
||||
- $ref: "#/components/parameters/Label"
|
||||
- $ref: "#/components/parameters/Worker"
|
||||
- $ref: "#/components/parameters/ParentJob"
|
||||
- $ref: "#/components/parameters/ScriptExactPath"
|
||||
- $ref: "#/components/parameters/ScriptStartPath"
|
||||
- $ref: "#/components/parameters/SchedulePath"
|
||||
- $ref: "#/components/parameters/ScriptExactHash"
|
||||
- $ref: "#/components/parameters/StartedBefore"
|
||||
- $ref: "#/components/parameters/StartedAfter"
|
||||
- $ref: "#/components/parameters/CreatedBefore"
|
||||
- $ref: "#/components/parameters/CreatedAfter"
|
||||
- $ref: "#/components/parameters/CreatedOrStartedBefore"
|
||||
- $ref: "#/components/parameters/Running"
|
||||
- $ref: "#/components/parameters/ScheduledForBeforeNow"
|
||||
- $ref: "#/components/parameters/CreatedOrStartedAfter"
|
||||
- $ref: "#/components/parameters/CreatedOrStartedAfterCompletedJob"
|
||||
- $ref: "#/components/parameters/JobKinds"
|
||||
- $ref: "#/components/parameters/Suspended"
|
||||
- $ref: "#/components/parameters/ArgsFilter"
|
||||
- $ref: "#/components/parameters/Tag"
|
||||
- $ref: "#/components/parameters/ResultFilter"
|
||||
- $ref: "#/components/parameters/Page"
|
||||
- $ref: "#/components/parameters/PerPage"
|
||||
- name: is_skipped
|
||||
description: is the job skipped
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: is_flow_step
|
||||
description: is the job a flow step
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: has_null_parent
|
||||
description: has null parent
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: success
|
||||
description: filter on successful jobs
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: all_workspaces
|
||||
description: get jobs from all workspaces (only valid if request come from the `admins` workspace)
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: is_not_schedule
|
||||
description: is not a scheduled job
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: uuids of jobs
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/jobs/queue/list_filtered_uuids:
|
||||
get:
|
||||
summary: get the ids of all queued jobs matching the given filters
|
||||
operationId: listFilteredQueueUuids
|
||||
summary: get the ids of all jobs matching the given filters
|
||||
operationId: listFilteredUuids
|
||||
tags:
|
||||
- job
|
||||
parameters:
|
||||
@@ -9010,7 +8739,7 @@ paths:
|
||||
summary: delete nats trigger
|
||||
operationId: deleteNatsTrigger
|
||||
tags:
|
||||
- nats_trigger
|
||||
- nats_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
@@ -9039,6 +8768,7 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/NatsTrigger"
|
||||
|
||||
|
||||
/w/{workspace}/nats_triggers/list:
|
||||
get:
|
||||
summary: list nats triggers
|
||||
@@ -9073,6 +8803,7 @@ paths:
|
||||
items:
|
||||
$ref: "#/components/schemas/NatsTrigger"
|
||||
|
||||
|
||||
/w/{workspace}/nats_triggers/exists/{path}:
|
||||
get:
|
||||
summary: does nats trigger exists
|
||||
@@ -9119,6 +8850,7 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
|
||||
/w/{workspace}/nats_triggers/test:
|
||||
post:
|
||||
summary: test NATS connection
|
||||
@@ -9539,7 +9271,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/GcpTriggerData"
|
||||
$ref: "#/components/schemas/NewGcpTrigger"
|
||||
responses:
|
||||
"201":
|
||||
description: gcp trigger created
|
||||
@@ -9563,7 +9295,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/GcpTriggerData"
|
||||
$ref: "#/components/schemas/EditGcpTrigger"
|
||||
responses:
|
||||
"200":
|
||||
description: gcp trigger updated
|
||||
@@ -9757,7 +9489,7 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
|
||||
/w/{workspace}/gcp_triggers/subscriptions/list/{path}:
|
||||
post:
|
||||
summary: list all subscription of a give topic from google cloud service
|
||||
@@ -9791,8 +9523,8 @@ paths:
|
||||
tags:
|
||||
- postgres_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
responses:
|
||||
"200":
|
||||
description: boolean that indicates if postgres is set to logical level or not
|
||||
@@ -9995,6 +9727,7 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
|
||||
/w/{workspace}/postgres_triggers/publication/delete/{publication}/{path}:
|
||||
delete:
|
||||
summary: delete postgres publication
|
||||
@@ -11107,8 +10840,7 @@ paths:
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
[
|
||||
enum: [
|
||||
script,
|
||||
group_,
|
||||
resource,
|
||||
@@ -11346,6 +11078,7 @@ paths:
|
||||
items:
|
||||
$ref: "#/components/schemas/Capture"
|
||||
|
||||
|
||||
/w/{workspace}/capture/move/{runnable_kind}/{path}:
|
||||
post:
|
||||
summary: move captures and configs for a script or flow
|
||||
@@ -11603,7 +11336,8 @@ paths:
|
||||
|
||||
/w/{workspace}/job_helpers/duckdb_connection_settings:
|
||||
post:
|
||||
summary: Converts an S3 resource to the set of instructions necessary to connect
|
||||
summary:
|
||||
Converts an S3 resource to the set of instructions necessary to connect
|
||||
DuckDB to an S3 bucket
|
||||
operationId: duckdbConnectionSettings
|
||||
tags:
|
||||
@@ -11632,7 +11366,8 @@ paths:
|
||||
type: string
|
||||
/w/{workspace}/job_helpers/v2/duckdb_connection_settings:
|
||||
post:
|
||||
summary: Converts an S3 resource to the set of instructions necessary to connect
|
||||
summary:
|
||||
Converts an S3 resource to the set of instructions necessary to connect
|
||||
DuckDB to an S3 bucket
|
||||
operationId: duckdbConnectionSettingsV2
|
||||
tags:
|
||||
@@ -11668,7 +11403,8 @@ paths:
|
||||
|
||||
/w/{workspace}/job_helpers/polars_connection_settings:
|
||||
post:
|
||||
summary: Converts an S3 resource to the set of arguments necessary to connect
|
||||
summary:
|
||||
Converts an S3 resource to the set of arguments necessary to connect
|
||||
Polars to an S3 bucket
|
||||
operationId: polarsConnectionSettings
|
||||
tags:
|
||||
@@ -11712,7 +11448,8 @@ paths:
|
||||
- client_kwargs
|
||||
/w/{workspace}/job_helpers/v2/polars_connection_settings:
|
||||
post:
|
||||
summary: Converts an S3 resource to the set of arguments necessary to connect
|
||||
summary:
|
||||
Converts an S3 resource to the set of arguments necessary to connect
|
||||
Polars to an S3 bucket
|
||||
operationId: polarsConnectionSettingsV2
|
||||
tags:
|
||||
@@ -11789,7 +11526,8 @@ paths:
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
requestBody:
|
||||
description: S3 resource path to use. If empty, the S3 resource defined in the
|
||||
description:
|
||||
S3 resource path to use. If empty, the S3 resource defined in the
|
||||
workspace settings will be used
|
||||
required: true
|
||||
content:
|
||||
@@ -12833,7 +12571,8 @@ components:
|
||||
type: string
|
||||
ParentJob:
|
||||
name: parent_job
|
||||
description: The parent job that is at the origin and responsible for the execution
|
||||
description:
|
||||
The parent job that is at the origin and responsible for the execution
|
||||
of this script if any
|
||||
in: query
|
||||
schema:
|
||||
@@ -12853,7 +12592,8 @@ components:
|
||||
type: string
|
||||
NewJobId:
|
||||
name: job_id
|
||||
description: The job id to assign to the created job. if missing, job is chosen
|
||||
description:
|
||||
The job id to assign to the created job. if missing, job is chosen
|
||||
randomly using the ULID scheme. If a job id already exists in the queue
|
||||
or as a completed job, the request to create one will fail (Bad Request)
|
||||
in: query
|
||||
@@ -12944,7 +12684,8 @@ components:
|
||||
format: date-time
|
||||
CreatedOrStartedAfter:
|
||||
name: created_or_started_after
|
||||
description: filter on created_at for non non started job and started_at otherwise
|
||||
description:
|
||||
filter on created_at for non non started job and started_at otherwise
|
||||
after (exclusive) timestamp
|
||||
in: query
|
||||
schema:
|
||||
@@ -12952,7 +12693,8 @@ components:
|
||||
format: date-time
|
||||
CreatedOrStartedAfterCompletedJob:
|
||||
name: created_or_started_after_completed_jobs
|
||||
description: filter on created_at for non non started job and started_at otherwise
|
||||
description:
|
||||
filter on created_at for non non started job and started_at otherwise
|
||||
after (exclusive) timestamp but only for the completed jobs
|
||||
in: query
|
||||
schema:
|
||||
@@ -12960,7 +12702,8 @@ components:
|
||||
format: date-time
|
||||
CreatedOrStartedBefore:
|
||||
name: created_or_started_before
|
||||
description: filter on created_at for non non started job and started_at otherwise
|
||||
description:
|
||||
filter on created_at for non non started job and started_at otherwise
|
||||
before (inclusive) timestamp
|
||||
in: query
|
||||
schema:
|
||||
@@ -13048,7 +12791,8 @@ components:
|
||||
enum: [Create, Update, Delete, Execute]
|
||||
JobKinds:
|
||||
name: job_kinds
|
||||
description: filter on job kind (values 'preview', 'script', 'dependencies', 'flow')
|
||||
description:
|
||||
filter on job kind (values 'preview', 'script', 'dependencies', 'flow')
|
||||
separated by,
|
||||
in: query
|
||||
schema:
|
||||
@@ -13456,7 +13200,7 @@ components:
|
||||
"singlescriptflow",
|
||||
"flowscript",
|
||||
"flownode",
|
||||
"appscript"
|
||||
"appscript",
|
||||
]
|
||||
schedule_path:
|
||||
type: string
|
||||
@@ -13563,7 +13307,7 @@ components:
|
||||
"singlescriptflow",
|
||||
"flowscript",
|
||||
"flownode",
|
||||
"appscript"
|
||||
"appscript",
|
||||
]
|
||||
schedule_path:
|
||||
type: string
|
||||
@@ -14013,7 +13757,7 @@ components:
|
||||
"bytes",
|
||||
"dict",
|
||||
"datetime",
|
||||
"sql"
|
||||
"sql",
|
||||
]
|
||||
- type: object
|
||||
properties:
|
||||
@@ -14053,7 +13797,7 @@ components:
|
||||
"bytes",
|
||||
"dict",
|
||||
"datetime",
|
||||
"sql"
|
||||
"sql",
|
||||
]
|
||||
- type: object
|
||||
properties:
|
||||
@@ -14079,7 +13823,7 @@ components:
|
||||
"bytes",
|
||||
"dict",
|
||||
"datetime",
|
||||
"sql"
|
||||
"sql",
|
||||
]
|
||||
- type: object
|
||||
properties:
|
||||
@@ -14111,7 +13855,8 @@ components:
|
||||
|
||||
ScriptLang:
|
||||
type: string
|
||||
enum: [
|
||||
enum:
|
||||
[
|
||||
python3,
|
||||
deno,
|
||||
go,
|
||||
@@ -14132,7 +13877,7 @@ components:
|
||||
csharp,
|
||||
nu,
|
||||
java
|
||||
# for related places search: ADD_NEW_LANG
|
||||
# for related places search: ADD_NEW_LANG
|
||||
]
|
||||
|
||||
Preview:
|
||||
@@ -14142,8 +13887,6 @@ components:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
script_hash:
|
||||
type: string
|
||||
args:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
language:
|
||||
@@ -14550,15 +14293,16 @@ components:
|
||||
- edited_at
|
||||
- is_flow
|
||||
|
||||
|
||||
AuthenticationMethod:
|
||||
type: string
|
||||
enum:
|
||||
- none
|
||||
- windmill
|
||||
- api_key
|
||||
- basic_http
|
||||
- custom_script
|
||||
- signature
|
||||
- none
|
||||
- windmill
|
||||
- api_key
|
||||
- basic_http
|
||||
- custom_script
|
||||
- signature
|
||||
|
||||
HttpTrigger:
|
||||
allOf:
|
||||
@@ -14900,7 +14644,7 @@ components:
|
||||
|
||||
MqttQoS:
|
||||
type: string
|
||||
enum: ["qos0", "qos1", "qos2"]
|
||||
enum: ['qos0', 'qos1', 'qos2']
|
||||
|
||||
MqttV3Config:
|
||||
type: object
|
||||
@@ -15045,6 +14789,8 @@ components:
|
||||
type: string
|
||||
authenticate:
|
||||
type: boolean
|
||||
base_endpoint:
|
||||
type: string
|
||||
required:
|
||||
- authenticate
|
||||
- base_endpoint
|
||||
@@ -15066,8 +14812,6 @@ components:
|
||||
$ref: "#/components/schemas/DeliveryType"
|
||||
delivery_config:
|
||||
$ref: "#/components/schemas/PushConfig"
|
||||
subscription_mode:
|
||||
$ref: "#/components/schemas/SubscriptionMode"
|
||||
last_server_ping:
|
||||
type: string
|
||||
format: date-time
|
||||
@@ -15081,9 +14825,7 @@ components:
|
||||
- subscription_id
|
||||
- enabled
|
||||
- delivery_type
|
||||
- subscription_mode
|
||||
|
||||
|
||||
|
||||
SubscriptionMode:
|
||||
type: string
|
||||
enum:
|
||||
@@ -15091,24 +14833,55 @@ components:
|
||||
- create_update
|
||||
description: "The mode of subscription. 'existing' means using an existing GCP subscription, while 'create_update' involves creating or updating a new subscription."
|
||||
|
||||
|
||||
GcpTriggerData:
|
||||
GcpExistingSubscription:
|
||||
type: object
|
||||
properties:
|
||||
gcp_resource_path:
|
||||
type: string
|
||||
subscription_mode:
|
||||
$ref: "#/components/schemas/SubscriptionMode"
|
||||
topic_id:
|
||||
type: string
|
||||
subscription_id:
|
||||
type: string
|
||||
base_endpoint:
|
||||
type: string
|
||||
required:
|
||||
- subscription_id
|
||||
- base_endpoint
|
||||
|
||||
GcpCreateUpdateSubscription:
|
||||
type: object
|
||||
properties:
|
||||
subscription_id:
|
||||
type: string
|
||||
delivery_type:
|
||||
$ref: "#/components/schemas/DeliveryType"
|
||||
delivery_config:
|
||||
$ref: "#/components/schemas/PushConfig"
|
||||
required:
|
||||
- delivery_type
|
||||
|
||||
|
||||
GcpSubscriptionModeConfig:
|
||||
type: object
|
||||
properties:
|
||||
subscription_mode:
|
||||
$ref: "#/components/schemas/SubscriptionMode"
|
||||
required:
|
||||
- subscription_mode
|
||||
allOf:
|
||||
- oneOf:
|
||||
- $ref: "#/components/schemas/GcpExistingSubscription"
|
||||
- $ref: "#/components/schemas/GcpCreateUpdateSubscription"
|
||||
description: |
|
||||
"This is a union type representing the subscription mode.
|
||||
- 'existing': Represents an existing GCP subscription, and should be accompanied by an 'ExistingGcpSubscription' object.
|
||||
- 'create_update': Represents a new or updated GCP subscription, and should be accompanied by a 'CreateUpdateConfig' object."
|
||||
|
||||
NewGcpTrigger:
|
||||
type: object
|
||||
properties:
|
||||
gcp_resource_path:
|
||||
type: string
|
||||
topic_id:
|
||||
type: string
|
||||
subscription_mode:
|
||||
$ref: "#/components/schemas/GcpSubscriptionModeConfig"
|
||||
path:
|
||||
type: string
|
||||
script_path:
|
||||
@@ -15125,6 +14898,34 @@ components:
|
||||
- topic_id
|
||||
- subscription_mode
|
||||
|
||||
EditGcpTrigger:
|
||||
type: object
|
||||
properties:
|
||||
gcp_resource_path:
|
||||
type: string
|
||||
topic_id:
|
||||
type: string
|
||||
subscription_mode:
|
||||
$ref: "#/components/schemas/GcpSubscriptionModeConfig"
|
||||
path:
|
||||
type: string
|
||||
script_path:
|
||||
type: string
|
||||
is_flow:
|
||||
type: boolean
|
||||
enabled:
|
||||
type: boolean
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
- is_flow
|
||||
- enabled
|
||||
- mqtt_resource_path
|
||||
- subscription_id
|
||||
- delivery_type
|
||||
- topic_id
|
||||
- subscription_mode
|
||||
|
||||
GetAllTopicSubscription:
|
||||
type: object
|
||||
properties:
|
||||
@@ -15141,13 +14942,7 @@ components:
|
||||
type: string
|
||||
required:
|
||||
- subscription_id
|
||||
|
||||
AwsAuthResourceType:
|
||||
type: string
|
||||
enum:
|
||||
- oidc
|
||||
- credentials
|
||||
|
||||
|
||||
SqsTrigger:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/TriggerExtraProperty"
|
||||
@@ -15155,8 +14950,6 @@ components:
|
||||
properties:
|
||||
queue_url:
|
||||
type: string
|
||||
aws_auth_resource_type:
|
||||
$ref: "#/components/schemas/AwsAuthResourceType"
|
||||
aws_resource_path:
|
||||
type: string
|
||||
message_attributes:
|
||||
@@ -15177,15 +14970,12 @@ components:
|
||||
- queue_url
|
||||
- aws_resource_path
|
||||
- enabled
|
||||
- aws_auth_resource_type
|
||||
|
||||
NewSqsTrigger:
|
||||
type: object
|
||||
properties:
|
||||
queue_url:
|
||||
type: string
|
||||
aws_auth_resource_type:
|
||||
$ref: "#/components/schemas/AwsAuthResourceType"
|
||||
aws_resource_path:
|
||||
type: string
|
||||
message_attributes:
|
||||
@@ -15206,15 +14996,12 @@ components:
|
||||
- path
|
||||
- script_path
|
||||
- is_flow
|
||||
- aws_auth_resource_type
|
||||
|
||||
EditSqsTrigger:
|
||||
type: object
|
||||
properties:
|
||||
queue_url:
|
||||
type: string
|
||||
aws_auth_resource_type:
|
||||
$ref: "#/components/schemas/AwsAuthResourceType"
|
||||
aws_resource_path:
|
||||
type: string
|
||||
message_attributes:
|
||||
@@ -15236,7 +15023,7 @@ components:
|
||||
- script_path
|
||||
- is_flow
|
||||
- enabled
|
||||
- aws_auth_resource_type
|
||||
|
||||
|
||||
Slot:
|
||||
type: object
|
||||
@@ -15332,8 +15119,8 @@ components:
|
||||
error:
|
||||
type: string
|
||||
last_server_ping:
|
||||
type: string
|
||||
format: date-time
|
||||
type: string
|
||||
format: date-time
|
||||
required:
|
||||
- enabled
|
||||
- postgres_resource_path
|
||||
@@ -15956,8 +15743,6 @@ components:
|
||||
execution_mode:
|
||||
type: string
|
||||
enum: [viewer, publisher, anonymous]
|
||||
raw_app:
|
||||
type: boolean
|
||||
required:
|
||||
- id
|
||||
- workspace_id
|
||||
@@ -16131,7 +15916,13 @@ components:
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum: ["S3Storage", "AzureBlobStorage", "AzureWorkloadIdentity", "S3AwsOidc"]
|
||||
enum:
|
||||
[
|
||||
"S3Storage",
|
||||
"AzureBlobStorage",
|
||||
"AzureWorkloadIdentity",
|
||||
"S3AwsOidc",
|
||||
]
|
||||
s3_resource_path:
|
||||
type: string
|
||||
azure_blob_resource_path:
|
||||
@@ -16146,7 +15937,12 @@ components:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
["S3Storage", "AzureBlobStorage", "AzureWorkloadIdentity", "S3AwsOidc"]
|
||||
[
|
||||
"S3Storage",
|
||||
"AzureBlobStorage",
|
||||
"AzureWorkloadIdentity",
|
||||
"S3AwsOidc",
|
||||
]
|
||||
s3_resource_path:
|
||||
type: string
|
||||
azure_blob_resource_path:
|
||||
@@ -16626,7 +16422,7 @@ components:
|
||||
type: array
|
||||
description: List of channels within the team
|
||||
items:
|
||||
$ref: "#/components/schemas/ChannelInfo"
|
||||
$ref: '#/components/schemas/ChannelInfo'
|
||||
|
||||
ChannelInfo:
|
||||
type: object
|
||||
@@ -16704,29 +16500,4 @@ components:
|
||||
presigned:
|
||||
type: string
|
||||
required:
|
||||
- s3
|
||||
|
||||
TeamsChannel:
|
||||
type: object
|
||||
required:
|
||||
- team_id
|
||||
- team_name
|
||||
- channel_id
|
||||
- channel_name
|
||||
properties:
|
||||
team_id:
|
||||
type: string
|
||||
description: Microsoft Teams team ID
|
||||
minLength: 1
|
||||
team_name:
|
||||
type: string
|
||||
description: Microsoft Teams team name
|
||||
minLength: 1
|
||||
channel_id:
|
||||
type: string
|
||||
description: Microsoft Teams channel ID
|
||||
minLength: 1
|
||||
channel_name:
|
||||
type: string
|
||||
description: Microsoft Teams channel name
|
||||
minLength: 1
|
||||
- s3
|
||||
@@ -21,8 +21,8 @@ pub fn workspaced_service(
|
||||
_base_internal_url: String,
|
||||
) -> (
|
||||
Router,
|
||||
Vec<tokio::task::JoinHandle<()>>,
|
||||
Option<windmill_worker::JobCompletedSender>,
|
||||
Option<tokio::task::JoinHandle<()>>,
|
||||
windmill_worker::JobCompletedSender,
|
||||
) {
|
||||
use windmill_common::worker::Connection;
|
||||
use windmill_worker::JobCompletedSender;
|
||||
@@ -32,7 +32,7 @@ pub fn workspaced_service(
|
||||
|
||||
let router = Router::new();
|
||||
|
||||
(router, vec![], Some(job_completed_tx))
|
||||
(router, None, job_completed_tx)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
|
||||
@@ -24,10 +24,10 @@ use crate::{
|
||||
},
|
||||
users::fetch_api_authed_from_permissioned_as,
|
||||
};
|
||||
#[cfg(feature = "parquet")]
|
||||
use axum::response::Response;
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::{Extension, Json, Multipart, Path, Query},
|
||||
extract::{Extension, Json, Path, Query},
|
||||
response::IntoResponse,
|
||||
routing::{delete, get, post},
|
||||
Router,
|
||||
@@ -88,13 +88,10 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/get/draft/*path", get(get_app_w_draft))
|
||||
.route("/secret_of/*path", get(get_secret_id))
|
||||
.route("/get/v/*id", get(get_app_by_id))
|
||||
.route("/get_data/v/*id", get(get_raw_app_data))
|
||||
.route("/exists/*path", get(exists_app))
|
||||
.route("/update/*path", post(update_app))
|
||||
.route("/update_raw/*path", post(update_app_raw))
|
||||
.route("/delete/*path", delete(delete_app))
|
||||
.route("/create", post(create_app))
|
||||
.route("/create_raw", post(create_app_raw))
|
||||
.route("/history/p/*path", get(get_app_history))
|
||||
.route("/get_latest_version/*path", get(get_latest_version))
|
||||
.route("/history_update/a/:id/v/:version", post(update_app_history))
|
||||
@@ -138,12 +135,6 @@ pub struct ListableApp {
|
||||
#[sqlx(default)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub deployment_msg: Option<String>,
|
||||
#[serde(skip_serializing_if = "is_false")]
|
||||
pub raw_app: bool,
|
||||
}
|
||||
|
||||
fn is_false(b: &bool) -> bool {
|
||||
!b
|
||||
}
|
||||
|
||||
#[derive(FromRow, Serialize, Deserialize)]
|
||||
@@ -337,8 +328,7 @@ async fn list_apps(
|
||||
"app.extra_perms",
|
||||
"favorite.path IS NOT NULL as starred",
|
||||
"draft.path IS NOT NULL as has_draft",
|
||||
"draft_only",
|
||||
"app_version.raw_app",
|
||||
"draft_only"
|
||||
])
|
||||
.left()
|
||||
.join("favorite")
|
||||
@@ -397,44 +387,6 @@ async fn list_apps(
|
||||
Ok(Json(rows))
|
||||
}
|
||||
|
||||
async fn get_raw_app_data(Path((w_id, version_id)): Path<(String, String)>) -> Result<Response> {
|
||||
let file_path = format!("/tmp/wmill/{}/{}", w_id, version_id);
|
||||
let file = tokio::fs::File::open(file_path).await?;
|
||||
let stream = tokio_util::io::ReaderStream::new(file);
|
||||
let res = Response::builder().header(
|
||||
http::header::CONTENT_TYPE,
|
||||
if version_id.ends_with(".css") {
|
||||
"text/css"
|
||||
} else {
|
||||
"text/javascript"
|
||||
},
|
||||
);
|
||||
Ok(res.body(Body::from_stream(stream)).unwrap())
|
||||
}
|
||||
|
||||
// async fn get_app_version(
|
||||
// authed: ApiAuthed,
|
||||
// Extension(user_db): Extension<UserDB>,
|
||||
// Path((w_id, path)): Path<(String, StripPath)>,
|
||||
// ) -> JsonResult<i64> {
|
||||
// let path = path.to_path();
|
||||
// let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
// let version_o = sqlx::query_scalar!(
|
||||
// "SELECT app.versions[array_upper(app.versions, 1)] as version FROM app
|
||||
// WHERE app.path = $1 AND app.workspace_id = $2",
|
||||
// path,
|
||||
// &w_id,
|
||||
// )
|
||||
// .fetch_optional(&mut *tx)
|
||||
// .await?
|
||||
// .flatten();
|
||||
// tx.commit().await?;
|
||||
|
||||
// let version = not_found_if_none(version_o, "App", path)?;
|
||||
// Ok(Json(version))
|
||||
// }
|
||||
|
||||
async fn get_app(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
@@ -775,97 +727,6 @@ async fn get_secret_id(
|
||||
Ok(hx)
|
||||
}
|
||||
|
||||
macro_rules! process_app_multipart {
|
||||
($authed:expr, $user_db:expr, $db:expr, $w_id:expr, $path:expr, $multipart:expr, $internal_fn:expr) => {
|
||||
async {
|
||||
let mut saved_app = None;
|
||||
let mut uploaded_js = false;
|
||||
|
||||
//todo: use s3 instead
|
||||
let file_path = format!("/tmp/wmill/{}", $w_id);
|
||||
std::fs::create_dir_all(&file_path).unwrap();
|
||||
|
||||
let mut multipart = $multipart;
|
||||
while let Some(field) = multipart.next_field().await.unwrap() {
|
||||
let name = field.name().unwrap().to_string();
|
||||
let data = field.bytes().await.unwrap();
|
||||
if name == "app" {
|
||||
let app = serde_json::from_slice(&data).map_err(to_anyhow)?;
|
||||
let (ntx, npath, nid) = $internal_fn(
|
||||
$authed.clone(),
|
||||
$db.clone(),
|
||||
$user_db.clone(),
|
||||
$w_id,
|
||||
$path,
|
||||
true,
|
||||
app,
|
||||
)
|
||||
.await?;
|
||||
saved_app = Some((npath, nid, ntx));
|
||||
} else if name == "js" {
|
||||
if let Some((_npath, id, _tx)) = saved_app.as_ref() {
|
||||
let file_path = format!("{}/{}.js", file_path, id);
|
||||
std::fs::write(file_path, data).unwrap();
|
||||
uploaded_js = true;
|
||||
} else {
|
||||
return Err(Error::BadRequest(
|
||||
"App payload need to be created first".to_string(),
|
||||
));
|
||||
}
|
||||
} else if name == "css" {
|
||||
if let Some((_npath, id, _tx)) = saved_app.as_ref() {
|
||||
let file_path = format!("{}/{}.css", file_path, id);
|
||||
std::fs::write(file_path, data).unwrap();
|
||||
} else {
|
||||
return Err(Error::BadRequest(
|
||||
"App payload need to be created first".to_string(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::BadRequest(format!("Unsupported field: {}", name)));
|
||||
}
|
||||
}
|
||||
if !uploaded_js {
|
||||
return Err(Error::BadRequest("js or css file not uploaded".to_string()));
|
||||
}
|
||||
if let Some((npath, id, tx)) = saved_app {
|
||||
tx.commit().await?;
|
||||
Ok((npath, id))
|
||||
} else {
|
||||
Err(Error::BadRequest("App not created".to_string()))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async fn create_app_raw<'a>(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Path(w_id): Path<String>,
|
||||
multipart: Multipart,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
let (path, _id) = process_app_multipart!(
|
||||
authed,
|
||||
user_db,
|
||||
db,
|
||||
&w_id,
|
||||
"",
|
||||
multipart,
|
||||
|authed, db, user_db, w_id, _path, raw_app, app| create_app_internal(
|
||||
authed, db, user_db, w_id, raw_app, app
|
||||
)
|
||||
)
|
||||
.await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::CreateApp { workspace: w_id, path: path.clone() },
|
||||
);
|
||||
Ok((StatusCode::CREATED, path))
|
||||
}
|
||||
|
||||
async fn list_paths_from_workspace_runnable(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
@@ -894,36 +755,17 @@ async fn create_app(
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(app): Json<CreateApp>,
|
||||
Json(mut app): Json<CreateApp>,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
let path = app.path.clone();
|
||||
let (new_tx, _path, _id) = create_app_internal(authed, db, user_db, &w_id, false, app).await?;
|
||||
|
||||
new_tx.commit().await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::CreateApp { workspace: w_id, path: path.clone() },
|
||||
);
|
||||
|
||||
Ok((StatusCode::CREATED, path))
|
||||
}
|
||||
|
||||
async fn create_app_internal<'a>(
|
||||
authed: ApiAuthed,
|
||||
db: sqlx::Pool<sqlx::Postgres>,
|
||||
user_db: UserDB,
|
||||
w_id: &String,
|
||||
raw_app: bool,
|
||||
mut app: CreateApp,
|
||||
) -> Result<(sqlx::Transaction<'a, sqlx::Postgres>, String, i64)> {
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
|
||||
app.policy.on_behalf_of = Some(username_to_permissioned_as(&authed.username));
|
||||
app.policy.on_behalf_of_email = Some(authed.email.clone());
|
||||
let path = app.path.clone();
|
||||
|
||||
if &app.path == "" {
|
||||
return Err(Error::BadRequest("App path cannot be empty".to_string()));
|
||||
}
|
||||
|
||||
let exists = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM app WHERE path = $1 AND workspace_id = $2)",
|
||||
&app.path,
|
||||
@@ -932,19 +774,21 @@ async fn create_app_internal<'a>(
|
||||
.fetch_one(&mut *tx)
|
||||
.await?
|
||||
.unwrap_or(false);
|
||||
|
||||
if exists {
|
||||
return Err(Error::BadRequest(format!(
|
||||
"App with path {} already exists",
|
||||
&app.path
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(custom_path) = &app.custom_path {
|
||||
require_admin(authed.is_admin, &authed.username)?;
|
||||
|
||||
let exists = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM app WHERE custom_path = $1 AND ($2::TEXT IS NULL OR workspace_id = $2))",
|
||||
custom_path,
|
||||
if *CLOUD_HOSTED { Some(w_id) } else { None }
|
||||
if *CLOUD_HOSTED { Some(&w_id) } else { None }
|
||||
)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?.unwrap_or(false);
|
||||
@@ -956,6 +800,7 @@ async fn create_app_internal<'a>(
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
"DELETE FROM draft WHERE path = $1 AND workspace_id = $2 AND typ = 'app'",
|
||||
&app.path,
|
||||
@@ -963,6 +808,7 @@ async fn create_app_internal<'a>(
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
let id = sqlx::query_scalar!(
|
||||
"INSERT INTO app
|
||||
(workspace_id, path, summary, policy, versions, draft_only, custom_path)
|
||||
@@ -973,24 +819,24 @@ async fn create_app_internal<'a>(
|
||||
json!(app.policy),
|
||||
app.draft_only,
|
||||
app.custom_path
|
||||
.as_ref()
|
||||
.map(|s| if s.is_empty() { None } else { Some(s) })
|
||||
.flatten()
|
||||
)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
|
||||
let v_id = sqlx::query_scalar!(
|
||||
"INSERT INTO app_version
|
||||
(app_id, value, created_by, raw_app)
|
||||
VALUES ($1, $2::text::json, $3, $4) RETURNING id",
|
||||
(app_id, value, created_by)
|
||||
VALUES ($1, $2::text::json, $3) RETURNING id",
|
||||
id,
|
||||
//to preserve key orders
|
||||
serde_json::to_string(&app.value).unwrap(),
|
||||
authed.username,
|
||||
raw_app
|
||||
)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE app SET versions = array_append(versions, $1::bigint) WHERE id = $2",
|
||||
v_id,
|
||||
@@ -1004,20 +850,22 @@ async fn create_app_internal<'a>(
|
||||
&authed,
|
||||
"apps.create",
|
||||
ActionKind::Create,
|
||||
w_id,
|
||||
&w_id,
|
||||
Some(&app.path),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut args: HashMap<String, Box<serde_json::value::RawValue>> = HashMap::new();
|
||||
if let Some(dm) = &app.deployment_message {
|
||||
if let Some(dm) = app.deployment_message {
|
||||
args.insert("deployment_message".to_string(), to_raw_value(&dm));
|
||||
}
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let (dependency_job_uuid, new_tx) = push(
|
||||
&db,
|
||||
tx,
|
||||
w_id,
|
||||
&w_id,
|
||||
JobPayload::AppDependencies { path: app.path.clone(), version: v_id },
|
||||
PushArgs { args: &args, extra: None },
|
||||
&authed.username,
|
||||
@@ -1041,7 +889,14 @@ async fn create_app_internal<'a>(
|
||||
.await?;
|
||||
tracing::info!("Pushed app dependency job {}", dependency_job_uuid);
|
||||
|
||||
Ok((new_tx, path, v_id))
|
||||
new_tx.commit().await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::CreateApp { workspace: w_id, path: app.path.clone() },
|
||||
);
|
||||
|
||||
Ok((StatusCode::CREATED, app.path))
|
||||
}
|
||||
|
||||
async fn list_hub_apps(Extension(db): Extension<DB>) -> impl IntoResponse {
|
||||
@@ -1162,76 +1017,12 @@ async fn update_app(
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Json(ns): Json<EditApp>,
|
||||
) -> Result<String> {
|
||||
// create_app_internal(authed, user_db, db, &w_id, &mut app).await?;
|
||||
let path = path.to_path();
|
||||
let opath = path.to_string();
|
||||
let (new_tx, npath, _v_id) =
|
||||
update_app_internal(authed, db, user_db, &w_id, path, false, ns).await?;
|
||||
new_tx.commit().await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::UpdateApp {
|
||||
workspace: w_id.clone(),
|
||||
old_path: opath.clone(),
|
||||
new_path: npath.clone(),
|
||||
},
|
||||
);
|
||||
|
||||
Ok(format!("app {} updated (npath: {:?})", opath, npath))
|
||||
}
|
||||
|
||||
async fn update_app_raw<'a>(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
multipart: Multipart,
|
||||
) -> Result<String> {
|
||||
let path = path.to_path();
|
||||
let opath = path.to_string();
|
||||
let (npath, _id) = process_app_multipart!(
|
||||
authed,
|
||||
user_db,
|
||||
db,
|
||||
&w_id,
|
||||
path,
|
||||
multipart,
|
||||
update_app_internal
|
||||
)
|
||||
.await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::UpdateApp {
|
||||
workspace: w_id.clone(),
|
||||
old_path: opath.to_owned(),
|
||||
new_path: npath.clone(),
|
||||
},
|
||||
);
|
||||
|
||||
Ok(format!("app {} updated (npath: {:?})", opath, npath))
|
||||
}
|
||||
// async fn create_app_internal<'a>(
|
||||
// authed: ApiAuthed,
|
||||
// db: sqlx::Pool<sqlx::Postgres>,
|
||||
// user_db: UserDB,
|
||||
// w_id: &String,
|
||||
// app: &mut CreateApp,
|
||||
// )
|
||||
|
||||
async fn update_app_internal<'a>(
|
||||
authed: ApiAuthed,
|
||||
db: sqlx::Pool<sqlx::Postgres>,
|
||||
user_db: UserDB,
|
||||
w_id: &str,
|
||||
path: &str,
|
||||
raw_app: bool,
|
||||
ns: EditApp,
|
||||
) -> Result<(sqlx::Transaction<'a, sqlx::Postgres>, String, i64)> {
|
||||
use sql_builder::prelude::*;
|
||||
|
||||
let path = path.to_path();
|
||||
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
|
||||
let npath = if ns.policy.is_some()
|
||||
|| ns.path.is_some()
|
||||
|| ns.summary.is_some()
|
||||
@@ -1278,7 +1069,7 @@ async fn update_app_internal<'a>(
|
||||
let exists = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM app WHERE custom_path = $1 AND ($2::TEXT IS NULL OR workspace_id = $2) AND NOT (path = $3 AND workspace_id = $4))",
|
||||
ncustom_path,
|
||||
if *CLOUD_HOSTED { Some(w_id) } else { None },
|
||||
if *CLOUD_HOSTED { Some(&w_id) } else { None },
|
||||
path,
|
||||
w_id
|
||||
)
|
||||
@@ -1325,13 +1116,12 @@ async fn update_app_internal<'a>(
|
||||
|
||||
let v_id = sqlx::query_scalar!(
|
||||
"INSERT INTO app_version
|
||||
(app_id, value, created_by, raw_app)
|
||||
VALUES ($1, $2::text::json, $3, $4) RETURNING id",
|
||||
(app_id, value, created_by)
|
||||
VALUES ($1, $2::text::json, $3) RETURNING id",
|
||||
app_id,
|
||||
//to preserve key orders
|
||||
serde_json::to_string(&nvalue).unwrap(),
|
||||
authed.username,
|
||||
raw_app
|
||||
)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
@@ -1362,6 +1152,7 @@ async fn update_app_internal<'a>(
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
"DELETE FROM draft WHERE path = $1 AND workspace_id = $2 AND typ = 'app'",
|
||||
path,
|
||||
@@ -1369,26 +1160,29 @@ async fn update_app_internal<'a>(
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
"apps.update",
|
||||
ActionKind::Update,
|
||||
w_id,
|
||||
&w_id,
|
||||
Some(&npath),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let mut args: HashMap<String, Box<serde_json::value::RawValue>> = HashMap::new();
|
||||
if let Some(dm) = ns.deployment_message {
|
||||
args.insert("deployment_message".to_string(), to_raw_value(&dm));
|
||||
}
|
||||
args.insert("parent_path".to_string(), to_raw_value(&path));
|
||||
|
||||
let (dependency_job_uuid, new_tx) = push(
|
||||
&db,
|
||||
tx,
|
||||
w_id,
|
||||
&w_id,
|
||||
JobPayload::AppDependencies { path: npath.clone(), version: v_id },
|
||||
PushArgs { args: &args, extra: None },
|
||||
&authed.username,
|
||||
@@ -1411,7 +1205,18 @@ async fn update_app_internal<'a>(
|
||||
)
|
||||
.await?;
|
||||
tracing::info!("Pushed app dependency job {}", dependency_job_uuid);
|
||||
Ok((new_tx, npath, v_id))
|
||||
new_tx.commit().await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::UpdateApp {
|
||||
workspace: w_id,
|
||||
old_path: path.to_owned(),
|
||||
new_path: npath.clone(),
|
||||
},
|
||||
);
|
||||
|
||||
Ok(format!("app {} updated (npath: {:?})", path, npath))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
|
||||
@@ -511,13 +511,6 @@ where
|
||||
let path_vec: Vec<&str> = original_uri.path().split("/").collect();
|
||||
let workspace_id = if path_vec.len() >= 4 && path_vec[0] == "" && path_vec[2] == "w" {
|
||||
Some(path_vec[3].to_owned())
|
||||
} else if path_vec.len() >= 5
|
||||
&& path_vec[0] == ""
|
||||
&& path_vec[1] == "api"
|
||||
&& path_vec[2] == "mcp"
|
||||
&& path_vec[3] == "w"
|
||||
{
|
||||
Some(path_vec[4].to_string())
|
||||
} else {
|
||||
if path_vec.len() >= 5
|
||||
&& path_vec[0] == ""
|
||||
|
||||
@@ -18,22 +18,16 @@ use {
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
use crate::gcp_triggers_ee::{
|
||||
manage_google_subscription, process_google_push_request, validate_jwt_token,
|
||||
CreateUpdateConfig, SubscriptionMode,
|
||||
manage_google_subscription, process_google_push_request, validate_jwt_token, SubscriptionMode,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "sqs_trigger"))]
|
||||
use windmill_common::auth::aws::AwsAuthResourceType;
|
||||
|
||||
#[cfg(any(
|
||||
feature = "http_trigger",
|
||||
all(feature = "enterprise", feature = "gcp_trigger")
|
||||
))]
|
||||
use {
|
||||
axum::extract::Request,
|
||||
http::HeaderMap,
|
||||
serde::de::DeserializeOwned,
|
||||
windmill_common::{error::Error, utils::empty_string_as_none},
|
||||
axum::extract::Request, http::HeaderMap, serde::de::DeserializeOwned,
|
||||
windmill_common::error::Error,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "kafka"))]
|
||||
@@ -156,20 +150,14 @@ pub struct SqsTriggerConfig {
|
||||
pub queue_url: String,
|
||||
pub aws_resource_path: String,
|
||||
pub message_attributes: Option<Vec<String>>,
|
||||
pub aws_auth_resource_type: AwsAuthResourceType,
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct GcpTriggerConfig {
|
||||
pub gcp_resource_path: String,
|
||||
pub subscription_mode: SubscriptionMode,
|
||||
#[serde(default, deserialize_with = "empty_string_as_none")]
|
||||
pub subscription_id: Option<String>,
|
||||
#[serde(default, deserialize_with = "empty_string_as_none")]
|
||||
pub base_endpoint: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub create_update: Option<CreateUpdateConfig>,
|
||||
pub subscription_mode: SubscriptionMode,
|
||||
pub topic_id: String,
|
||||
}
|
||||
|
||||
@@ -378,15 +366,11 @@ async fn set_gcp_trigger_config(
|
||||
&gcp_config.gcp_resource_path,
|
||||
&capture_config.path,
|
||||
&gcp_config.topic_id,
|
||||
&mut gcp_config.subscription_id,
|
||||
&mut gcp_config.base_endpoint,
|
||||
gcp_config.subscription_mode,
|
||||
gcp_config.create_update,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
gcp_config.create_update = Some(config);
|
||||
gcp_config.subscription_mode = SubscriptionMode::CreateUpdate;
|
||||
|
||||
gcp_config.subscription_mode = SubscriptionMode::CreateUpdate(config);
|
||||
capture_config.trigger_config = Some(TriggerConfig::Gcp(gcp_config));
|
||||
|
||||
Ok(capture_config)
|
||||
@@ -903,7 +887,7 @@ async fn gcp_payload(
|
||||
|
||||
let authed = fetch_api_authed(owner.clone(), email, &w_id, &db, None).await?;
|
||||
|
||||
let Some(config) = &gcp_trigger_config.create_update else {
|
||||
let SubscriptionMode::CreateUpdate(config) = &gcp_trigger_config.subscription_mode else {
|
||||
return Err(Error::BadConfig("Bad config".to_string()));
|
||||
};
|
||||
|
||||
|
||||
@@ -253,7 +253,7 @@ impl ModelInstance {
|
||||
let token_ids = Tensor::new(&tokens[..], &Device::Cpu)?.unsqueeze(0)?;
|
||||
let token_type_ids = token_ids.zeros_like()?;
|
||||
|
||||
let embedding = self.model.forward(&token_ids, &token_type_ids, None)?;
|
||||
let embedding = self.model.forward(&token_ids, &token_type_ids)?;
|
||||
let embedding = (embedding.sum(1)? / embedding.dim(1)? as f64)?;
|
||||
let embedding = normalize_l2(&embedding)?;
|
||||
|
||||
|
||||
@@ -52,12 +52,11 @@ pub struct ExistingGcpSubscription {
|
||||
pub base_endpoint: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, sqlx::Type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[sqlx(type_name = "GCP_SUBSCRIPTION_MODE", rename_all = "snake_case")]
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "subscription_mode", rename_all = "snake_case")]
|
||||
pub enum SubscriptionMode {
|
||||
Existing,
|
||||
CreateUpdate,
|
||||
Existing(ExistingGcpSubscription),
|
||||
CreateUpdate(CreateUpdateConfig),
|
||||
}
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
@@ -78,11 +77,7 @@ pub async fn manage_google_subscription(
|
||||
_gcp_resource_path: &str,
|
||||
_path: &str,
|
||||
_topic_id: &str,
|
||||
_subscription_id: &mut Option<String>,
|
||||
_base_endpoint: &mut Option<String>,
|
||||
_subscription_mode: SubscriptionMode,
|
||||
_create_update_config: Option<CreateUpdateConfig>,
|
||||
_trigger_mode: bool,
|
||||
) -> WindmillResult<CreateUpdateConfig> {
|
||||
Ok(CreateUpdateConfig::default())
|
||||
}
|
||||
@@ -122,7 +117,6 @@ pub struct GcpTrigger {
|
||||
pub subscription_id: String,
|
||||
pub delivery_type: DeliveryType,
|
||||
pub delivery_config: Option<SqlxJson<PushConfig>>,
|
||||
pub subscription_mode: SubscriptionMode,
|
||||
pub topic_id: String,
|
||||
pub path: String,
|
||||
pub script_path: String,
|
||||
|
||||
@@ -253,8 +253,8 @@ mod zoom {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
struct ZoomPayload {
|
||||
#[serde(rename = "plainToken")]
|
||||
plain_token: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
|
||||
use axum::body::Body;
|
||||
use axum::http::HeaderValue;
|
||||
#[cfg(feature = "deno_core")]
|
||||
use deno_core::{op2, serde_v8, v8, JsRuntime, OpState};
|
||||
use futures::{StreamExt, TryFutureExt};
|
||||
use futures::TryFutureExt;
|
||||
use http::{HeaderMap, HeaderName};
|
||||
use itertools::Itertools;
|
||||
use quick_cache::sync::Cache;
|
||||
@@ -142,13 +140,6 @@ pub fn workspaced_service() -> Router {
|
||||
.layer(cors.clone())
|
||||
.layer(ce_headers.clone()),
|
||||
)
|
||||
.route(
|
||||
"/run/batch_rerun_jobs",
|
||||
post(batch_rerun_jobs)
|
||||
.head(|| async { "" })
|
||||
.layer(cors.clone())
|
||||
.layer(ce_headers.clone()),
|
||||
)
|
||||
.route(
|
||||
"/run/workflow_as_code/:job_id/:entrypoint",
|
||||
post(run_workflow_as_code)
|
||||
@@ -212,13 +203,6 @@ pub fn workspaced_service() -> Router {
|
||||
"/list",
|
||||
get(list_jobs).layer(Extension(api_list_jobs_query_duration)),
|
||||
)
|
||||
.route(
|
||||
"/list_selected_job_groups",
|
||||
// We use post because sending a huge array as a query param can produce
|
||||
// URLs that may be too long
|
||||
post(list_selected_job_groups),
|
||||
)
|
||||
.route("/list_filtered_uuids", get(list_filtered_job_uuids))
|
||||
.route("/queue/list", get(list_queue_jobs))
|
||||
.route("/queue/count", get(count_queue_jobs))
|
||||
.route("/queue/list_filtered_uuids", get(list_filtered_uuids))
|
||||
@@ -661,48 +645,6 @@ async fn get_flow_job_debug_info(
|
||||
}
|
||||
}
|
||||
|
||||
async fn list_selected_job_groups(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(uuids): Json<Vec<Uuid>>,
|
||||
) -> error::Result<Response> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let results = sqlx::query_scalar!(
|
||||
r#"SELECT jsonb_build_object(
|
||||
'kind', jb.kind,
|
||||
'script_path', jb.runnable_path,
|
||||
'latest_schema', COALESCE(
|
||||
(SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.workspace_id = $1 AND s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),
|
||||
(SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.workspace_id = $1 AND flow.path = jb.runnable_path AND jb.kind = 'flow')
|
||||
),
|
||||
'schemas', ARRAY(
|
||||
SELECT jsonb_build_object(
|
||||
'script_hash', LPAD(TO_HEX(COALESCE(s.hash, f.id)), 16, '0'),
|
||||
'job_ids', ARRAY_AGG(DISTINCT j.id),
|
||||
'schema', (ARRAY_AGG(COALESCE(s.schema, f.schema)))[1]
|
||||
) FROM v2_job j
|
||||
LEFT JOIN script s ON s.hash = j.runnable_id AND j.kind = 'script'
|
||||
LEFT JOIN flow_version f ON f.id = j.runnable_id AND j.kind = 'flow'
|
||||
WHERE j.id = ANY(ARRAY_AGG(jb.id))
|
||||
GROUP BY COALESCE(s.hash, f.id)
|
||||
)
|
||||
) FROM v2_job jb
|
||||
WHERE (jb.kind = 'flow' OR jb.kind = 'script')
|
||||
AND jb.workspace_id = $1 AND jb.id = ANY($2)
|
||||
GROUP BY jb.kind, jb.runnable_path"#,
|
||||
&w_id,
|
||||
&uuids
|
||||
)
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(Json(results).into_response())
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct GetJobQuery {
|
||||
pub no_logs: Option<bool>,
|
||||
@@ -1803,37 +1745,6 @@ async fn cancel_selection(
|
||||
cancel_jobs(jobs_to_cancel, &db, authed.username.as_str(), w_id.as_str()).await
|
||||
}
|
||||
|
||||
async fn list_filtered_job_uuids(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
Query(lq): Query<ListCompletedQuery>,
|
||||
) -> error::JsonResult<Vec<Uuid>> {
|
||||
require_admin(authed.is_admin, &authed.username)?;
|
||||
check_scopes(&authed, || format!("jobs:listjobs"))?;
|
||||
|
||||
let mut sqlb = list_completed_jobs_query(
|
||||
w_id.as_str(),
|
||||
None,
|
||||
0,
|
||||
&lq,
|
||||
&["v2_job.id"],
|
||||
false,
|
||||
get_scope_tags(&authed),
|
||||
);
|
||||
let sqlb2 = list_queue_jobs_query(
|
||||
w_id.as_str(),
|
||||
&lq.into(),
|
||||
&["v2_job.id"],
|
||||
Pagination { page: None, per_page: None },
|
||||
false,
|
||||
get_scope_tags(&authed),
|
||||
);
|
||||
let query = sqlb.union_all(sqlb2.subquery()?).subquery()?;
|
||||
let ids = sqlx::query_scalar(query.as_str()).fetch_all(&db).await?;
|
||||
Ok(Json(ids))
|
||||
}
|
||||
|
||||
async fn list_filtered_uuids(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
@@ -1990,7 +1901,7 @@ async fn list_jobs(
|
||||
let sqlc = if lq.running.is_none() {
|
||||
Some(list_completed_jobs_query(
|
||||
&w_id,
|
||||
Some(per_page + offset),
|
||||
per_page + offset,
|
||||
0,
|
||||
&ListCompletedQuery { order_desc: Some(true), ..lqc },
|
||||
UnifiedJob::completed_job_fields(),
|
||||
@@ -2029,9 +1940,7 @@ async fn list_jobs(
|
||||
} else {
|
||||
if sqlc.is_none() {
|
||||
return Err(error::Error::BadRequest(
|
||||
"cannot specify success, label, created_or_started_before, or starte
|
||||
d_before with running"
|
||||
.to_string(),
|
||||
"cannot specify success, label, created_or_started_before, or started_before with running".to_string(),
|
||||
));
|
||||
}
|
||||
sqlc.unwrap().limit(per_page).offset(offset).query()?
|
||||
@@ -3095,17 +3004,16 @@ struct CancelJob {
|
||||
enum PreviewKind {
|
||||
Code,
|
||||
Identity,
|
||||
Http,
|
||||
Noop,
|
||||
Bundle,
|
||||
Tarbundle,
|
||||
ScriptHash,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Preview {
|
||||
content: Option<String>,
|
||||
kind: Option<PreviewKind>,
|
||||
script_hash: Option<String>,
|
||||
path: Option<String>,
|
||||
args: Option<HashMap<String, Box<JsonRawValue>>>,
|
||||
language: Option<ScriptLang>,
|
||||
@@ -3244,272 +3152,6 @@ pub async fn check_license_key_valid() -> error::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
use windmill_common::flows::InputTransform;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct BatchReRunJobsBodyArgs {
|
||||
job_ids: Vec<Uuid>,
|
||||
script_options_by_path: HashMap<String, BatchReRunOptions>,
|
||||
flow_options_by_path: HashMap<String, BatchReRunOptions>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct BatchReRunOptions {
|
||||
input_transforms: Option<HashMap<String, InputTransform>>,
|
||||
use_latest_version: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow, Serialize, Clone)]
|
||||
struct BatchReRunQueryReturnType {
|
||||
id: Uuid,
|
||||
kind: JobKind,
|
||||
script_path: String,
|
||||
script_hash: ScriptHash,
|
||||
input: serde_json::Value,
|
||||
scheduled_for: chrono::DateTime<Utc>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
schema: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
#[op2]
|
||||
#[string]
|
||||
fn get_deno_core_job_value(state: &mut OpState) -> Option<String> {
|
||||
let obj = state.borrow::<BatchReRunQueryReturnType>();
|
||||
let str = serde_json::to_string(&obj).ok()?;
|
||||
Some(str)
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
async fn batch_rerun_compute_js_expression(
|
||||
expr: String,
|
||||
job: BatchReRunQueryReturnType,
|
||||
) -> error::Result<Box<RawValue>> {
|
||||
let ext = deno_core::Extension {
|
||||
name: "batch_rerun_arg_transform_ext",
|
||||
ops: vec![get_deno_core_job_value()].into(),
|
||||
..Default::default()
|
||||
};
|
||||
let mut isolate =
|
||||
JsRuntime::new(deno_core::RuntimeOptions { extensions: vec![ext], ..Default::default() });
|
||||
|
||||
{
|
||||
let op_state = isolate.op_state();
|
||||
let mut op_state = op_state.borrow_mut();
|
||||
op_state.put(BatchReRunQueryReturnType { schema: None, ..job });
|
||||
}
|
||||
isolate
|
||||
.execute_script(
|
||||
"<batch_rerun_arg_transform>",
|
||||
"let job = JSON.parse(Deno.core.ops.get_deno_core_job_value());",
|
||||
)
|
||||
.map_err(|e| Error::ExecutionErr(e.to_string()))?;
|
||||
|
||||
// Run user expr
|
||||
let result = isolate
|
||||
.execute_script("<batch_rerun_arg_transform>", expr)
|
||||
.map_err(|e| Error::ExecutionErr(e.to_string()))?;
|
||||
let mut scope = isolate.handle_scope();
|
||||
let result = v8::Local::new(&mut scope, result);
|
||||
let result: serde_json::Value =
|
||||
serde_v8::from_v8(&mut scope, result).map_err(|e| Error::ExecutionErr(e.to_string()))?;
|
||||
let result = JsonRawValue::from_string(result.to_string())?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
async fn batch_rerun_jobs(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(body): Json<BatchReRunJobsBodyArgs>,
|
||||
) -> Response {
|
||||
let stream = batch_rerun_jobs_inner(authed, db, user_db, w_id, body);
|
||||
|
||||
let body = axum::body::Body::from_stream(stream.map(Result::<_, std::convert::Infallible>::Ok));
|
||||
|
||||
Response::builder()
|
||||
.status(201)
|
||||
.header("Content-Type", "text/event-stream")
|
||||
.header("Cache-Control", "no-cache")
|
||||
.body(body)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn batch_rerun_jobs_inner(
|
||||
authed: ApiAuthed,
|
||||
db: DB,
|
||||
user_db: UserDB,
|
||||
w_id: String,
|
||||
body: BatchReRunJobsBodyArgs,
|
||||
) -> impl futures::Stream<Item = String> {
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(10);
|
||||
tokio::spawn(async move {
|
||||
let mut job_stream = sqlx::query_as!(
|
||||
BatchReRunQueryReturnType,
|
||||
r#"SELECT
|
||||
j.id,
|
||||
j.kind AS "kind: _",
|
||||
COALESCE(s.path, f.path) AS "script_path!",
|
||||
COALESCE(s.hash, f.id) AS "script_hash!: _",
|
||||
COALESCE(jc.started_at, jq.scheduled_for, make_date(1970, 1, 1)) AS "scheduled_for!: _",
|
||||
args AS input,
|
||||
COALESCE(s.schema, f.schema) AS "schema: _"
|
||||
FROM v2_job j
|
||||
LEFT JOIN script s ON j.runnable_id = s.hash AND j.kind = 'script'
|
||||
LEFT JOIN flow_version f ON j.runnable_id = f.id AND j.runnable_path = f.path AND j.kind = 'flow'
|
||||
LEFT JOIN v2_job_completed jc ON jc.id = j.id
|
||||
LEFT JOIN v2_job_queue jq ON jq.id = j.id
|
||||
WHERE j.id = ANY($1)
|
||||
AND j.workspace_id = $2
|
||||
AND COALESCE(s.hash, f.id) IS NOT NULL
|
||||
AND COALESCE(s.path, f.path) IS NOT NULL"#,
|
||||
&body.job_ids,
|
||||
w_id
|
||||
).fetch(&db);
|
||||
while let Some(Ok(job)) = job_stream.next().await {
|
||||
let job_result =
|
||||
batch_rerun_handle_job(&job, &authed, &db, &user_db, &w_id, &body).await;
|
||||
let send_to_stream_result = tx
|
||||
.send(match job_result {
|
||||
Ok(uuid) => format!("{}\n", uuid),
|
||||
Err(err) => format!("Error: {}\n", err.to_string()),
|
||||
})
|
||||
.await;
|
||||
match send_to_stream_result {
|
||||
Ok(_) => {}
|
||||
Err(e) => tracing::error!("Couldn't re-run job {}: {}", job.id, e.to_string()),
|
||||
}
|
||||
}
|
||||
});
|
||||
tokio_stream::wrappers::ReceiverStream::new(rx)
|
||||
}
|
||||
|
||||
async fn batch_rerun_handle_job(
|
||||
job: &BatchReRunQueryReturnType,
|
||||
authed: &ApiAuthed,
|
||||
db: &DB,
|
||||
user_db: &UserDB,
|
||||
w_id: &String,
|
||||
body: &BatchReRunJobsBodyArgs,
|
||||
) -> error::Result<String> {
|
||||
let options = if matches!(job.kind, JobKind::Script) {
|
||||
&body.script_options_by_path
|
||||
} else {
|
||||
&body.flow_options_by_path
|
||||
}
|
||||
.get(&job.script_path);
|
||||
|
||||
let mut args: HashMap<String, Box<RawValue>> = serde_json::from_value(job.input.clone())?;
|
||||
let use_latest_version = options.and_then(|o| o.use_latest_version).unwrap_or(false);
|
||||
let input_transforms = options
|
||||
.and_then(|o| o.input_transforms.as_ref())
|
||||
.map(|t| t.iter())
|
||||
.into_iter()
|
||||
.flatten();
|
||||
|
||||
let latest_schema;
|
||||
let schema = if use_latest_version {
|
||||
latest_schema = sqlx::query_scalar!(
|
||||
r#"SELECT COALESCE(
|
||||
(SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),
|
||||
(SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.path = jb.runnable_path AND jb.kind = 'flow')
|
||||
) FROM v2_job jb
|
||||
WHERE jb.id = $1 AND jb.workspace_id = $2
|
||||
GROUP BY jb.kind, jb.runnable_path"#,
|
||||
&job.id,
|
||||
&w_id
|
||||
).fetch_optional(db).await?.flatten();
|
||||
latest_schema.as_ref()
|
||||
} else {
|
||||
job.schema.as_ref()
|
||||
};
|
||||
let schema = schema
|
||||
.and_then(serde_json::Value::as_object)
|
||||
.and_then(|s| s.get("properties"))
|
||||
.and_then(serde_json::Value::as_object);
|
||||
for (property_name, transform) in input_transforms {
|
||||
let schema_has_key = schema
|
||||
.map(|s| s.contains_key(property_name))
|
||||
.unwrap_or(false);
|
||||
if !schema_has_key {
|
||||
continue;
|
||||
}
|
||||
match transform {
|
||||
InputTransform::Static { value } => {
|
||||
args.insert(property_name.clone(), value.clone());
|
||||
}
|
||||
InputTransform::Javascript { expr } => {
|
||||
#[cfg(not(feature = "deno_core"))]
|
||||
Err(error::Error::ExecutionErr(
|
||||
format!("deno_core feature is not activated, cannot evaluate: {expr}")
|
||||
.to_string(),
|
||||
))?;
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
args.insert(
|
||||
property_name.clone(),
|
||||
batch_rerun_compute_js_expression(expr.clone(), job.clone()).await?,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call appropriate function to push job to queue
|
||||
match job.kind {
|
||||
JobKind::Flow => {
|
||||
let result = run_flow_by_path_inner(
|
||||
authed.clone(),
|
||||
db.clone(),
|
||||
user_db.clone(),
|
||||
w_id.clone(),
|
||||
StripPath(job.script_path.clone()),
|
||||
RunJobQuery { ..Default::default() },
|
||||
PushArgsOwned { extra: None, args },
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
if let Ok((_, uuid)) = result {
|
||||
return Ok(uuid);
|
||||
}
|
||||
}
|
||||
JobKind::Script => {
|
||||
let result = if use_latest_version {
|
||||
run_script_by_path_inner(
|
||||
authed.clone(),
|
||||
db.clone(),
|
||||
user_db.clone(),
|
||||
w_id.clone(),
|
||||
StripPath(job.script_path.clone()),
|
||||
RunJobQuery { ..Default::default() },
|
||||
PushArgsOwned { extra: None, args },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
run_job_by_hash_inner(
|
||||
authed.clone(),
|
||||
db.clone(),
|
||||
user_db.clone(),
|
||||
w_id.clone(),
|
||||
job.script_hash,
|
||||
RunJobQuery { ..Default::default() },
|
||||
PushArgsOwned { extra: None, args },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
};
|
||||
if let Ok((_, uuid)) = result {
|
||||
return Ok(uuid);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Err(error::Error::ExecutionErr(
|
||||
format!("Couldn't re-run job {}", job.id).to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn run_flow_by_path(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
@@ -4622,6 +4264,7 @@ pub async fn run_wait_result_script_by_hash(
|
||||
check_license_key_valid().await?;
|
||||
|
||||
let args = args.to_push_args_owned(&authed, &db, &w_id).await?;
|
||||
|
||||
check_queue_too_long(&db, run_query.queue_limit).await?;
|
||||
|
||||
let hash = script_hash.0;
|
||||
@@ -4855,10 +4498,7 @@ async fn run_preview_script(
|
||||
Some(PreviewKind::Identity) => JobPayload::Identity,
|
||||
Some(PreviewKind::Noop) => JobPayload::Noop,
|
||||
_ => JobPayload::Code(RawCode {
|
||||
hash: preview
|
||||
.script_hash
|
||||
.as_ref()
|
||||
.and_then(|s| windmill_common::scripts::to_i64(s).ok()),
|
||||
hash: None,
|
||||
content: preview.content.unwrap_or_default(),
|
||||
path: preview.path,
|
||||
language: preview.language.unwrap_or(ScriptLang::Deno),
|
||||
@@ -5455,21 +5095,6 @@ async fn add_batch_jobs(
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id)
|
||||
SELECT unnest($1::uuid[]), $2, $3, $4, $5, $6, $7, $8",
|
||||
&uuids,
|
||||
authed.email,
|
||||
authed.username,
|
||||
authed.is_admin,
|
||||
authed.is_operator,
|
||||
&[],
|
||||
&[],
|
||||
w_id,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
if let Some(flow_status) = flow_status {
|
||||
sqlx::query!(
|
||||
"INSERT INTO v2_job_status (id, flow_status)
|
||||
@@ -5838,7 +5463,7 @@ pub fn filter_list_completed_query(
|
||||
if let Some(label) = &lq.label {
|
||||
if lq.allow_wildcards.unwrap_or(false) {
|
||||
let wh = format!(
|
||||
"EXISTS (SELECT 1 FROM jsonb_array_elements_text(result->'wm_labels') label WHERE jsonb_typeof(result->'wm_labels') = 'array' AND label LIKE '{}')",
|
||||
"EXISTS (SELECT 1 FROM jsonb_array_elements_text(result->'wm_labels') label WHERE label LIKE '{}')",
|
||||
&label.replace("*", "%").replace("'", "''")
|
||||
);
|
||||
sqlb.and_where("result ? 'wm_labels'");
|
||||
@@ -5969,7 +5594,7 @@ pub fn filter_list_completed_query(
|
||||
|
||||
pub fn list_completed_jobs_query(
|
||||
w_id: &str,
|
||||
per_page: Option<usize>,
|
||||
per_page: usize,
|
||||
offset: usize,
|
||||
lq: &ListCompletedQuery,
|
||||
fields: &[&str],
|
||||
@@ -5980,10 +5605,8 @@ pub fn list_completed_jobs_query(
|
||||
.fields(fields)
|
||||
.order_by("v2_job.created_at", lq.order_desc.unwrap_or(true))
|
||||
.offset(offset)
|
||||
.limit(per_page)
|
||||
.clone();
|
||||
if let Some(per_page) = per_page {
|
||||
sqlb.limit(per_page);
|
||||
}
|
||||
|
||||
if let Some(tags) = tags {
|
||||
sqlb.and_where_in(
|
||||
@@ -6044,7 +5667,7 @@ async fn list_completed_jobs(
|
||||
|
||||
let sql = list_completed_jobs_query(
|
||||
&w_id,
|
||||
Some(per_page),
|
||||
per_page,
|
||||
offset,
|
||||
&lq,
|
||||
&[
|
||||
|
||||
@@ -18,8 +18,6 @@ use crate::oauth2_ee::SlackVerifier;
|
||||
#[cfg(feature = "smtp")]
|
||||
use crate::smtp_server_ee::SmtpServer;
|
||||
|
||||
#[cfg(feature = "mcp")]
|
||||
use crate::mcp::{setup_mcp_server, Runner as McpRunner};
|
||||
use crate::tracing_init::MyOnFailure;
|
||||
use crate::{
|
||||
tracing_init::{MyMakeSpan, MyOnResponse},
|
||||
@@ -29,7 +27,6 @@ use crate::{
|
||||
|
||||
#[cfg(feature = "agent_worker_server")]
|
||||
use agent_workers_ee::AgentCache;
|
||||
|
||||
use anyhow::Context;
|
||||
use argon2::Argon2;
|
||||
use axum::extract::DefaultBodyLimit;
|
||||
@@ -142,9 +139,6 @@ mod workspaces_ee;
|
||||
mod workspaces_export;
|
||||
mod workspaces_extra;
|
||||
|
||||
#[cfg(feature = "mcp")]
|
||||
mod mcp;
|
||||
|
||||
pub const DEFAULT_BODY_LIMIT: usize = 2097152 * 100; // 200MB
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
@@ -225,7 +219,6 @@ pub async fn run_server(
|
||||
mut killpill_rx: tokio::sync::broadcast::Receiver<()>,
|
||||
port_tx: tokio::sync::oneshot::Sender<String>,
|
||||
server_mode: bool,
|
||||
mcp_mode: bool,
|
||||
_base_internal_url: String,
|
||||
) -> anyhow::Result<()> {
|
||||
let user_db = UserDB::new(db.clone());
|
||||
@@ -411,7 +404,7 @@ pub async fn run_server(
|
||||
Router::new()
|
||||
};
|
||||
|
||||
if !*CLOUD_HOSTED && server_mode && !mcp_mode {
|
||||
if !*CLOUD_HOSTED && server_mode {
|
||||
#[cfg(feature = "websocket")]
|
||||
{
|
||||
let ws_killpill_rx = killpill_rx.resubscribe();
|
||||
@@ -455,41 +448,9 @@ pub async fn run_server(
|
||||
}
|
||||
}
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(addr)
|
||||
.await
|
||||
.context("binding main windmill server")?;
|
||||
let port = listener.local_addr().map(|x| x.port()).unwrap_or(8000);
|
||||
let ip = listener
|
||||
.local_addr()
|
||||
.map(|x| x.ip().to_string())
|
||||
.unwrap_or("localhost".to_string());
|
||||
|
||||
// Setup MCP server
|
||||
#[allow(unused_variables)]
|
||||
let (mcp_router, mcp_main_ct, mcp_service_ct) = {
|
||||
#[cfg(feature = "mcp")]
|
||||
if server_mode || mcp_mode {
|
||||
let (mcp_sse_server, mcp_router) = setup_mcp_server(addr, "/api/mcp/w/:workspace_id")?;
|
||||
#[cfg(feature = "mcp")]
|
||||
let mcp_main_ct = mcp_sse_server.config.ct.clone(); // Token to signal shutdown *to* MCP
|
||||
#[cfg(feature = "mcp")]
|
||||
let mcp_service_ct = mcp_sse_server.with_service(McpRunner::new); // Token to wait for MCP *service* shutdown
|
||||
(mcp_router, Some(mcp_main_ct), Some(mcp_service_ct))
|
||||
} else {
|
||||
(Router::new(), None, None)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "mcp"))]
|
||||
(Router::new(), None::<()>, None::<()>)
|
||||
};
|
||||
|
||||
#[cfg(feature = "agent_worker_server")]
|
||||
let (agent_workers_router, agent_workers_bg_processor, agent_workers_killpill_tx) =
|
||||
if server_mode {
|
||||
agent_workers_ee::workspaced_service(db.clone(), _base_internal_url.clone())
|
||||
} else {
|
||||
(Router::new(), vec![], None)
|
||||
};
|
||||
agent_workers_ee::workspaced_service(db.clone(), _base_internal_url.clone());
|
||||
|
||||
#[cfg(feature = "agent_worker_server")]
|
||||
let agent_cache = Arc::new(AgentCache::new());
|
||||
@@ -570,6 +531,26 @@ pub async fn run_server(
|
||||
.nest("/ai", ai::global_service())
|
||||
.route_layer(from_extractor::<ApiAuthed>())
|
||||
.route_layer(from_extractor::<users::Tokened>())
|
||||
.nest("/agent_workers", {
|
||||
#[cfg(feature = "agent_worker_server")]
|
||||
{
|
||||
agent_workers_ee::global_service().layer(Extension(agent_cache.clone()))
|
||||
}
|
||||
#[cfg(not(feature = "agent_worker_server"))]
|
||||
{
|
||||
Router::new()
|
||||
}
|
||||
})
|
||||
.nest("/w/:workspace_id/agent_workers", {
|
||||
#[cfg(feature = "agent_worker_server")]
|
||||
{
|
||||
agent_workers_router.layer(Extension(agent_cache.clone()))
|
||||
}
|
||||
#[cfg(not(feature = "agent_worker_server"))]
|
||||
{
|
||||
Router::new()
|
||||
}
|
||||
})
|
||||
.nest("/jobs", jobs::global_root_service())
|
||||
.nest(
|
||||
"/srch/w/:workspace_id/index",
|
||||
@@ -605,28 +586,6 @@ pub async fn run_server(
|
||||
.layer(from_extractor::<OptAuthed>())
|
||||
.layer(cors.clone()),
|
||||
)
|
||||
.nest("/mcp/w/:workspace_id", mcp_router)
|
||||
.layer(from_extractor::<OptAuthed>())
|
||||
.nest("/agent_workers", {
|
||||
#[cfg(feature = "agent_worker_server")]
|
||||
{
|
||||
agent_workers_ee::global_service().layer(Extension(agent_cache.clone()))
|
||||
}
|
||||
#[cfg(not(feature = "agent_worker_server"))]
|
||||
{
|
||||
Router::new()
|
||||
}
|
||||
})
|
||||
.nest("/w/:workspace_id/agent_workers", {
|
||||
#[cfg(feature = "agent_worker_server")]
|
||||
{
|
||||
agent_workers_router.layer(Extension(agent_cache.clone()))
|
||||
}
|
||||
#[cfg(not(feature = "agent_worker_server"))]
|
||||
{
|
||||
Router::new()
|
||||
}
|
||||
})
|
||||
.nest(
|
||||
"/w/:workspace_id/jobs_u",
|
||||
jobs::workspace_unauthed_service().layer(cors.clone()),
|
||||
@@ -735,6 +694,14 @@ pub async fn run_server(
|
||||
.on_failure(MyOnFailure {}),
|
||||
)
|
||||
};
|
||||
let listener = tokio::net::TcpListener::bind(addr)
|
||||
.await
|
||||
.context("binding main windmill server")?;
|
||||
let port = listener.local_addr().map(|x| x.port()).unwrap_or(8000);
|
||||
let ip = listener
|
||||
.local_addr()
|
||||
.map(|x| x.ip().to_string())
|
||||
.unwrap_or("localhost".to_string());
|
||||
|
||||
let server = axum::serve(listener, app.into_make_service());
|
||||
|
||||
@@ -752,33 +719,19 @@ pub async fn run_server(
|
||||
let server = server.with_graceful_shutdown(async move {
|
||||
killpill_rx.recv().await.ok();
|
||||
#[cfg(feature = "agent_worker_server")]
|
||||
if let Some(agent_workers_killpill_tx) = agent_workers_killpill_tx {
|
||||
if let Err(e) = agent_workers_killpill_tx.kill().await {
|
||||
tracing::error!("Error killing agent workers: {e:#}");
|
||||
}
|
||||
if let Err(e) = agent_workers_killpill_tx.kill().await {
|
||||
tracing::error!("Error killing agent workers: {e:#}");
|
||||
}
|
||||
tracing::info!("Graceful shutdown of server");
|
||||
|
||||
#[cfg(feature = "mcp")]
|
||||
{
|
||||
if let Some(mcp_main_ct) = mcp_main_ct {
|
||||
tracing::info!("Received shutdown signal, cancelling MCP server...");
|
||||
mcp_main_ct.cancel();
|
||||
}
|
||||
if let Some(mcp_service_ct) = mcp_service_ct {
|
||||
tracing::info!("Waiting for MCP service cancellation...");
|
||||
mcp_service_ct.cancelled().await;
|
||||
tracing::info!("MCP service cancelled.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.await?;
|
||||
|
||||
#[cfg(feature = "agent_worker_server")]
|
||||
for (i, bg_processor) in agent_workers_bg_processor.into_iter().enumerate() {
|
||||
tracing::info!("server off. shutting down agent worker bg processor {i}");
|
||||
if let Some(bg_processor) = agent_workers_bg_processor {
|
||||
tracing::info!("server off. shutting down agent workers bg processor");
|
||||
bg_processor.await?;
|
||||
tracing::info!("agent worker bg processor {i} shut down");
|
||||
tracing::info!("agent workers bg processor shut down");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user