Compare commits
45 Commits
v1.396.1
...
preprocess
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9dcddf834e | ||
|
|
e6ff31ca1d | ||
|
|
398a09b741 | ||
|
|
fbd0071a54 | ||
|
|
81c2bb0691 | ||
|
|
aeb5b5bcd1 | ||
|
|
590321fd3c | ||
|
|
8a277a0ad9 | ||
|
|
8b7bbe80a0 | ||
|
|
34a50ef8ca | ||
|
|
93fa6a0e97 | ||
|
|
74c2cabd9f | ||
|
|
17a8700956 | ||
|
|
8a469e7595 | ||
|
|
ad69c08228 | ||
|
|
4d5415dbd2 | ||
|
|
99c24109de | ||
|
|
61f27acbd5 | ||
|
|
5c6c0ff3cc | ||
|
|
a500994cc2 | ||
|
|
a17195d88f | ||
|
|
e99e7b2b0b | ||
|
|
01c16f29a8 | ||
|
|
30017cc2b1 | ||
|
|
9c506a83cb | ||
|
|
50a6f789fa | ||
|
|
5be7be03a6 | ||
|
|
70fa78c10d | ||
|
|
304dac3447 | ||
|
|
815f34b2af | ||
|
|
794f87aa0d | ||
|
|
9e9562e1a4 | ||
|
|
8212532b29 | ||
|
|
2a9d1b587b | ||
|
|
aec6972e29 | ||
|
|
69b8754aef | ||
|
|
a20ee6096a | ||
|
|
30fe28ceec | ||
|
|
3fd626c8d2 | ||
|
|
07372e7e65 | ||
|
|
623ece8c67 | ||
|
|
63f70f67d7 | ||
|
|
1b3e90ce7a | ||
|
|
6855b8da9a | ||
|
|
69b53603a5 |
10
.github/workflows/docker-image.yml
vendored
10
.github/workflows/docker-image.yml
vendored
@@ -1,10 +1,8 @@
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME:
|
||||
${{ github.event_name != 'pull_request' && github.repository ||
|
||||
IMAGE_NAME: ${{ github.event_name != 'pull_request' && github.repository ||
|
||||
'windmill-labs/windmill-test' }}
|
||||
DEV_SHA:
|
||||
${{ github.event_name != 'pull_request' && 'dev' || format('pr-{0}',
|
||||
DEV_SHA: ${{ github.event_name != 'pull_request' && 'dev' || format('pr-{0}',
|
||||
github.event.number) }}
|
||||
|
||||
name: Build windmill:main
|
||||
@@ -745,7 +743,7 @@ jobs:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
file: "./docker/DockerfileRust"
|
||||
file: "./docker/DockerfileFull"
|
||||
tags: |
|
||||
${{ steps.meta-public.outputs.tags }}
|
||||
labels: |
|
||||
@@ -790,7 +788,7 @@ jobs:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
file: "./docker/DockerfileRustEe"
|
||||
file: "./docker/DockerfileFullEe"
|
||||
tags: |
|
||||
${{ steps.meta-ee-public.outputs.tags }}
|
||||
labels: |
|
||||
|
||||
75
CHANGELOG.md
75
CHANGELOG.md
@@ -1,5 +1,80 @@
|
||||
# Changelog
|
||||
|
||||
## [1.399.0](https://github.com/windmill-labs/windmill/compare/v1.398.1...v1.399.0) (2024-09-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add tag filtering to external JWT authentication ([#4425](https://github.com/windmill-labs/windmill/issues/4425)) ([590321f](https://github.com/windmill-labs/windmill/commit/590321fd3c88046a657e0a751b62a19424102060))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** version the whole client for deno compatibility ([81c2bb0](https://github.com/windmill-labs/windmill/commit/81c2bb069176a95b8fb9c52b31e5e03c1cc78afc))
|
||||
* correct AI generation for CRON and regex ([#4437](https://github.com/windmill-labs/windmill/issues/4437)) ([aeb5b5b](https://github.com/windmill-labs/windmill/commit/aeb5b5bcd163e2e7d1413d7e4aed8bd769996f24))
|
||||
|
||||
## [1.398.1](https://github.com/windmill-labs/windmill/compare/v1.398.0...v1.398.1) (2024-09-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* time handling in bun and deno ([#4423](https://github.com/windmill-labs/windmill/issues/4423)) ([61f27ac](https://github.com/windmill-labs/windmill/commit/61f27acbd5b477f0e2453d4b02d406a7aef76009))
|
||||
|
||||
## [1.398.0](https://github.com/windmill-labs/windmill/compare/v1.397.4...v1.398.0) (2024-09-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** add http routing templates ([#4421](https://github.com/windmill-labs/windmill/issues/4421)) ([e99e7b2](https://github.com/windmill-labs/windmill/commit/e99e7b2b0bb1bc63d1253d147b80bac06eaff103))
|
||||
* http routing ([#4339](https://github.com/windmill-labs/windmill/issues/4339)) ([304dac3](https://github.com/windmill-labs/windmill/commit/304dac34475b8871621c54a768275fae1e9f845e))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow no body in job requests ([#4413](https://github.com/windmill-labs/windmill/issues/4413)) ([70fa78c](https://github.com/windmill-labs/windmill/commit/70fa78c10d8afb3e06be84e3449fdfdeb2673765))
|
||||
* **frontend:** Fix delete branch one index ([#4418](https://github.com/windmill-labs/windmill/issues/4418)) ([30017cc](https://github.com/windmill-labs/windmill/commit/30017cc2b1401ea439e3fbec5d7764b206100a38))
|
||||
* migrate smtp instance settings to global settings ([#4416](https://github.com/windmill-labs/windmill/issues/4416)) ([50a6f78](https://github.com/windmill-labs/windmill/commit/50a6f789fa8a03d70407b0429da049cc0740b6fa))
|
||||
* no failed renewal alert if trial ([#4414](https://github.com/windmill-labs/windmill/issues/4414)) ([5be7be0](https://github.com/windmill-labs/windmill/commit/5be7be03a6df34a9fae96ed7f844d693f7cfce3e))
|
||||
* update pip to 24.2 and python 3.11.8->3.11.10 ([a17195d](https://github.com/windmill-labs/windmill/commit/a17195d88ff52b72f0c2cdd95c6218df8ef6d7cf))
|
||||
* various improvements for ansible ([#4419](https://github.com/windmill-labs/windmill/issues/4419)) ([a500994](https://github.com/windmill-labs/windmill/commit/a500994cc2cd9cc6cbad722059cebd0bf74a8850))
|
||||
|
||||
## [1.397.4](https://github.com/windmill-labs/windmill/compare/v1.397.3...v1.397.4) (2024-09-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** make CLI not require latest version of windmill ([794f87a](https://github.com/windmill-labs/windmill/commit/794f87aa0d33a0b782fcb2726754fdc51bc1d73d))
|
||||
|
||||
## [1.397.3](https://github.com/windmill-labs/windmill/compare/v1.397.2...v1.397.3) (2024-09-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** make CLI compatible with Node 18 ([8212532](https://github.com/windmill-labs/windmill/commit/8212532b29fbcf971cb320b8cfc84f3a8d8ad795))
|
||||
|
||||
## [1.397.2](https://github.com/windmill-labs/windmill/compare/v1.397.1...v1.397.2) (2024-09-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bun:** never pre-bundle BASE_URL env variable ([69b8754](https://github.com/windmill-labs/windmill/commit/69b8754aef32682bf812be783f79b81cd8526c7a))
|
||||
|
||||
## [1.397.1](https://github.com/windmill-labs/windmill/compare/v1.397.0...v1.397.1) (2024-09-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **go:** put shared directory in job dir and not go parent ([623ece8](https://github.com/windmill-labs/windmill/commit/623ece8c6791b2c18648a68c562fcd5c8291be9b))
|
||||
* improve ability to paste from macos in vscode extension ([07372e7](https://github.com/windmill-labs/windmill/commit/07372e7e65d77df338718929f133e477a1daf993))
|
||||
* update git sync script ([30fe28c](https://github.com/windmill-labs/windmill/commit/30fe28ceecb9043eee6811dce8c3e2cd9224927b))
|
||||
|
||||
## [1.397.0](https://github.com/windmill-labs/windmill/compare/v1.396.1...v1.397.0) (2024-09-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* ansible playbook support ([#4399](https://github.com/windmill-labs/windmill/issues/4399)) ([6855b8d](https://github.com/windmill-labs/windmill/commit/6855b8da9ad92aed514ee1ab214c9550d04e7a22))
|
||||
|
||||
## [1.396.1](https://github.com/windmill-labs/windmill/compare/v1.396.0...v1.396.1) (2024-09-18)
|
||||
|
||||
|
||||
|
||||
BIN
Coupa Supplier Portal - Register.pdf
Normal file
BIN
Coupa Supplier Portal - Register.pdf
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
ARG DEBIAN_IMAGE=debian:bookworm-slim
|
||||
ARG RUST_IMAGE=rust:1.80-slim-bookworm
|
||||
ARG PYTHON_IMAGE=python:3.11.8-slim-bookworm
|
||||
ARG PYTHON_IMAGE=python:3.11.10-slim-bookworm
|
||||
|
||||
FROM ${RUST_IMAGE} AS rust_base
|
||||
|
||||
@@ -95,6 +95,8 @@ ARG WITH_KUBECTL=true
|
||||
ARG WITH_HELM=true
|
||||
ARG WITH_GIT=true
|
||||
|
||||
RUN pip install --upgrade pip==24.2
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y ca-certificates wget curl jq unzip build-essential unixodbc xmlsec1 software-properties-common \
|
||||
&& apt-get clean \
|
||||
|
||||
89
backend/.sqlx/query-02f1a6eeb27067dc438459238e7b016f5ccf9e3fe0ffbe88471f15aad8f74441.json
generated
Normal file
89
backend/.sqlx/query-02f1a6eeb27067dc438459238e7b016f5ccf9e3fe0ffbe88471f15aad8f74441.json
generated
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as \"http_method: _\" FROM http_trigger WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "route_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "is_async",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "requires_auth",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "http_method: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "http_method",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"get",
|
||||
"post",
|
||||
"put",
|
||||
"delete",
|
||||
"patch"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "02f1a6eeb27067dc438459238e7b016f5ccf9e3fe0ffbe88471f15aad8f74441"
|
||||
}
|
||||
@@ -32,6 +32,11 @@
|
||||
"ordinal": 5,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "format_extension",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -46,6 +51,7 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
@@ -57,7 +57,8 @@
|
||||
"mssql",
|
||||
"php",
|
||||
"bunnative",
|
||||
"rust"
|
||||
"rust",
|
||||
"ansible"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,8 @@
|
||||
"mssql",
|
||||
"php",
|
||||
"bunnative",
|
||||
"rust"
|
||||
"rust",
|
||||
"ansible"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['failure_module'], $1), ARRAY['step'], $2)\n WHERE id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0aff8b0f2cddc0dd696759f48368c7315ff39060be5430c59582e1e05a4b3d90"
|
||||
}
|
||||
15
backend/.sqlx/query-0e52a588f3edeb8fb58d6d62247b8590e51171e2811c62737bdb81fb0ac8f182.json
generated
Normal file
15
backend/.sqlx/query-0e52a588f3edeb8fb58d6d62247b8590e51171e2811c62737bdb81fb0ac8f182.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE worker_ping SET \n ping_at = now(), \n jobs_executed = 1, \n current_job_id = $1, \n current_job_workspace_id = 'admins' \n WHERE worker = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0e52a588f3edeb8fb58d6d62247b8590e51171e2811c62737bdb81fb0ac8f182"
|
||||
}
|
||||
36
backend/.sqlx/query-16c4fb122ceb30722f54c92db97427b0ddd36f495d549f8cd23d659d2c866a63.json
generated
Normal file
36
backend/.sqlx/query-16c4fb122ceb30722f54c92db97427b0ddd36f495d549f8cd23d659d2c866a63.json
generated
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE route_path_key = $1 AND http_method = $2)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
{
|
||||
"Custom": {
|
||||
"name": "http_method",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"get",
|
||||
"post",
|
||||
"put",
|
||||
"delete",
|
||||
"patch"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "16c4fb122ceb30722f54c92db97427b0ddd36f495d549f8cd23d659d2c866a63"
|
||||
}
|
||||
16
backend/.sqlx/query-17e3e2a3232865c33fa535b5d99455942e30f932c5b97f1e5b508128f39a288f.json
generated
Normal file
16
backend/.sqlx/query-17e3e2a3232865c33fa535b5d99455942e30f932c5b97f1e5b508128f39a288f.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1), ARRAY['step'], $2)\n WHERE id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "17e3e2a3232865c33fa535b5d99455942e30f932c5b97f1e5b508128f39a288f"
|
||||
}
|
||||
@@ -51,7 +51,8 @@
|
||||
"mssql",
|
||||
"php",
|
||||
"bunnative",
|
||||
"rust"
|
||||
"rust",
|
||||
"ansible"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT tag, dedicated_worker, flow_version.value->>'early_return' as early_return \n FROM flow \n LEFT JOIN flow_version\n ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]\n WHERE flow.path = $1 and flow.workspace_id = $2",
|
||||
"query": "SELECT tag, dedicated_worker, flow_version.value->>'preprocessor_module' IS NOT NULL as has_preprocessor \n FROM flow \n LEFT JOIN flow_version\n ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]\n WHERE flow.path = $1 and flow.workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -15,8 +15,8 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "early_return",
|
||||
"type_info": "Text"
|
||||
"name": "has_preprocessor",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -31,5 +31,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "872dcaec230579e4480adf23075e323557efbe52c813e3a6a0da6b855291951e"
|
||||
"hash": "3fedf12ee5d44e8af9c3864cc70a8f607dccc015ce2d5150c56c68074cd0f84d"
|
||||
}
|
||||
14
backend/.sqlx/query-4c97fcc93b31c4b3262419d6ee183773a95de3b6694a398cb80d288bef4f130f.json
generated
Normal file
14
backend/.sqlx/query-4c97fcc93b31c4b3262419d6ee183773a95de3b6694a398cb80d288bef4f130f.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE completed_job SET args = '{\"reason\":\"PREPROCESSOR_ARGS_ARE_DISCARDED\"}'::jsonb WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4c97fcc93b31c4b3262419d6ee183773a95de3b6694a398cb80d288bef4f130f"
|
||||
}
|
||||
38
backend/.sqlx/query-4d8640e84fccf1a0b799d8396a51e69345137e68d5096c70ba0a4332075d97ea.json
generated
Normal file
38
backend/.sqlx/query-4d8640e84fccf1a0b799d8396a51e69345137e68d5096c70ba0a4332075d97ea.json
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE http_trigger \n SET route_path = $1, route_path_key = $2, script_path = $3, path = $4, is_flow = $5, http_method = $6, edited_by = $7, email = $8, is_async = $9, requires_auth = $10, edited_at = now() \n WHERE workspace_id = $11 AND path = $12",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
{
|
||||
"Custom": {
|
||||
"name": "http_method",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"get",
|
||||
"post",
|
||||
"put",
|
||||
"delete",
|
||||
"patch"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4d8640e84fccf1a0b799d8396a51e69345137e68d5096c70ba0a4332075d97ea"
|
||||
}
|
||||
108
backend/.sqlx/query-4fb95eae1c871241efe2ef79615ce03cba0e4a12aad3274e4829d98e38ca1491.json
generated
Normal file
108
backend/.sqlx/query-4fb95eae1c871241efe2ef79615ce03cba0e4a12aad3274e4829d98e38ca1491.json
generated
Normal file
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, http_method as \"http_method: _\", edited_by, email, edited_at, extra_perms, is_async, requires_auth\n FROM http_trigger\n WHERE workspace_id = $1 AND path = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "route_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "route_path_key",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "http_method: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "http_method",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"get",
|
||||
"post",
|
||||
"put",
|
||||
"delete",
|
||||
"patch"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "is_async",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "requires_auth",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "4fb95eae1c871241efe2ef79615ce03cba0e4a12aad3274e4829d98e38ca1491"
|
||||
}
|
||||
36
backend/.sqlx/query-55d44f569f8ebfccddf975e1a330ef0dc286f4138efe923832371cdbac7157b0.json
generated
Normal file
36
backend/.sqlx/query-55d44f569f8ebfccddf975e1a330ef0dc286f4138efe923832371cdbac7157b0.json
generated
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE http_trigger SET script_path = $1, path = $2, is_flow = $3, http_method = $4, edited_by = $5, email = $6, is_async = $7, requires_auth = $8, edited_at = now() \n WHERE workspace_id = $9 AND path = $10",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
{
|
||||
"Custom": {
|
||||
"name": "http_method",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"get",
|
||||
"post",
|
||||
"put",
|
||||
"delete",
|
||||
"patch"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "55d44f569f8ebfccddf975e1a330ef0dc286f4138efe923832371cdbac7157b0"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "select path, tag, concurrency_key, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority, delete_after_use, timeout from script where hash = $1 AND workspace_id = $2",
|
||||
"query": "select path, tag, concurrency_key, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority, delete_after_use, timeout, has_preprocessor from script where hash = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -56,7 +56,8 @@
|
||||
"mssql",
|
||||
"php",
|
||||
"bunnative",
|
||||
"rust"
|
||||
"rust",
|
||||
"ansible"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -81,6 +82,11 @@
|
||||
"ordinal": 10,
|
||||
"name": "timeout",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "has_preprocessor",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -100,8 +106,9 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "8b10c9ade85c0307e300224e87962ceba8e7ccbe53b56c4670a1634bb9d0e89c"
|
||||
"hash": "64ce3eabed6de7a4f99884640c21218ac19ab2a4bc5f8b42deb2476f85d0deac"
|
||||
}
|
||||
@@ -32,6 +32,11 @@
|
||||
"ordinal": 5,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "format_extension",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -45,6 +50,7 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT config FROM config WHERE name = 'server'",
|
||||
"query": "SELECT value FROM global_settings WHERE name = 'smtp_settings'",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "config",
|
||||
"name": "value",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
@@ -13,8 +13,8 @@
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "8ca76fcadc659d7069b97930cdde2d5200f3cef9dedb83b76c8a6a433d2a4de3"
|
||||
"hash": "83ec97f6aad154e0e06ee05a3647dab8f89b1b2d7a569c7eac8c4169e37b9f8b"
|
||||
}
|
||||
37
backend/.sqlx/query-867e0f8299e22a6b8e02d21adeb0281b1a10593715db823a5adeeac3ab1058cd.json
generated
Normal file
37
backend/.sqlx/query-867e0f8299e22a6b8e02d21adeb0281b1a10593715db823a5adeeac3ab1058cd.json
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE route_path_key = $1 AND workspace_id = $2 AND http_method = $3)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
{
|
||||
"Custom": {
|
||||
"name": "http_method",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"get",
|
||||
"post",
|
||||
"put",
|
||||
"delete",
|
||||
"patch"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "867e0f8299e22a6b8e02d21adeb0281b1a10593715db823a5adeeac3ab1058cd"
|
||||
}
|
||||
17
backend/.sqlx/query-870d7feec169f70d140b7477562315ed2fd4662975bf0d01cfbffac86a959368.json
generated
Normal file
17
backend/.sqlx/query-870d7feec169f70d140b7477562315ed2fd4662975bf0d01cfbffac86a959368.json
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2), ARRAY['step'], $3)\n WHERE id = $4",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "870d7feec169f70d140b7477562315ed2fd4662975bf0d01cfbffac86a959368"
|
||||
}
|
||||
41
backend/.sqlx/query-98081ee9a4a4a52ae70d74d26e8608a010722d7ea9dc343ae8f03e38978ffaba.json
generated
Normal file
41
backend/.sqlx/query-98081ee9a4a4a52ae70d74d26e8608a010722d7ea9dc343ae8f03e38978ffaba.json
generated
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT tag, dedicated_worker, flow_version.value->>'early_return' as early_return, flow_version.value->>'preprocessor_module' IS NOT NULL as has_preprocessor\n FROM flow \n LEFT JOIN flow_version\n ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]\n WHERE flow.path = $1 and flow.workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "tag",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "dedicated_worker",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "early_return",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "has_preprocessor",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "98081ee9a4a4a52ae70d74d26e8608a010722d7ea9dc343ae8f03e38978ffaba"
|
||||
}
|
||||
23
backend/.sqlx/query-9a050ab74cfc13a4b855408e61ebcb0a9d27e5563fa7af5e00a4916b03a62ddb.json
generated
Normal file
23
backend/.sqlx/query-9a050ab74cfc13a4b855408e61ebcb0a9d27e5563fa7af5e00a4916b03a62ddb.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE path = $1 AND workspace_id = $2)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "9a050ab74cfc13a4b855408e61ebcb0a9d27e5563fa7af5e00a4916b03a62ddb"
|
||||
}
|
||||
15
backend/.sqlx/query-9c17ddca92e0a93051b36bb688ad6c1c24108e45c94be52595ca6cb82135f4eb.json
generated
Normal file
15
backend/.sqlx/query-9c17ddca92e0a93051b36bb688ad6c1c24108e45c94be52595ca6cb82135f4eb.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE queue SET args = (select result FROM completed_job WHERE id = $1) WHERE id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "9c17ddca92e0a93051b36bb688ad6c1c24108e45c94be52595ca6cb82135f4eb"
|
||||
}
|
||||
@@ -56,7 +56,8 @@
|
||||
"mssql",
|
||||
"php",
|
||||
"bunnative",
|
||||
"rust"
|
||||
"rust",
|
||||
"ansible"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@
|
||||
"mssql",
|
||||
"php",
|
||||
"bunnative",
|
||||
"rust"
|
||||
"rust",
|
||||
"ansible"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
22
backend/.sqlx/query-9ebb9c16948a695a053068d9a1df0152691271ac33d8c82db47be11746ccbcef.json
generated
Normal file
22
backend/.sqlx/query-9ebb9c16948a695a053068d9a1df0152691271ac33d8c82db47be11746ccbcef.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "9ebb9c16948a695a053068d9a1df0152691271ac33d8c82db47be11746ccbcef"
|
||||
}
|
||||
16
backend/.sqlx/query-ad8f9e0b06f288051cbec3c91877430a1d61353b9122fd42777d92e4cbc9f4fa.json
generated
Normal file
16
backend/.sqlx/query-ad8f9e0b06f288051cbec3c91877430a1d61353b9122fd42777d92e4cbc9f4fa.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['failure_module'], $1), ARRAY['step'], $2)\n WHERE id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ad8f9e0b06f288051cbec3c91877430a1d61353b9122fd42777d92e4cbc9f4fa"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30)",
|
||||
"query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -37,7 +37,8 @@
|
||||
"mssql",
|
||||
"php",
|
||||
"bunnative",
|
||||
"rust"
|
||||
"rust",
|
||||
"ansible"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -71,10 +72,11 @@
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"Varchar"
|
||||
"Varchar",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8e7ff45c5378c3a3406ba94dc0653afa5d28c072203617c598caefaaf1bafcfb"
|
||||
"hash": "b49b1eaf58e62063c7a03039d0c36e65991b1d21a3306390fb1c7ca38babafe3"
|
||||
}
|
||||
23
backend/.sqlx/query-b57a188ca137162a2848ebf81fa3aeca9a14ac628c61a358e2c6612c57249b0f.json
generated
Normal file
23
backend/.sqlx/query-b57a188ca137162a2848ebf81fa3aeca9a14ac628c61a358e2c6612c57249b0f.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1 AND path = $2)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "b57a188ca137162a2848ebf81fa3aeca9a14ac628c61a358e2c6612c57249b0f"
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2), ARRAY['step'], $3)\n WHERE id = $4",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "b64c7f3b483c74c51aecda65664c36d9686112182dbf35651e494da739b922f9"
|
||||
}
|
||||
@@ -32,6 +32,11 @@
|
||||
"ordinal": 5,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "format_extension",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -45,6 +50,7 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
15
backend/.sqlx/query-c14009d133956710f4435a5984c18b9517e256978512f014e1bf5c270f499772.json
generated
Normal file
15
backend/.sqlx/query-c14009d133956710f4435a5984c18b9517e256978512f014e1bf5c270f499772.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1)\n WHERE id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Jsonb",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c14009d133956710f4435a5984c18b9517e256978512f014e1bf5c270f499772"
|
||||
}
|
||||
37
backend/.sqlx/query-c229744534f17f7622c3dee21bb1e7292ff17e6dffe58e80e53bff8baade07c8.json
generated
Normal file
37
backend/.sqlx/query-c229744534f17f7622c3dee21bb1e7292ff17e6dffe58e80e53bff8baade07c8.json
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO http_trigger (workspace_id, path, route_path, route_path_key, script_path, is_flow, is_async, requires_auth, http_method, edited_by, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, now())",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Bool",
|
||||
"Bool",
|
||||
{
|
||||
"Custom": {
|
||||
"name": "http_method",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"get",
|
||||
"post",
|
||||
"put",
|
||||
"delete",
|
||||
"patch"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c229744534f17f7622c3dee21bb1e7292ff17e6dffe58e80e53bff8baade07c8"
|
||||
}
|
||||
28
backend/.sqlx/query-cf1cef7e0fe2e7e3db96b0ec005360361b9eec023a6fc2a4a7a917f59d86af4d.json
generated
Normal file
28
backend/.sqlx/query-cf1cef7e0fe2e7e3db96b0ec005360361b9eec023a6fc2a4a7a917f59d86af4d.json
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT name, format_extension FROM resource_type WHERE format_extension IS NOT NULL AND (workspace_id = $1 OR workspace_id = 'admins')",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "format_extension",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "cf1cef7e0fe2e7e3db96b0ec005360361b9eec023a6fc2a4a7a917f59d86af4d"
|
||||
}
|
||||
@@ -77,7 +77,8 @@
|
||||
"mssql",
|
||||
"php",
|
||||
"bunnative",
|
||||
"rust"
|
||||
"rust",
|
||||
"ansible"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
15
backend/.sqlx/query-dc9a906d6c6156a84fccf4e3a2a7c08d8ed4984409b669162f0a1fc1aa48e188.json
generated
Normal file
15
backend/.sqlx/query-dc9a906d6c6156a84fccf4e3a2a7c08d8ed4984409b669162f0a1fc1aa48e188.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM http_trigger WHERE workspace_id = $1 AND path = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "dc9a906d6c6156a84fccf4e3a2a7c08d8ed4984409b669162f0a1fc1aa48e188"
|
||||
}
|
||||
@@ -32,6 +32,11 @@
|
||||
"ordinal": 5,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "format_extension",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -43,6 +48,7 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT (flow_status->'step')::integer as step, jsonb_array_length(flow_status->'modules') as len FROM queue WHERE id = $1",
|
||||
"query": "SELECT (flow_status->'step')::integer as step, jsonb_array_length(flow_status->'modules') as len FROM queue WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -24,5 +24,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "f80e94176224ef23416da99b26ad5c330d65016fb93345b054cbb63a4bf37fc8"
|
||||
"hash": "f9e0e35b4789a4da89f7bb21fa6b250fd64ee0e0b299471979db5ff8ee906929"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "select hash, tag, concurrency_key, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority, delete_after_use, timeout 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 AND\n deleted = false AND lock IS not NULL AND lock_error_logs IS NULL)",
|
||||
"query": "select hash, tag, concurrency_key, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority, delete_after_use, timeout, has_preprocessor 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 AND\n deleted = false AND lock IS not NULL AND lock_error_logs IS NULL)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -56,7 +56,8 @@
|
||||
"mssql",
|
||||
"php",
|
||||
"bunnative",
|
||||
"rust"
|
||||
"rust",
|
||||
"ansible"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -81,6 +82,11 @@
|
||||
"ordinal": 10,
|
||||
"name": "timeout",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "has_preprocessor",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -100,8 +106,9 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "e1c715020f1efb00123171edfb6173bfeaaa95d4f870240e42b76e5df304ce99"
|
||||
"hash": "fc7ac95d8d5f4532a2b0e50f84c1e273d25543401639a68f868e1e4fd8b0640a"
|
||||
}
|
||||
87
backend/.sqlx/query-fe4f91ca7e179e58041a5c0b1a36015175e9301c01a99e8f2b82cb853349a183.json
generated
Normal file
87
backend/.sqlx/query-fe4f91ca7e179e58041a5c0b1a36015175e9301c01a99e8f2b82cb853349a183.json
generated
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as \"http_method: _\" FROM http_trigger",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "route_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "is_async",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "requires_auth",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "http_method: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "http_method",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"get",
|
||||
"post",
|
||||
"put",
|
||||
"delete",
|
||||
"patch"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "fe4f91ca7e179e58041a5c0b1a36015175e9301c01a99e8f2b82cb853349a183"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource_type\n (workspace_id, name, schema, description, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, now())",
|
||||
"query": "INSERT INTO resource_type\n (workspace_id, name, schema, description, created_by, format_extension, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now())",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -9,10 +9,11 @@
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d249343a1d9fce2f7ca9e6e447eb32d7d11b61acdab80dda24db4a9ba3e1f7e6"
|
||||
"hash": "ffedbb3a2676a6d7b71f81f89109a02a8dba90d40144e942527f8a3fc36dfbc1"
|
||||
}
|
||||
186
backend/Cargo.lock
generated
186
backend/Cargo.lock
generated
@@ -696,9 +696,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1"
|
||||
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1056,9 +1056,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.7.5"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
|
||||
checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
@@ -1083,7 +1083,7 @@ dependencies = [
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper 1.0.1",
|
||||
"tokio",
|
||||
"tower 0.4.13",
|
||||
"tower 0.5.1",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@@ -1091,9 +1091,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
|
||||
checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
@@ -1104,7 +1104,7 @@ dependencies = [
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
"sync_wrapper 0.1.2",
|
||||
"sync_wrapper 1.0.1",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@@ -1735,9 +1735,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.17"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
|
||||
checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -1745,9 +1745,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.17"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
|
||||
checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -1757,9 +1757,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.13"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
||||
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@@ -2795,7 +2795,7 @@ dependencies = [
|
||||
"rustls-webpki 0.102.8",
|
||||
"serde",
|
||||
"tokio",
|
||||
"webpki-roots 0.26.5",
|
||||
"webpki-roots 0.26.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4670,9 +4670,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.158"
|
||||
version = "0.2.159"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
@@ -4710,7 +4710,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"libc",
|
||||
"redox_syscall 0.5.4",
|
||||
"redox_syscall 0.5.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4892,7 +4892,7 @@ dependencies = [
|
||||
"smtp-proto",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.0",
|
||||
"webpki-roots 0.26.5",
|
||||
"webpki-roots 0.26.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5442,7 +5442,7 @@ dependencies = [
|
||||
"md-5 0.10.6",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"quick-xml 0.36.1",
|
||||
"quick-xml 0.36.2",
|
||||
"rand 0.8.5",
|
||||
"reqwest 0.12.7",
|
||||
"ring 0.17.8",
|
||||
@@ -5672,7 +5672,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.5.4",
|
||||
"redox_syscall 0.5.5",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
@@ -5991,9 +5991,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
@@ -6012,9 +6012,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.7.0"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
|
||||
checksum = "d30538d42559de6b034bc76fd6dd4c38961b1ee5c6c56e3808c50128fdbc22ce"
|
||||
|
||||
[[package]]
|
||||
name = "postgres-native-tls"
|
||||
@@ -6120,7 +6120,7 @@ version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
|
||||
dependencies = [
|
||||
"toml_edit 0.22.21",
|
||||
"toml_edit 0.22.22",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6374,9 +6374,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.36.1"
|
||||
version = "0.36.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96a05e2e8efddfa51a84ca47cec303fac86c8541b686d37cac5efc0e094417bc"
|
||||
checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
@@ -6623,9 +6623,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.4"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853"
|
||||
checksum = "62871f2d65009c0256aed1b9cfeeb8ac272833c404e13d53d400cd0dad7a2ac0"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
@@ -7415,9 +7415,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.11.1"
|
||||
version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
|
||||
checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@@ -7755,9 +7755,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "simdutf8"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
|
||||
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
||||
|
||||
[[package]]
|
||||
name = "simple_asn1"
|
||||
@@ -8051,7 +8051,7 @@ dependencies = [
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid 1.10.0",
|
||||
"webpki-roots 0.26.5",
|
||||
"webpki-roots 0.26.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9019,9 +9019,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.41"
|
||||
version = "0.4.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909"
|
||||
checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
@@ -9061,18 +9061,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.63"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.63"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -9433,7 +9433,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.22.21",
|
||||
"toml_edit 0.22.22",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9460,15 +9460,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.21"
|
||||
version = "0.22.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf"
|
||||
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||
dependencies = [
|
||||
"indexmap 2.5.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow 0.6.18",
|
||||
"winnow 0.6.19",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9497,7 +9497,6 @@ dependencies = [
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9506,8 +9505,14 @@ version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper 0.1.2",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9924,9 +9929,9 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-id"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1b6def86329695390197b82c1e244a54a131ceb66c996f2088a3876e2ae083f"
|
||||
checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-id-start"
|
||||
@@ -9972,15 +9977,15 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.13"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_categories"
|
||||
@@ -10044,7 +10049,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
"webpki-roots 0.26.5",
|
||||
"webpki-roots 0.26.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10340,9 +10345,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.5"
|
||||
version = "0.26.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a"
|
||||
checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
@@ -10377,7 +10382,7 @@ version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d"
|
||||
dependencies = [
|
||||
"redox_syscall 0.5.4",
|
||||
"redox_syscall 0.5.5",
|
||||
"wasite",
|
||||
"web-sys",
|
||||
]
|
||||
@@ -10421,7 +10426,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -10463,7 +10468,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -10497,6 +10502,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"magic-crypt",
|
||||
"mail-parser",
|
||||
"matchit",
|
||||
"mime_guess",
|
||||
"native-tls",
|
||||
"object_store",
|
||||
@@ -10547,7 +10553,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"chrono",
|
||||
@@ -10565,7 +10571,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
@@ -10578,7 +10584,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@@ -10622,7 +10628,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-git-sync"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"rsmq_async",
|
||||
@@ -10637,7 +10643,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-indexer"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -10658,7 +10664,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"convert_case 0.6.0",
|
||||
"serde",
|
||||
@@ -10667,7 +10673,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -10679,7 +10685,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gosyn",
|
||||
@@ -10691,7 +10697,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-graphql"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -10703,7 +10709,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-php"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.13.0",
|
||||
@@ -10714,7 +10720,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.13.0",
|
||||
@@ -10725,7 +10731,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-imports"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -10743,7 +10749,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-rust"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
@@ -10760,7 +10766,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -10772,7 +10778,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -10790,7 +10796,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-wasm"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"getrandom 0.2.15",
|
||||
@@ -10806,11 +10812,22 @@ dependencies = [
|
||||
"windmill-parser-rust",
|
||||
"windmill-parser-sql",
|
||||
"windmill-parser-ts",
|
||||
"windmill-parser-yaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-yaml"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
"windmill-parser",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -10843,7 +10860,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-sql-datatype-parser-wasm"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
@@ -10853,7 +10870,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -10921,7 +10938,9 @@ dependencies = [
|
||||
"windmill-parser-rust",
|
||||
"windmill-parser-sql",
|
||||
"windmill-parser-ts",
|
||||
"windmill-parser-yaml",
|
||||
"windmill-queue",
|
||||
"yaml-rust",
|
||||
"zstd 0.12.4",
|
||||
]
|
||||
|
||||
@@ -11123,9 +11142,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.18"
|
||||
version = "0.6.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
|
||||
checksum = "c52ac009d615e79296318c1bcce2d422aaca15ad08515e344feeda07df67a587"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -11181,6 +11200,15 @@ dependencies = [
|
||||
"lzma-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -23,10 +23,11 @@ members = [
|
||||
"./parsers/windmill-parser-py",
|
||||
"./parsers/windmill-parser-py-imports",
|
||||
"./parsers/windmill-sql-datatype-parser-wasm",
|
||||
"./parsers/windmill-parser-yaml",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.396.1"
|
||||
version = "1.399.0"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -118,6 +119,7 @@ windmill-parser-py = { path = "./parsers/windmill-parser-py" }
|
||||
windmill-parser-py-imports = { path = "./parsers/windmill-parser-py-imports" }
|
||||
windmill-parser-go = { path = "./parsers/windmill-parser-go" }
|
||||
windmill-parser-rust = { path = "./parsers/windmill-parser-rust" }
|
||||
windmill-parser-yaml = { path = "./parsers/windmill-parser-yaml" }
|
||||
windmill-parser-bash = { path = "./parsers/windmill-parser-bash" }
|
||||
windmill-parser-sql = { path = "./parsers/windmill-parser-sql" }
|
||||
windmill-parser-graphql = { path = "./parsers/windmill-parser-graphql" }
|
||||
@@ -250,6 +252,7 @@ indexmap = { version = "2.2.5", features = ["serde"]}
|
||||
tokio-native-tls = "^0"
|
||||
openssl = "=0.10"
|
||||
mail-parser = "^0"
|
||||
matchit = "=0.7.3"
|
||||
|
||||
datafusion = "39.0.0"
|
||||
object_store = { version = "0.10.0", features = ["aws", "azure"] }
|
||||
@@ -277,3 +280,4 @@ toml = "0.7"
|
||||
syn = { version = "2.0.74", features = ["full"] }
|
||||
quote = "1.0.36"
|
||||
regex-lite = "0.1.6"
|
||||
yaml-rust = "0.4.5"
|
||||
|
||||
@@ -1 +1 @@
|
||||
534f50aae2f89281304b3c3ba943d18afc07fad4
|
||||
3d37b6c31155265d8d026ae9d6ced0b433078f87
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add up migration script here
|
||||
ALTER TYPE SCRIPT_LANG ADD VALUE IF NOT EXISTS 'ansible';
|
||||
UPDATE config set config = jsonb_set(config, '{worker_tags}', config->'worker_tags' || '["ansible"]'::jsonb) where name = 'worker__default' and config @> '{"worker_tags": ["deno", "python3", "go", "bash", "powershell", "dependency", "flow", "hub", "other", "bun", "php", "rust"]}'::jsonb AND NOT config->'worker_tags' @> '"ansible"'::jsonb;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE resource_type
|
||||
DROP COLUMN format_extension;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add up migration script here
|
||||
ALTER TABLE resource_type
|
||||
ADD COLUMN format_extension VARCHAR(20);
|
||||
7
backend/migrations/20240917172503_job_routing.down.sql
Normal file
7
backend/migrations/20240917172503_job_routing.down.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
-- Add down migration script here
|
||||
DROP TABLE http_trigger;
|
||||
DROP TYPE http_method;
|
||||
|
||||
ALTER TABLE script DROP COLUMN has_preprocessor;
|
||||
|
||||
DROP FUNCTION prevent_route_path_change();
|
||||
61
backend/migrations/20240917172503_job_routing.up.sql
Normal file
61
backend/migrations/20240917172503_job_routing.up.sql
Normal file
@@ -0,0 +1,61 @@
|
||||
-- Add up migration script here
|
||||
CREATE TYPE http_method AS ENUM ('get', 'post', 'put', 'delete', 'patch');
|
||||
|
||||
CREATE TABLE http_trigger (
|
||||
path VARCHAR(255) NOT NULL,
|
||||
route_path VARCHAR(255) NOT NULL,
|
||||
route_path_key VARCHAR(255) NOT NULL,
|
||||
script_path VARCHAR(255) NOT NULL,
|
||||
is_flow BOOLEAN NOT NULL,
|
||||
workspace_id VARCHAR(50) NOT NULL,
|
||||
edited_by VARCHAR(50) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
edited_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
extra_perms JSONB NOT NULL DEFAULT '{}',
|
||||
is_async BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
requires_auth BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
http_method http_method NOT NULL,
|
||||
PRIMARY KEY (path, workspace_id)
|
||||
);
|
||||
|
||||
|
||||
GRANT SELECT, UPDATE ON http_trigger TO windmill_user;
|
||||
GRANT ALL ON http_trigger TO windmill_admin;
|
||||
|
||||
ALTER TABLE http_trigger ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY see_folder_extra_perms_user_select ON http_trigger FOR SELECT TO windmill_user
|
||||
USING (SPLIT_PART(http_trigger.path, '/', 1) = 'f' AND SPLIT_PART(http_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_read'), ',')::text[]));
|
||||
CREATE POLICY see_folder_extra_perms_user_update ON http_trigger FOR UPDATE TO windmill_user
|
||||
USING (SPLIT_PART(http_trigger.path, '/', 1) = 'f' AND SPLIT_PART(http_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
|
||||
|
||||
|
||||
CREATE POLICY see_extra_perms_user_select ON http_trigger FOR SELECT TO windmill_user
|
||||
USING (extra_perms ? CONCAT('u/', current_setting('session.user')));
|
||||
CREATE POLICY see_extra_perms_user_update ON http_trigger FOR UPDATE TO windmill_user
|
||||
USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean);
|
||||
|
||||
CREATE POLICY see_extra_perms_groups_select ON http_trigger FOR SELECT TO windmill_user
|
||||
USING (extra_perms ?| regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]);
|
||||
CREATE POLICY see_extra_perms_groups_update ON http_trigger FOR UPDATE TO windmill_user
|
||||
USING (exists(
|
||||
SELECT key, value FROM jsonb_each_text(extra_perms)
|
||||
WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[])
|
||||
AND value::boolean));
|
||||
|
||||
CREATE OR REPLACE FUNCTION prevent_route_path_change()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF CURRENT_USER <> 'windmill_admin' AND NEW.route_path <> OLD.route_path THEN
|
||||
RAISE EXCEPTION 'Modification of route_path is only allowed by admins';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER check_route_path_change
|
||||
BEFORE UPDATE ON http_trigger
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION prevent_route_path_change();
|
||||
|
||||
ALTER TABLE script ADD COLUMN has_preprocessor BOOLEAN;
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,8 @@
|
||||
-- Add up migration script here
|
||||
INSERT INTO global_settings (name, value)
|
||||
SELECT 'smtp_settings', config
|
||||
FROM config
|
||||
WHERE name = 'server'
|
||||
ON CONFLICT (name) DO UPDATE SET value = excluded.value;
|
||||
|
||||
DELETE FROM config WHERE name = 'server';
|
||||
@@ -16,7 +16,13 @@ pub fn parse_bash_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let parsed = parse_bash_file(&code)?;
|
||||
if let Some(x) = parsed {
|
||||
let args = x;
|
||||
Ok(MainArgSignature { star_args: false, star_kwargs: false, args, no_main_func: None })
|
||||
Ok(MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args,
|
||||
no_main_func: None,
|
||||
has_preprocessor: None,
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing bash script".to_string()))
|
||||
}
|
||||
@@ -26,7 +32,13 @@ pub fn parse_powershell_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let parsed = parse_powershell_file(&code)?;
|
||||
if let Some(x) = parsed {
|
||||
let args = x;
|
||||
Ok(MainArgSignature { star_args: false, star_kwargs: false, args, no_main_func: None })
|
||||
Ok(MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args,
|
||||
no_main_func: None,
|
||||
has_preprocessor: None,
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing powershell script".to_string()))
|
||||
}
|
||||
@@ -173,7 +185,8 @@ non_required="${5:-}"
|
||||
oidx: None
|
||||
}
|
||||
],
|
||||
no_main_func: None
|
||||
no_main_func: None,
|
||||
has_preprocessor: None
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ pub fn parse_go_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
star_kwargs: false,
|
||||
args,
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: None,
|
||||
})
|
||||
} else {
|
||||
Ok(MainArgSignature {
|
||||
@@ -49,6 +50,7 @@ pub fn parse_go_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
star_kwargs: false,
|
||||
args: vec![],
|
||||
no_main_func: Some(true),
|
||||
has_preprocessor: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -241,7 +243,8 @@ func main(x int, y string, z bool, l []string, o struct { Name string `json:"nam
|
||||
oidx: None
|
||||
},
|
||||
],
|
||||
no_main_func: Some(false)
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: None
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -15,7 +15,13 @@ pub fn parse_graphql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let parsed = parse_graphql_file(&code)?;
|
||||
if let Some(x) = parsed {
|
||||
let args = x;
|
||||
Ok(MainArgSignature { star_args: false, star_kwargs: false, args, no_main_func: None })
|
||||
Ok(MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args,
|
||||
no_main_func: None,
|
||||
has_preprocessor: None,
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
}
|
||||
@@ -119,7 +125,8 @@ query($i: Int, $arr: [String]!, $wahoo: String = "wahoo") {
|
||||
oidx: None
|
||||
}
|
||||
],
|
||||
no_main_func: None
|
||||
no_main_func: None,
|
||||
has_preprocessor: None
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ pub fn parse_php_signature(
|
||||
star_kwargs: false,
|
||||
args,
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: None,
|
||||
})
|
||||
} else {
|
||||
Ok(MainArgSignature {
|
||||
@@ -89,6 +90,7 @@ pub fn parse_php_signature(
|
||||
star_kwargs: false,
|
||||
args: vec![],
|
||||
no_main_func: Some(true),
|
||||
has_preprocessor: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -159,7 +161,8 @@ function main(string $input1 = \"hey\", bool $input2 = false, int $input3 = 3, f
|
||||
oidx: None
|
||||
}
|
||||
],
|
||||
no_main_func: Some(false)
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: None
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -63,9 +63,17 @@ pub fn parse_python_signature(
|
||||
) -> anyhow::Result<MainArgSignature> {
|
||||
let main_name = override_main.unwrap_or("main".to_string());
|
||||
|
||||
let has_preprocessor = !filter_non_main(code, "preprocessor").is_empty();
|
||||
|
||||
let filtered_code = filter_non_main(code, &main_name);
|
||||
if filtered_code.is_empty() {
|
||||
return Err(anyhow::anyhow!("No main function found".to_string(),));
|
||||
return Ok(MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args: vec![],
|
||||
no_main_func: Some(true),
|
||||
has_preprocessor: Some(has_preprocessor),
|
||||
});
|
||||
}
|
||||
let ast = Suite::parse(&filtered_code, "main.py")
|
||||
.map_err(|e| anyhow::anyhow!("Error parsing code: {}", e.to_string()))?;
|
||||
@@ -119,6 +127,7 @@ pub fn parse_python_signature(
|
||||
})
|
||||
.collect(),
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: Some(has_preprocessor),
|
||||
})
|
||||
} else {
|
||||
Ok(MainArgSignature {
|
||||
@@ -126,6 +135,7 @@ pub fn parse_python_signature(
|
||||
star_kwargs: false,
|
||||
args: vec![],
|
||||
no_main_func: Some(true),
|
||||
has_preprocessor: Some(has_preprocessor),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -325,6 +335,7 @@ def main(test1: str, name: datetime.datetime = datetime.now(), byte: bytes = byt
|
||||
},
|
||||
],
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: Some(false)
|
||||
}
|
||||
);
|
||||
|
||||
@@ -389,6 +400,7 @@ def main(test1: str,
|
||||
}
|
||||
],
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: Some(false)
|
||||
}
|
||||
);
|
||||
|
||||
@@ -448,6 +460,7 @@ def main(test1: str,
|
||||
}
|
||||
],
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: Some(false)
|
||||
}
|
||||
);
|
||||
|
||||
@@ -491,6 +504,7 @@ def main(test1: Literal["foo", "bar"], test2: List[Literal["foo", "bar"]]): retu
|
||||
}
|
||||
],
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: Some(false)
|
||||
}
|
||||
);
|
||||
|
||||
@@ -521,6 +535,59 @@ def main(test1: DynSelect_foo): return
|
||||
oidx: None
|
||||
}],
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: Some(false)
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_python_sig_6() -> anyhow::Result<()> {
|
||||
let code = r#"
|
||||
|
||||
import os
|
||||
|
||||
def hello(): return
|
||||
|
||||
"#;
|
||||
//println!("{}", serde_json::to_string()?);
|
||||
assert_eq!(
|
||||
parse_python_signature(code, None)?,
|
||||
MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args: vec![],
|
||||
no_main_func: Some(true),
|
||||
has_preprocessor: Some(false)
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_python_sig_7() -> anyhow::Result<()> {
|
||||
let code = r#"
|
||||
|
||||
import os
|
||||
|
||||
def preprocessor(): return
|
||||
|
||||
def main(): return
|
||||
|
||||
|
||||
|
||||
"#;
|
||||
//println!("{}", serde_json::to_string()?);
|
||||
assert_eq!(
|
||||
parse_python_signature(code, None)?,
|
||||
MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args: vec![],
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: Some(true)
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ pub fn parse_rust_signature(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
star_kwargs: false,
|
||||
args,
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: None,
|
||||
})
|
||||
} else {
|
||||
Ok(MainArgSignature {
|
||||
@@ -36,6 +37,7 @@ pub fn parse_rust_signature(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
star_kwargs: false,
|
||||
args: vec![],
|
||||
no_main_func: Some(true),
|
||||
has_preprocessor: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -455,10 +457,7 @@ fn main(
|
||||
assert_eq!(ret.args[2].typ, Typ::Str(None));
|
||||
|
||||
assert_eq!(ret.args[3].name, "my_string_vec");
|
||||
assert_eq!(
|
||||
ret.args[3].otyp,
|
||||
Some("Vec < String >".to_string())
|
||||
);
|
||||
assert_eq!(ret.args[3].otyp, Some("Vec < String >".to_string()));
|
||||
assert_eq!(ret.args[3].typ, Typ::List(Box::new(Typ::Str(None))));
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,13 @@ pub fn parse_mysql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let parsed = parse_mysql_file(&code)?;
|
||||
if let Some(x) = parsed {
|
||||
let args = x;
|
||||
Ok(MainArgSignature { star_args: false, star_kwargs: false, args, no_main_func: None })
|
||||
Ok(MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args,
|
||||
no_main_func: None,
|
||||
has_preprocessor: None,
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
}
|
||||
@@ -30,7 +36,13 @@ pub fn parse_pgsql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let parsed = parse_pg_file(&code)?;
|
||||
if let Some(x) = parsed {
|
||||
let args = x;
|
||||
Ok(MainArgSignature { star_args: false, star_kwargs: false, args, no_main_func: None })
|
||||
Ok(MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args,
|
||||
no_main_func: None,
|
||||
has_preprocessor: None,
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
}
|
||||
@@ -40,7 +52,13 @@ pub fn parse_bigquery_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let parsed = parse_bigquery_file(&code)?;
|
||||
if let Some(x) = parsed {
|
||||
let args = x;
|
||||
Ok(MainArgSignature { star_args: false, star_kwargs: false, args, no_main_func: None })
|
||||
Ok(MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args,
|
||||
no_main_func: None,
|
||||
has_preprocessor: None,
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
}
|
||||
@@ -50,7 +68,13 @@ pub fn parse_snowflake_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let parsed = parse_snowflake_file(&code)?;
|
||||
if let Some(x) = parsed {
|
||||
let args = x;
|
||||
Ok(MainArgSignature { star_args: false, star_kwargs: false, args, no_main_func: None })
|
||||
Ok(MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args,
|
||||
no_main_func: None,
|
||||
has_preprocessor: None,
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
}
|
||||
@@ -60,7 +84,13 @@ pub fn parse_mssql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let parsed = parse_mssql_file(&code)?;
|
||||
if let Some(x) = parsed {
|
||||
let args = x;
|
||||
Ok(MainArgSignature { star_args: false, star_kwargs: false, args, no_main_func: None })
|
||||
Ok(MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args,
|
||||
no_main_func: None,
|
||||
has_preprocessor: None,
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
}
|
||||
@@ -570,7 +600,8 @@ SELECT * FROM table WHERE token=$1::TEXT AND image=$2::BIGINT
|
||||
oidx: Some(2),
|
||||
},
|
||||
],
|
||||
no_main_func: None
|
||||
no_main_func: None,
|
||||
has_preprocessor: None
|
||||
}
|
||||
);
|
||||
|
||||
@@ -618,7 +649,8 @@ SELECT $2::TEXT;
|
||||
oidx: Some(3),
|
||||
},
|
||||
],
|
||||
no_main_func: None
|
||||
no_main_func: None,
|
||||
has_preprocessor: None
|
||||
}
|
||||
);
|
||||
|
||||
@@ -744,7 +776,8 @@ SELECT ?, ?;
|
||||
oidx: None,
|
||||
},
|
||||
],
|
||||
no_main_func: None
|
||||
no_main_func: None,
|
||||
has_preprocessor: None
|
||||
}
|
||||
);
|
||||
|
||||
@@ -791,7 +824,8 @@ SELECT :param2;
|
||||
oidx: None,
|
||||
},
|
||||
],
|
||||
no_main_func: None
|
||||
no_main_func: None,
|
||||
has_preprocessor: None
|
||||
}
|
||||
);
|
||||
|
||||
@@ -830,7 +864,8 @@ SELECT @token;
|
||||
oidx: None,
|
||||
},
|
||||
],
|
||||
no_main_func: None
|
||||
no_main_func: None,
|
||||
has_preprocessor: None
|
||||
}
|
||||
);
|
||||
|
||||
@@ -877,7 +912,8 @@ SELECT ?;
|
||||
oidx: None,
|
||||
}
|
||||
],
|
||||
no_main_func: None
|
||||
no_main_func: None,
|
||||
has_preprocessor: None
|
||||
}
|
||||
);
|
||||
|
||||
@@ -924,7 +960,8 @@ SELECT @p2;
|
||||
oidx: None,
|
||||
},
|
||||
],
|
||||
no_main_func: None
|
||||
no_main_func: None,
|
||||
has_preprocessor: None
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -159,6 +159,14 @@ pub fn parse_deno_signature(
|
||||
})?
|
||||
.body;
|
||||
|
||||
let has_preprocessor = ast.iter().any(|x| match x {
|
||||
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
|
||||
decl: Decl::Fn(FnDecl { ident: Ident { sym, .. }, .. }),
|
||||
..
|
||||
})) => &sym.to_string() == "preprocessor",
|
||||
_ => false,
|
||||
});
|
||||
|
||||
let main_name = main_override.unwrap_or("main".to_string());
|
||||
let params = ast.into_iter().find_map(|x| match x {
|
||||
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
|
||||
@@ -178,6 +186,7 @@ pub fn parse_deno_signature(
|
||||
.map(|x| parse_param(x, &cm, skip_dflt, &mut c))
|
||||
.collect::<anyhow::Result<Vec<Arg>>>()?,
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: Some(has_preprocessor),
|
||||
};
|
||||
Ok(r)
|
||||
} else {
|
||||
@@ -186,6 +195,7 @@ pub fn parse_deno_signature(
|
||||
star_kwargs: false,
|
||||
args: vec![],
|
||||
no_main_func: Some(true),
|
||||
has_preprocessor: Some(has_preprocessor),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ ts-parser = [ "dep:windmill-parser-ts"]
|
||||
php-parser = [ "dep:windmill-parser-php"]
|
||||
rust-parser = [ "dep:windmill-parser-rust"]
|
||||
graphql-parser = [ "dep:windmill-parser-graphql"]
|
||||
ansible-parser = [ "dep:windmill-parser-yaml"]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
@@ -37,6 +38,7 @@ windmill-parser-ts = { workspace = true, optional = true }
|
||||
windmill-parser-php = { workspace = true, optional = true }
|
||||
windmill-parser-graphql = { workspace = true, optional = true }
|
||||
windmill-parser-rust = { workspace = true, optional = true }
|
||||
windmill-parser-yaml = { workspace = true, optional = true }
|
||||
wasm-bindgen.workspace = true
|
||||
serde_json.workspace = true
|
||||
getrandom = { workspace = true, features = ["js"] }
|
||||
|
||||
@@ -10,35 +10,41 @@ wasm-pack build --release --target web --out-dir $OUT_DIR --all-features \
|
||||
OUT_DIR="pkg-ts"
|
||||
wasm-pack build --release --target web --out-dir $OUT_DIR --features "ts-parser" \
|
||||
-Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort
|
||||
sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-ts"/' $OUT_DIR/package.json
|
||||
sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-ts"/' $OUT_DIR/package.json
|
||||
|
||||
# sql languages, graphql and bash/powershell, since they all use regex
|
||||
OUT_DIR="pkg-regex"
|
||||
wasm-pack build --release --target web --out-dir $OUT_DIR \
|
||||
--features "sql-parser,graphql-parser,bash-parser" \
|
||||
-Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort
|
||||
sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-regex"/' $OUT_DIR/package.json
|
||||
sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-regex"/' $OUT_DIR/package.json
|
||||
|
||||
# python
|
||||
OUT_DIR="pkg-py"
|
||||
wasm-pack build --release --target web --out-dir $OUT_DIR --features "py-parser" \
|
||||
-Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort
|
||||
sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-py"/' $OUT_DIR/package.json
|
||||
sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-py"/' $OUT_DIR/package.json
|
||||
|
||||
# go
|
||||
OUT_DIR="pkg-go"
|
||||
wasm-pack build --release --target web --out-dir $OUT_DIR --features "go-parser" \
|
||||
-Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort
|
||||
sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-go"/' $OUT_DIR/package.json
|
||||
sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-go"/' $OUT_DIR/package.json
|
||||
|
||||
# php
|
||||
OUT_DIR="pkg-php"
|
||||
wasm-pack build --release --target web --out-dir $OUT_DIR --features "php-parser" \
|
||||
-Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort
|
||||
sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-php"/' $OUT_DIR/package.json
|
||||
sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-php"/' $OUT_DIR/package.json
|
||||
|
||||
# rust
|
||||
OUT_DIR="pkg-rust"
|
||||
wasm-pack build --release --target web --out-dir $OUT_DIR --features "rust-parser" \
|
||||
-Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort
|
||||
sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-rust"/' $OUT_DIR/package.json
|
||||
sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-rust"/' $OUT_DIR/package.json
|
||||
|
||||
# ansible
|
||||
OUT_DIR="pkg-yaml"
|
||||
wasm-pack build --release --target web --out-dir $OUT_DIR --features "ansible-parser" \
|
||||
-Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort
|
||||
sed -i '' 's/"windmill-parser-wasm"/"windmill-parser-wasm-yaml"/' $OUT_DIR/package.json
|
||||
@@ -42,3 +42,9 @@ OUT_DIR="pkg-rust"
|
||||
wasm-pack build --release --target web --out-dir $OUT_DIR --features "rust-parser" \
|
||||
-Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort
|
||||
sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-rust"/' $OUT_DIR/package.json
|
||||
|
||||
# ansible
|
||||
OUT_DIR="pkg-yaml"
|
||||
wasm-pack build --release --target web --out-dir $OUT_DIR --features "ansible-parser" \
|
||||
-Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort
|
||||
sed -i 's/"windmill-parser-wasm"/"windmill-parser-wasm-yaml"/' $OUT_DIR/package.json
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
set -eou pipefail
|
||||
|
||||
deno task wasmbuild --out ../../../cli/wasm/ -p windmill-parser-wasm
|
||||
deno task wasmbuild --out ../../../cli/wasm/ -p windmill-parser-wasm --all-features
|
||||
|
||||
@@ -21,3 +21,6 @@ popd
|
||||
|
||||
pushd "pkg-rust" && npm publish ${args}
|
||||
popd
|
||||
|
||||
pushd "pkg-yaml" && npm publish ${args}
|
||||
popd
|
||||
|
||||
@@ -17,8 +17,12 @@ fn wrap_sig(r: anyhow::Result<MainArgSignature>) -> String {
|
||||
|
||||
#[cfg(feature = "ts-parser")]
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_deno(code: &str) -> String {
|
||||
wrap_sig(windmill_parser_ts::parse_deno_signature(code, false, None))
|
||||
pub fn parse_deno(code: &str, main_override: Option<String>) -> String {
|
||||
wrap_sig(windmill_parser_ts::parse_deno_signature(
|
||||
code,
|
||||
false,
|
||||
main_override,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "ts-parser")]
|
||||
@@ -65,8 +69,11 @@ pub fn parse_go(code: &str) -> String {
|
||||
|
||||
#[cfg(feature = "py-parser")]
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_python(code: &str) -> String {
|
||||
wrap_sig(windmill_parser_py::parse_python_signature(code, None))
|
||||
pub fn parse_python(code: &str, main_override: Option<String>) -> String {
|
||||
wrap_sig(windmill_parser_py::parse_python_signature(
|
||||
code,
|
||||
main_override,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "sql-parser")]
|
||||
@@ -122,3 +129,9 @@ pub fn parse_php(code: &str) -> String {
|
||||
pub fn parse_rust(code: &str) -> String {
|
||||
wrap_sig(windmill_parser_rust::parse_rust_signature(code))
|
||||
}
|
||||
|
||||
#[cfg(feature = "ansible-parser")]
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_ansible(code: &str) -> String {
|
||||
wrap_sig(windmill_parser_yaml::parse_ansible_sig(code))
|
||||
}
|
||||
|
||||
@@ -139,7 +139,8 @@ export function main(test1?: string, test2: string = \"burkina\",
|
||||
oidx: None
|
||||
}
|
||||
],
|
||||
no_main_func: Some(false)
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: Some(false)
|
||||
}
|
||||
);
|
||||
|
||||
@@ -215,7 +216,8 @@ export function main(test2 = \"burkina\",
|
||||
oidx: None
|
||||
}
|
||||
],
|
||||
no_main_func: Some(false)
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: Some(false)
|
||||
}
|
||||
);
|
||||
|
||||
@@ -264,7 +266,8 @@ export function main(foo: FooBar, {a, b}: FooBar, {c, d}: FooBar = {a: \"foo\",
|
||||
oidx: None
|
||||
}
|
||||
],
|
||||
no_main_func: Some(false)
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: Some(false)
|
||||
}
|
||||
);
|
||||
|
||||
@@ -294,7 +297,8 @@ export function main(foo: (\"foo\" | \"bar\")[]) {
|
||||
has_default: false,
|
||||
oidx: None
|
||||
}],
|
||||
no_main_func: Some(false)
|
||||
no_main_func: Some(false),
|
||||
has_preprocessor: Some(false)
|
||||
}
|
||||
);
|
||||
|
||||
@@ -434,6 +438,7 @@ Write-Output 'Testing...'
|
||||
}
|
||||
],
|
||||
no_main_func: None,
|
||||
has_preprocessor: None
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
15
backend/parsers/windmill-parser-yaml/Cargo.toml
Normal file
15
backend/parsers/windmill-parser-yaml/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "windmill-parser-yaml"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "windmill_parser_yaml"
|
||||
path = "./src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
yaml-rust.workspace = true
|
||||
windmill-parser.workspace = true
|
||||
anyhow.workspace = true
|
||||
serde_json.workspace = true
|
||||
482
backend/parsers/windmill-parser-yaml/src/lib.rs
Normal file
482
backend/parsers/windmill-parser-yaml/src/lib.rs
Normal file
@@ -0,0 +1,482 @@
|
||||
use anyhow::anyhow;
|
||||
use serde_json::json;
|
||||
use windmill_parser::{Arg, MainArgSignature, ObjectProperty, Typ};
|
||||
use yaml_rust::{Yaml, YamlEmitter, YamlLoader};
|
||||
|
||||
pub fn parse_ansible_sig(inner_content: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let docs = YamlLoader::load_from_str(inner_content)
|
||||
.map_err(|e| anyhow!("Failed to parse yaml: {}", e))?;
|
||||
|
||||
if docs.len() < 2 {
|
||||
return Ok(MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args: vec![],
|
||||
no_main_func: None,
|
||||
has_preprocessor: None,
|
||||
});
|
||||
}
|
||||
|
||||
let mut args = vec![];
|
||||
if let Yaml::Hash(doc) = &docs[0] {
|
||||
for (key, value) in doc {
|
||||
match key {
|
||||
Yaml::String(key) if key == "extra_vars" => {
|
||||
if let Yaml::Hash(v) = &value {
|
||||
for (key, arg) in v {
|
||||
if let Yaml::String(arg_name) = key {
|
||||
// if not then it is a static resource or var, so skip
|
||||
// it will be fetched by the worker
|
||||
if let ArgTyp::Typ(typ) = parse_ansible_arg_typ(arg) {
|
||||
let default = get_default_for_typ(&arg);
|
||||
args.push(Arg {
|
||||
name: arg_name.to_string(),
|
||||
otyp: None,
|
||||
typ,
|
||||
has_default: default.is_some(),
|
||||
default,
|
||||
oidx: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Yaml::String(key) if key == "inventory" => {
|
||||
for inv in parse_inventories(value)? {
|
||||
if inv.pinned_resource.is_some() {
|
||||
continue;
|
||||
}
|
||||
args.push(Arg {
|
||||
name: inv.name,
|
||||
otyp: None,
|
||||
typ: Typ::Resource(inv.resource_type.unwrap_or("Unknown".to_string())),
|
||||
has_default: inv.default.is_some(),
|
||||
default: inv.default.map(|v| json!(format!("$res:{}", v))),
|
||||
oidx: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args,
|
||||
no_main_func: None,
|
||||
has_preprocessor: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_default_for_typ(arg: &Yaml) -> Option<serde_json::Value> {
|
||||
if let Yaml::Hash(arg) = arg {
|
||||
if let Some(def) = arg.get(&Yaml::String("default".to_string())) {
|
||||
if let Some(Yaml::String(typ)) = arg.get(&Yaml::String("type".to_string())) {
|
||||
if let Yaml::String(path) = def {
|
||||
if typ.as_str() == "windmill_resource" {
|
||||
return Some(serde_json::Value::String(format!("$res:{}", path)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Some(yaml_to_json(def));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
enum ArgTyp {
|
||||
Typ(Typ),
|
||||
StaticVar(String),
|
||||
StaticResource(String),
|
||||
}
|
||||
|
||||
fn parse_ansible_arg_typ(arg: &Yaml) -> ArgTyp {
|
||||
if let Yaml::Hash(a) = arg {
|
||||
if let Some(Yaml::String(typ)) = a.get(&Yaml::String("type".to_string())) {
|
||||
if typ.as_str() == "windmill_variable" {
|
||||
if let Some(Yaml::String(variable_path)) =
|
||||
a.get(&Yaml::String("variable".to_string()))
|
||||
{
|
||||
return ArgTyp::StaticVar(variable_path.to_string());
|
||||
}
|
||||
}
|
||||
if typ.as_str() == "windmill_resource" {
|
||||
if let Some(Yaml::String(resource_path)) =
|
||||
a.get(&Yaml::String("resource".to_string()))
|
||||
{
|
||||
return ArgTyp::StaticResource(resource_path.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArgTyp::Typ(parse_ansible_typ(arg))
|
||||
}
|
||||
|
||||
fn parse_ansible_typ(arg: &Yaml) -> Typ {
|
||||
if let Yaml::Hash(arg) = arg {
|
||||
if let Some(Yaml::String(typ)) = arg.get(&Yaml::String("type".to_string())) {
|
||||
match typ.as_ref() {
|
||||
"boolean" => Typ::Bool,
|
||||
"integer" => Typ::Int,
|
||||
"number" => Typ::Float,
|
||||
"string" => {
|
||||
if let Some(Yaml::String(fmt)) = arg.get(&Yaml::String("format".to_string())) {
|
||||
match fmt.as_ref() {
|
||||
"date-time" | "datetime" => Typ::Datetime,
|
||||
"email" => Typ::Email,
|
||||
_ => Typ::Str(None),
|
||||
}
|
||||
} else {
|
||||
Typ::Str(None)
|
||||
}
|
||||
}
|
||||
"object" => {
|
||||
if let Some(Yaml::Hash(props)) =
|
||||
arg.get(&Yaml::String("properties".to_string()))
|
||||
{
|
||||
let mut prop_vec = vec![];
|
||||
for (key, value) in props {
|
||||
if let Yaml::String(key) = key {
|
||||
prop_vec.push(ObjectProperty {
|
||||
key: key.clone(),
|
||||
typ: Box::new(parse_ansible_typ(value)),
|
||||
})
|
||||
}
|
||||
}
|
||||
Typ::Object(prop_vec)
|
||||
} else {
|
||||
Typ::Object(vec![])
|
||||
}
|
||||
}
|
||||
"array" => {
|
||||
if let Some(items) = arg.get(&Yaml::String("items".to_string())) {
|
||||
Typ::List(Box::new(parse_ansible_typ(items)))
|
||||
} else {
|
||||
Typ::List(Box::new(Typ::Unknown))
|
||||
}
|
||||
}
|
||||
"windmill_resource" => {
|
||||
if let Some(Yaml::String(res_name)) =
|
||||
arg.get(&Yaml::String("resource_type".to_string()))
|
||||
{
|
||||
Typ::Resource(res_name.clone())
|
||||
} else {
|
||||
Typ::Resource("".to_string())
|
||||
}
|
||||
}
|
||||
_ => Typ::Unknown,
|
||||
}
|
||||
} else {
|
||||
Typ::Unknown
|
||||
}
|
||||
} else {
|
||||
Typ::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AnsiblePlaybookOptions {
|
||||
// There are a lot more options
|
||||
// TODO: Add the options as customers require them
|
||||
// Add it here and then on the executors cmd_options
|
||||
pub verbosity: Option<String>,
|
||||
pub forks: Option<i64>,
|
||||
pub timeout: Option<i64>,
|
||||
pub flush_cache: Option<()>,
|
||||
pub force_handlers: Option<()>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FileResource {
|
||||
pub resource_path: String,
|
||||
pub target_path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AnsibleInventory {
|
||||
pub default: Option<String>,
|
||||
pub name: String,
|
||||
resource_type: Option<String>,
|
||||
pub pinned_resource: Option<String>,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AnsibleRequirements {
|
||||
pub python_reqs: Vec<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,
|
||||
}
|
||||
|
||||
fn parse_inventories(inventory_yaml: &Yaml) -> anyhow::Result<Vec<AnsibleInventory>> {
|
||||
if let Yaml::Array(arr) = inventory_yaml {
|
||||
let mut ret = vec![];
|
||||
for (i, inv) in arr.iter().enumerate() {
|
||||
if let Yaml::Hash(inv) = inv {
|
||||
let resource_type = inv
|
||||
.get(&Yaml::String("resource_type".to_string()))
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
let windmill_path = inv
|
||||
.get(&Yaml::String("default".to_string()))
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
let name = if i == 0 {
|
||||
"inventory.ini".to_string()
|
||||
} else {
|
||||
format!("inventory-{}.ini", i)
|
||||
};
|
||||
|
||||
let name = inv
|
||||
.get(&Yaml::String("name".to_string()))
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or(name.as_str())
|
||||
.to_string();
|
||||
|
||||
let pin = inv
|
||||
.get(&Yaml::String("resource".to_string()))
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
ret.push(AnsibleInventory {
|
||||
default: windmill_path,
|
||||
name,
|
||||
resource_type,
|
||||
pinned_resource: pin,
|
||||
});
|
||||
}
|
||||
}
|
||||
return Ok(ret);
|
||||
}
|
||||
return Err(anyhow!("Invalid inventory definition"));
|
||||
}
|
||||
|
||||
pub fn parse_ansible_reqs(
|
||||
inner_content: &str,
|
||||
) -> anyhow::Result<(String, Option<AnsibleRequirements>, String)> {
|
||||
let mut logs = String::new();
|
||||
let docs = YamlLoader::load_from_str(inner_content)
|
||||
.map_err(|e| anyhow!("Failed to parse yaml: {}", e))?;
|
||||
|
||||
if docs.len() < 2 {
|
||||
return Ok((logs, None, inner_content.to_string()));
|
||||
}
|
||||
|
||||
let opts = AnsiblePlaybookOptions {
|
||||
verbosity: None,
|
||||
forks: None,
|
||||
timeout: None,
|
||||
flush_cache: None,
|
||||
force_handlers: None,
|
||||
};
|
||||
let mut ret = AnsibleRequirements {
|
||||
python_reqs: vec![],
|
||||
collections: None,
|
||||
file_resources: vec![],
|
||||
inventories: vec![],
|
||||
vars: vec![],
|
||||
resources: vec![],
|
||||
options: opts,
|
||||
};
|
||||
|
||||
if let Yaml::Hash(doc) = &docs[0] {
|
||||
for (key, value) in doc {
|
||||
match key {
|
||||
Yaml::String(key) if key == "dependencies" => {
|
||||
if let Yaml::Hash(deps) = value {
|
||||
if let Some(galaxy_requirements) =
|
||||
deps.get(&Yaml::String("galaxy".to_string()))
|
||||
{
|
||||
let mut out_str = String::new();
|
||||
let mut emitter = YamlEmitter::new(&mut out_str);
|
||||
emitter.dump(galaxy_requirements)?;
|
||||
ret.collections = Some(out_str);
|
||||
}
|
||||
if let Some(Yaml::Array(py_reqs)) =
|
||||
deps.get(&Yaml::String("python".to_string()))
|
||||
{
|
||||
ret.python_reqs = py_reqs
|
||||
.iter()
|
||||
.map(|d| d.as_str().map(|s| s.to_string()))
|
||||
.filter_map(|x| x)
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
Yaml::String(key) if key == "file_resources" => {
|
||||
if let Yaml::Array(file_resources) = value {
|
||||
let resources: anyhow::Result<Vec<FileResource>> =
|
||||
file_resources.iter().map(parse_file_resource).collect();
|
||||
ret.file_resources.append(&mut resources?);
|
||||
}
|
||||
}
|
||||
Yaml::String(key) if key == "extra_vars" => {
|
||||
if let Yaml::Hash(v) = &value {
|
||||
for (key, arg) in v {
|
||||
if let Yaml::String(arg_name) = key {
|
||||
match parse_ansible_arg_typ(arg) {
|
||||
ArgTyp::StaticVar(p) => {
|
||||
ret.vars
|
||||
.push((arg_name.to_string(), format!("$var:{}", p)));
|
||||
}
|
||||
ArgTyp::StaticResource(p) => {
|
||||
ret.resources
|
||||
.push((arg_name.to_string(), format!("$res:{}", p)));
|
||||
}
|
||||
ArgTyp::Typ(_) => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Yaml::String(key) if key == "inventory" => {
|
||||
ret.inventories = parse_inventories(value)?;
|
||||
}
|
||||
Yaml::String(key) if key == "options" => {
|
||||
if let Yaml::Array(opts) = &value {
|
||||
ret.options = parse_ansible_options(opts);
|
||||
}
|
||||
}
|
||||
Yaml::String(key) => logs.push_str(&format!("\nUnknown field `{}`. Ignoring", key)),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut out_str = String::new();
|
||||
let mut emitter = YamlEmitter::new(&mut out_str);
|
||||
|
||||
for i in 1..docs.len() {
|
||||
emitter.dump(&docs[i])?;
|
||||
}
|
||||
Ok((logs, Some(ret), out_str))
|
||||
}
|
||||
|
||||
fn parse_ansible_options(opts: &Vec<Yaml>) -> AnsiblePlaybookOptions {
|
||||
let mut ret = AnsiblePlaybookOptions {
|
||||
verbosity: None,
|
||||
forks: None,
|
||||
timeout: None,
|
||||
flush_cache: None,
|
||||
force_handlers: None,
|
||||
};
|
||||
for opt in opts {
|
||||
if let Yaml::String(o) = opt {
|
||||
match o.as_str() {
|
||||
"flush_cache" => {
|
||||
ret.flush_cache = Some(());
|
||||
}
|
||||
"force_handlers" => {
|
||||
ret.force_handlers = Some(());
|
||||
}
|
||||
s if count_consecutive_vs(s) > 0 => {
|
||||
ret.verbosity = Some("v".repeat(count_consecutive_vs(s).min(6)));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
if let Yaml::Hash(m) = opt {
|
||||
if let Some((Yaml::String(name), value)) = m.iter().last() {
|
||||
match name.as_str() {
|
||||
"forks" => {
|
||||
if let Yaml::Integer(count) = value {
|
||||
ret.forks = Some(*count);
|
||||
}
|
||||
}
|
||||
"timeout" => {
|
||||
if let Yaml::Integer(timeout) = value {
|
||||
ret.timeout = Some(*timeout);
|
||||
}
|
||||
}
|
||||
"verbosity" => {
|
||||
if let Yaml::String(verbosity) = value {
|
||||
let c = count_consecutive_vs(verbosity);
|
||||
if c > 0 && c <= 6 {
|
||||
ret.verbosity = Some("v".repeat(c.min(6)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn count_consecutive_vs(s: &str) -> usize {
|
||||
let mut max_count = 0;
|
||||
let mut current_count = 0;
|
||||
|
||||
for c in s.chars() {
|
||||
if c == 'v' {
|
||||
current_count += 1;
|
||||
if current_count == 6 {
|
||||
return 6; // Stop early if we reach 6
|
||||
}
|
||||
} else {
|
||||
current_count = 0; // Reset count if the character is not 'v'
|
||||
}
|
||||
max_count = max_count.max(current_count);
|
||||
}
|
||||
|
||||
max_count
|
||||
}
|
||||
|
||||
fn parse_file_resource(yaml: &Yaml) -> anyhow::Result<FileResource> {
|
||||
if let Yaml::Hash(f) = yaml {
|
||||
if let Some(Yaml::String(resource_path)) = f.get(&Yaml::String("resource".to_string())) {
|
||||
let target_path = f
|
||||
.get(&Yaml::String("target".to_string()))
|
||||
.and_then(|x| x.as_str())
|
||||
.map(|x| x.to_string())
|
||||
.ok_or(anyhow!(
|
||||
"No `target` provided for file resource {}. Please input a target relative path for the ansible playbook to see this file.",
|
||||
resource_path
|
||||
))?;
|
||||
return Ok(FileResource { resource_path: resource_path.clone(), target_path });
|
||||
}
|
||||
return Err(anyhow!(
|
||||
"File resource should have a `resource` field, linking to a text file resource"
|
||||
));
|
||||
}
|
||||
return Err(anyhow!("Invalid file resource: Should be a dictionary."));
|
||||
}
|
||||
|
||||
fn yaml_to_json(yaml: &Yaml) -> serde_json::Value {
|
||||
match yaml {
|
||||
Yaml::Array(arr) => {
|
||||
let json_array: Vec<serde_json::Value> = arr.into_iter().map(yaml_to_json).collect();
|
||||
serde_json::Value::Array(json_array)
|
||||
}
|
||||
Yaml::Hash(hash) => {
|
||||
let json_object = hash
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
let key = match k {
|
||||
Yaml::String(s) => s.clone(),
|
||||
_ => k.as_str().unwrap_or("").to_string(),
|
||||
};
|
||||
(key, yaml_to_json(v))
|
||||
})
|
||||
.collect();
|
||||
serde_json::Value::Object(json_object)
|
||||
}
|
||||
Yaml::String(s) => serde_json::Value::String(s.to_string()),
|
||||
Yaml::Integer(i) => serde_json::Value::Number(i.clone().into()),
|
||||
Yaml::Real(r) => serde_json::Value::Number(r.parse().unwrap_or(0.into())),
|
||||
Yaml::Boolean(b) => serde_json::Value::Bool(*b),
|
||||
Yaml::Null => serde_json::Value::Null,
|
||||
_ => serde_json::Value::Null,
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ pub struct MainArgSignature {
|
||||
pub star_kwargs: bool,
|
||||
pub args: Vec<Arg>,
|
||||
pub no_main_func: Option<bool>,
|
||||
pub has_preprocessor: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone, Debug, PartialEq)]
|
||||
@@ -153,7 +154,10 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_mixed_delimiters() {
|
||||
assert_eq!(to_snake_case("test-Case_with Spaces"), "test_case_with_spaces");
|
||||
assert_eq!(
|
||||
to_snake_case("test-Case_with Spaces"),
|
||||
"test_case_with_spaces"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
|
||||
use anyhow::Context;
|
||||
use git_version::git_version;
|
||||
use monitor::{send_current_log_file_to_object_store, send_logs_to_object_store};
|
||||
use monitor::{
|
||||
reload_timeout_wait_result_setting, send_current_log_file_to_object_store,
|
||||
send_logs_to_object_store,
|
||||
};
|
||||
use rand::Rng;
|
||||
use sqlx::{postgres::PgListener, Pool, Postgres};
|
||||
use std::{
|
||||
@@ -35,7 +38,7 @@ use windmill_common::{
|
||||
JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING,
|
||||
OAUTH_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,
|
||||
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, SMTP_SETTING, TIMEOUT_WAIT_RESULT_SETTING,
|
||||
},
|
||||
scripts::ScriptLang,
|
||||
stats_ee::schedule_stats,
|
||||
@@ -74,7 +77,7 @@ use crate::monitor::{
|
||||
reload_critical_error_channels_setting, reload_extra_pip_index_url_setting,
|
||||
reload_hub_base_url_setting, reload_job_default_timeout_setting, reload_jwt_secret_setting,
|
||||
reload_license_key, reload_npm_config_registry_setting, reload_pip_index_url_setting,
|
||||
reload_retention_period_setting, reload_scim_token_setting, reload_server_config,
|
||||
reload_retention_period_setting, reload_scim_token_setting, reload_smtp_config,
|
||||
reload_worker_config,
|
||||
};
|
||||
|
||||
@@ -173,7 +176,7 @@ async fn cache_hub_scripts(file_path: Option<String>) -> anyhow::Result<()> {
|
||||
None,
|
||||
&job_dir,
|
||||
"cache_init",
|
||||
windmill_worker::get_common_bun_proc_envs("").await,
|
||||
windmill_worker::get_common_bun_proc_envs(None).await,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
@@ -563,9 +566,7 @@ Windmill Community Edition {GIT_VERSION}
|
||||
"notify_config_change" => {
|
||||
match n.payload() {
|
||||
"server" if server_mode => {
|
||||
tracing::info!("Server config change detected: {}", n.payload());
|
||||
|
||||
reload_server_config(&db).await;
|
||||
tracing::error!("Server config change detected but server config is obsolete: {}", n.payload());
|
||||
},
|
||||
a@ _ if worker_mode && a == format!("worker__{}", *WORKER_GROUP) => {
|
||||
tracing::info!("Worker config change detected: {}", n.payload());
|
||||
@@ -609,6 +610,12 @@ Windmill Community Edition {GIT_VERSION}
|
||||
tracing::error!("Error loading default tag per workspace workspaces: {e:#}");
|
||||
}
|
||||
}
|
||||
SMTP_SETTING => {
|
||||
reload_smtp_config(&db).await;
|
||||
},
|
||||
TIMEOUT_WAIT_RESULT_SETTING => {
|
||||
reload_timeout_wait_result_setting(&db).await
|
||||
},
|
||||
RETENTION_PERIOD_SECS_SETTING => {
|
||||
reload_retention_period_setting(&db).await
|
||||
},
|
||||
|
||||
@@ -22,6 +22,7 @@ use tokio::{
|
||||
#[cfg(feature = "embedding")]
|
||||
use windmill_api::embeddings::update_embeddings_db;
|
||||
use windmill_api::{
|
||||
jobs::TIMEOUT_WAIT_RESULT,
|
||||
oauth2_ee::{build_oauth_clients, OAuthClient},
|
||||
DEFAULT_BODY_LIMIT, IS_SECURE, OAUTH_CLIENTS, REQUEST_SIZE_LIMIT, SAML_METADATA, SCIM_TOKEN,
|
||||
};
|
||||
@@ -40,17 +41,17 @@ use windmill_common::{
|
||||
KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING, OAUTH_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,
|
||||
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, TIMEOUT_WAIT_RESULT_SETTING,
|
||||
},
|
||||
jobs::QueuedJob,
|
||||
oauth2::REQUIRE_PREEXISTING_USER_FOR_OAUTH,
|
||||
server::load_server_config,
|
||||
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, make_pull_query, make_suspended_pull_query, reload_custom_tags_setting,
|
||||
DEFAULT_TAGS_PER_WORKSPACE, DEFAULT_TAGS_WORKSPACES, SERVER_CONFIG, WORKER_CONFIG,
|
||||
DEFAULT_TAGS_PER_WORKSPACE, DEFAULT_TAGS_WORKSPACES, SMTP_CONFIG, WORKER_CONFIG,
|
||||
WORKER_GROUP,
|
||||
},
|
||||
BASE_URL, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, JOB_RETENTION_SECS,
|
||||
@@ -172,8 +173,9 @@ pub async fn initial_load(
|
||||
reload_s3_cache_setting(&db).await;
|
||||
}
|
||||
|
||||
reload_smtp_config(&db).await;
|
||||
|
||||
if server_mode {
|
||||
reload_server_config(&db).await;
|
||||
reload_retention_period_setting(&db).await;
|
||||
reload_request_size(&db).await;
|
||||
reload_saml_metadata_setting(&db).await;
|
||||
@@ -698,6 +700,15 @@ pub async fn reload_scim_token_setting(db: &DB) {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn reload_timeout_wait_result_setting(db: &DB) {
|
||||
reload_option_setting_with_tracing(
|
||||
db,
|
||||
TIMEOUT_WAIT_RESULT_SETTING,
|
||||
"TIMEOUT_WAIT_RESULT",
|
||||
TIMEOUT_WAIT_RESULT.clone(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
pub async fn reload_saml_metadata_setting(db: &DB) {
|
||||
reload_option_setting_with_tracing(
|
||||
db,
|
||||
@@ -1129,14 +1140,14 @@ pub async fn expose_queue_metrics(db: &Pool<Postgres>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn reload_server_config(db: &Pool<Postgres>) {
|
||||
let config = load_server_config(&db).await;
|
||||
if let Err(e) = config {
|
||||
tracing::error!("Error reloading server config: {:?}", e)
|
||||
pub async fn reload_smtp_config(db: &Pool<Postgres>) {
|
||||
let smtp_config = load_smtp_config(&db).await;
|
||||
if let Err(e) = smtp_config {
|
||||
tracing::error!("Error reloading smtp config: {:?}", e)
|
||||
} else {
|
||||
let mut wc = SERVER_CONFIG.write().await;
|
||||
tracing::info!("Reloading server config...");
|
||||
*wc = config.unwrap()
|
||||
let mut wc = SMTP_CONFIG.write().await;
|
||||
tracing::info!("Reloading smtp config...");
|
||||
*wc = smtp_config.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1912,6 +1912,109 @@ def main():
|
||||
assert_eq!(result, serde_json::json!("test-workspace"));
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_bun_job_datetime(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await;
|
||||
let port = server.addr.port();
|
||||
|
||||
let content = r#"
|
||||
export async function main(a: Date) {
|
||||
return typeof a;
|
||||
}
|
||||
"#
|
||||
.to_owned();
|
||||
|
||||
let result = RunJob::from(JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
content,
|
||||
path: None,
|
||||
lock: None,
|
||||
language: ScriptLang::Bun,
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None,
|
||||
}))
|
||||
.arg("a", json!("2024-09-24T10:00:00.000Z"))
|
||||
.run_until_complete(&db, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result, serde_json::json!("object"));
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_deno_job_datetime(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await;
|
||||
let port = server.addr.port();
|
||||
|
||||
let content = r#"
|
||||
export async function main(a: Date) {
|
||||
return typeof a;
|
||||
}
|
||||
"#
|
||||
.to_owned();
|
||||
|
||||
let result = RunJob::from(JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
content,
|
||||
path: None,
|
||||
lock: None,
|
||||
language: ScriptLang::Deno,
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None,
|
||||
}))
|
||||
.arg("a", json!("2024-09-24T10:00:00.000Z"))
|
||||
.run_until_complete(&db, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result, serde_json::json!("object"));
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_python_job_datetime_and_bytes(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await;
|
||||
let port = server.addr.port();
|
||||
|
||||
let content = r#"
|
||||
from datetime import datetime
|
||||
def main(a: datetime, b: bytes):
|
||||
return (isinstance(a, datetime), isinstance(b, bytes))
|
||||
"#
|
||||
.to_owned();
|
||||
|
||||
let result = RunJob::from(JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
content,
|
||||
path: None,
|
||||
lock: None,
|
||||
language: ScriptLang::Python3,
|
||||
custom_concurrency_key: None,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None,
|
||||
}))
|
||||
.arg("a", json!("2024-09-24T10:00:00.000Z"))
|
||||
.arg("b", json!("dGVzdA=="))
|
||||
.run_until_complete(&db, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result, serde_json::json!([true, true]));
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_empty_loop_1(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
@@ -3294,6 +3397,7 @@ async fn run_deployed_relative_imports(
|
||||
visible_to_runner_only: None,
|
||||
no_main_func: None,
|
||||
codebase: None,
|
||||
has_preprocessor: None,
|
||||
},
|
||||
)
|
||||
.await
|
||||
@@ -3324,6 +3428,7 @@ async fn run_deployed_relative_imports(
|
||||
dedicated_worker: None,
|
||||
language,
|
||||
priority: None,
|
||||
apply_preprocessor: false,
|
||||
})
|
||||
.push(&db2)
|
||||
.await;
|
||||
|
||||
@@ -94,6 +94,7 @@ object_store = { workspace = true, optional = true}
|
||||
openidconnect = { workspace = true, optional = true}
|
||||
url = { workspace = true, optional = true}
|
||||
jsonwebtoken = { workspace = true }
|
||||
matchit.workspace = true
|
||||
|
||||
pin-project.workspace = true
|
||||
crc.workspace = true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.396.1
|
||||
version: 1.399.0
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -742,6 +742,35 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/settings/test_critical_channels:
|
||||
post:
|
||||
summary: test critical channels
|
||||
operationId: testCriticalChannels
|
||||
tags:
|
||||
- setting
|
||||
requestBody:
|
||||
description: test critical channel payload
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
slack_channel:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: status
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
|
||||
/settings/test_license_key:
|
||||
post:
|
||||
summary: test license key
|
||||
@@ -3088,6 +3117,22 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/resources/file_resource_type_to_file_ext_map:
|
||||
get:
|
||||
summary: get map from resource type to format extension
|
||||
operationId: fileResourceTypeToFileExtMap
|
||||
tags:
|
||||
- resource
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
responses:
|
||||
"200":
|
||||
description: map from resource type to file ext
|
||||
content:
|
||||
application/json:
|
||||
schema: {}
|
||||
|
||||
|
||||
/w/{workspace}/resources/type/delete/{path}:
|
||||
delete:
|
||||
summary: delete resource_type
|
||||
@@ -4166,6 +4211,11 @@ paths:
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
- name: skip_preprocessor
|
||||
description: skip the preprocessor
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- $ref: "#/components/parameters/ParentJob"
|
||||
- $ref: "#/components/parameters/WorkerTag"
|
||||
- $ref: "#/components/parameters/CacheTtl"
|
||||
@@ -5312,6 +5362,11 @@ paths:
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
- name: skip_preprocessor
|
||||
description: skip the preprocessor
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- $ref: "#/components/parameters/ParentJob"
|
||||
- $ref: "#/components/parameters/WorkerTag"
|
||||
- $ref: "#/components/parameters/NewJobId"
|
||||
@@ -5420,6 +5475,11 @@ paths:
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
- name: skip_preprocessor
|
||||
description: skip the preprocessor
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- $ref: "#/components/parameters/ParentJob"
|
||||
- $ref: "#/components/parameters/WorkerTag"
|
||||
- $ref: "#/components/parameters/CacheTtl"
|
||||
@@ -6806,6 +6866,190 @@ paths:
|
||||
"201":
|
||||
description: default error handler set
|
||||
|
||||
|
||||
/w/{workspace}/http_triggers/create:
|
||||
post:
|
||||
summary: create http trigger
|
||||
operationId: createHttpTrigger
|
||||
tags:
|
||||
- http_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
requestBody:
|
||||
description: new http trigger
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/NewHttpTrigger"
|
||||
responses:
|
||||
"201":
|
||||
description: http trigger created
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/http_triggers/update/{path}:
|
||||
post:
|
||||
summary: update http trigger
|
||||
operationId: updateHttpTrigger
|
||||
tags:
|
||||
- http_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
requestBody:
|
||||
description: updated trigger
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EditHttpTrigger"
|
||||
responses:
|
||||
"200":
|
||||
description: http trigger updated
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/http_triggers/delete/{path}:
|
||||
delete:
|
||||
summary: delete http trigger
|
||||
operationId: deleteHttpTrigger
|
||||
tags:
|
||||
- http_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
responses:
|
||||
"200":
|
||||
description: http trigger deleted
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/http_triggers/get/{path}:
|
||||
get:
|
||||
summary: get http trigger
|
||||
operationId: getHttpTrigger
|
||||
tags:
|
||||
- http_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
responses:
|
||||
"200":
|
||||
description: http trigger deleted
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/HttpTrigger"
|
||||
|
||||
|
||||
/w/{workspace}/http_triggers/list:
|
||||
get:
|
||||
summary: list http triggers
|
||||
operationId: listHttpTriggers
|
||||
tags:
|
||||
- http_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
required: true
|
||||
- $ref: "#/components/parameters/Page"
|
||||
- $ref: "#/components/parameters/PerPage"
|
||||
- name: path
|
||||
description: filter by path
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
- name: is_flow
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: path_start
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: http trigger list
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/HttpTrigger"
|
||||
|
||||
|
||||
/w/{workspace}/http_triggers/exists/{path}:
|
||||
get:
|
||||
summary: does http trigger exists
|
||||
operationId: existsHttpTrigger
|
||||
tags:
|
||||
- http_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
responses:
|
||||
"200":
|
||||
description: http trigger exists
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: boolean
|
||||
|
||||
/w/{workspace}/http_triggers/route_exists:
|
||||
post:
|
||||
summary: does route exists
|
||||
operationId: existsRoute
|
||||
tags:
|
||||
- http_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
requestBody:
|
||||
description: route exists request
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
route_path:
|
||||
type: string
|
||||
http_method:
|
||||
type: string
|
||||
enum: ["get", "post", "put", "delete", "patch"]
|
||||
required:
|
||||
- kind
|
||||
- route_path
|
||||
- http_method
|
||||
responses:
|
||||
"200":
|
||||
description: route exists
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: boolean
|
||||
|
||||
/w/{workspace}/http_triggers/used:
|
||||
get:
|
||||
summary: whether http triggers are used
|
||||
operationId: used
|
||||
tags:
|
||||
- http_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
responses:
|
||||
"200":
|
||||
description: whether http triggers are used
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: boolean
|
||||
|
||||
/groups/list:
|
||||
get:
|
||||
summary: list instance groups
|
||||
@@ -7632,6 +7876,7 @@ paths:
|
||||
folder,
|
||||
app,
|
||||
raw_app,
|
||||
http_trigger,
|
||||
]
|
||||
responses:
|
||||
"200":
|
||||
@@ -7668,6 +7913,7 @@ paths:
|
||||
folder,
|
||||
app,
|
||||
raw_app,
|
||||
http_trigger,
|
||||
]
|
||||
requestBody:
|
||||
description: acl to add
|
||||
@@ -7715,6 +7961,7 @@ paths:
|
||||
folder,
|
||||
app,
|
||||
raw_app,
|
||||
http_trigger,
|
||||
]
|
||||
requestBody:
|
||||
description: acl to add
|
||||
@@ -9362,6 +9609,7 @@ components:
|
||||
bun,
|
||||
php,
|
||||
rust,
|
||||
ansible,
|
||||
]
|
||||
kind:
|
||||
type: string
|
||||
@@ -9404,6 +9652,8 @@ components:
|
||||
type: boolean
|
||||
codebase:
|
||||
type: string
|
||||
has_preprocessor:
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- hash
|
||||
@@ -9421,6 +9671,7 @@ components:
|
||||
- kind
|
||||
- starred
|
||||
- no_main_func
|
||||
- has_preprocessor
|
||||
|
||||
|
||||
NewScript:
|
||||
@@ -9461,6 +9712,7 @@ components:
|
||||
bun,
|
||||
php,
|
||||
rust,
|
||||
ansible,
|
||||
]
|
||||
kind:
|
||||
type: string
|
||||
@@ -9501,6 +9753,8 @@ components:
|
||||
type: boolean
|
||||
codebase:
|
||||
type: string
|
||||
has_preprocessor:
|
||||
type: boolean
|
||||
required:
|
||||
- path
|
||||
- summary
|
||||
@@ -9679,6 +9933,7 @@ components:
|
||||
bun,
|
||||
php,
|
||||
rust,
|
||||
ansible,
|
||||
]
|
||||
email:
|
||||
type: string
|
||||
@@ -9795,6 +10050,7 @@ components:
|
||||
bun,
|
||||
php,
|
||||
rust,
|
||||
ansible,
|
||||
]
|
||||
is_skipped:
|
||||
type: boolean
|
||||
@@ -10299,6 +10555,9 @@ components:
|
||||
no_main_func:
|
||||
type: boolean
|
||||
nullable: true
|
||||
has_preprocessor:
|
||||
type: boolean
|
||||
nullable: true
|
||||
required:
|
||||
- star_args
|
||||
- start_kwargs
|
||||
@@ -10306,6 +10565,7 @@ components:
|
||||
- type
|
||||
- error
|
||||
- no_main_func
|
||||
- has_preprocessor
|
||||
|
||||
Preview:
|
||||
type: object
|
||||
@@ -10335,6 +10595,7 @@ components:
|
||||
bun,
|
||||
php,
|
||||
rust,
|
||||
ansible,
|
||||
]
|
||||
tag:
|
||||
type: string
|
||||
@@ -10482,6 +10743,8 @@ components:
|
||||
edited_at:
|
||||
type: string
|
||||
format: date-time
|
||||
format_extension:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
|
||||
@@ -10692,6 +10955,122 @@ components:
|
||||
- is_flow
|
||||
- args
|
||||
|
||||
HttpTrigger:
|
||||
type: object
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
edited_by:
|
||||
type: string
|
||||
edited_at:
|
||||
type: string
|
||||
format: date-time
|
||||
script_path:
|
||||
type: string
|
||||
route_path:
|
||||
type: string
|
||||
is_flow:
|
||||
type: boolean
|
||||
extra_perms:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: boolean
|
||||
email:
|
||||
type: string
|
||||
workspace_id:
|
||||
type: string
|
||||
http_method:
|
||||
type: string
|
||||
enum:
|
||||
- get
|
||||
- post
|
||||
- put
|
||||
- delete
|
||||
- patch
|
||||
is_async:
|
||||
type: boolean
|
||||
requires_auth:
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- path
|
||||
- edited_by
|
||||
- edited_at
|
||||
- script_path
|
||||
- route_path
|
||||
- extra_perms
|
||||
- is_flow
|
||||
- email
|
||||
- workspace_id
|
||||
- is_async
|
||||
- requires_auth
|
||||
- http_method
|
||||
|
||||
NewHttpTrigger:
|
||||
type: object
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
script_path:
|
||||
type: string
|
||||
route_path:
|
||||
type: string
|
||||
is_flow:
|
||||
type: boolean
|
||||
http_method:
|
||||
type: string
|
||||
enum:
|
||||
- get
|
||||
- post
|
||||
- put
|
||||
- delete
|
||||
- patch
|
||||
is_async:
|
||||
type: boolean
|
||||
requires_auth:
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
- route_path
|
||||
- is_flow
|
||||
- is_async
|
||||
- requires_auth
|
||||
- http_method
|
||||
|
||||
EditHttpTrigger:
|
||||
type: object
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
script_path:
|
||||
type: string
|
||||
route_path:
|
||||
type: string
|
||||
is_flow:
|
||||
type: boolean
|
||||
http_method:
|
||||
type: string
|
||||
enum:
|
||||
- get
|
||||
- post
|
||||
- put
|
||||
- delete
|
||||
- patch
|
||||
is_async:
|
||||
type: boolean
|
||||
requires_auth:
|
||||
type: boolean
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
- is_flow
|
||||
- kind
|
||||
- is_async
|
||||
- requires_auth
|
||||
- http_method
|
||||
|
||||
Group:
|
||||
type: object
|
||||
properties:
|
||||
@@ -11478,6 +11857,7 @@ components:
|
||||
bun,
|
||||
php,
|
||||
rust,
|
||||
ansible,
|
||||
]
|
||||
required:
|
||||
- raw_code
|
||||
|
||||
@@ -144,7 +144,7 @@ async fn get_concurrent_intervals(
|
||||
Query(iq): Query<ExtendedJobsParams>,
|
||||
Query(lq): Query<ListCompletedQuery>,
|
||||
) -> JsonResult<ExtendedJobs> {
|
||||
check_scopes(&authed, || format!("listjobs"))?;
|
||||
check_scopes(&authed, || format!("jobs:listjobs"))?;
|
||||
|
||||
if lq.success.is_some() && lq.running.is_some_and(|x| x) {
|
||||
return Err(error::Error::BadRequest(
|
||||
|
||||
@@ -1258,6 +1258,7 @@ mod tests {
|
||||
delete_after_use: None,
|
||||
continue_on_error: None,
|
||||
})),
|
||||
preprocessor_module: None,
|
||||
same_worker: false,
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
@@ -1330,7 +1331,8 @@ mod tests {
|
||||
"expr": "previous.isEmpty()",
|
||||
"skip_if_stopped": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"preprocessor_module": Option::<String>::None
|
||||
});
|
||||
assert_eq!(dbg!(serde_json::json!(fv)), dbg!(expect));
|
||||
}
|
||||
|
||||
@@ -23,8 +23,17 @@ use windmill_common::{
|
||||
utils::{not_found_if_none, StripPath},
|
||||
};
|
||||
|
||||
const KINDS: [&str; 9] = [
|
||||
"script", "group_", "resource", "schedule", "variable", "flow", "folder", "app", "raw_app",
|
||||
const KINDS: [&str; 10] = [
|
||||
"script",
|
||||
"group_",
|
||||
"resource",
|
||||
"schedule",
|
||||
"variable",
|
||||
"flow",
|
||||
"folder",
|
||||
"app",
|
||||
"raw_app",
|
||||
"http_trigger",
|
||||
];
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
|
||||
651
backend/windmill-api/src/http_triggers.rs
Normal file
651
backend/windmill-api/src/http_triggers.rs
Normal file
@@ -0,0 +1,651 @@
|
||||
use axum::{
|
||||
extract::{Path, Query},
|
||||
response::IntoResponse,
|
||||
routing::{delete, get, post},
|
||||
Extension, Json, Router,
|
||||
};
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sql_builder::{bind::Bind, SqlBuilder};
|
||||
use sqlx::prelude::FromRow;
|
||||
use std::collections::HashMap;
|
||||
use tower_http::cors::CorsLayer;
|
||||
use windmill_audit::{audit_ee::audit_log, ActionKind};
|
||||
use windmill_common::{
|
||||
auth::fetch_authed_from_permissioned_as,
|
||||
db::UserDB,
|
||||
error::{self, JsonResult},
|
||||
users::username_to_permissioned_as,
|
||||
utils::{not_found_if_none, paginate, require_admin, Pagination, StripPath},
|
||||
worker::{to_raw_value, CLOUD_HOSTED},
|
||||
};
|
||||
use windmill_queue::PushArgsOwned;
|
||||
|
||||
use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
jobs::{
|
||||
run_flow_by_path_inner, run_script_by_path_inner, run_wait_result_flow_by_path_internal,
|
||||
run_wait_result_script_by_path_internal, RunJobQuery,
|
||||
},
|
||||
users::OptAuthed,
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref ROUTE_PATH_KEY_RE: regex::Regex = regex::Regex::new(r"/:\w+").unwrap();
|
||||
}
|
||||
|
||||
pub fn routes_global_service() -> Router {
|
||||
let cors = CorsLayer::new()
|
||||
.allow_methods([
|
||||
http::Method::GET,
|
||||
http::Method::POST,
|
||||
http::Method::DELETE,
|
||||
http::Method::PUT,
|
||||
http::Method::PATCH,
|
||||
])
|
||||
.allow_headers([http::header::CONTENT_TYPE, http::header::AUTHORIZATION])
|
||||
.allow_origin(tower_http::cors::Any);
|
||||
Router::new()
|
||||
.route(
|
||||
"/*path",
|
||||
get(route_job)
|
||||
.post(route_job)
|
||||
.delete(route_job)
|
||||
.put(route_job)
|
||||
.patch(route_job)
|
||||
.head(|| async { "" }),
|
||||
)
|
||||
.layer(cors)
|
||||
}
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
Router::new()
|
||||
.route("/create", post(create_trigger))
|
||||
.route("/list", get(list_triggers))
|
||||
.route("/get/*path", get(get_trigger))
|
||||
.route("/update/*path", post(update_trigger))
|
||||
.route("/delete/*path", delete(delete_trigger))
|
||||
.route("/exists/*path", get(exists_trigger))
|
||||
.route("/used", get(used))
|
||||
.route("/route_exists", post(exists_route))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, sqlx::Type)]
|
||||
#[sqlx(type_name = "HTTP_METHOD", rename_all = "lowercase")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
enum HttpMethod {
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Patch,
|
||||
}
|
||||
|
||||
impl From<HttpMethod> for http::Method {
|
||||
fn from(method: HttpMethod) -> Self {
|
||||
match method {
|
||||
HttpMethod::Get => http::Method::GET,
|
||||
HttpMethod::Post => http::Method::POST,
|
||||
HttpMethod::Put => http::Method::PUT,
|
||||
HttpMethod::Delete => http::Method::DELETE,
|
||||
HttpMethod::Patch => http::Method::PATCH,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct NewTrigger {
|
||||
path: String,
|
||||
route_path: String,
|
||||
script_path: String,
|
||||
is_flow: bool,
|
||||
is_async: bool,
|
||||
requires_auth: bool,
|
||||
http_method: HttpMethod,
|
||||
}
|
||||
|
||||
#[derive(FromRow, Serialize)]
|
||||
struct Trigger {
|
||||
workspace_id: String,
|
||||
path: String,
|
||||
route_path: String,
|
||||
route_path_key: String,
|
||||
script_path: String,
|
||||
is_flow: bool,
|
||||
edited_by: String,
|
||||
email: String,
|
||||
edited_at: chrono::DateTime<chrono::Utc>,
|
||||
extra_perms: serde_json::Value,
|
||||
is_async: bool,
|
||||
requires_auth: bool,
|
||||
http_method: HttpMethod,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct EditTrigger {
|
||||
path: String,
|
||||
route_path: Option<String>,
|
||||
script_path: String,
|
||||
is_flow: bool,
|
||||
is_async: bool,
|
||||
requires_auth: bool,
|
||||
http_method: HttpMethod,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ListTriggerQuery {
|
||||
pub page: Option<usize>,
|
||||
pub per_page: Option<usize>,
|
||||
pub path: Option<String>,
|
||||
pub is_flow: Option<bool>,
|
||||
pub path_start: Option<String>,
|
||||
}
|
||||
|
||||
async fn list_triggers(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path(w_id): Path<String>,
|
||||
Query(lst): Query<ListTriggerQuery>,
|
||||
) -> error::JsonResult<Vec<Trigger>> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let (per_page, offset) = paginate(Pagination { per_page: lst.per_page, page: lst.page });
|
||||
let mut sqlb = SqlBuilder::select_from("http_trigger")
|
||||
.field("*")
|
||||
.order_by("edited_at", true)
|
||||
.and_where("workspace_id = ?".bind(&w_id))
|
||||
.offset(offset)
|
||||
.limit(per_page)
|
||||
.clone();
|
||||
if let Some(path) = lst.path {
|
||||
sqlb.and_where_eq("script_path", "?".bind(&path));
|
||||
}
|
||||
if let Some(is_flow) = lst.is_flow {
|
||||
sqlb.and_where_eq("is_flow", "?".bind(&is_flow));
|
||||
}
|
||||
if let Some(path_start) = &lst.path_start {
|
||||
sqlb.and_where_like_left("path", path_start);
|
||||
}
|
||||
let sql = sqlb
|
||||
.sql()
|
||||
.map_err(|e| error::Error::InternalErr(e.to_string()))?;
|
||||
let rows = sqlx::query_as::<_, Trigger>(&sql)
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(Json(rows))
|
||||
}
|
||||
|
||||
async fn get_trigger(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> error::JsonResult<Trigger> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let path = path.to_path();
|
||||
let trigger = sqlx::query_as!(
|
||||
Trigger,
|
||||
r#"SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, http_method as "http_method: _", edited_by, email, edited_at, extra_perms, is_async, requires_auth
|
||||
FROM http_trigger
|
||||
WHERE workspace_id = $1 AND path = $2"#,
|
||||
w_id,
|
||||
path,
|
||||
)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
let trigger = not_found_if_none(trigger, "Trigger", path)?;
|
||||
|
||||
Ok(Json(trigger))
|
||||
}
|
||||
|
||||
async fn create_trigger(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(ct): Json<NewTrigger>,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
require_admin(authed.is_admin, &authed.username)?;
|
||||
|
||||
let route_path_key = ROUTE_PATH_KEY_RE.replace_all(ct.route_path.as_str(), ":key");
|
||||
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
sqlx::query!(
|
||||
"INSERT INTO http_trigger (workspace_id, path, route_path, route_path_key, script_path, is_flow, is_async, requires_auth, http_method, edited_by, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, now())",
|
||||
w_id,
|
||||
ct.path,
|
||||
ct.route_path,
|
||||
&route_path_key,
|
||||
ct.script_path,
|
||||
ct.is_flow,
|
||||
ct.is_async,
|
||||
ct.requires_auth,
|
||||
ct.http_method as HttpMethod,
|
||||
&authed.username,
|
||||
&authed.email
|
||||
)
|
||||
.execute(&mut *tx).await?;
|
||||
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
"http_triggers.create",
|
||||
ActionKind::Create,
|
||||
&w_id,
|
||||
Some(ct.path.as_str()),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
Ok((StatusCode::CREATED, format!("{}", ct.path)))
|
||||
}
|
||||
|
||||
async fn update_trigger(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Json(ct): Json<EditTrigger>,
|
||||
) -> error::Result<String> {
|
||||
let path = path.to_path();
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
if authed.is_admin {
|
||||
if ct.route_path.is_none() {
|
||||
return Err(error::Error::BadRequest(
|
||||
"route_path is required".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let route_path_key =
|
||||
ROUTE_PATH_KEY_RE.replace_all(ct.route_path.as_ref().unwrap().as_str(), ":key");
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE http_trigger
|
||||
SET route_path = $1, route_path_key = $2, script_path = $3, path = $4, is_flow = $5, http_method = $6, edited_by = $7, email = $8, is_async = $9, requires_auth = $10, edited_at = now()
|
||||
WHERE workspace_id = $11 AND path = $12",
|
||||
ct.route_path,
|
||||
&route_path_key,
|
||||
ct.script_path,
|
||||
ct.path,
|
||||
ct.is_flow,
|
||||
ct.http_method as HttpMethod,
|
||||
&authed.username,
|
||||
&authed.email,
|
||||
ct.is_async,
|
||||
ct.requires_auth,
|
||||
w_id,
|
||||
path,
|
||||
)
|
||||
.execute(&mut *tx).await?;
|
||||
} else {
|
||||
sqlx::query!(
|
||||
"UPDATE http_trigger SET script_path = $1, path = $2, is_flow = $3, http_method = $4, edited_by = $5, email = $6, is_async = $7, requires_auth = $8, edited_at = now()
|
||||
WHERE workspace_id = $9 AND path = $10",
|
||||
ct.script_path,
|
||||
ct.path,
|
||||
ct.is_flow,
|
||||
ct.http_method as HttpMethod,
|
||||
&authed.username,
|
||||
&authed.email,
|
||||
ct.is_async,
|
||||
ct.requires_auth,
|
||||
w_id,
|
||||
path,
|
||||
)
|
||||
.execute(&mut *tx).await?;
|
||||
}
|
||||
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
"http_triggers.update",
|
||||
ActionKind::Create,
|
||||
&w_id,
|
||||
Some(path),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(path.to_string())
|
||||
}
|
||||
|
||||
async fn delete_trigger(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> error::Result<String> {
|
||||
require_admin(authed.is_admin, &authed.username)?;
|
||||
let path = path.to_path();
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
sqlx::query!(
|
||||
"DELETE FROM http_trigger WHERE workspace_id = $1 AND path = $2",
|
||||
w_id,
|
||||
path,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
"http_triggers.delete",
|
||||
ActionKind::Delete,
|
||||
&w_id,
|
||||
Some(path),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(format!("HTTP trigger {path} deleted"))
|
||||
}
|
||||
|
||||
async fn used(Extension(db): Extension<DB>, Path(w_id): Path<String>) -> JsonResult<bool> {
|
||||
let used = sqlx::query_scalar!(
|
||||
r#"SELECT EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1)"#,
|
||||
w_id,
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?
|
||||
.unwrap_or(false);
|
||||
Ok(Json(used))
|
||||
}
|
||||
|
||||
async fn exists_trigger(
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> JsonResult<bool> {
|
||||
let path = path.to_path();
|
||||
let exists = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM http_trigger WHERE path = $1 AND workspace_id = $2)",
|
||||
path,
|
||||
w_id,
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?
|
||||
.unwrap_or(false);
|
||||
Ok(Json(exists))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct RouteExists {
|
||||
route_path: String,
|
||||
http_method: HttpMethod,
|
||||
}
|
||||
|
||||
async fn exists_route(
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(RouteExists { route_path, http_method }): Json<RouteExists>,
|
||||
) -> JsonResult<bool> {
|
||||
let route_path_key = ROUTE_PATH_KEY_RE.replace_all(route_path.as_str(), ":key");
|
||||
let exists = if *CLOUD_HOSTED {
|
||||
sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM http_trigger WHERE route_path_key = $1 AND workspace_id = $2 AND http_method = $3)",
|
||||
&route_path_key,
|
||||
w_id,
|
||||
http_method as HttpMethod
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?
|
||||
.unwrap_or(false)
|
||||
} else {
|
||||
sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM http_trigger WHERE route_path_key = $1 AND http_method = $2)",
|
||||
&route_path_key,
|
||||
http_method as HttpMethod
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?
|
||||
.unwrap_or(false)
|
||||
};
|
||||
Ok(Json(exists))
|
||||
}
|
||||
|
||||
struct TriggerRoute {
|
||||
path: String,
|
||||
script_path: String,
|
||||
is_flow: bool,
|
||||
route_path: String,
|
||||
workspace_id: String,
|
||||
is_async: bool,
|
||||
requires_auth: bool,
|
||||
edited_by: String,
|
||||
email: String,
|
||||
http_method: HttpMethod,
|
||||
}
|
||||
|
||||
async fn fetch_api_authed(
|
||||
username: String,
|
||||
email: String,
|
||||
w_id: &str,
|
||||
db: &DB,
|
||||
username_override: String,
|
||||
) -> error::Result<ApiAuthed> {
|
||||
let permissioned_as = username_to_permissioned_as(username.as_str());
|
||||
let authed =
|
||||
fetch_authed_from_permissioned_as(permissioned_as, email.clone(), w_id, db).await?;
|
||||
Ok(ApiAuthed {
|
||||
username: username,
|
||||
email: email,
|
||||
is_admin: authed.is_admin,
|
||||
is_operator: authed.is_operator,
|
||||
groups: authed.groups,
|
||||
folders: authed.folders,
|
||||
scopes: authed.scopes,
|
||||
username_override: Some(username_override),
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_http_route_trigger(
|
||||
route_path: &str,
|
||||
opt_authed: Option<ApiAuthed>,
|
||||
db: &DB,
|
||||
user_db: UserDB,
|
||||
) -> error::Result<(TriggerRoute, String, HashMap<String, String>, ApiAuthed)> {
|
||||
let (mut triggers, route_path) = if *CLOUD_HOSTED {
|
||||
let mut splitted = route_path.split("/");
|
||||
let w_id = splitted.next().ok_or_else(|| {
|
||||
error::Error::BadRequest("Missing workspace id in route path".to_string())
|
||||
})?;
|
||||
let route_path = StripPath(splitted.collect::<Vec<_>>().join("/"));
|
||||
let triggers = sqlx::query_as!(
|
||||
TriggerRoute,
|
||||
r#"SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as "http_method: _" FROM http_trigger WHERE workspace_id = $1"#,
|
||||
w_id
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await?;
|
||||
(triggers, route_path)
|
||||
} else {
|
||||
let triggers = sqlx::query_as!(
|
||||
TriggerRoute,
|
||||
r#"SELECT path, script_path, is_flow, route_path, workspace_id, is_async, requires_auth, edited_by, email, http_method as "http_method: _" FROM http_trigger"#,
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await?;
|
||||
(triggers, StripPath(route_path.to_string()))
|
||||
};
|
||||
|
||||
let mut router = matchit::Router::new();
|
||||
|
||||
for (idx, trigger) in triggers.iter().enumerate() {
|
||||
let route_path = trigger.route_path.clone();
|
||||
router.insert(route_path.as_str(), idx).unwrap_or_else(|e| {
|
||||
tracing::warn!(
|
||||
"Failed to consider http trigger route {}: {:?}",
|
||||
route_path,
|
||||
e,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
let trigger_idx = router.at(route_path.0.as_str()).ok();
|
||||
|
||||
let matchit::Match { value: trigger_idx, params } =
|
||||
not_found_if_none(trigger_idx, "Trigger", route_path.0.as_str())?;
|
||||
|
||||
let trigger = triggers.remove(trigger_idx.to_owned());
|
||||
|
||||
let params: HashMap<String, String> = params
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect();
|
||||
|
||||
let username_override = if trigger.requires_auth {
|
||||
if let Some(authed) = opt_authed {
|
||||
// check that the user has access to the trigger
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let exists = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1 AND path = $2)",
|
||||
trigger.workspace_id,
|
||||
trigger.path
|
||||
)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?
|
||||
.unwrap_or(false);
|
||||
tx.commit().await?;
|
||||
if exists {
|
||||
Some(authed.display_username().to_owned())
|
||||
} else {
|
||||
return Err(error::Error::NotAuthorized("Unauthorized".to_string()));
|
||||
}
|
||||
} else {
|
||||
return Err(error::Error::NotAuthorized(
|
||||
"Requires authentication".to_string(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let authed = fetch_api_authed(
|
||||
trigger.edited_by.clone(),
|
||||
trigger.email.clone(),
|
||||
&trigger.workspace_id,
|
||||
&db,
|
||||
username_override.unwrap_or("anonymous".to_string()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok((trigger, route_path.0, params, authed))
|
||||
}
|
||||
|
||||
async fn route_job(
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path(route_path): Path<StripPath>,
|
||||
OptAuthed(opt_authed): OptAuthed,
|
||||
Query(query): Query<HashMap<String, String>>,
|
||||
method: http::Method,
|
||||
headers: HeaderMap,
|
||||
mut args: PushArgsOwned,
|
||||
) -> impl IntoResponse {
|
||||
let route_path = route_path.to_path();
|
||||
let (trigger, called_path, params, authed) =
|
||||
match get_http_route_trigger(route_path, opt_authed, &db, user_db.clone()).await {
|
||||
Ok(trigger) => trigger,
|
||||
Err(e) => return e.into_response(),
|
||||
};
|
||||
let headers = headers
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
|
||||
.collect::<HashMap<String, String>>();
|
||||
let extra = args.extra.get_or_insert_with(HashMap::new);
|
||||
extra.insert(
|
||||
"wm_trigger".to_string(),
|
||||
to_raw_value(&serde_json::json!({
|
||||
"kind": "http",
|
||||
"http": {
|
||||
"route": trigger.route_path,
|
||||
"path": called_path,
|
||||
"method": method.to_string().to_lowercase(),
|
||||
"params": params,
|
||||
"query": query,
|
||||
"headers": headers
|
||||
},
|
||||
})),
|
||||
);
|
||||
let http_method = http::Method::from(trigger.http_method);
|
||||
|
||||
if http_method != method {
|
||||
return error::Error::BadRequest("Invalid HTTP method".to_string()).into_response();
|
||||
}
|
||||
|
||||
let label_prefix = Some(format!(
|
||||
"http-{}-{}-",
|
||||
http_method.as_str().to_lowercase(),
|
||||
trigger.route_path
|
||||
));
|
||||
|
||||
let run_query = RunJobQuery::default();
|
||||
|
||||
if trigger.is_flow {
|
||||
if trigger.is_async {
|
||||
run_flow_by_path_inner(
|
||||
authed,
|
||||
db,
|
||||
user_db,
|
||||
rsmq,
|
||||
trigger.workspace_id.clone(),
|
||||
StripPath(trigger.script_path.to_owned()),
|
||||
run_query,
|
||||
args,
|
||||
label_prefix,
|
||||
)
|
||||
.await
|
||||
.into_response()
|
||||
} else {
|
||||
run_wait_result_flow_by_path_internal(
|
||||
db,
|
||||
run_query,
|
||||
StripPath(trigger.script_path.to_owned()),
|
||||
authed,
|
||||
rsmq,
|
||||
user_db,
|
||||
args,
|
||||
trigger.workspace_id.clone(),
|
||||
label_prefix,
|
||||
)
|
||||
.await
|
||||
.into_response()
|
||||
}
|
||||
} else {
|
||||
if trigger.is_async {
|
||||
run_script_by_path_inner(
|
||||
authed,
|
||||
db,
|
||||
user_db,
|
||||
rsmq,
|
||||
trigger.workspace_id.clone(),
|
||||
StripPath(trigger.script_path.to_owned()),
|
||||
run_query,
|
||||
args,
|
||||
label_prefix,
|
||||
)
|
||||
.await
|
||||
.into_response()
|
||||
} else {
|
||||
run_wait_result_script_by_path_internal(
|
||||
db,
|
||||
run_query,
|
||||
StripPath(trigger.script_path.to_owned()),
|
||||
authed,
|
||||
rsmq,
|
||||
user_db,
|
||||
trigger.workspace_id.clone(),
|
||||
args,
|
||||
label_prefix,
|
||||
)
|
||||
.await
|
||||
.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ use crate::add_webhook_allowed_origin;
|
||||
use crate::concurrency_groups::join_concurrency_key;
|
||||
use crate::db::ApiAuthed;
|
||||
|
||||
use crate::users::get_scope_tags;
|
||||
use crate::utils::content_plain;
|
||||
use crate::{
|
||||
db::DB,
|
||||
@@ -58,7 +59,7 @@ use tower_http::cors::{Any, CorsLayer};
|
||||
use urlencoding::encode;
|
||||
use windmill_audit::audit_ee::{audit_log, AuditAuthor};
|
||||
use windmill_audit::ActionKind;
|
||||
use windmill_common::worker::{to_raw_value, CUSTOM_TAGS_PER_WORKSPACE, SERVER_CONFIG};
|
||||
use windmill_common::worker::{to_raw_value, CUSTOM_TAGS_PER_WORKSPACE};
|
||||
use windmill_common::{
|
||||
db::UserDB,
|
||||
error::{self, to_anyhow, Error},
|
||||
@@ -556,9 +557,10 @@ pub async fn get_path_tag_limits_cache_for_hash<'c, R: rsmq_async::RsmqConnectio
|
||||
Option<i16>,
|
||||
Option<bool>,
|
||||
Option<i32>,
|
||||
Option<bool>,
|
||||
)> {
|
||||
let script = sqlx::query!(
|
||||
"select path, tag, concurrency_key, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority, delete_after_use, timeout from script where hash = $1 AND workspace_id = $2",
|
||||
"select path, tag, concurrency_key, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority, delete_after_use, timeout, has_preprocessor from script where hash = $1 AND workspace_id = $2",
|
||||
hash,
|
||||
w_id
|
||||
)
|
||||
@@ -583,6 +585,7 @@ pub async fn get_path_tag_limits_cache_for_hash<'c, R: rsmq_async::RsmqConnectio
|
||||
script.priority,
|
||||
script.delete_after_use,
|
||||
script.timeout,
|
||||
script.has_preprocessor,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1058,6 +1061,7 @@ pub struct RunJobQuery {
|
||||
pub tag: Option<String>,
|
||||
pub timeout: Option<i32>,
|
||||
pub cache_ttl: Option<i32>,
|
||||
pub skip_preprocessor: Option<bool>,
|
||||
}
|
||||
|
||||
impl RunJobQuery {
|
||||
@@ -1245,13 +1249,18 @@ pub fn list_queue_jobs_query(
|
||||
lq: &ListQueueQuery,
|
||||
fields: &[&str],
|
||||
join_outstanding_wait_times: bool,
|
||||
tags: Option<Vec<&str>>,
|
||||
) -> SqlBuilder {
|
||||
let sqlb = SqlBuilder::select_from("queue")
|
||||
let mut sqlb = SqlBuilder::select_from("queue")
|
||||
.fields(fields)
|
||||
.order_by("created_at", lq.order_desc.unwrap_or(true))
|
||||
.limit(1000)
|
||||
.clone();
|
||||
|
||||
if let Some(tags) = tags {
|
||||
sqlb.and_where_in("tag", &tags.iter().map(|x| quote(x)).collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
filter_list_queue_query(sqlb, lq, w_id, join_outstanding_wait_times)
|
||||
}
|
||||
|
||||
@@ -1307,6 +1316,7 @@ async fn list_queue_jobs(
|
||||
"workspace_id",
|
||||
],
|
||||
false,
|
||||
get_scope_tags(&authed),
|
||||
)
|
||||
.sql()?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
@@ -1495,6 +1505,10 @@ async fn list_filtered_uuids(
|
||||
|
||||
sqlb.and_where_is_null("schedule_path");
|
||||
|
||||
if let Some(tags) = get_scope_tags(&authed) {
|
||||
sqlb.and_where_in("tag", &tags.iter().map(|x| quote(x)).collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
sqlb = filter_list_queue_query(sqlb, &lq, w_id.as_str(), false);
|
||||
|
||||
let sql = sqlb.query()?;
|
||||
@@ -1554,7 +1568,7 @@ async fn list_jobs(
|
||||
Query(lq): Query<ListCompletedQuery>,
|
||||
Extension(_api_list_jobs_query_duration): Extension<Option<Histo>>,
|
||||
) -> error::JsonResult<Vec<Job>> {
|
||||
check_scopes(&authed, || format!("listjobs"))?;
|
||||
check_scopes(&authed, || format!("jobs:listjobs"))?;
|
||||
|
||||
let (per_page, offset) = paginate(pagination);
|
||||
let lqc = lq.clone();
|
||||
@@ -1572,6 +1586,7 @@ async fn list_jobs(
|
||||
&ListCompletedQuery { order_desc: Some(true), ..lqc },
|
||||
UnifiedJob::completed_job_fields(),
|
||||
true,
|
||||
get_scope_tags(&authed),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
@@ -1587,6 +1602,7 @@ async fn list_jobs(
|
||||
&ListQueueQuery { order_desc: Some(true), ..lq.into() },
|
||||
UnifiedJob::queued_job_fields(),
|
||||
true,
|
||||
get_scope_tags(&authed),
|
||||
);
|
||||
|
||||
if let Some(sqlc) = sqlc {
|
||||
@@ -1637,7 +1653,7 @@ pub async fn resume_suspended_flow_as_owner(
|
||||
Path((_w_id, flow_id)): Path<(String, Uuid)>,
|
||||
QueryOrBody(value): QueryOrBody<serde_json::Value>,
|
||||
) -> error::Result<StatusCode> {
|
||||
check_scopes(&authed, || format!("resumeflow"))?;
|
||||
check_scopes(&authed, || format!("jobs:resumeflow"))?;
|
||||
let value = value.unwrap_or(serde_json::Value::Null);
|
||||
let mut tx = db.begin().await?;
|
||||
|
||||
@@ -2784,14 +2800,18 @@ pub async fn run_flow_by_path_inner(
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let (tag, dedicated_worker) = sqlx::query!(
|
||||
"SELECT tag, dedicated_worker from flow WHERE path = $1 and workspace_id = $2",
|
||||
let (tag, dedicated_worker, has_preprocessor) = sqlx::query!(
|
||||
"SELECT tag, dedicated_worker, flow_version.value->>'preprocessor_module' IS NOT NULL as has_preprocessor
|
||||
FROM flow
|
||||
LEFT JOIN flow_version
|
||||
ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]
|
||||
WHERE flow.path = $1 and flow.workspace_id = $2",
|
||||
flow_path,
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(&mut tx)
|
||||
.await?
|
||||
.map(|x| (x.tag, x.dedicated_worker))
|
||||
.map(|x| (x.tag, x.dedicated_worker, x.has_preprocessor))
|
||||
.ok_or_else(|| {
|
||||
Error::NotFound(format!(
|
||||
"flow not found at path {flow_path} in workspace {w_id}"
|
||||
@@ -2807,7 +2827,12 @@ pub async fn run_flow_by_path_inner(
|
||||
&db,
|
||||
tx,
|
||||
&w_id,
|
||||
JobPayload::Flow { path: flow_path.to_string(), dedicated_worker },
|
||||
JobPayload::Flow {
|
||||
path: flow_path.to_string(),
|
||||
dedicated_worker,
|
||||
apply_preprocessor: !run_query.skip_preprocessor.unwrap_or(false)
|
||||
&& has_preprocessor.unwrap_or(false),
|
||||
},
|
||||
PushArgs { args: &args.args, extra: args.extra },
|
||||
&label_prefix
|
||||
.map(|x| x + authed.display_username())
|
||||
@@ -2973,7 +2998,7 @@ pub async fn run_script_by_path_inner(
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let (job_payload, tag, _delete_after_use, timeout) =
|
||||
script_path_to_payload(script_path, &mut tx, &w_id).await?;
|
||||
script_path_to_payload(script_path, &mut tx, &w_id, run_query.skip_preprocessor).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(&db).await?;
|
||||
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
@@ -3049,7 +3074,15 @@ pub async fn run_workflow_as_code(
|
||||
None,
|
||||
run_query.timeout,
|
||||
),
|
||||
JobKind::Script => script_path_to_payload(job.script_path(), &mut tx, &w_id).await?,
|
||||
JobKind::Script => {
|
||||
script_path_to_payload(
|
||||
job.script_path(),
|
||||
&mut tx,
|
||||
&w_id,
|
||||
run_query.skip_preprocessor,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!("Not supported").into()),
|
||||
};
|
||||
|
||||
@@ -3145,6 +3178,17 @@ impl Drop for Guard {
|
||||
}
|
||||
}
|
||||
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref TIMEOUT_WAIT_RESULT: Arc<RwLock<Option<u64>>> = Arc::new(RwLock::new(
|
||||
std::env::var("TIMEOUT_WAIT_RESULT")
|
||||
.ok()
|
||||
.and_then(|x| x.parse::<u64>().ok())
|
||||
));
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct WindmillCompositeResult {
|
||||
windmill_status_code: Option<u16>,
|
||||
@@ -3159,7 +3203,7 @@ async fn run_wait_result(
|
||||
username: &str,
|
||||
) -> error::Result<Response> {
|
||||
let mut result = None;
|
||||
let timeout = SERVER_CONFIG.read().await.timeout_wait_result.clone();
|
||||
let timeout = TIMEOUT_WAIT_RESULT.read().await.clone().unwrap_or(600);
|
||||
let timeout_ms = if timeout <= 0 {
|
||||
2000
|
||||
} else {
|
||||
@@ -3451,7 +3495,7 @@ pub async fn run_wait_result_job_by_path_get(
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let (job_payload, tag, delete_after_use, timeout) =
|
||||
script_path_to_payload(script_path, &mut tx, &w_id).await?;
|
||||
script_path_to_payload(script_path, &mut tx, &w_id, run_query.skip_preprocessor).await?;
|
||||
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
@@ -3527,7 +3571,7 @@ pub async fn run_wait_result_flow_by_path_get(
|
||||
let args = PushArgsOwned { extra: Some(payload_args), args: HashMap::new() };
|
||||
|
||||
run_wait_result_flow_by_path_internal(
|
||||
db, run_query, flow_path, authed, rsmq, user_db, args, w_id,
|
||||
db, run_query, flow_path, authed, rsmq, user_db, args, w_id, None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -3553,11 +3597,12 @@ pub async fn run_wait_result_script_by_path(
|
||||
user_db,
|
||||
w_id,
|
||||
args,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn run_wait_result_script_by_path_internal(
|
||||
pub async fn run_wait_result_script_by_path_internal(
|
||||
db: sqlx::Pool<Postgres>,
|
||||
run_query: RunJobQuery,
|
||||
script_path: StripPath,
|
||||
@@ -3566,6 +3611,7 @@ async fn run_wait_result_script_by_path_internal(
|
||||
user_db: UserDB,
|
||||
w_id: String,
|
||||
args: PushArgsOwned,
|
||||
label_prefix: Option<String>,
|
||||
) -> error::Result<Response> {
|
||||
check_queue_too_long(&db, QUEUE_LIMIT_WAIT_RESULT.or(run_query.queue_limit)).await?;
|
||||
let script_path = script_path.to_path();
|
||||
@@ -3574,7 +3620,7 @@ async fn run_wait_result_script_by_path_internal(
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let (job_payload, tag, delete_after_use, timeout) =
|
||||
script_path_to_payload(script_path, &mut tx, &w_id).await?;
|
||||
script_path_to_payload(script_path, &mut tx, &w_id, run_query.skip_preprocessor).await?;
|
||||
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
@@ -3587,7 +3633,9 @@ async fn run_wait_result_script_by_path_internal(
|
||||
&w_id,
|
||||
job_payload,
|
||||
PushArgs { args: &args.args, extra: args.extra },
|
||||
authed.display_username(),
|
||||
&label_prefix
|
||||
.map(|x| x + authed.display_username())
|
||||
.unwrap_or_else(|| authed.display_username().to_string()),
|
||||
&authed.email,
|
||||
username_to_permissioned_as(&authed.username),
|
||||
None,
|
||||
@@ -3644,6 +3692,7 @@ pub async fn run_wait_result_script_by_hash(
|
||||
priority,
|
||||
delete_after_use,
|
||||
timeout,
|
||||
has_preprocessor,
|
||||
) = get_path_tag_limits_cache_for_hash(&mut tx, &w_id, hash).await?;
|
||||
if let Some(run_query_cache_ttl) = run_query.cache_ttl {
|
||||
cache_ttl = Some(run_query_cache_ttl);
|
||||
@@ -3669,6 +3718,8 @@ pub async fn run_wait_result_script_by_hash(
|
||||
language,
|
||||
dedicated_worker,
|
||||
priority,
|
||||
apply_preprocessor: !run_query.skip_preprocessor.unwrap_or(false)
|
||||
&& has_preprocessor.unwrap_or(false),
|
||||
},
|
||||
PushArgs { args: &args.args, extra: args.extra },
|
||||
authed.display_username(),
|
||||
@@ -3712,12 +3763,12 @@ pub async fn run_wait_result_flow_by_path(
|
||||
check_license_key_valid().await?;
|
||||
|
||||
run_wait_result_flow_by_path_internal(
|
||||
db, run_query, flow_path, authed, rsmq, user_db, args, w_id,
|
||||
db, run_query, flow_path, authed, rsmq, user_db, args, w_id, None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn run_wait_result_flow_by_path_internal(
|
||||
pub async fn run_wait_result_flow_by_path_internal(
|
||||
db: sqlx::Pool<Postgres>,
|
||||
run_query: RunJobQuery,
|
||||
flow_path: StripPath,
|
||||
@@ -3726,6 +3777,7 @@ async fn run_wait_result_flow_by_path_internal(
|
||||
user_db: UserDB,
|
||||
args: PushArgsOwned,
|
||||
w_id: String,
|
||||
label_prefix: Option<String>,
|
||||
) -> error::Result<Response> {
|
||||
check_queue_too_long(&db, run_query.queue_limit).await?;
|
||||
|
||||
@@ -3736,8 +3788,8 @@ async fn run_wait_result_flow_by_path_internal(
|
||||
|
||||
let scheduled_for = run_query.get_scheduled_for(&db).await?;
|
||||
|
||||
let (tag, dedicated_worker, early_return) = sqlx::query!(
|
||||
"SELECT tag, dedicated_worker, flow_version.value->>'early_return' as early_return
|
||||
let (tag, dedicated_worker, early_return, has_preprocessor) = sqlx::query!(
|
||||
"SELECT tag, dedicated_worker, flow_version.value->>'early_return' as early_return, flow_version.value->>'preprocessor_module' IS NOT NULL as has_preprocessor
|
||||
FROM flow
|
||||
LEFT JOIN flow_version
|
||||
ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]
|
||||
@@ -3747,7 +3799,7 @@ async fn run_wait_result_flow_by_path_internal(
|
||||
)
|
||||
.fetch_optional(&mut tx)
|
||||
.await?
|
||||
.map(|x| (x.tag, x.dedicated_worker, x.early_return))
|
||||
.map(|x| (x.tag, x.dedicated_worker, x.early_return, x.has_preprocessor))
|
||||
.ok_or_else(|| {
|
||||
Error::NotFound(format!(
|
||||
"flow not found at path {flow_path} in workspace {w_id}"
|
||||
@@ -3763,9 +3815,16 @@ async fn run_wait_result_flow_by_path_internal(
|
||||
&db,
|
||||
tx,
|
||||
&w_id,
|
||||
JobPayload::Flow { path: flow_path.to_string(), dedicated_worker },
|
||||
JobPayload::Flow {
|
||||
path: flow_path.to_string(),
|
||||
dedicated_worker,
|
||||
apply_preprocessor: !run_query.skip_preprocessor.unwrap_or(false)
|
||||
&& has_preprocessor.unwrap_or(false),
|
||||
},
|
||||
PushArgs { args: &args.args, extra: args.extra },
|
||||
authed.display_username(),
|
||||
&label_prefix
|
||||
.map(|x| x + authed.display_username())
|
||||
.unwrap_or_else(|| authed.display_username().to_string()),
|
||||
&authed.email,
|
||||
username_to_permissioned_as(&authed.username),
|
||||
scheduled_for,
|
||||
@@ -3801,7 +3860,7 @@ async fn run_preview_script(
|
||||
#[cfg(feature = "enterprise")]
|
||||
check_license_key_valid().await?;
|
||||
|
||||
check_scopes(&authed, || format!("runscript"))?;
|
||||
check_scopes(&authed, || format!("jobs:runscript"))?;
|
||||
if authed.is_operator {
|
||||
return Err(error::Error::NotAuthorized(
|
||||
"Operators cannot run preview jobs for security reasons".to_string(),
|
||||
@@ -3871,7 +3930,7 @@ async fn run_bundle_preview_script(
|
||||
|
||||
check_license_key_valid().await?;
|
||||
|
||||
check_scopes(&authed, || format!("runscript"))?;
|
||||
check_scopes(&authed, || format!("jobs:runscript"))?;
|
||||
if authed.is_operator {
|
||||
return Err(error::Error::NotAuthorized(
|
||||
"Operators cannot run preview jobs for security reasons".to_string(),
|
||||
@@ -4202,6 +4261,7 @@ async fn add_batch_jobs(
|
||||
_priority,
|
||||
_delete_after_use,
|
||||
timeout,
|
||||
_,
|
||||
) = get_latest_deployed_hash_for_path(&db, &w_id, &path).await?;
|
||||
(
|
||||
Some(script_hash),
|
||||
@@ -4228,7 +4288,11 @@ async fn add_batch_jobs(
|
||||
JobPayload::RawFlow { value: fv.clone(), path: None, restarted_from: None }
|
||||
} else {
|
||||
if let Some(path) = batch_info.path.as_ref() {
|
||||
JobPayload::Flow { path: path.to_string(), dedicated_worker: None }
|
||||
JobPayload::Flow {
|
||||
path: path.to_string(),
|
||||
dedicated_worker: None,
|
||||
apply_preprocessor: false,
|
||||
}
|
||||
} else {
|
||||
Err(anyhow::anyhow!(
|
||||
"Path is required if no value is not provided"
|
||||
@@ -4352,7 +4416,7 @@ async fn run_preview_flow_job(
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
Json(raw_flow): Json<PreviewFlow>,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
check_scopes(&authed, || format!("runflow"))?;
|
||||
check_scopes(&authed, || format!("jobs:runflow"))?;
|
||||
if authed.is_operator {
|
||||
return Err(error::Error::NotAuthorized(
|
||||
"Operators cannot run preview jobs for security reasons".to_string(),
|
||||
@@ -4450,6 +4514,7 @@ pub async fn run_job_by_hash_inner(
|
||||
priority,
|
||||
_delete_after_use, // not taken into account in async endpoints
|
||||
timeout,
|
||||
has_preprocessor,
|
||||
) = get_path_tag_limits_cache_for_hash(&mut tx, &w_id, hash).await?;
|
||||
check_scopes(&authed, || format!("run:script/{path}"))?;
|
||||
if let Some(run_query_cache_ttl) = run_query.cache_ttl {
|
||||
@@ -4475,6 +4540,8 @@ pub async fn run_job_by_hash_inner(
|
||||
language,
|
||||
dedicated_worker,
|
||||
priority,
|
||||
apply_preprocessor: !run_query.skip_preprocessor.unwrap_or(false)
|
||||
&& has_preprocessor.unwrap_or(false),
|
||||
},
|
||||
PushArgs { args: &args.args, extra: args.extra },
|
||||
&label_prefix
|
||||
@@ -4609,8 +4676,8 @@ async fn get_job_update(
|
||||
"progress_perc"
|
||||
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?
|
||||
.fetch_optional(&db)
|
||||
.await?.and_then(|inner| inner)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -4789,14 +4856,19 @@ pub fn list_completed_jobs_query(
|
||||
lq: &ListCompletedQuery,
|
||||
fields: &[&str],
|
||||
join_outstanding_wait_times: bool,
|
||||
tags: Option<Vec<&str>>,
|
||||
) -> SqlBuilder {
|
||||
let sqlb = SqlBuilder::select_from("completed_job")
|
||||
let mut sqlb = SqlBuilder::select_from("completed_job")
|
||||
.fields(fields)
|
||||
.order_by("created_at", lq.order_desc.unwrap_or(true))
|
||||
.offset(offset)
|
||||
.limit(per_page)
|
||||
.clone();
|
||||
|
||||
if let Some(tags) = tags {
|
||||
sqlb.and_where_in("tag", &tags.iter().map(|x| quote(x)).collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
filter_list_completed_query(sqlb, lq, w_id, join_outstanding_wait_times)
|
||||
}
|
||||
#[derive(Deserialize, Clone)]
|
||||
@@ -4841,7 +4913,7 @@ async fn list_completed_jobs(
|
||||
Query(pagination): Query<Pagination>,
|
||||
Query(lq): Query<ListCompletedQuery>,
|
||||
) -> error::JsonResult<Vec<ListableCompletedJob>> {
|
||||
check_scopes(&authed, || format!("listjobs"))?;
|
||||
check_scopes(&authed, || format!("jobs:listjobs"))?;
|
||||
|
||||
let (per_page, offset) = paginate(pagination);
|
||||
|
||||
@@ -4883,6 +4955,7 @@ async fn list_completed_jobs(
|
||||
"'CompletedJob' as type",
|
||||
],
|
||||
false,
|
||||
get_scope_tags(&authed),
|
||||
)
|
||||
.sql()?;
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
@@ -5107,7 +5180,7 @@ async fn delete_completed_job<'a>(
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, id)): Path<(String, Uuid)>,
|
||||
) -> error::Result<Response> {
|
||||
check_scopes(&authed, || format!("deletejob"))?;
|
||||
check_scopes(&authed, || format!("jobs:deletejob"))?;
|
||||
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ mod flows;
|
||||
mod folders;
|
||||
mod granular_acls;
|
||||
mod groups;
|
||||
mod http_triggers;
|
||||
mod indexer_ee;
|
||||
mod inputs;
|
||||
mod integration;
|
||||
@@ -283,7 +284,8 @@ pub async fn run_server(
|
||||
)
|
||||
.nest("/variables", variables::workspaced_service())
|
||||
.nest("/workspaces", workspaces::workspaced_service())
|
||||
.nest("/oidc", oidc_ee::workspaced_service()),
|
||||
.nest("/oidc", oidc_ee::workspaced_service())
|
||||
.nest("/http_triggers", http_triggers::workspaced_service()),
|
||||
)
|
||||
.nest("/workspaces", workspaces::global_service())
|
||||
.nest(
|
||||
@@ -336,7 +338,7 @@ pub async fn run_server(
|
||||
)
|
||||
.nest(
|
||||
"/w/:workspace_id/capture_u",
|
||||
capture::global_service().layer(cors),
|
||||
capture::global_service().layer(cors.clone()),
|
||||
)
|
||||
.nest(
|
||||
"/auth",
|
||||
@@ -346,6 +348,10 @@ pub async fn run_server(
|
||||
"/oauth",
|
||||
oauth2_ee::global_service().layer(Extension(Arc::clone(&sp_extension))),
|
||||
)
|
||||
.nest(
|
||||
"/r",
|
||||
http_triggers::routes_global_service().layer(from_extractor::<OptAuthed>()),
|
||||
)
|
||||
.route("/version", get(git_v))
|
||||
.route("/uptodate", get(is_up_to_date))
|
||||
.route("/ee_license", get(ee_license))
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
* LICENSE-AGPL for a copy of the license.
|
||||
*/
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
users::{maybe_refresh_folders, require_owner_of_path, Tokened},
|
||||
@@ -56,6 +58,7 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/type/exists/:name", get(exists_resource_type))
|
||||
.route("/type/update/:name", post(update_resource_type))
|
||||
.route("/type/delete/:name", delete(delete_resource_type))
|
||||
.route("/file_resource_type_to_file_ext_map", get(file_resource_ext_to_resource_type))
|
||||
.route("/type/create", post(create_resource_type))
|
||||
}
|
||||
|
||||
@@ -71,6 +74,7 @@ pub struct ResourceType {
|
||||
pub description: Option<String>,
|
||||
pub created_by: Option<String>,
|
||||
pub edited_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub format_extension: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -78,6 +82,7 @@ pub struct CreateResourceType {
|
||||
pub name: String,
|
||||
pub schema: Option<serde_json::Value>,
|
||||
pub description: Option<String>,
|
||||
pub format_extension: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -913,6 +918,35 @@ async fn update_resource_value(
|
||||
Ok(format!("value of resource {} updated", path))
|
||||
}
|
||||
|
||||
async fn file_resource_ext_to_resource_type(
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
) -> JsonResult<HashMap<String, String>> {
|
||||
#[derive(Serialize, sqlx::FromRow)]
|
||||
struct LocalFileResourceExtension {
|
||||
name: String,
|
||||
format_extension: Option<String>,
|
||||
}
|
||||
|
||||
let r = sqlx::query_as!(LocalFileResourceExtension, "
|
||||
SELECT name, format_extension FROM resource_type WHERE format_extension IS NOT NULL AND (workspace_id = $1 OR workspace_id = 'admins')", w_id)
|
||||
.fetch_all(&db)
|
||||
.await?;
|
||||
|
||||
let hashmap: HashMap<String, String> = r
|
||||
.into_iter()
|
||||
.filter_map(|entry| {
|
||||
if let Some(format_extension) = entry.format_extension {
|
||||
Some((entry.name, format_extension))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Json(hashmap))
|
||||
}
|
||||
|
||||
async fn list_resource_types(
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
@@ -996,13 +1030,14 @@ async fn create_resource_type(
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO resource_type
|
||||
(workspace_id, name, schema, description, created_by, edited_at)
|
||||
VALUES ($1, $2, $3, $4, $5, now())",
|
||||
(workspace_id, name, schema, description, created_by, format_extension, edited_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, now())",
|
||||
w_id,
|
||||
resource_type.name,
|
||||
resource_type.schema,
|
||||
resource_type.description,
|
||||
authed.username
|
||||
authed.username,
|
||||
resource_type.format_extension,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
@@ -103,6 +103,8 @@ pub struct ScriptWDraft {
|
||||
pub visible_to_runner_only: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub no_main_func: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub has_preprocessor: Option<bool>,
|
||||
}
|
||||
|
||||
pub fn global_service() -> Router {
|
||||
@@ -581,6 +583,7 @@ async fn create_script_internal<'c>(
|
||||
|| ns.language == ScriptLang::Bunnative
|
||||
|| ns.language == ScriptLang::Deno
|
||||
|| ns.language == ScriptLang::Rust
|
||||
|| ns.language == ScriptLang::Ansible
|
||||
|| ns.language == ScriptLang::Php)
|
||||
{
|
||||
Some(String::new())
|
||||
@@ -612,8 +615,8 @@ async fn create_script_internal<'c>(
|
||||
content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, \
|
||||
draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, \
|
||||
dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, \
|
||||
delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase) \
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30)",
|
||||
delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor) \
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31)",
|
||||
&w_id,
|
||||
&hash.0,
|
||||
ns.path,
|
||||
@@ -643,7 +646,8 @@ async fn create_script_internal<'c>(
|
||||
ns.concurrency_key,
|
||||
ns.visible_to_runner_only,
|
||||
ns.no_main_func,
|
||||
codebase
|
||||
codebase,
|
||||
ns.has_preprocessor,
|
||||
)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
@@ -738,6 +742,7 @@ async fn create_script_internal<'c>(
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let permissioned_as = username_to_permissioned_as(&authed.username);
|
||||
if needs_lock_gen {
|
||||
let tag = if ns.dedicated_worker.is_some_and(|x| x) {
|
||||
@@ -878,7 +883,7 @@ async fn get_script_by_path_w_draft(
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let script_o = sqlx::query_as::<_, ScriptWDraft>(
|
||||
"SELECT hash, script.path, summary, description, content, language, kind, tag, schema, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, ws_error_handler_muted, draft.value as draft, dedicated_worker, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func FROM script LEFT JOIN draft ON
|
||||
"SELECT hash, script.path, summary, description, content, language, kind, tag, schema, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, ws_error_handler_muted, draft.value as draft, dedicated_worker, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, has_preprocessor FROM script LEFT JOIN draft ON
|
||||
script.path = draft.path AND script.workspace_id = draft.workspace_id AND draft.typ = 'script'
|
||||
WHERE script.path = $1 AND script.workspace_id = $2 \
|
||||
AND script.created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND \
|
||||
|
||||
@@ -24,6 +24,8 @@ use axum::{
|
||||
#[cfg(feature = "enterprise")]
|
||||
use axum::extract::Query;
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
use windmill_common::ee::{send_critical_alert, CriticalAlertKind, CriticalErrorChannel};
|
||||
use serde::Deserialize;
|
||||
use windmill_common::{
|
||||
error::{self, JsonResult, Result},
|
||||
@@ -55,7 +57,8 @@ pub fn global_service() -> Router {
|
||||
get(get_latest_key_renewal_attempt),
|
||||
)
|
||||
.route("/renew_license_key", post(renew_license_key))
|
||||
.route("/customer_portal", post(create_customer_portal_session));
|
||||
.route("/customer_portal", post(create_customer_portal_session))
|
||||
.route("/test_critical_channels", post(test_critical_channels));
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
{
|
||||
@@ -387,3 +390,27 @@ pub async fn create_customer_portal_session(
|
||||
|
||||
return Ok(url);
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn test_critical_channels(
|
||||
Extension(db): Extension<DB>,
|
||||
authed: ApiAuthed,
|
||||
Json(test_critical_channels): Json<Vec<CriticalErrorChannel>>,
|
||||
) -> Result<String> {
|
||||
require_super_admin(&db, &authed.email).await?;
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
send_critical_alert(
|
||||
"Test critical error".to_string(),
|
||||
&db,
|
||||
CriticalAlertKind::CriticalError,
|
||||
Some(test_critical_channels),
|
||||
)
|
||||
.await;
|
||||
Ok("Sent test critical error".to_string())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
pub async fn test_critical_channels() -> Result<String> {
|
||||
Ok("Critical channels require EE".to_string())
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ use windmill_audit::ActionKind;
|
||||
use windmill_common::global_settings::AUTOMATE_USERNAME_CREATION_SETTING;
|
||||
use windmill_common::users::truncate_token;
|
||||
use windmill_common::utils::{paginate, send_email};
|
||||
use windmill_common::worker::{CLOUD_HOSTED, SERVER_CONFIG};
|
||||
use windmill_common::worker::{CLOUD_HOSTED, SMTP_CONFIG};
|
||||
use windmill_common::{
|
||||
auth::{get_folders_for_user, get_groups_for_user, JWTAuthClaims, JWT_SECRET},
|
||||
db::UserDB,
|
||||
@@ -651,9 +651,12 @@ where
|
||||
{
|
||||
if let Some(authed) = cache.get_authed(workspace_id.clone(), &token).await {
|
||||
parts.extensions.insert(authed.clone());
|
||||
if authed.scopes.is_some()
|
||||
&& (path_vec.len() < 3
|
||||
|| (path_vec[4] != "jobs" && path_vec[4] != "jobs_u"))
|
||||
if authed.scopes.as_ref().is_some_and(|scopes| {
|
||||
scopes
|
||||
.iter()
|
||||
.any(|s| s.starts_with("jobs:") || s.starts_with("run:"))
|
||||
}) && (path_vec.len() < 3
|
||||
|| (path_vec[4] != "jobs" && path_vec[4] != "jobs_u"))
|
||||
{
|
||||
BRUTE_FORCE_COUNTER.increment().await;
|
||||
return Err((
|
||||
@@ -681,15 +684,30 @@ pub fn check_scopes<F>(authed: &ApiAuthed, required: F) -> error::Result<()>
|
||||
where
|
||||
F: FnOnce() -> String,
|
||||
{
|
||||
if let Some(scopes) = &authed.scopes {
|
||||
if authed.scopes.as_ref().is_some_and(|scopes| {
|
||||
scopes
|
||||
.iter()
|
||||
.any(|s| s.starts_with("jobs:") || s.starts_with("run:"))
|
||||
}) {
|
||||
let req = &required();
|
||||
if !scopes.contains(req) {
|
||||
if !authed.scopes.as_ref().unwrap().contains(req) {
|
||||
return Err(Error::BadRequest(format!("missing required scope: {req}")));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_scope_tags(authed: &ApiAuthed) -> Option<Vec<&str>> {
|
||||
authed
|
||||
.scopes
|
||||
.as_ref()?
|
||||
.iter()
|
||||
.find_map(|s| match s.split(":").collect::<Vec<_>>().as_slice() {
|
||||
["if_jobs", "filter_tags", tags] => Some(tags.split(",").collect::<Vec<_>>()),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OptAuthed(pub Option<ApiAuthed>);
|
||||
|
||||
@@ -1966,7 +1984,7 @@ pub fn send_email_if_possible(subject: &str, content: &str, to: &str) {
|
||||
}
|
||||
|
||||
pub async fn send_email_if_possible_intern(subject: &str, content: &str, to: String) -> Result<()> {
|
||||
if let Some(smtp) = SERVER_CONFIG.read().await.smtp.clone() {
|
||||
if let Some(smtp) = SMTP_CONFIG.read().await.clone() {
|
||||
send_email(subject, content, vec![to], smtp, None).await?;
|
||||
}
|
||||
return Ok(());
|
||||
|
||||
@@ -2317,6 +2317,8 @@ struct ScriptMetadata {
|
||||
pub codebase: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub concurrency_key: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub has_preprocessor: Option<bool>,
|
||||
}
|
||||
|
||||
pub fn is_none_or_false(val: &Option<bool>) -> bool {
|
||||
@@ -2575,6 +2577,7 @@ async fn tarball_workspace(
|
||||
}
|
||||
ScriptLang::Php => "php",
|
||||
ScriptLang::Rust => "rs",
|
||||
ScriptLang::Ansible => "playbook.yml",
|
||||
};
|
||||
archive
|
||||
.write_to_archive(&script.content, &format!("{}.{}", script.path, ext))
|
||||
@@ -2601,6 +2604,7 @@ async fn tarball_workspace(
|
||||
no_main_func: script.no_main_func,
|
||||
codebase: script.codebase,
|
||||
concurrency_key: script.concurrency_key,
|
||||
has_preprocessor: script.has_preprocessor,
|
||||
};
|
||||
let metadata_str = serde_json::to_string_pretty(&metadata).unwrap();
|
||||
archive
|
||||
|
||||
@@ -25,6 +25,7 @@ pub struct JWTAuthClaims {
|
||||
pub workspace_id: String,
|
||||
pub exp: usize,
|
||||
pub job_id: Option<String>,
|
||||
pub scopes: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
||||
@@ -26,7 +26,10 @@ pub async fn get_license_plan() -> LicensePlan {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum CriticalErrorChannel {}
|
||||
pub enum CriticalErrorChannel {
|
||||
Email { email: String },
|
||||
Slack { slack_channel: String },
|
||||
}
|
||||
|
||||
pub enum CriticalAlertKind {
|
||||
#[cfg(feature = "enterprise")]
|
||||
@@ -36,7 +39,13 @@ pub enum CriticalAlertKind {
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn send_critical_alert(_error_message: String, _db: &DB, _kind: CriticalAlertKind) {}
|
||||
pub async fn send_critical_alert(
|
||||
_error_message: String,
|
||||
_db: &DB,
|
||||
_kind: CriticalAlertKind,
|
||||
_channels: Option<Vec<CriticalErrorChannel>>,
|
||||
) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn schedule_key_renewal(_http_client: &reqwest::Client, _db: &crate::db::DB) -> () {
|
||||
|
||||
@@ -29,6 +29,7 @@ pub struct FlowStatus {
|
||||
pub step: i32,
|
||||
pub modules: Vec<FlowStatusModule>,
|
||||
pub failure_module: Box<FlowStatusModuleWParent>,
|
||||
pub preprocessor_module: Option<FlowStatusModule>,
|
||||
|
||||
#[serde(skip_serializing_if = "HashMap::is_empty")]
|
||||
#[serde(default)]
|
||||
@@ -355,7 +356,11 @@ impl FlowStatusModule {
|
||||
impl FlowStatus {
|
||||
pub fn new(f: &FlowValue) -> Self {
|
||||
Self {
|
||||
step: 0,
|
||||
step: if f.preprocessor_module.is_some() {
|
||||
-1
|
||||
} else {
|
||||
0
|
||||
},
|
||||
approval_conditions: None,
|
||||
modules: f
|
||||
.modules
|
||||
@@ -372,6 +377,13 @@ impl FlowStatus {
|
||||
.unwrap_or_else(|| "failure".to_string()),
|
||||
},
|
||||
}),
|
||||
preprocessor_module: if f.preprocessor_module.is_some() {
|
||||
Some(FlowStatusModule::WaitingForPriorSteps {
|
||||
id: f.preprocessor_module.as_ref().unwrap().id.clone(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
},
|
||||
cleanup_module: FlowCleanupModule { flow_jobs_to_clean: vec![] },
|
||||
retry: RetryStatus { fail_count: 0, failed_jobs: vec![] },
|
||||
restarted_from: None,
|
||||
|
||||
@@ -102,6 +102,8 @@ pub struct FlowValue {
|
||||
#[serde(default)]
|
||||
pub failure_module: Option<Box<FlowModule>>,
|
||||
#[serde(default)]
|
||||
pub preprocessor_module: Option<Box<FlowModule>>,
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "is_default")]
|
||||
pub same_worker: bool,
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ pub const EXTRA_PIP_INDEX_URL_SETTING: &str = "pip_extra_index_url";
|
||||
pub const PIP_INDEX_URL_SETTING: &str = "pip_index_url";
|
||||
pub const SCIM_TOKEN_SETTING: &str = "scim_token";
|
||||
pub const SAML_METADATA_SETTING: &str = "saml_metadata";
|
||||
pub const SMTP_SETTING: &str = "smtp_settings";
|
||||
pub const TIMEOUT_WAIT_RESULT_SETTING: &str = "timeout_wait_result";
|
||||
|
||||
pub const UNIQUE_ID_SETTING: &str = "uid";
|
||||
pub const DISABLE_STATS_SETTING: &str = "disable_stats";
|
||||
|
||||
@@ -11,6 +11,8 @@ use uuid::Uuid;
|
||||
|
||||
pub const ENTRYPOINT_OVERRIDE: &str = "_ENTRYPOINT_OVERRIDE";
|
||||
|
||||
pub const PREPROCESSOR_FAKE_ENTRYPOINT: &str = "__WM_PREPROCESSOR";
|
||||
|
||||
use crate::{
|
||||
error::{self, to_anyhow, Error},
|
||||
flow_status::{FlowStatus, RestartedFrom},
|
||||
@@ -302,6 +304,7 @@ pub enum JobPayload {
|
||||
dedicated_worker: Option<bool>,
|
||||
language: ScriptLang,
|
||||
priority: Option<i16>,
|
||||
apply_preprocessor: bool,
|
||||
},
|
||||
Code(RawCode),
|
||||
Dependencies {
|
||||
@@ -331,6 +334,7 @@ pub enum JobPayload {
|
||||
Flow {
|
||||
path: String,
|
||||
dedicated_worker: Option<bool>,
|
||||
apply_preprocessor: bool,
|
||||
},
|
||||
RestartedFlow {
|
||||
completed_job_id: Uuid,
|
||||
@@ -383,6 +387,7 @@ pub async fn script_path_to_payload<'e, E: sqlx::Executor<'e, Database = Postgre
|
||||
script_path: &str,
|
||||
db: E,
|
||||
w_id: &str,
|
||||
skip_preprocessor: Option<bool>,
|
||||
) -> error::Result<(JobPayload, Option<Tag>, Option<bool>, Option<i32>)> {
|
||||
let (job_payload, tag, delete_after_use, script_timeout) = if script_path.starts_with("hub/") {
|
||||
(
|
||||
@@ -404,6 +409,7 @@ pub async fn script_path_to_payload<'e, E: sqlx::Executor<'e, Database = Postgre
|
||||
priority,
|
||||
delete_after_use,
|
||||
script_timeout,
|
||||
has_preprocessor,
|
||||
) = get_latest_deployed_hash_for_path(db, w_id, script_path).await?;
|
||||
(
|
||||
JobPayload::ScriptHash {
|
||||
@@ -416,6 +422,8 @@ pub async fn script_path_to_payload<'e, E: sqlx::Executor<'e, Database = Postgre
|
||||
language,
|
||||
dedicated_worker,
|
||||
priority,
|
||||
apply_preprocessor: !skip_preprocessor.unwrap_or(false)
|
||||
&& has_preprocessor.unwrap_or(false),
|
||||
},
|
||||
tag,
|
||||
delete_after_use,
|
||||
@@ -473,7 +481,7 @@ pub async fn get_payload_tag_from_prefixed_path<'e, E: sqlx::Executor<'e, Databa
|
||||
w_id: &str,
|
||||
) -> Result<(JobPayload, Option<String>), Error> {
|
||||
let (payload, tag, _, _) = if path.starts_with("script/") {
|
||||
script_path_to_payload(path.strip_prefix("script/").unwrap(), db, w_id).await?
|
||||
script_path_to_payload(path.strip_prefix("script/").unwrap(), db, w_id, Some(true)).await?
|
||||
} else if path.starts_with("flow/") {
|
||||
let path = path.strip_prefix("flow/").unwrap().to_string();
|
||||
let r = sqlx::query!(
|
||||
@@ -486,7 +494,12 @@ pub async fn get_payload_tag_from_prefixed_path<'e, E: sqlx::Executor<'e, Databa
|
||||
let (tag, dedicated_worker) = r
|
||||
.map(|x| (x.tag, x.dedicated_worker))
|
||||
.unwrap_or_else(|| (None, None));
|
||||
(JobPayload::Flow { path, dedicated_worker }, tag, None, None)
|
||||
(
|
||||
JobPayload::Flow { path, dedicated_worker, apply_preprocessor: false },
|
||||
tag,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
} else {
|
||||
return Err(Error::BadRequest(format!(
|
||||
"path must start with script/ or flow/ (got {})",
|
||||
|
||||
@@ -271,9 +271,10 @@ pub async fn get_latest_deployed_hash_for_path<'e, E: sqlx::Executor<'e, Databas
|
||||
Option<i16>,
|
||||
Option<bool>,
|
||||
Option<i32>,
|
||||
Option<bool>,
|
||||
)> {
|
||||
let r_o = sqlx::query!(
|
||||
"select hash, tag, concurrency_key, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority, delete_after_use, timeout from script where path = $1 AND workspace_id = $2 AND
|
||||
"select hash, tag, concurrency_key, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority, delete_after_use, timeout, has_preprocessor 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 AND
|
||||
deleted = false AND lock IS not NULL AND lock_error_logs IS NULL)",
|
||||
script_path,
|
||||
@@ -296,6 +297,7 @@ pub async fn get_latest_deployed_hash_for_path<'e, E: sqlx::Executor<'e, Databas
|
||||
script.priority,
|
||||
script.delete_after_use,
|
||||
script.timeout,
|
||||
script.has_preprocessor,
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ pub enum ScriptLang {
|
||||
Mssql,
|
||||
Php,
|
||||
Rust,
|
||||
Ansible,
|
||||
}
|
||||
|
||||
impl ScriptLang {
|
||||
@@ -64,7 +65,8 @@ impl ScriptLang {
|
||||
ScriptLang::Mssql => "mssql",
|
||||
ScriptLang::Graphql => "graphql",
|
||||
ScriptLang::Php => "php",
|
||||
ScriptLang::Rust => "rust"
|
||||
ScriptLang::Rust => "rust",
|
||||
ScriptLang::Ansible => "ansible",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,6 +193,8 @@ pub struct Script {
|
||||
pub no_main_func: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub codebase: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub has_preprocessor: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, sqlx::FromRow)]
|
||||
@@ -287,6 +291,7 @@ pub struct NewScript {
|
||||
pub visible_to_runner_only: Option<bool>,
|
||||
pub no_main_func: Option<bool>,
|
||||
pub codebase: Option<String>,
|
||||
pub has_preprocessor: Option<bool>,
|
||||
}
|
||||
|
||||
fn lock_deserialize<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
|
||||
|
||||
@@ -13,22 +13,20 @@ pub struct Smtp {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq)]
|
||||
pub struct ServerConfigOpt {
|
||||
pub struct SmtpConfigOpt {
|
||||
pub smtp_host: Option<String>,
|
||||
pub smtp_username: Option<String>,
|
||||
pub smtp_password: Option<String>,
|
||||
pub smtp_port: Option<u16>,
|
||||
pub smtp_from: Option<String>,
|
||||
pub smtp_tls_implicit: Option<bool>,
|
||||
pub timeout_wait_result: Option<u64>,
|
||||
}
|
||||
|
||||
pub async fn load_server_config(db: &DB) -> error::Result<ServerConfig> {
|
||||
let config: ServerConfigOpt =
|
||||
sqlx::query_scalar!("SELECT config FROM config WHERE name = 'server'",)
|
||||
pub async fn load_smtp_config(db: &DB) -> error::Result<Option<Smtp>> {
|
||||
let config: SmtpConfigOpt =
|
||||
sqlx::query_scalar!("SELECT value FROM global_settings WHERE name = 'smtp_settings'",)
|
||||
.fetch_optional(db)
|
||||
.await?
|
||||
.flatten()
|
||||
.map(|x| serde_json::from_value(x).ok())
|
||||
.flatten()
|
||||
.unwrap_or_default();
|
||||
@@ -77,20 +75,10 @@ pub async fn load_server_config(db: &DB) -> error::Result<ServerConfig> {
|
||||
tracing::warn!("SMTP not configured");
|
||||
}
|
||||
|
||||
Ok(ServerConfig {
|
||||
smtp,
|
||||
timeout_wait_result: config
|
||||
.timeout_wait_result
|
||||
.ok_or(
|
||||
std::env::var("TIMEOUT_WAIT_RESULT")
|
||||
.ok()
|
||||
.and_then(|x| x.parse::<u64>().ok()),
|
||||
)
|
||||
.unwrap_or(600),
|
||||
})
|
||||
Ok(smtp)
|
||||
}
|
||||
|
||||
impl Default for ServerConfigOpt {
|
||||
impl Default for SmtpConfigOpt {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
smtp_from: None,
|
||||
@@ -99,13 +87,6 @@ impl Default for ServerConfigOpt {
|
||||
smtp_port: None,
|
||||
smtp_tls_implicit: None,
|
||||
smtp_username: None,
|
||||
timeout_wait_result: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct ServerConfig {
|
||||
pub smtp: Option<Smtp>,
|
||||
pub timeout_wait_result: u64,
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ pub async fn report_critical_error(error_message: String, _db: DB) -> () {
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
send_critical_alert(error_message, &_db, CriticalAlertKind::CriticalError).await;
|
||||
send_critical_alert(error_message, &_db, CriticalAlertKind::CriticalError, None).await;
|
||||
}
|
||||
|
||||
pub async fn report_recovered_critical_error(message: String, _db: DB) -> () {
|
||||
@@ -304,5 +304,11 @@ pub async fn report_recovered_critical_error(message: String, _db: DB) -> () {
|
||||
tracing::error!("Failed to save critical error to database: {}", err);
|
||||
}
|
||||
#[cfg(feature = "enterprise")]
|
||||
send_critical_alert(message, &_db, CriticalAlertKind::RecoveredCriticalError).await;
|
||||
send_critical_alert(
|
||||
message,
|
||||
&_db,
|
||||
CriticalAlertKind::RecoveredCriticalError,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@ use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
fs::File,
|
||||
io::Write,
|
||||
path::Path,
|
||||
path::{Component, Path, PathBuf},
|
||||
str::FromStr,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::{error, global_settings::CUSTOM_TAGS_SETTING, server::ServerConfig, DB};
|
||||
use crate::{error, global_settings::CUSTOM_TAGS_SETTING, server::Smtp, DB};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref WORKER_GROUP: String = std::env::var("WORKER_GROUP").unwrap_or_else(|_| "default".to_string());
|
||||
@@ -41,6 +41,7 @@ lazy_static::lazy_static! {
|
||||
"graphql".to_string(),
|
||||
"php".to_string(),
|
||||
"rust".to_string(),
|
||||
"ansible".to_string(),
|
||||
"dependency".to_string(),
|
||||
"flow".to_string(),
|
||||
"other".to_string()
|
||||
@@ -65,8 +66,7 @@ lazy_static::lazy_static! {
|
||||
pub static ref WORKER_SUSPENDED_PULL_QUERY: Arc<RwLock<String>> = Arc::new(RwLock::new("".to_string()));
|
||||
|
||||
|
||||
pub static ref SERVER_CONFIG: Arc<RwLock<ServerConfig>> = Arc::new(RwLock::new(ServerConfig { smtp: Default::default(), timeout_wait_result: 20 }));
|
||||
|
||||
pub static ref SMTP_CONFIG: Arc<RwLock<Option<Smtp>>> = Arc::new(RwLock::new(None));
|
||||
|
||||
|
||||
pub static ref CLOUD_HOSTED: bool = std::env::var("CLOUD_HOSTED").is_ok();
|
||||
@@ -167,6 +167,67 @@ pub fn write_file(dir: &str, path: &str, content: &str) -> error::Result<File> {
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
/// from : https://github.com/rust-lang/cargo/blob/fede83ccf973457de319ba6fa0e36ead454d2e20/src/cargo/util/paths.rs#L61
|
||||
fn normalize_path(path: &Path) -> PathBuf {
|
||||
let mut components = path.components().peekable();
|
||||
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
|
||||
components.next();
|
||||
PathBuf::from(c.as_os_str())
|
||||
} else {
|
||||
PathBuf::new()
|
||||
};
|
||||
|
||||
for component in components {
|
||||
match component {
|
||||
Component::Prefix(..) => unreachable!(),
|
||||
Component::RootDir => {
|
||||
ret.push(component.as_os_str());
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
ret.pop();
|
||||
}
|
||||
Component::Normal(c) => {
|
||||
ret.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
pub fn write_file_at_user_defined_location(
|
||||
job_dir: &str,
|
||||
user_defined_path: &str,
|
||||
content: &str,
|
||||
) -> error::Result<PathBuf> {
|
||||
let job_dir = Path::new(job_dir);
|
||||
let user_path = PathBuf::from(user_defined_path);
|
||||
|
||||
let full_path = job_dir.join(&user_path);
|
||||
|
||||
// let normalized_job_dir = std::fs::canonicalize(job_dir)?;
|
||||
// let normalized_full_path = std::fs::canonicalize(&full_path)?;
|
||||
let normalized_job_dir = normalize_path(job_dir);
|
||||
let normalized_full_path = normalize_path(&full_path);
|
||||
|
||||
if !normalized_full_path.starts_with(&normalized_job_dir) {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::PermissionDenied,
|
||||
"Path is outside the allowed job directory.",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let full_path = normalized_full_path.as_path();
|
||||
if let Some(parent_dir) = full_path.parent() {
|
||||
std::fs::create_dir_all(parent_dir)?;
|
||||
}
|
||||
|
||||
let mut file = File::create(full_path)?;
|
||||
file.write_all(content.as_bytes())?;
|
||||
file.flush()?;
|
||||
Ok(normalized_full_path)
|
||||
}
|
||||
|
||||
pub async fn reload_custom_tags_setting(db: &DB) -> error::Result<()> {
|
||||
let q = sqlx::query!(
|
||||
"SELECT value FROM global_settings WHERE name = $1",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user