Compare commits
77 Commits
suspend-ne
...
v1.675.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31df861ee2 | ||
|
|
e605bc4e07 | ||
|
|
7bf6ac2b69 | ||
|
|
01e39d9cd1 | ||
|
|
02d0ee9198 | ||
|
|
dd39c110a8 | ||
|
|
342defecd2 | ||
|
|
2b865c0694 | ||
|
|
7c3bd67639 | ||
|
|
ff8e39c69b | ||
|
|
f394e674f2 | ||
|
|
6bb5cac0c4 | ||
|
|
3e9a9f44bb | ||
|
|
aff95c33b2 | ||
|
|
653356011c | ||
|
|
fda68a72e5 | ||
|
|
ef74a1bb4d | ||
|
|
1a0f580f3d | ||
|
|
6d58d1a74d | ||
|
|
ce290f68db | ||
|
|
dcd615fdc3 | ||
|
|
18eb6e0df7 | ||
|
|
adc9fe722d | ||
|
|
0aea49f960 | ||
|
|
ba214709b9 | ||
|
|
bffa61e33f | ||
|
|
27ca417201 | ||
|
|
c4c9ef5fd7 | ||
|
|
b960598431 | ||
|
|
f234df97ec | ||
|
|
a3073ad824 | ||
|
|
ceaa613522 | ||
|
|
0317d5891c | ||
|
|
7485a5db04 | ||
|
|
0cfa462c37 | ||
|
|
6656b46f10 | ||
|
|
5b7fa63bf1 | ||
|
|
cdf3c29664 | ||
|
|
81ab777cbb | ||
|
|
61a867f086 | ||
|
|
8581a3300d | ||
|
|
ff5fa9f64f | ||
|
|
55e8a5cff1 | ||
|
|
1a39bd538d | ||
|
|
1049c1026b | ||
|
|
619ebb65ce | ||
|
|
d2d6810db9 | ||
|
|
39af1b75af | ||
|
|
d569e9e29c | ||
|
|
381011a4a8 | ||
|
|
f0437eba19 | ||
|
|
d8edaad99c | ||
|
|
7fd0bf974d | ||
|
|
6a5cfbc159 | ||
|
|
8c3c97f7a6 | ||
|
|
c3a1c26be1 | ||
|
|
c87a6a0f2c | ||
|
|
350ffdce29 | ||
|
|
28c073056c | ||
|
|
c86846ac19 | ||
|
|
7ab0ea581d | ||
|
|
bcce627387 | ||
|
|
175af8032f | ||
|
|
1784bed4ac | ||
|
|
a46aa641f9 | ||
|
|
7069202190 | ||
|
|
df7a8eebcf | ||
|
|
2862c1cf56 | ||
|
|
d67223de9b | ||
|
|
da8886be85 | ||
|
|
040a199685 | ||
|
|
6c3c971af5 | ||
|
|
852c59efbb | ||
|
|
89d1acda24 | ||
|
|
12ea7e7423 | ||
|
|
375fb66abe | ||
|
|
52a04d210f |
23
.github/codex/pr-review.prompt.md
vendored
Normal file
23
.github/codex/pr-review.prompt.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
You are reviewing a GitHub pull request for this repository.
|
||||
|
||||
Review policy:
|
||||
- Read `CLAUDE.md` before reviewing code.
|
||||
- Only report issues you are confident are real and introduced by this pull request.
|
||||
- Focus on bugs, security problems, and clear `CLAUDE.md` violations.
|
||||
- Do not report style nits, speculative concerns, pre-existing issues, or problems that a normal linter/typechecker would obviously catch.
|
||||
- Keep the review high signal. If there is no clear issue, return no findings.
|
||||
|
||||
Repository context:
|
||||
- Read `./.github/codex/pr-review-context.md` for the PR metadata and the exact diff commands to use.
|
||||
- Review only the changes introduced by this PR.
|
||||
- Read additional files only when the diff is not enough to validate a finding.
|
||||
- Do not modify any files.
|
||||
|
||||
Output requirements:
|
||||
- Return a GitHub PR comment in markdown, not JSON.
|
||||
- Start with `## Codex Review`.
|
||||
- Give a short overall summary first.
|
||||
- If you found high-signal issues, list them in a short numbered list with file paths and line numbers when you know them confidently.
|
||||
- If you found no high-signal issues, say that explicitly.
|
||||
- End with a `### Reproduction instructions` section containing a short descriptive paragraph for a tester explaining how to navigate the app to observe the change. Do not make it a numbered list. If the diff is not enough to infer this safely, say that plainly.
|
||||
- Prefer at most 10 findings.
|
||||
145
.github/workflows/codex-pr-review.yml
vendored
Normal file
145
.github/workflows/codex-pr-review.yml
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
name: Codex Auto Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [ready_for_review, opened]
|
||||
|
||||
concurrency:
|
||||
group: codex-review-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
codex-review:
|
||||
runs-on: ubicloud-standard-2
|
||||
timeout-minutes: 30
|
||||
if: github.event.pull_request.draft == false && github.event.pull_request.head.repo.fork == false
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- name: Check Codex configuration
|
||||
id: codex_config
|
||||
env:
|
||||
CODEX_AUTH_JSON: ${{ secrets.CODEX_AUTH_JSON }}
|
||||
run: |
|
||||
if [ -n "$CODEX_AUTH_JSON" ]; then
|
||||
echo "enabled=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "enabled=false" >> "$GITHUB_OUTPUT"
|
||||
echo "CODEX_AUTH_JSON is not configured; skipping Codex review."
|
||||
fi
|
||||
|
||||
- name: Checkout repository
|
||||
if: steps.codex_config.outputs.enabled == 'true'
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Set up Node.js
|
||||
if: steps.codex_config.outputs.enabled == 'true'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Install Codex CLI
|
||||
if: steps.codex_config.outputs.enabled == 'true'
|
||||
run: npm install --global @openai/codex@0.117.0
|
||||
|
||||
- name: Configure file-backed Codex auth
|
||||
if: steps.codex_config.outputs.enabled == 'true'
|
||||
env:
|
||||
CODEX_AUTH_JSON: ${{ secrets.CODEX_AUTH_JSON }}
|
||||
run: |
|
||||
CODEX_HOME="$HOME/.codex"
|
||||
echo "CODEX_HOME=$CODEX_HOME" >> "$GITHUB_ENV"
|
||||
mkdir -p "$CODEX_HOME"
|
||||
chmod 700 "$CODEX_HOME"
|
||||
cat > "$CODEX_HOME/config.toml" <<'EOF'
|
||||
cli_auth_credentials_store = "file"
|
||||
EOF
|
||||
printf '%s' "$CODEX_AUTH_JSON" > "$CODEX_HOME/auth.json"
|
||||
chmod 600 "$CODEX_HOME/auth.json"
|
||||
node -e 'JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"))' "$CODEX_HOME/auth.json"
|
||||
|
||||
- name: Pre-fetch base and head refs for the PR
|
||||
if: steps.codex_config.outputs.enabled == 'true'
|
||||
env:
|
||||
PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: |
|
||||
git fetch --no-tags origin \
|
||||
"$PR_BASE_REF" \
|
||||
"+refs/pull/$PR_NUMBER/head"
|
||||
|
||||
- name: Write Codex review context
|
||||
if: steps.codex_config.outputs.enabled == 'true'
|
||||
env:
|
||||
PR_REPOSITORY: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_BODY: ${{ github.event.pull_request.body || '' }}
|
||||
run: |
|
||||
mkdir -p .github/codex
|
||||
node <<'NODE'
|
||||
const fs = require('fs');
|
||||
const lines = [
|
||||
`Repository: ${process.env.PR_REPOSITORY}`,
|
||||
`PR number: ${process.env.PR_NUMBER}`,
|
||||
`Base SHA: ${process.env.PR_BASE_SHA}`,
|
||||
`Head SHA: ${process.env.PR_HEAD_SHA}`,
|
||||
'',
|
||||
'PR title:',
|
||||
process.env.PR_TITLE || '(empty)',
|
||||
'',
|
||||
'PR body:',
|
||||
process.env.PR_BODY || '(empty)',
|
||||
'',
|
||||
'Changed commits command:',
|
||||
`git log --oneline ${process.env.PR_BASE_SHA}...${process.env.PR_HEAD_SHA}`,
|
||||
'',
|
||||
'Changed files command:',
|
||||
`git diff --stat ${process.env.PR_BASE_SHA}...${process.env.PR_HEAD_SHA}`,
|
||||
'',
|
||||
'Full review diff command:',
|
||||
`git diff --unified=0 ${process.env.PR_BASE_SHA}...${process.env.PR_HEAD_SHA}`
|
||||
];
|
||||
fs.writeFileSync('.github/codex/pr-review-context.md', `${lines.join('\n')}\n`);
|
||||
NODE
|
||||
|
||||
- name: Run Codex review
|
||||
if: steps.codex_config.outputs.enabled == 'true'
|
||||
run: |
|
||||
codex exec \
|
||||
-C "$GITHUB_WORKSPACE" \
|
||||
-m gpt-5.4 \
|
||||
-c 'model_reasoning_effort="xhigh"' \
|
||||
-s read-only \
|
||||
-o codex-final-message.md \
|
||||
- < .github/codex/pr-review.prompt.md
|
||||
|
||||
- name: Post Codex review comment
|
||||
if: steps.codex_config.outputs.enabled == 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = `${process.env.GITHUB_WORKSPACE}/codex-final-message.md`;
|
||||
if (!fs.existsSync(path)) {
|
||||
core.info('Codex did not produce a final message; skipping PR comment.');
|
||||
return;
|
||||
}
|
||||
const body = fs.readFileSync(path, 'utf8').trim();
|
||||
if (!body) {
|
||||
core.info('Codex final message was empty; skipping PR comment.');
|
||||
return;
|
||||
}
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
body,
|
||||
});
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -29,3 +29,4 @@ frontend/.svelte-kit
|
||||
backend/chrome_profiler.json
|
||||
.fast-check/
|
||||
__pycache__/
|
||||
.playwright-mcp/
|
||||
|
||||
@@ -43,7 +43,7 @@ profiles:
|
||||
- Pane 0: this pane (claude agent)
|
||||
- Pane 1: backend (cargo watch -x run)
|
||||
- Pane 2: frontend (npm run dev)
|
||||
To check logs, use: \`tmux capture-pane -t .1 -p -S -50\` (backend) or \`tmux capture-pane -t .2 -p -S -50\` (frontend).
|
||||
To check logs, use: \`tmux capture-pane -t $(tmux display-message -t "$TMUX_PANE" -p '#{session_name}:#{window_name}').1 -p -S -50\` (backend) or \`tmux capture-pane -t $(tmux display-message -t "$TMUX_PANE" -p '#{session_name}:#{window_name}').2 -p -S -50\` (frontend).
|
||||
For this window specifically, backend is running on: ${BACKEND_PORT} and frontend is running on: ${FRONTEND_PORT}.
|
||||
To connect to the database, use this connection string: ${DATABASE_URL}
|
||||
Because we are running backend with cargo watch, to verify your changes, just check the logs in the backend pane. No need for cargo check.
|
||||
@@ -72,7 +72,7 @@ profiles:
|
||||
Pane layout (current window):
|
||||
- Pane 0: this pane (claude agent)
|
||||
- Pane 1: frontend (npm run dev)
|
||||
To check logs, use: \`tmux capture-pane -t .1 -p -S -50\` (frontend).
|
||||
To check logs, use: \`tmux capture-pane -t $(tmux display-message -t "$TMUX_PANE" -p '#{session_name}:#{window_name}').1 -p -S -50\` (frontend).
|
||||
On this window specifically, frontend is running on: ${FRONTEND_PORT}.
|
||||
To connect to the database, use this connection string: ${DATABASE_URL}
|
||||
Because we are running frontend with npm run dev, to verify your changes, just check the logs in the frontend pane. No need for npm run build.
|
||||
|
||||
132
CHANGELOG.md
132
CHANGELOG.md
@@ -1,5 +1,137 @@
|
||||
# Changelog
|
||||
|
||||
## [1.675.0](https://github.com/windmill-labs/windmill/compare/v1.674.2...v1.675.0) (2026-04-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add object storage usage view and manual log cleanup ([#8724](https://github.com/windmill-labs/windmill/issues/8724)) ([02d0ee9](https://github.com/windmill-labs/windmill/commit/02d0ee919880823a33b112bcaf626a8933e1f715))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add admin check to count_completed_jobs_detail and document query builder SQL safety ([#8722](https://github.com/windmill-labs/windmill/issues/8722)) ([dd39c11](https://github.com/windmill-labs/windmill/commit/dd39c110a8468bf31d42428fc978cd302426fa86))
|
||||
* allow private AI base URLs in ai_proxy integration test ([#8715](https://github.com/windmill-labs/windmill/issues/8715)) ([2b865c0](https://github.com/windmill-labs/windmill/commit/2b865c0694d79ce6477e5f14a077b73837007500))
|
||||
* enrich OTEL spans with job_kind, trigger_kind, trigger, created_by, and script_hash ([#8718](https://github.com/windmill-labs/windmill/issues/8718)) ([7bf6ac2](https://github.com/windmill-labs/windmill/commit/7bf6ac2b694fc829327248ff2480c20c97e03e48))
|
||||
* split DB health endpoint and add slow query controls ([#8725](https://github.com/windmill-labs/windmill/issues/8725)) ([01e39d9](https://github.com/windmill-labs/windmill/commit/01e39d9cd1b841d085bcc28a578654a5486cf76e))
|
||||
|
||||
## [1.674.2](https://github.com/windmill-labs/windmill/compare/v1.674.1...v1.674.2) (2026-04-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* enforce RLS on $var: resolution in AI proxy (GHSA-jwg4-v3cj-rvfm) ([#8713](https://github.com/windmill-labs/windmill/issues/8713)) ([ff8e39c](https://github.com/windmill-labs/windmill/commit/ff8e39c69b1438defcaabd9d4906e7adafa7010c))
|
||||
* SSRF via X-Resource-Path header in AI proxy endpoint ([#8712](https://github.com/windmill-labs/windmill/issues/8712)) ([f394e67](https://github.com/windmill-labs/windmill/commit/f394e674f22af13bb77915f33aa1e8de402b6fe1))
|
||||
|
||||
## [1.674.1](https://github.com/windmill-labs/windmill/compare/v1.674.0...v1.674.1) (2026-04-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* create pg connection for cloud-hosted jobs instead of panicking ([#8710](https://github.com/windmill-labs/windmill/issues/8710)) ([aff95c3](https://github.com/windmill-labs/windmill/commit/aff95c33b2fd4c248dfaf595b8d18a6dbc50f0e6))
|
||||
|
||||
## [1.674.0](https://github.com/windmill-labs/windmill/compare/v1.673.0...v1.674.0) (2026-04-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add application-level heartbeat support for websocket triggers ([#8686](https://github.com/windmill-labs/windmill/issues/8686)) ([5b7fa63](https://github.com/windmill-labs/windmill/commit/5b7fa63bf1800313e9b82465b8a4399a48634371))
|
||||
* add Azure Key Vault as secret storage backend ([#8704](https://github.com/windmill-labs/windmill/issues/8704)) ([dcd615f](https://github.com/windmill-labs/windmill/commit/dcd615fdc3c66ec2a8e39c01f8142a7e7c82f534))
|
||||
* add http/protobuf support for OTEL exporters ([#8702](https://github.com/windmill-labs/windmill/issues/8702)) ([0aea49f](https://github.com/windmill-labs/windmill/commit/0aea49f9607d5cbb5bcfa3068a179c9b7bf9afd6))
|
||||
* add optional labels to scripts, flows, apps, schedules, triggers ([#8609](https://github.com/windmill-labs/windmill/issues/8609)) ([c4c9ef5](https://github.com/windmill-labs/windmill/commit/c4c9ef5fd7b41052b08ee941725434e8ca4ac970))
|
||||
* add powershell common parameters support ([#8683](https://github.com/windmill-labs/windmill/issues/8683)) ([0317d58](https://github.com/windmill-labs/windmill/commit/0317d5891cfcfbde7b04795c034c088e933ee3d0))
|
||||
* sql.raw in Typescript client ([#8706](https://github.com/windmill-labs/windmill/issues/8706)) ([ce290f6](https://github.com/windmill-labs/windmill/commit/ce290f68db866c07b30c97c2c0b3e39fee0a26d8))
|
||||
* Support .ducklake() and .datatable() in agent workers ([#8697](https://github.com/windmill-labs/windmill/issues/8697)) ([fda68a7](https://github.com/windmill-labs/windmill/commit/fda68a72e5dfcded2350d1ff33ca4c695ab337b7))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add secretKeyRef support for jwt_secret and rsa_keys ([#8698](https://github.com/windmill-labs/windmill/issues/8698)) ([ba21470](https://github.com/windmill-labs/windmill/commit/ba214709b94f9467738e66b016331e97ac7d5d10))
|
||||
* align script push metadata warning with generated locks ([#8690](https://github.com/windmill-labs/windmill/issues/8690)) ([6656b46](https://github.com/windmill-labs/windmill/commit/6656b46f10408e1c15961a72cde4c13b5c5b3923))
|
||||
* debounce S3 proxy logs ([#8694](https://github.com/windmill-labs/windmill/issues/8694)) ([a3073ad](https://github.com/windmill-labs/windmill/commit/a3073ad8244efd9043e27f6731f7b53dbda662c1))
|
||||
* dedicated worker dispatch, cross-workspace deps, UI improvements ([#8689](https://github.com/windmill-labs/windmill/issues/8689)) ([bffa61e](https://github.com/windmill-labs/windmill/commit/bffa61e33f2305bbeb79a2c91989a47baa7dff31))
|
||||
* gate relock_skip tests on private feature and update ee-repo-ref ([#8703](https://github.com/windmill-labs/windmill/issues/8703)) ([adc9fe7](https://github.com/windmill-labs/windmill/commit/adc9fe722d8511a5914d81faac40af757e7f5e3f))
|
||||
* hide deprecated cli metadata commands ([#8699](https://github.com/windmill-labs/windmill/issues/8699)) ([b960598](https://github.com/windmill-labs/windmill/commit/b96059843168c072f24072f93fecd80431e5d4cf))
|
||||
* optimize S3 proxy performance ([#8685](https://github.com/windmill-labs/windmill/issues/8685)) ([0cfa462](https://github.com/windmill-labs/windmill/commit/0cfa462c379e887fdb5ad5e3bbff7798648d4e91))
|
||||
* pipeline DISCARD ALL with first query on cached pg connections ([#8707](https://github.com/windmill-labs/windmill/issues/8707)) ([6d58d1a](https://github.com/windmill-labs/windmill/commit/6d58d1a74d1e69b163210a795502a7b3931001b5))
|
||||
* resolve schedule update deadlock ([#8701](https://github.com/windmill-labs/windmill/issues/8701)) ([27ca417](https://github.com/windmill-labs/windmill/commit/27ca417201c99cf6fe0ae5b52a63c0395033e196))
|
||||
* support raw app deployment history ([#8657](https://github.com/windmill-labs/windmill/issues/8657)) ([f234df9](https://github.com/windmill-labs/windmill/commit/f234df97ec3cdc480ee9d403370a3512496b024b))
|
||||
* use pre-aggregated stats for telemetry job usage queries ([#8688](https://github.com/windmill-labs/windmill/issues/8688)) ([cdf3c29](https://github.com/windmill-labs/windmill/commit/cdf3c29664e4142c0f4487c07e585d1af3f97f91))
|
||||
|
||||
## [1.673.0](https://github.com/windmill-labs/windmill/compare/v1.672.0...v1.673.0) (2026-04-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add endpoint to restart workers in a worker group ([#8659](https://github.com/windmill-labs/windmill/issues/8659)) ([f0437eb](https://github.com/windmill-labs/windmill/commit/f0437eba1925a9aa4c430008027d637a0c89ee39))
|
||||
* add Entra ID (Azure Workload Identity) database auth ([#8526](https://github.com/windmill-labs/windmill/issues/8526)) ([6a5cfbc](https://github.com/windmill-labs/windmill/commit/6a5cfbc159a0ad7925fd7ce5eefc8eaa21bbb70b))
|
||||
* add LIMIT_WINDOWS_TO_1CU env var for Windows worker memory limits ([#8681](https://github.com/windmill-labs/windmill/issues/8681)) ([d2d6810](https://github.com/windmill-labs/windmill/commit/d2d6810db954114f3333853bd3476cb8fc735f92))
|
||||
* restore bun for dedicated workers, fix dispatch & serialization, cross-workspace deps ([#8645](https://github.com/windmill-labs/windmill/issues/8645)) ([619ebb6](https://github.com/windmill-labs/windmill/commit/619ebb65ce8dce8264add31c3147919802a8286a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add HMAC signature verification to Slack interactive callback endpoint ([#8611](https://github.com/windmill-labs/windmill/issues/8611)) ([55e8a5c](https://github.com/windmill-labs/windmill/commit/55e8a5cff1f185b1dbd332d37b877972efa1ed7d))
|
||||
* correct raw app flow inputs ([#8667](https://github.com/windmill-labs/windmill/issues/8667)) ([28c0730](https://github.com/windmill-labs/windmill/commit/28c073056c65d4ed1600e39679497e5af964347f))
|
||||
* pass selected language to AI agent when generating flow scripts ([#8680](https://github.com/windmill-labs/windmill/issues/8680)) ([381011a](https://github.com/windmill-labs/windmill/commit/381011a4a8e48454e9c146c64db502293e646b99))
|
||||
* poll for preview results to avoid undici headers timeout ([#8682](https://github.com/windmill-labs/windmill/issues/8682)) ([ff5fa9f](https://github.com/windmill-labs/windmill/commit/ff5fa9f64fe4aaf33e06b20f02373894b5df0f95))
|
||||
* pre-fix trigger edited_by for superadmins not in workspace ([#8669](https://github.com/windmill-labs/windmill/issues/8669)) ([350ffdc](https://github.com/windmill-labs/windmill/commit/350ffdce297ba5b84f9dd247eede6da0c6b0956c))
|
||||
* resolve race condition where flow sync push reverts to stale version ([#8673](https://github.com/windmill-labs/windmill/issues/8673)) ([d569e9e](https://github.com/windmill-labs/windmill/commit/d569e9e29c588243a90b1cd25f866efb0d178640))
|
||||
* respect disabled fields in JSON input mode ([#8663](https://github.com/windmill-labs/windmill/issues/8663)) ([7fd0bf9](https://github.com/windmill-labs/windmill/commit/7fd0bf974d2ba2644bb01dd5e9ddc84749e166f5))
|
||||
* Run typed pg queries in a single protocol conversation ([#8679](https://github.com/windmill-labs/windmill/issues/8679)) ([8581a33](https://github.com/windmill-labs/windmill/commit/8581a3300d056040b7e3ab77d629c74f034c9c97))
|
||||
* sanitize MCP tool schemas for JSON Schema draft 2020-12 compliance ([#8666](https://github.com/windmill-labs/windmill/issues/8666)) ([8c3c97f](https://github.com/windmill-labs/windmill/commit/8c3c97f7a670d47019cc666219f8187f48499672))
|
||||
* skip generate-metadata confirmation prompt in non-interactive CI ([#8678](https://github.com/windmill-labs/windmill/issues/8678)) ([39af1b7](https://github.com/windmill-labs/windmill/commit/39af1b75afc8458f85dec4fe51dfaed3d0cb000d))
|
||||
* strip f/ prefix from folder paths when deploying from workspace forks ([#8662](https://github.com/windmill-labs/windmill/issues/8662)) ([7ab0ea5](https://github.com/windmill-labs/windmill/commit/7ab0ea581d349fbfdb56d22cf9903a90efa045bb))
|
||||
* support branch-specific folder.meta.yaml in missing-meta check ([#8661](https://github.com/windmill-labs/windmill/issues/8661)) ([c87a6a0](https://github.com/windmill-labs/windmill/commit/c87a6a0f2c1346bf5e21f128d32d89bdca039243))
|
||||
* validate rd redirect on login with same rules as logout ([#8655](https://github.com/windmill-labs/windmill/issues/8655)) ([bcce627](https://github.com/windmill-labs/windmill/commit/bcce62738791a4e9b9f4dbc64731eef163230172))
|
||||
|
||||
## [1.672.0](https://github.com/windmill-labs/windmill/compare/v1.671.0...v1.672.0) (2026-04-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add R language support ([#8263](https://github.com/windmill-labs/windmill/issues/8263)) ([a46aa64](https://github.com/windmill-labs/windmill/commit/a46aa641f9d72809c52a0eb11a877a0f2d587c32))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* approval page freeze, stale state, and missing approval link ([#8653](https://github.com/windmill-labs/windmill/issues/8653)) ([7069202](https://github.com/windmill-labs/windmill/commit/70692021909443b86ed61fa621fe49f28742fb54))
|
||||
|
||||
## [1.671.0](https://github.com/windmill-labs/windmill/compare/v1.670.0...v1.671.0) (2026-03-31)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add configurable preview job tag override in default tags settings ([#8649](https://github.com/windmill-labs/windmill/issues/8649)) ([da8886b](https://github.com/windmill-labs/windmill/commit/da8886be8575dd925b6d24c55ab379bc6984c5f8))
|
||||
* improve CLI flow log streaming and job inspection ([#8644](https://github.com/windmill-labs/windmill/issues/8644)) ([6c3c971](https://github.com/windmill-labs/windmill/commit/6c3c971af5aa1362632ee0deeddf91b8bc47c853))
|
||||
* support hub flows in raw app runnables ([#8627](https://github.com/windmill-labs/windmill/issues/8627)) ([040a199](https://github.com/windmill-labs/windmill/commit/040a199685cea5c99c944bacb5584a381d6ec829))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* return default_args/enums in approval info and fix subflow resume buttons ([#8648](https://github.com/windmill-labs/windmill/issues/8648)) ([852c59e](https://github.com/windmill-labs/windmill/commit/852c59efbb04510e5e6f99919707effcf6769a2f))
|
||||
|
||||
## [1.670.0](https://github.com/windmill-labs/windmill/compare/v1.669.1...v1.670.0) (2026-03-31)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add OR logic support to kafka/websocket trigger filters ([#8580](https://github.com/windmill-labs/windmill/issues/8580)) ([3876902](https://github.com/windmill-labs/windmill/commit/3876902a7be798fd5ef208bc5756b28fb55e569e))
|
||||
* expose getJob and getJobLogs as MCP tools ([#8632](https://github.com/windmill-labs/windmill/issues/8632)) ([cd8edcd](https://github.com/windmill-labs/windmill/commit/cd8edcd94f2bf44c3e771000cb0bbad08accc0e7))
|
||||
* support multiline secrets in resource password fields ([#8637](https://github.com/windmill-labs/windmill/issues/8637)) ([26050f9](https://github.com/windmill-labs/windmill/commit/26050f96c34f14826298760174a45f3559d3266c))
|
||||
* support sensitive/secret fields for non-string types ([#8635](https://github.com/windmill-labs/windmill/issues/8635)) ([375fb66](https://github.com/windmill-labs/windmill/commit/375fb66abe2d1861b53dc2b36d2cf0e2eb82c3a8))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* cap input history per_page to 100 on cloud ([#8624](https://github.com/windmill-labs/windmill/issues/8624)) ([8e973c8](https://github.com/windmill-labs/windmill/commit/8e973c892d768be2da2e6b4b7af9e40b62333052))
|
||||
* compute highest workspace role across all instance groups ([#8633](https://github.com/windmill-labs/windmill/issues/8633)) ([92b9ac7](https://github.com/windmill-labs/windmill/commit/92b9ac72c5fc9a5085fcb2e9d835ccbb53bcd4b0))
|
||||
* Ducklake UI Nits ([#8628](https://github.com/windmill-labs/windmill/issues/8628)) ([ef1757f](https://github.com/windmill-labs/windmill/commit/ef1757f5d747e513d201eb6fa48918dba8248abe))
|
||||
* preserve flow notes/groups and field ordering in generate-metadata ([#8641](https://github.com/windmill-labs/windmill/issues/8641)) ([#8642](https://github.com/windmill-labs/windmill/issues/8642)) ([52a04d2](https://github.com/windmill-labs/windmill/commit/52a04d210f476f4598007f67770bc6520b045950))
|
||||
* remove timeout on python client httpx to prevent ducklake query timeouts ([#8636](https://github.com/windmill-labs/windmill/issues/8636)) ([c5fccd2](https://github.com/windmill-labs/windmill/commit/c5fccd2f69ad8a6e46c514cf89b9aa21b380e6fe))
|
||||
* resolve missing form schema for nested suspend steps in FlowNode sub-flows ([#8643](https://github.com/windmill-labs/windmill/issues/8643)) ([12ea7e7](https://github.com/windmill-labs/windmill/commit/12ea7e74237560a9dfc99b6bc1338e3343b57640))
|
||||
* smarter secret masking based on secret length ([#8629](https://github.com/windmill-labs/windmill/issues/8629)) ([bfc2aef](https://github.com/windmill-labs/windmill/commit/bfc2aefdb8ab92b7284de7f9e485a5504502d944))
|
||||
|
||||
## [1.669.1](https://github.com/windmill-labs/windmill/compare/v1.669.0...v1.669.1) (2026-03-30)
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ Open-source platform for internal tools, workflows, API integrations, background
|
||||
- **DB**: `psql postgres://postgres:changeme@localhost:5432/windmill`
|
||||
- **Login**: `admin@windmill.dev` / `changeme`
|
||||
- **Instance settings**: navigate to `/#superadmin-settings`
|
||||
- **Migrations**: use `cargo sqlx migrate add -r <name>` from `backend/` to create new migrations (never generate timestamps manually)
|
||||
|
||||
## Banned Patterns
|
||||
|
||||
|
||||
@@ -34,7 +34,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO websocket_trigger (\n workspace_id,\n path,\n url,\n script_path,\n is_flow,\n mode,\n filters,\n filter_logic,\n initial_messages,\n url_runnable_args,\n edited_by,\n can_return_message,\n can_return_error_result,\n permissioned_as,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, now(), $15, $16, $17\n )\n ",
|
||||
"query": "\n INSERT INTO websocket_trigger (\n workspace_id,\n path,\n url,\n script_path,\n is_flow,\n mode,\n filters,\n filter_logic,\n initial_messages,\n url_runnable_args,\n edited_by,\n can_return_message,\n can_return_error_result,\n permissioned_as,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry,\n heartbeat\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, now(), $15, $16, $17, $18\n )\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -32,10 +32,11 @@
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "6948eb5aabf82f2f4a08dd4410eb472080ecab3ed652912397245e5216ae0389"
|
||||
"hash": "11fd92de8688ef6b4d524aade507850a9cb3e097f2d957219d04ad134a5e0399"
|
||||
}
|
||||
@@ -43,7 +43,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO schedule (\n workspace_id, path, schedule, timezone, edited_by, script_path,\n is_flow, args, enabled, email, permissioned_as,\n on_failure, on_failure_times, on_failure_exact, on_failure_extra_args,\n on_recovery, on_recovery_times, on_recovery_extra_args,\n on_success, on_success_extra_args,\n ws_error_handler_muted, retry, summary, no_flow_overlap,\n tag, paused_until, cron_version, description, dynamic_skip\n ) VALUES (\n $1, $2, $3, $4, $5, $6,\n $7, $8, $9, $10, $11,\n $12, $13, $14, $15,\n $16, $17, $18,\n $19, $20,\n $21, $22, $23, $24,\n $25, $26, $27, $28, $29\n )\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip\n ",
|
||||
"query": "\n INSERT INTO schedule (\n workspace_id, path, schedule, timezone, edited_by, script_path,\n is_flow, args, enabled, email, permissioned_as,\n on_failure, on_failure_times, on_failure_exact, on_failure_extra_args,\n on_recovery, on_recovery_times, on_recovery_extra_args,\n on_success, on_success_extra_args,\n ws_error_handler_muted, retry, summary, no_flow_overlap,\n tag, paused_until, cron_version, description, dynamic_skip, labels\n ) VALUES (\n $1, $2, $3, $4, $5, $6,\n $7, $8, $9, $10, $11,\n $12, $13, $14, $15,\n $16, $17, $18,\n $19, $20,\n $21, $22, $23, $24,\n $25, $26, $27, $28, $29, $30\n )\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip,\n labels\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -162,6 +162,11 @@
|
||||
"ordinal": 31,
|
||||
"name": "dynamic_skip",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 32,
|
||||
"name": "labels",
|
||||
"type_info": "TextArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -194,7 +199,8 @@
|
||||
"Timestamptz",
|
||||
"Text",
|
||||
"Text",
|
||||
"Varchar"
|
||||
"Varchar",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
@@ -229,8 +235,9 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "dd20f94d560238096390371c98ded1f80825a11cd61c0bb431678ad9ab4a138e"
|
||||
"hash": "2effe885aafaa0c5fc1f969b8c6ffbd38c53fb586251ba780b75a007e5a47ed6"
|
||||
}
|
||||
16
backend/.sqlx/query-32f26d69b15a74fc96dd585f9eb3c998447ca8b97894bb1a2b238f984c1dc588.json
generated
Normal file
16
backend/.sqlx/query-32f26d69b15a74fc96dd585f9eb3c998447ca8b97894bb1a2b238f984c1dc588.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE flow SET versions = array_append(versions, $1) WHERE path = $2 AND workspace_id = $3 AND (versions[array_upper(versions, 1)] IS DISTINCT FROM $1)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "32f26d69b15a74fc96dd585f9eb3c998447ca8b97894bb1a2b238f984c1dc588"
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = EXCLUDED.value, description = EXCLUDED.description, resource_type = EXCLUDED.resource_type, edited_at = now()",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "39426bd3018b390ea2073419884cf6cb506c75e84c65438c9026831eb10d340b"
|
||||
}
|
||||
@@ -45,31 +45,36 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "labels",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "is_expired",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 10,
|
||||
"name": "is_refreshed",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 11,
|
||||
"name": "refresh_error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 12,
|
||||
"name": "is_linked",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 13,
|
||||
"name": "is_oauth?",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 14,
|
||||
"name": "account",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
@@ -89,6 +94,7 @@
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
|
||||
@@ -42,6 +42,11 @@
|
||||
"ordinal": 7,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "labels",
|
||||
"type_info": "TextArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -57,6 +62,7 @@
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO variable\n (workspace_id, path, value, is_secret, description, account, is_oauth, expires_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
"query": "INSERT INTO variable\n (workspace_id, path, value, is_secret, description, account, is_oauth, expires_at, labels)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -12,10 +12,11 @@
|
||||
"Varchar",
|
||||
"Int4",
|
||||
"Bool",
|
||||
"Timestamptz"
|
||||
"Timestamptz",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "2be66f23536223549db9b50025932b6b1bad90b8fa47d97acb7d75aa3c37ef86"
|
||||
"hash": "48efd8f89df9c1dd8f8a4eb2b2640fcb380f2470c8488371b207a42cb7d4cd26"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE\n websocket_trigger\n SET\n url = $1,\n script_path = $2,\n path = $3,\n is_flow = $4,\n filters = $5,\n filter_logic = $6,\n initial_messages = $7,\n url_runnable_args = $8,\n edited_by = $9,\n permissioned_as = $10,\n can_return_message = $11,\n can_return_error_result = $12,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $15,\n error_handler_args = $16,\n retry = $17\n WHERE\n workspace_id = $13 AND path = $14\n ",
|
||||
"query": "\n UPDATE\n websocket_trigger\n SET\n url = $1,\n script_path = $2,\n path = $3,\n is_flow = $4,\n filters = $5,\n filter_logic = $6,\n initial_messages = $7,\n url_runnable_args = $8,\n edited_by = $9,\n permissioned_as = $10,\n can_return_message = $11,\n can_return_error_result = $12,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $15,\n error_handler_args = $16,\n retry = $17,\n heartbeat = $18\n WHERE\n workspace_id = $13 AND path = $14\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -21,10 +21,11 @@
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e3d4f89ce36337af15d237b543eaca47771b480ff194884f9c947dcaf71d6cf9"
|
||||
"hash": "492edd53e497a45c314d41044e85a5e3492227b5049dc6870d60817ad929e7af"
|
||||
}
|
||||
16
backend/.sqlx/query-4c333861e736b8138162f0ff3bf2844c71b95c75bd8f173eac51336ab176ccc0.json
generated
Normal file
16
backend/.sqlx/query-4c333861e736b8138162f0ff3bf2844c71b95c75bd8f173eac51336ab176ccc0.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE variable SET labels = $1 WHERE path = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"TextArray",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4c333861e736b8138162f0ff3bf2844c71b95c75bd8f173eac51336ab176ccc0"
|
||||
}
|
||||
@@ -70,7 +70,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO script\n (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, cache_ignore_s3_path, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, auto_kind, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s, runnable_settings_handle, modules)\n\n SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, content, created_by, schema, is_template, extra_perms, NULL, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, cache_ignore_s3_path, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, auto_kind, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s, runnable_settings_handle, modules\n\n FROM script WHERE hash = $2 AND workspace_id = $3;\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Int8",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "51f09f073842a6990535b887d8267fab305c21e4d7703bedbadf405b5c2d7582"
|
||||
}
|
||||
@@ -34,7 +34,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH inserted_job AS (\n INSERT INTO v2_job (\n id, -- 1\n workspace_id, -- 2\n raw_code, -- 3\n raw_lock, -- 4\n raw_flow, -- 5\n tag, -- 6\n parent_job, -- 7\n created_by, -- 8\n permissioned_as, -- 9\n runnable_id, -- 10\n runnable_path, -- 11\n args, -- 12\n kind, -- 13\n trigger, -- 14\n script_lang, -- 15\n same_worker, -- 16\n pre_run_error, -- 17 \n permissioned_as_email, -- 18\n visible_to_owner, -- 19\n flow_innermost_root_job, -- 20\n root_job, -- 38\n concurrent_limit, -- 21\n concurrency_time_window_s, -- 22\n timeout, -- 23\n flow_step_id, -- 24\n cache_ttl, -- 25\n priority, -- 26\n trigger_kind, -- 39\n script_entrypoint_override, -- 12\n preprocessed -- 27,\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18,\n $19, $20, $38, $21, $22, $23, $24, $25, $26, $39::job_trigger_kind,\n ($12::JSONB)->>'_ENTRYPOINT_OVERRIDE', $27)\n ),\n inserted_runtime AS (\n INSERT INTO v2_job_runtime (id, ping) VALUES ($1, null)\n ),\n inserted_job_perms AS (\n INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id, end_user_email) \n values ($1, $32, $33, $34, $35, $36, $37, $2, $41) \n ON CONFLICT (job_id) DO UPDATE SET email = EXCLUDED.email, username = EXCLUDED.username, is_admin = EXCLUDED.is_admin, is_operator = EXCLUDED.is_operator, folders = EXCLUDED.folders, groups = EXCLUDED.groups, workspace_id = EXCLUDED.workspace_id, end_user_email = EXCLUDED.end_user_email\n )\n INSERT INTO v2_job_queue\n (workspace_id, id, running, scheduled_for, started_at, tag, priority, cache_ignore_s3_path, runnable_settings_handle)\n VALUES ($2, $1, $28, COALESCE($29, now()), CASE WHEN $27 OR $40 THEN now() END, $30, $31, $42, $43)",
|
||||
"query": "WITH inserted_job AS (\n INSERT INTO v2_job (\n id, -- 1\n workspace_id, -- 2\n raw_code, -- 3\n raw_lock, -- 4\n raw_flow, -- 5\n tag, -- 6\n parent_job, -- 7\n created_by, -- 8\n permissioned_as, -- 9\n runnable_id, -- 10\n runnable_path, -- 11\n args, -- 12\n kind, -- 13\n trigger, -- 14\n script_lang, -- 15\n same_worker, -- 16\n pre_run_error, -- 17 \n permissioned_as_email, -- 18\n visible_to_owner, -- 19\n flow_innermost_root_job, -- 20\n root_job, -- 38\n concurrent_limit, -- 21\n concurrency_time_window_s, -- 22\n timeout, -- 23\n flow_step_id, -- 24\n cache_ttl, -- 25\n priority, -- 26\n trigger_kind, -- 39\n script_entrypoint_override, -- 12\n preprocessed, -- 27,\n labels -- 44\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18,\n $19, $20, $38, $21, $22, $23, $24, $25, $26, $39::job_trigger_kind,\n ($12::JSONB)->>'_ENTRYPOINT_OVERRIDE', $27, $44)\n ),\n inserted_runtime AS (\n INSERT INTO v2_job_runtime (id, ping) VALUES ($1, null)\n ),\n inserted_job_perms AS (\n INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id, end_user_email) \n values ($1, $32, $33, $34, $35, $36, $37, $2, $41) \n ON CONFLICT (job_id) DO UPDATE SET email = EXCLUDED.email, username = EXCLUDED.username, is_admin = EXCLUDED.is_admin, is_operator = EXCLUDED.is_operator, folders = EXCLUDED.folders, groups = EXCLUDED.groups, workspace_id = EXCLUDED.workspace_id, end_user_email = EXCLUDED.end_user_email\n )\n INSERT INTO v2_job_queue\n (workspace_id, id, running, scheduled_for, started_at, tag, priority, cache_ignore_s3_path, runnable_settings_handle)\n VALUES ($2, $1, $28, COALESCE($29, now()), CASE WHEN $27 OR $40 THEN now() END, $30, $31, $42, $43)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -76,7 +76,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -131,10 +132,11 @@
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Int8"
|
||||
"Int8",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "14276a040cb4db88d71fccdc3579e8c0bb132b70668301b535872d1632753e30"
|
||||
"hash": "5425e2c5e29fc5145dde5ea53d5307ca90d8dd76da2ca560b310b12820be2576"
|
||||
}
|
||||
@@ -148,7 +148,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n script_lang as \"language: _\",\n COUNT(*) as \"count!\",\n SUM(duration_ms)::BIGINT as \"total_duration!\"\n FROM\n v2_job_completed\n JOIN\n v2_job\n ON\n v2_job.id = v2_job_completed.id\n WHERE\n created_at > NOW() - INTERVAL '48 hours'\n GROUP BY\n script_lang\n ",
|
||||
"query": "\n SELECT\n NULLIF(script_lang, 'other')::script_lang as \"language: _\",\n SUM(job_count)::BIGINT as \"count!\",\n SUM(total_duration_ms)::BIGINT as \"total_duration!\"\n FROM worker_group_job_stats\n WHERE hour >= $1\n GROUP BY script_lang\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -33,7 +33,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -51,13 +52,15 @@
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "464895f217536bf713ec381b6c3b7c0417161bf69e596e2e34fc8d9384824776"
|
||||
"hash": "5c0d0c7111b70b119b3182c9195361388ead36dc6ab03c6a7c17fb97d2b60a67"
|
||||
}
|
||||
@@ -43,7 +43,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT value, is_secret \n FROM variable \n WHERE path = $1 AND workspace_id = $2",
|
||||
"query": "SELECT value, is_secret\n FROM variable\n WHERE path = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -25,5 +25,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "a5a03b9235b25bca359235f2e546197f02ca1cf898d7f2686419749b1cb0679e"
|
||||
"hash": "61f3627cbd2a50b745c4e491ed8ee71e58db22b7f53202c5360a01c92091fc08"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n script_lang as \"language: _\",\n COUNT(*) as \"count!\",\n SUM(duration_ms)::BIGINT as \"total_duration!\"\n FROM\n v2_job_completed\n JOIN\n v2_job\n ON\n v2_job.id = v2_job_completed.id\n WHERE\n created_at >= CURRENT_DATE - INTERVAL '1 day'\n AND created_at < CURRENT_DATE\n GROUP BY\n script_lang\n ",
|
||||
"query": "\n SELECT\n NULLIF(script_lang, 'other')::script_lang as \"language: _\",\n SUM(job_count)::BIGINT as \"count!\",\n SUM(total_duration_ms)::BIGINT as \"total_duration!\"\n FROM worker_group_job_stats\n WHERE hour >= $1 AND hour < $2\n GROUP BY script_lang\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -33,7 +33,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -51,13 +52,16 @@
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "19478c2a3c05b2ed17b110f8ff58d48ce3ede5fdd7f8d64840f8c62cf89f99e7"
|
||||
"hash": "6348061d79b0b6b25bda7425f7a37344e04107f060f949cc197b4df9bca18fb8"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE schedule SET\n enabled = $1,\n email = $2\n WHERE path = $3 AND workspace_id = $4\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip\n ",
|
||||
"query": "\n UPDATE schedule SET\n enabled = $1,\n email = $2\n WHERE path = $3 AND workspace_id = $4\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip,\n labels\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -162,6 +162,11 @@
|
||||
"ordinal": 31,
|
||||
"name": "dynamic_skip",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 32,
|
||||
"name": "labels",
|
||||
"type_info": "TextArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -204,8 +209,9 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "ad96768ff61fab1cfb9421683bb13b64f5f157fafe114d50fb910dc36ebe0f91"
|
||||
"hash": "642ca096c1c151fc01445593c8d2ed879a9a7be6ff044a9ce1b581ba725665fe"
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO flow\n (workspace_id, path, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, on_behalf_of_email, concurrency_key, versions, value, schema, edited_by, edited_at)\n SELECT workspace_id, $1, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, on_behalf_of_email, concurrency_key, versions, value, schema, edited_by, edited_at\n FROM flow\n WHERE path = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "676c758d9c4492dada50edd3ad06dc628e2ba56eab5e1a50c99481da9793759e"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n flow_version.id AS version,\n flow_version.value->>'early_return' as early_return,\n flow_version.value->>'preprocessor_module' IS NOT NULL as has_preprocessor,\n (flow_version.value->>'chat_input_enabled')::boolean as chat_input_enabled,\n flow.tag,\n flow.dedicated_worker,\n flow.on_behalf_of_email,\n flow.edited_by\n FROM\n flow_version\n INNER JOIN flow\n ON flow.path = flow_version.path AND\n flow.workspace_id = flow_version.workspace_id\n WHERE\n flow_version.workspace_id = $1 AND\n flow_version.path = $2 AND\n flow_version.id = $3\n ",
|
||||
"query": "\n SELECT\n flow_version.id AS version,\n flow_version.value->>'early_return' as early_return,\n flow_version.value->>'preprocessor_module' IS NOT NULL as has_preprocessor,\n (flow_version.value->>'chat_input_enabled')::boolean as chat_input_enabled,\n flow.tag,\n flow.dedicated_worker,\n flow.on_behalf_of_email,\n flow.edited_by,\n flow.labels\n FROM\n flow_version\n INNER JOIN flow\n ON flow.path = flow_version.path AND\n flow.workspace_id = flow_version.workspace_id\n WHERE\n flow_version.workspace_id = $1 AND\n flow_version.path = $2 AND\n flow_version.id = $3\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -42,6 +42,11 @@
|
||||
"ordinal": 7,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "labels",
|
||||
"type_info": "TextArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -59,8 +64,9 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "209dc4c1b91eeab1c12ffcd9f9e16f315c689ca772c736b333dcdf07c8086087"
|
||||
"hash": "6d992a933bb878733b7afd7a4295b9ad6f5276b60ce20e0378d6148976e02777"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "select hash, tag, concurrency_key, concurrent_limit, concurrency_time_window_s, debounce_key, debounce_delay_s, cache_ttl, cache_ignore_s3_path, runnable_settings_handle, language as \"language: ScriptLang\", dedicated_worker, priority, timeout, on_behalf_of_email, created_by FROM script\n WHERE path = $1 AND workspace_id = $2 AND archived = false AND (lock IS NOT NULL OR $3 = false)\n ORDER BY created_at DESC LIMIT 1",
|
||||
"query": "select hash, tag, concurrency_key, concurrent_limit, concurrency_time_window_s, debounce_key, debounce_delay_s, cache_ttl, cache_ignore_s3_path, runnable_settings_handle, language as \"language: ScriptLang\", dedicated_worker, priority, timeout, on_behalf_of_email, created_by, labels FROM script\n WHERE path = $1 AND workspace_id = $2 AND archived = false AND (lock IS NOT NULL OR $3 = false)\n ORDER BY created_at DESC LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -83,7 +83,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -113,6 +114,11 @@
|
||||
"ordinal": 15,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "labels",
|
||||
"type_info": "TextArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -138,8 +144,9 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "a33673ebc4d1eb4c3513987dbc43e2c80974598e1d9fe7203145bfc29928ba65"
|
||||
"hash": "71767e6684957af5dff56a1bb64f980f712f92798d11feaeb0832962ad9ddb2e"
|
||||
}
|
||||
14
backend/.sqlx/query-78f7c4efd7be2aad03f0ec67ab882e1f9aaa7141ab75046a71ac789ab659bdc6.json
generated
Normal file
14
backend/.sqlx/query-78f7c4efd7be2aad03f0ec67ab882e1f9aaa7141ab75046a71ac789ab659bdc6.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO notify_event (channel, payload) VALUES ('restart_worker_group', $1)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "78f7c4efd7be2aad03f0ec67ab882e1f9aaa7141ab75046a71ac789ab659bdc6"
|
||||
}
|
||||
@@ -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, auto_kind, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s, cache_ignore_s3_path, runnable_settings_handle, modules) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37, $38, $39)",
|
||||
"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, auto_kind, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s, cache_ignore_s3_path, runnable_settings_handle, modules, labels) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37, $38, $39, $40)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -44,7 +44,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -88,10 +89,11 @@
|
||||
"Int4",
|
||||
"Bool",
|
||||
"Int8",
|
||||
"Jsonb"
|
||||
"Jsonb",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4d983f1e3e63a1a70edf5d867d9f23f2069a7a4ba1dcc1331ecccdf1c6a95cb8"
|
||||
"hash": "790d79ec7abe6ebe1092afd9de4c5fc383272d2057ac5b47a7425f095f4e8788"
|
||||
}
|
||||
@@ -76,7 +76,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
16
backend/.sqlx/query-86c0ef92652085b93979a8836bedb33a366a84426a115c75f58dcc43ab91705e.json
generated
Normal file
16
backend/.sqlx/query-86c0ef92652085b93979a8836bedb33a366a84426a115c75f58dcc43ab91705e.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE http_trigger SET labels = $1 WHERE workspace_id = $2 AND path = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"TextArray",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "86c0ef92652085b93979a8836bedb33a366a84426a115c75f58dcc43ab91705e"
|
||||
}
|
||||
@@ -41,7 +41,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
26
backend/.sqlx/query-a450c5d5fe4bc15e13ea389fadd21a769b029b650a334e48bb3198c049bb92fe.json
generated
Normal file
26
backend/.sqlx/query-a450c5d5fe4bc15e13ea389fadd21a769b029b650a334e48bb3198c049bb92fe.json
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, path FROM variable WHERE is_secret = true AND value LIKE '$azure_kv:%'",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "a450c5d5fe4bc15e13ea389fadd21a769b029b650a334e48bb3198c049bb92fe"
|
||||
}
|
||||
@@ -133,7 +133,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n table_name as \"table_name!\",\n SUM(live_tuples)::bigint as \"live_tuples!\",\n SUM(dead_tuples)::bigint as \"dead_tuples!\",\n MAX(last_autovacuum) as \"last_autovacuum\",\n MAX(last_autoanalyze) as \"last_autoanalyze\"\n FROM (\n SELECT\n CASE\n WHEN i.inhparent IS NOT NULL THEN schemaname || '.' || p.relname\n ELSE schemaname || '.' || s.relname\n END as table_name,\n COALESCE(n_live_tup, 0) as live_tuples,\n COALESCE(n_dead_tup, 0) as dead_tuples,\n last_autovacuum,\n last_autoanalyze\n FROM pg_stat_user_tables s\n LEFT JOIN pg_class c ON c.relname = s.relname AND c.relnamespace = (\n SELECT oid FROM pg_namespace WHERE nspname = s.schemaname\n )\n LEFT JOIN pg_inherits i ON i.inhrelid = c.oid\n LEFT JOIN pg_class p ON p.oid = i.inhparent\n ) sub\n GROUP BY table_name\n ORDER BY SUM(dead_tuples) DESC",
|
||||
"query": "SELECT\n table_name as \"table_name!\",\n SUM(live_tuples)::bigint as \"live_tuples!\",\n SUM(dead_tuples)::bigint as \"dead_tuples!\",\n MAX(last_autovacuum) as \"last_autovacuum\",\n MAX(last_autoanalyze) as \"last_autoanalyze\"\n FROM (\n SELECT\n CASE\n WHEN i.inhparent IS NOT NULL THEN schemaname || '.' || p.relname\n ELSE schemaname || '.' || s.relname\n END as table_name,\n COALESCE(n_live_tup, 0) as live_tuples,\n COALESCE(n_dead_tup, 0) as dead_tuples,\n last_autovacuum,\n last_autoanalyze\n FROM pg_stat_user_tables s\n LEFT JOIN pg_class c ON c.relname = s.relname AND c.relnamespace = (\n SELECT oid FROM pg_namespace WHERE nspname = s.schemaname\n )\n LEFT JOIN pg_inherits i ON i.inhrelid = c.oid\n LEFT JOIN pg_class p ON p.oid = i.inhparent\n ) sub\n GROUP BY table_name\n HAVING SUM(live_tuples) + SUM(dead_tuples) >= 1000\n ORDER BY SUM(dead_tuples) DESC",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -40,5 +40,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "30930bfb0513f1a70194a900011b2e890bc4146bb0419210cd76743cacda8bfa"
|
||||
"hash": "a5455608f60ad3939b337e374cbb2f4dbf98db86864e58fef47447390bb5fcac"
|
||||
}
|
||||
@@ -38,7 +38,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
16
backend/.sqlx/query-a969194571dd3f12e628ce0f01b0ddc09bbcf4506eff2290664c295d16fec4ae.json
generated
Normal file
16
backend/.sqlx/query-a969194571dd3f12e628ce0f01b0ddc09bbcf4506eff2290664c295d16fec4ae.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO script\n (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, cache_ignore_s3_path, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, auto_kind, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s, runnable_settings_handle, modules, labels)\n\n SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, content, created_by, schema, is_template, extra_perms, NULL, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, cache_ignore_s3_path, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, auto_kind, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s, runnable_settings_handle, modules, labels\n\n FROM script WHERE hash = $2 AND workspace_id = $3;\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Int8",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a969194571dd3f12e628ce0f01b0ddc09bbcf4506eff2290664c295d16fec4ae"
|
||||
}
|
||||
@@ -153,7 +153,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
22
backend/.sqlx/query-b3c84299ed9872960c2287ef6e3a9dca85eeb2d22e7004021c987d08598b5585.json
generated
Normal file
22
backend/.sqlx/query-b3c84299ed9872960c2287ef6e3a9dca85eeb2d22e7004021c987d08598b5585.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM v2_job_completed\n WHERE completed_at <= now() - ($1::bigint::text || ' s')::interval",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "b3c84299ed9872960c2287ef6e3a9dca85eeb2d22e7004021c987d08598b5585"
|
||||
}
|
||||
@@ -43,7 +43,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
16
backend/.sqlx/query-b657b718ec381fbb1d41841c88118b88c4d7ea8f0cde663ed8f8226faf75b0f6.json
generated
Normal file
16
backend/.sqlx/query-b657b718ec381fbb1d41841c88118b88c4d7ea8f0cde663ed8f8226faf75b0f6.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE app SET labels = $1 WHERE path = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"TextArray",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "b657b718ec381fbb1d41841c88118b88c4d7ea8f0cde663ed8f8226faf75b0f6"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO flow (\n workspace_id, path, summary, description,\n dependency_job, lock_error_logs, draft_only, tag,\n dedicated_worker, visible_to_runner_only, on_behalf_of_email,\n ws_error_handler_muted,\n value, schema, edited_by, edited_at\n ) VALUES (\n $1, $2, $3, $4,\n NULL, '', $5, $6,\n $7, $8, $9,\n $10,\n $11, $12::text::json, $13, now()\n )",
|
||||
"query": "INSERT INTO flow (\n workspace_id, path, summary, description,\n dependency_job, lock_error_logs, draft_only, tag,\n dedicated_worker, visible_to_runner_only, on_behalf_of_email,\n ws_error_handler_muted,\n value, schema, edited_by, edited_at, labels\n ) VALUES (\n $1, $2, $3, $4,\n NULL, '', $5, $6,\n $7, $8, $9,\n $10,\n $11, $12::text::json, $13, now(), $14\n )",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -17,10 +17,11 @@
|
||||
"Bool",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar"
|
||||
"Varchar",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "6bde827da007b470b9d0acccfc3e00ce6aac650b9138a236f34c614eed753849"
|
||||
"hash": "b6f95b3fd1d0431d96d0409424dbb20cb3020f93454978d078082482b86cbebe"
|
||||
}
|
||||
22
backend/.sqlx/query-b782179261b4054289a1a595f6e06ee2035ae9811bc49abce903a04439baf800.json
generated
Normal file
22
backend/.sqlx/query-b782179261b4054289a1a595f6e06ee2035ae9811bc49abce903a04439baf800.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT DISTINCT unnest(labels) as \"label!\" FROM (\n SELECT labels FROM script WHERE workspace_id = $1 AND labels IS NOT NULL\n UNION ALL SELECT labels FROM flow WHERE workspace_id = $1 AND labels IS NOT NULL\n UNION ALL SELECT labels FROM resource WHERE workspace_id = $1 AND labels IS NOT NULL\n UNION ALL SELECT labels FROM variable WHERE workspace_id = $1 AND labels IS NOT NULL\n UNION ALL SELECT labels FROM schedule WHERE workspace_id = $1 AND labels IS NOT NULL\n UNION ALL SELECT labels FROM app WHERE workspace_id = $1 AND labels IS NOT NULL\n ) t ORDER BY 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "label!",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "b782179261b4054289a1a595f6e06ee2035ae9811bc49abce903a04439baf800"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE schedule SET\n schedule = $1,\n timezone = $2,\n args = $3,\n on_failure = $4,\n on_failure_times = $5,\n on_failure_exact = $6,\n on_failure_extra_args = $7,\n on_recovery = $8,\n on_recovery_times = $9,\n on_recovery_extra_args = $10,\n on_success = $11,\n on_success_extra_args = $12,\n ws_error_handler_muted = $13,\n retry = $14,\n summary = $15,\n no_flow_overlap = $16,\n tag = $17,\n paused_until = $18,\n path = $19,\n workspace_id = $20,\n cron_version = COALESCE($21, cron_version),\n description = $22,\n dynamic_skip = $23,\n email = $24,\n edited_by = $25,\n permissioned_as = $26\n WHERE path = $19 AND workspace_id = $20\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip\n ",
|
||||
"query": "\n UPDATE schedule SET\n schedule = $1,\n timezone = $2,\n args = $3,\n on_failure = $4,\n on_failure_times = $5,\n on_failure_exact = $6,\n on_failure_extra_args = $7,\n on_recovery = $8,\n on_recovery_times = $9,\n on_recovery_extra_args = $10,\n on_success = $11,\n on_success_extra_args = $12,\n ws_error_handler_muted = $13,\n retry = $14,\n summary = $15,\n no_flow_overlap = $16,\n tag = $17,\n paused_until = $18,\n path = $19,\n workspace_id = $20,\n cron_version = COALESCE($21, cron_version),\n description = $22,\n dynamic_skip = $23,\n email = $24,\n edited_by = $25,\n permissioned_as = $26,\n labels = COALESCE($27, labels)\n WHERE path = $19 AND workspace_id = $20\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip,\n labels\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -162,6 +162,11 @@
|
||||
"ordinal": 31,
|
||||
"name": "dynamic_skip",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 32,
|
||||
"name": "labels",
|
||||
"type_info": "TextArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -191,7 +196,8 @@
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
"Varchar",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
@@ -226,8 +232,9 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "54b4c762add9b1ebfdb2a6d5abd6d20e86dc0e6544f0bb22fa4ec68aa54a4dc8"
|
||||
"hash": "bd2609f999054c5a2a85f2aafe4005bc22731c27f0bf27b3b06ec426a05eda2e"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT path, value FROM variable\n WHERE path LIKE ('u/' || $1 || '/%')\n AND workspace_id = $2\n AND is_secret = true\n AND value LIKE '$vault:%'",
|
||||
"query": "SELECT path, value FROM variable\n WHERE path LIKE ('u/' || $1 || '/%')\n AND workspace_id = $2\n AND is_secret = true\n AND (value LIKE '$vault:%' OR value LIKE '$azure_kv:%')",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -25,5 +25,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2fcddda99dd0aacf5007ed459cb27caa754424e062427edf5ddcb95f9d96888e"
|
||||
"hash": "bda9fc0c6e41d8afebee538e6997c6bad7843f93d0a5780632b02fa215aa404b"
|
||||
}
|
||||
@@ -40,7 +40,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE\n flow\n SET\n path = $1,\n summary = $2,\n description = $3,\n dependency_job = NULL,\n lock_error_logs = '',\n draft_only = NULL,\n tag = $4,\n dedicated_worker = $5,\n visible_to_runner_only = $6,\n on_behalf_of_email = $7,\n ws_error_handler_muted = $8,\n value = $9,\n schema = $10::text::json,\n edited_by = $11,\n edited_at = now()\n WHERE\n path = $12 AND workspace_id = $13",
|
||||
"query": "\n UPDATE\n flow\n SET\n path = $1,\n summary = $2,\n description = $3,\n dependency_job = NULL,\n lock_error_logs = '',\n draft_only = NULL,\n tag = $4,\n dedicated_worker = $5,\n visible_to_runner_only = $6,\n on_behalf_of_email = $7,\n ws_error_handler_muted = $8,\n value = $9,\n schema = $10::text::json,\n edited_by = $11,\n edited_at = now(),\n labels = COALESCE($14, labels)\n WHERE\n path = $12 AND workspace_id = $13",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -17,10 +17,11 @@
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
"Text",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "77ac7257be02fb04c4b3213e2221e6f60621b4b2909d770de744ef5671e12ed9"
|
||||
"hash": "c06796e8647cf278c6e0809562aaaa872403890eae3e606f379343671c2fff02"
|
||||
}
|
||||
23
backend/.sqlx/query-c569535d4aded82ffef5e5066e24948c8fcdb8d36913775449b982782c8583e9.json
generated
Normal file
23
backend/.sqlx/query-c569535d4aded82ffef5e5066e24948c8fcdb8d36913775449b982782c8583e9.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT value AS \"value!: Json<Box<RawValue>>\" FROM flow WHERE path = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "value!: Json<Box<RawValue>>",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "c569535d4aded82ffef5e5066e24948c8fcdb8d36913775449b982782c8583e9"
|
||||
}
|
||||
22
backend/.sqlx/query-c5e72bcfd4d389fce281811d7a731711d89fb79f7b2adcfbdf33c070b3adaf7d.json
generated
Normal file
22
backend/.sqlx/query-c5e72bcfd4d389fce281811d7a731711d89fb79f7b2adcfbdf33c070b3adaf7d.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM log_file WHERE log_ts <= now() - ($1::bigint::text || ' s')::interval",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "c5e72bcfd4d389fce281811d7a731711d89fb79f7b2adcfbdf33c070b3adaf7d"
|
||||
}
|
||||
29
backend/.sqlx/query-d21464c0e980b9d3a5c8f940c2abe43275843f33fb1998d9c8bd940ef127bfbf.json
generated
Normal file
29
backend/.sqlx/query-d21464c0e980b9d3a5c8f940c2abe43275843f33fb1998d9c8bd940ef127bfbf.json
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM log_file WHERE (file_path, hostname) IN (\n SELECT file_path, hostname FROM log_file\n WHERE log_ts <= now() - ($1::bigint::text || ' s')::interval\n LIMIT $2\n ) RETURNING file_path, hostname",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "file_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "hostname",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "d21464c0e980b9d3a5c8f940c2abe43275843f33fb1998d9c8bd940ef127bfbf"
|
||||
}
|
||||
@@ -68,7 +68,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
16
backend/.sqlx/query-ddda19024473b4e5b1d450bf56c667056f6b3696dea7d73758e53f825bfce13e.json
generated
Normal file
16
backend/.sqlx/query-ddda19024473b4e5b1d450bf56c667056f6b3696dea7d73758e53f825bfce13e.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO flow\n (workspace_id, path, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, on_behalf_of_email, concurrency_key, versions, value, schema, edited_by, edited_at, labels)\n SELECT workspace_id, $1, summary, description, archived, extra_perms, dependency_job, draft_only, tag, ws_error_handler_muted, dedicated_worker, timeout, visible_to_runner_only, on_behalf_of_email, concurrency_key, versions, value, schema, edited_by, edited_at, labels\n FROM flow\n WHERE path = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ddda19024473b4e5b1d450bf56c667056f6b3696dea7d73758e53f825bfce13e"
|
||||
}
|
||||
20
backend/.sqlx/query-deac41298e8b0d0870e314fef0813c24dd55d63bda78a0a5f35ed6f22bea6bef.json
generated
Normal file
20
backend/.sqlx/query-deac41298e8b0d0870e314fef0813c24dd55d63bda78a0a5f35ed6f22bea6bef.json
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type, created_by, edited_at, labels)\n VALUES ($1, $2, $3, $4, $5, $6, now(), $7) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = EXCLUDED.value, description = EXCLUDED.description, resource_type = EXCLUDED.resource_type, edited_at = now(), labels = EXCLUDED.labels",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "deac41298e8b0d0870e314fef0813c24dd55d63bda78a0a5f35ed6f22bea6bef"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO app\n (workspace_id, path, summary, policy, versions, draft_only, custom_path)\n VALUES ($1, $2, $3, $4, '{}', $5, $6) RETURNING id",
|
||||
"query": "INSERT INTO app\n (workspace_id, path, summary, policy, versions, draft_only, custom_path, labels)\n VALUES ($1, $2, $3, $4, '{}', $5, $6, $7) RETURNING id",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -16,12 +16,13 @@
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Bool",
|
||||
"Text"
|
||||
"Text",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "6b53f7c4bb73177316d6134698f3979f51b53dcd4d8ec50d312c9e7fe31ad5f5"
|
||||
"hash": "e4836a1ee97e4723bddc28b94e23a54b6aa1f458f3ecca8f35432153e54b143d"
|
||||
}
|
||||
@@ -133,7 +133,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
64
backend/.sqlx/query-edd6c09b7f012588788fd3c572d20eb439a80d52ae75ebd25128ffce759cd313.json
generated
Normal file
64
backend/.sqlx/query-edd6c09b7f012588788fd3c572d20eb439a80d52ae75ebd25128ffce759cd313.json
generated
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, name, language AS \"language: windmill_common::scripts::ScriptLang\"\n FROM workspace_dependencies\n WHERE archived = false\n ORDER BY workspace_id, name",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "language: windmill_common::scripts::ScriptLang",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "script_lang",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"python3",
|
||||
"deno",
|
||||
"go",
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"bun",
|
||||
"mysql",
|
||||
"bigquery",
|
||||
"snowflake",
|
||||
"graphql",
|
||||
"powershell",
|
||||
"mssql",
|
||||
"php",
|
||||
"bunnative",
|
||||
"rust",
|
||||
"ansible",
|
||||
"csharp",
|
||||
"oracledb",
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "edd6c09b7f012588788fd3c572d20eb439a80d52ae75ebd25128ffce759cd313"
|
||||
}
|
||||
72
backend/.sqlx/query-f0858450021df721d8a48b1b5dc887c5424562acd9769c80c5899193ef16b56b.json
generated
Normal file
72
backend/.sqlx/query-f0858450021df721d8a48b1b5dc887c5424562acd9769c80c5899193ef16b56b.json
generated
Normal file
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT DISTINCT ON (workspace_id, path)\n workspace_id, path, language AS \"language: windmill_common::scripts::ScriptLang\", content\n FROM script\n WHERE archived = false\n AND dedicated_worker = true\n AND language = ANY($1::text[]::SCRIPT_LANG[])\n ORDER BY workspace_id, path, created_at DESC",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "language: windmill_common::scripts::ScriptLang",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "script_lang",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"python3",
|
||||
"deno",
|
||||
"go",
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"bun",
|
||||
"mysql",
|
||||
"bigquery",
|
||||
"snowflake",
|
||||
"graphql",
|
||||
"powershell",
|
||||
"mssql",
|
||||
"php",
|
||||
"bunnative",
|
||||
"rust",
|
||||
"ansible",
|
||||
"csharp",
|
||||
"oracledb",
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "content",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f0858450021df721d8a48b1b5dc887c5424562acd9769c80c5899193ef16b56b"
|
||||
}
|
||||
@@ -40,7 +40,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -84,7 +85,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
"nu",
|
||||
"java",
|
||||
"duckdb",
|
||||
"ruby"
|
||||
"ruby",
|
||||
"rlang"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
16
backend/.sqlx/query-f6f850c419771b193f506ceca63de805cbe6bdee314ca40d912010db307bd13e.json
generated
Normal file
16
backend/.sqlx/query-f6f850c419771b193f506ceca63de805cbe6bdee314ca40d912010db307bd13e.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE resource SET labels = $1 WHERE path = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"TextArray",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f6f850c419771b193f506ceca63de805cbe6bdee314ca40d912010db307bd13e"
|
||||
}
|
||||
441
backend/Cargo.lock
generated
441
backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.669.1"
|
||||
version = "1.675.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -66,10 +66,13 @@ members = [
|
||||
"./parsers/windmill-parser-nu",
|
||||
"./parsers/windmill-parser-java",
|
||||
"./parsers/windmill-parser-ruby",
|
||||
"./parsers/windmill-parser-r",
|
||||
"./parsers/windmill-parser-bash",
|
||||
"./parsers/windmill-parser-py",
|
||||
"./parsers/windmill-parser-py-asset",
|
||||
"./parsers/windmill-parser-py-imports",
|
||||
# Uncomment to build wasm parsers:
|
||||
# "./parsers/windmill-parser-wasm",
|
||||
"./parsers/windmill-parser-wac",
|
||||
"./parsers/windmill-parser-sql",
|
||||
"./parsers/windmill-parser-sql-asset",
|
||||
@@ -82,7 +85,7 @@ members = [
|
||||
exclude = ["./windmill-duckdb-ffi-internal", "./parsers/windmill-parser-wasm"]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.669.1"
|
||||
version = "1.675.0"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -163,7 +166,8 @@ csharp = ["windmill-worker/csharp"]
|
||||
nu = ["windmill-worker/nu"]
|
||||
java = ["windmill-worker/java"]
|
||||
ruby = ["windmill-worker/ruby"]
|
||||
all_languages = ["python", "deno_core", "rust", "mysql", "oracledb", "duckdb", "mssql-kerberos", "bigquery", "csharp", "nu", "php", "java", "ruby"]
|
||||
rlang = ["windmill-worker/rlang"]
|
||||
all_languages = ["python", "deno_core", "rust", "mysql", "oracledb", "duckdb", "mssql-kerberos", "bigquery", "csharp", "nu", "php", "java", "ruby", "rlang"]
|
||||
# For windows we have another set of languages enabled
|
||||
all_languages_windows = ["python", "deno_core", "rust", "mysql", "oracledb", "duckdb", "mssql-winauth", "bigquery", "csharp", "nu", "php", "java"]
|
||||
# Edition meta-features: shared groups
|
||||
@@ -263,6 +267,7 @@ windmill-types.workspace = true
|
||||
opentelemetry = { workspace = true }
|
||||
opentelemetry_sdk = { workspace = true }
|
||||
windmill-trigger.workspace = true
|
||||
serial_test = "3"
|
||||
windmill-trigger-websocket.workspace = true
|
||||
windmill-trigger-postgres.workspace = true
|
||||
windmill-trigger-mqtt.workspace = true
|
||||
@@ -347,6 +352,7 @@ windmill-parser-yaml = { path = "./parsers/windmill-parser-yaml" }
|
||||
windmill-parser-csharp = { path = "./parsers/windmill-parser-csharp" }
|
||||
windmill-parser-java = { path = "./parsers/windmill-parser-java" }
|
||||
windmill-parser-ruby = { path = "./parsers/windmill-parser-ruby" }
|
||||
windmill-parser-r = { path = "./parsers/windmill-parser-r" }
|
||||
windmill-parser-nu = { path = "./parsers/windmill-parser-nu" }
|
||||
windmill-parser-bash = { path = "./parsers/windmill-parser-bash" }
|
||||
windmill-parser-sql = { path = "./parsers/windmill-parser-sql" }
|
||||
@@ -572,7 +578,7 @@ async-stream = "^0"
|
||||
opentelemetry = "0.30.0"
|
||||
tracing-opentelemetry = "0.31.0"
|
||||
opentelemetry_sdk = { version = "0.30.0", features = ["rt-tokio", "testing"] }
|
||||
opentelemetry-otlp = { version = "0.30.0", features = ["grpc-tonic", "tls"] }
|
||||
opentelemetry-otlp = { version = "0.30.0", features = ["grpc-tonic", "tls", "http-proto"] }
|
||||
opentelemetry-appender-tracing = "0.30.0"
|
||||
opentelemetry-semantic-conventions = { version = "0.30.0", features = ["semconv_experimental"] }
|
||||
opentelemetry-proto = { version = "0.30.0", features = ["with-serde", "gen-tonic"] }
|
||||
@@ -613,6 +619,7 @@ tree-sitter = { version = "0.23.0", features = [] }
|
||||
tree-sitter-c-sharp = "0.23.0"
|
||||
tree-sitter-java = "0.23.0"
|
||||
tree-sitter-ruby = "0.23.0"
|
||||
tree-sitter-r = "1.2.0"
|
||||
oracle = { version = "0.6.3", features = ["chrono"] }
|
||||
rumqttc = { version = "0.24.0", features = ["use-native-tls"]}
|
||||
strum = { version = "0.27", features = ["derive"] }
|
||||
|
||||
@@ -1 +1 @@
|
||||
e08a87450627bef9013498e40ee93a47bedda7ee
|
||||
504dcbc89529fac09af7ce39a14d3ec83555e070
|
||||
@@ -0,0 +1 @@
|
||||
-- No-op: this migration is a data fixup and cannot be reversed.
|
||||
@@ -0,0 +1,48 @@
|
||||
-- Pre-fix: before permissioned_as migration drops the email column, update edited_by
|
||||
-- for triggers where the user (edited_by) is not in the workspace but is a superadmin.
|
||||
-- This ensures the subsequent 20260318000000 migration stores the raw email as permissioned_as
|
||||
-- (via the `edited_by LIKE '%@%'` branch).
|
||||
-- For instances that already applied 20260318000000, this is a no-op (email column is gone);
|
||||
-- the 20260401000000 migration handles those as a fallback.
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
trigger_table TEXT;
|
||||
has_email BOOLEAN;
|
||||
BEGIN
|
||||
FOREACH trigger_table IN ARRAY ARRAY[
|
||||
'http_trigger',
|
||||
'websocket_trigger',
|
||||
'postgres_trigger',
|
||||
'mqtt_trigger',
|
||||
'kafka_trigger',
|
||||
'nats_trigger',
|
||||
'sqs_trigger',
|
||||
'gcp_trigger',
|
||||
'email_trigger'
|
||||
]
|
||||
LOOP
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = trigger_table AND column_name = 'email'
|
||||
) INTO has_email;
|
||||
|
||||
IF has_email THEN
|
||||
EXECUTE format($q$
|
||||
UPDATE %I t
|
||||
SET edited_by = t.email
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM usr u
|
||||
WHERE u.username = t.edited_by
|
||||
AND u.workspace_id = t.workspace_id
|
||||
)
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM password p
|
||||
WHERE p.email = t.email
|
||||
AND p.super_admin = true
|
||||
)
|
||||
$q$, trigger_table);
|
||||
END IF;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
16
backend/migrations/20260330000000_add_labels.down.sql
Normal file
16
backend/migrations/20260330000000_add_labels.down.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
ALTER TABLE script DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE flow DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE app DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE raw_app DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE schedule DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE http_trigger DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE websocket_trigger DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE kafka_trigger DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE nats_trigger DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE mqtt_trigger DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE postgres_trigger DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE sqs_trigger DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE gcp_trigger DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE email_trigger DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE resource DROP COLUMN IF EXISTS labels;
|
||||
ALTER TABLE variable DROP COLUMN IF EXISTS labels;
|
||||
33
backend/migrations/20260330000000_add_labels.up.sql
Normal file
33
backend/migrations/20260330000000_add_labels.up.sql
Normal file
@@ -0,0 +1,33 @@
|
||||
ALTER TABLE script ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE flow ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE app ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE raw_app ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE schedule ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE http_trigger ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE websocket_trigger ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE kafka_trigger ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE nats_trigger ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE mqtt_trigger ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE postgres_trigger ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE sqs_trigger ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE gcp_trigger ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE email_trigger ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE resource ADD COLUMN IF NOT EXISTS labels text[];
|
||||
ALTER TABLE variable ADD COLUMN IF NOT EXISTS labels text[];
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_script_labels ON script USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_flow_labels ON flow USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_app_labels ON app USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_raw_app_labels ON raw_app USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_schedule_labels ON schedule USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_resource_labels ON resource USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_variable_labels ON variable USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_http_trigger_labels ON http_trigger USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_websocket_trigger_labels ON websocket_trigger USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_kafka_trigger_labels ON kafka_trigger USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_nats_trigger_labels ON nats_trigger USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_mqtt_trigger_labels ON mqtt_trigger USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_postgres_trigger_labels ON postgres_trigger USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_sqs_trigger_labels ON sqs_trigger USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_gcp_trigger_labels ON gcp_trigger USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_email_trigger_labels ON email_trigger USING GIN (labels) WHERE labels IS NOT NULL;
|
||||
2
backend/migrations/20260331000000_add_rlang.up.sql
Normal file
2
backend/migrations/20260331000000_add_rlang.up.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TYPE SCRIPT_LANG ADD VALUE IF NOT EXISTS 'rlang';
|
||||
UPDATE config SET config = jsonb_set(config, '{worker_tags}', config->'worker_tags' || '["rlang"]'::jsonb) WHERE name = 'worker__default' AND config @> '{"worker_tags": ["deno", "python3", "go", "bash", "powershell", "dependency", "flow", "hub", "other", "bun", "php", "rust", "ansible", "csharp", "nu", "java", "duckdb", "ruby"]}'::jsonb AND NOT config->'worker_tags' @> '"rlang"'::jsonb;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- Remove the //nodejs annotation that was prepended by the up migration.
|
||||
-- Only removes it if it's at the very start of the content.
|
||||
UPDATE script
|
||||
SET content = regexp_replace(content, E'^//nodejs\n// dedicated workers were previously running in nodejs mode by default, remove this annotation to use bun\n', '')
|
||||
WHERE language = 'bun'
|
||||
AND dedicated_worker = true;
|
||||
@@ -0,0 +1,10 @@
|
||||
-- Bun dedicated workers were previously forced to run in nodejs mode at runtime.
|
||||
-- Now that bun is the default again, add the //nodejs annotation to existing
|
||||
-- bun dedicated scripts that don't already have it, so their behavior is preserved.
|
||||
UPDATE script
|
||||
SET content = '//nodejs
|
||||
// dedicated workers were previously running in nodejs mode by default, remove this annotation to use bun
|
||||
' || content
|
||||
WHERE language = 'bun'
|
||||
AND dedicated_worker = true
|
||||
AND content !~ '^//\s*nodejs';
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE websocket_trigger DROP COLUMN heartbeat;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE websocket_trigger ADD COLUMN heartbeat JSONB NULL;
|
||||
@@ -22,22 +22,82 @@ pub fn parse_bash_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
args,
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing bash script".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
/// PowerShell common parameter names that are automatically added by [CmdletBinding()].
|
||||
/// These should be filtered from the parsed signature since they are not user-defined.
|
||||
const POWERSHELL_COMMON_PARAMS: &[&str] = &[
|
||||
"verbose",
|
||||
"debug",
|
||||
"erroraction",
|
||||
"errorvariable",
|
||||
"informationaction",
|
||||
"informationvariable",
|
||||
"outvariable",
|
||||
"outbuffer",
|
||||
"pipelinevariable",
|
||||
"warningaction",
|
||||
"warningvariable",
|
||||
"whatif",
|
||||
"confirm",
|
||||
"progressaction",
|
||||
];
|
||||
|
||||
/// Detects whether the script uses [CmdletBinding()] and whether it declares SupportsShouldProcess.
|
||||
fn detect_cmdlet_binding(code: &str) -> (bool, bool) {
|
||||
let attr_region = match extract_powershell_param_block_with_attributes(code, true) {
|
||||
Some((region, _)) => region,
|
||||
None => return (false, false),
|
||||
};
|
||||
// Strip comment lines to avoid false positives from commented-out [CmdletBinding()]
|
||||
let uncommented: String = attr_region
|
||||
.lines()
|
||||
.filter(|line| !line.trim_start().starts_with('#'))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let lower = uncommented.to_lowercase();
|
||||
let has_cmd_binding = lower.contains("[cmdletbinding");
|
||||
let supports_should_process = has_cmd_binding
|
||||
&& lower.contains("supportsshouldprocess")
|
||||
&& !lower.contains("supportsshouldprocess=$false")
|
||||
&& !lower.contains("supportsshouldprocess = $false");
|
||||
(has_cmd_binding, supports_should_process)
|
||||
}
|
||||
|
||||
pub fn parse_powershell_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let parsed = parse_powershell_file(&code)?;
|
||||
if let Some(x) = parsed {
|
||||
let args = x;
|
||||
if let Some(args) = parsed {
|
||||
let (has_cmd_binding, supports_should_process) = detect_cmdlet_binding(code);
|
||||
|
||||
// Filter out common parameters only when CmdletBinding is present
|
||||
// (without CmdletBinding, $Verbose etc. are regular user-defined parameters)
|
||||
let args = if has_cmd_binding {
|
||||
args.into_iter()
|
||||
.filter(|arg| !POWERSHELL_COMMON_PARAMS.contains(&arg.name.to_lowercase().as_str()))
|
||||
.collect()
|
||||
} else {
|
||||
args
|
||||
};
|
||||
|
||||
Ok(MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args,
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
has_cmd_binding: if has_cmd_binding { Some(true) } else { None },
|
||||
supports_should_process: if supports_should_process {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing powershell script".to_string()))
|
||||
@@ -96,7 +156,10 @@ fn parse_bash_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
|
||||
/// This function uses the existing extract_powershell_param_block validation, which already
|
||||
/// ensures that only comments, whitespace, and attributes appear before param. So we can
|
||||
/// simply return everything from the beginning to the end of the param block.
|
||||
pub fn extract_powershell_param_block_with_attributes(code: &str, include_attributes: bool) -> Option<(&str, &str)> {
|
||||
pub fn extract_powershell_param_block_with_attributes(
|
||||
code: &str,
|
||||
include_attributes: bool,
|
||||
) -> Option<(&str, &str)> {
|
||||
// First, use the existing function to validate and find the param block
|
||||
let param_block = extract_powershell_param_block(code, true)?;
|
||||
|
||||
@@ -450,11 +513,15 @@ fn parse_powershell_parameters(content: &str) -> anyhow::Result<Vec<Arg>> {
|
||||
|
||||
// Check if this is a Parameter attribute with Mandatory (case-insensitive)
|
||||
let lower = bracket_content.to_lowercase();
|
||||
if lower.starts_with("parameter(") || lower.starts_with("parameter ") {
|
||||
if lower.starts_with("parameter(")
|
||||
|| lower.starts_with("parameter ")
|
||||
{
|
||||
// Check for Mandatory (case-insensitive)
|
||||
if lower.contains("mandatory") {
|
||||
// Check if it's explicitly set to false
|
||||
if !lower.contains("mandatory=$false") && !lower.contains("mandatory = $false") {
|
||||
if !lower.contains("mandatory=$false")
|
||||
&& !lower.contains("mandatory = $false")
|
||||
{
|
||||
is_mandatory = true;
|
||||
}
|
||||
}
|
||||
@@ -471,7 +538,11 @@ fn parse_powershell_parameters(content: &str) -> anyhow::Result<Vec<Arg>> {
|
||||
// Check if this looks like a type (simple word, possibly with [])
|
||||
let is_type = !bracket_content.contains('(')
|
||||
&& !bracket_content.contains('=')
|
||||
&& (bracket_content.chars().next().unwrap_or(' ').is_alphabetic()
|
||||
&& (bracket_content
|
||||
.chars()
|
||||
.next()
|
||||
.unwrap_or(' ')
|
||||
.is_alphabetic()
|
||||
|| bracket_content.starts_with('['));
|
||||
|
||||
if is_type && !found_dollar {
|
||||
@@ -529,7 +600,9 @@ fn parse_powershell_parameters(content: &str) -> anyhow::Result<Vec<Arg>> {
|
||||
|
||||
while let Some((i, ch)) = chars.peek().copied() {
|
||||
if in_string {
|
||||
if ch == string_char && content.chars().nth(i.saturating_sub(1)) != Some('`') {
|
||||
if ch == string_char
|
||||
&& content.chars().nth(i.saturating_sub(1)) != Some('`')
|
||||
{
|
||||
in_string = false;
|
||||
default_end = i + 1;
|
||||
chars.next();
|
||||
@@ -544,7 +617,9 @@ fn parse_powershell_parameters(content: &str) -> anyhow::Result<Vec<Arg>> {
|
||||
chars.next();
|
||||
} else if ch == ',' {
|
||||
break;
|
||||
} else if ch.is_whitespace() && chars.clone().skip(1).next().map(|(_, c)| c) == Some(',') {
|
||||
} else if ch.is_whitespace()
|
||||
&& chars.clone().skip(1).next().map(|(_, c)| c) == Some(',')
|
||||
{
|
||||
break;
|
||||
} else {
|
||||
default_end = i + 1;
|
||||
@@ -552,12 +627,19 @@ fn parse_powershell_parameters(content: &str) -> anyhow::Result<Vec<Arg>> {
|
||||
}
|
||||
}
|
||||
|
||||
default_value = Some(content[default_start..default_end].trim().to_string());
|
||||
default_value =
|
||||
Some(content[default_start..default_end].trim().to_string());
|
||||
}
|
||||
',' => {
|
||||
// End of parameter, finalize it
|
||||
if let Some(name) = var_name.take() {
|
||||
args.push(finalize_parameter(name, type_annotation.take(), default_value.take(), is_mandatory, validate_set.take())?);
|
||||
args.push(finalize_parameter(
|
||||
name,
|
||||
type_annotation.take(),
|
||||
default_value.take(),
|
||||
is_mandatory,
|
||||
validate_set.take(),
|
||||
)?);
|
||||
}
|
||||
|
||||
// Reset for next parameter
|
||||
@@ -576,7 +658,13 @@ fn parse_powershell_parameters(content: &str) -> anyhow::Result<Vec<Arg>> {
|
||||
|
||||
// Finalize last parameter
|
||||
if let Some(name) = var_name {
|
||||
args.push(finalize_parameter(name, type_annotation, default_value, is_mandatory, validate_set)?);
|
||||
args.push(finalize_parameter(
|
||||
name,
|
||||
type_annotation,
|
||||
default_value,
|
||||
is_mandatory,
|
||||
validate_set,
|
||||
)?);
|
||||
}
|
||||
|
||||
Ok(args)
|
||||
@@ -722,7 +810,8 @@ non_required="${5:-}"
|
||||
}
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -813,14 +902,19 @@ non_required="${5:-}"
|
||||
Arg {
|
||||
otyp: Some("string".to_string()), // [string] (last type bracket with Mandatory and ValidateSet)
|
||||
name: "Message".to_string(),
|
||||
typ: Typ::Str(Some(vec!["Green".to_string(), "Blue".to_string(), "Red".to_string()])), // ValidateSet enum
|
||||
typ: Typ::Str(Some(vec![
|
||||
"Green".to_string(),
|
||||
"Blue".to_string(),
|
||||
"Red".to_string()
|
||||
])), // ValidateSet enum
|
||||
default: None,
|
||||
has_default: false, // Required (Mandatory attribute)
|
||||
oidx: None
|
||||
}
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
@@ -970,19 +1064,13 @@ non_required="${5:-}"
|
||||
|
||||
// Valid: CmdletBinding with comments
|
||||
assert_eq!(
|
||||
extract_powershell_param_block(
|
||||
"# My function\n[CmdletBinding()]\nparam($Name)",
|
||||
false
|
||||
),
|
||||
extract_powershell_param_block("# My function\n[CmdletBinding()]\nparam($Name)", false),
|
||||
Some("$Name")
|
||||
);
|
||||
|
||||
// Valid: CmdletBinding with whitespace variations
|
||||
assert_eq!(
|
||||
extract_powershell_param_block(
|
||||
"[CmdletBinding()] \n param($Name)",
|
||||
false
|
||||
),
|
||||
extract_powershell_param_block("[CmdletBinding()] \n param($Name)", false),
|
||||
Some("$Name")
|
||||
);
|
||||
|
||||
@@ -1204,17 +1292,32 @@ param(
|
||||
// Test with CmdletBinding with parameters
|
||||
let code3 = "[CmdletBinding(DefaultParameterSetName='ByName')]\nparam($Name, $Id)";
|
||||
let result3 = extract_powershell_param_block_with_attributes(code3, true);
|
||||
assert_eq!(result3, Some(("[CmdletBinding(DefaultParameterSetName='ByName')]\nparam($Name, $Id)", "")));
|
||||
assert_eq!(
|
||||
result3,
|
||||
Some((
|
||||
"[CmdletBinding(DefaultParameterSetName='ByName')]\nparam($Name, $Id)",
|
||||
""
|
||||
))
|
||||
);
|
||||
|
||||
// Test with multiple attributes
|
||||
let code4 = "[CmdletBinding()]\n[OutputType([string])]\nparam($Value)";
|
||||
let result4 = extract_powershell_param_block_with_attributes(code4, true);
|
||||
assert_eq!(result4, Some(("[CmdletBinding()]\n[OutputType([string])]\nparam($Value)", "")));
|
||||
assert_eq!(
|
||||
result4,
|
||||
Some((
|
||||
"[CmdletBinding()]\n[OutputType([string])]\nparam($Value)",
|
||||
""
|
||||
))
|
||||
);
|
||||
|
||||
// Test with comment before attributes
|
||||
let code5 = "# My function\n[CmdletBinding()]\nparam($Name)";
|
||||
let result5 = extract_powershell_param_block_with_attributes(code5, true);
|
||||
assert_eq!(result5, Some(("# My function\n[CmdletBinding()]\nparam($Name)", "")));
|
||||
assert_eq!(
|
||||
result5,
|
||||
Some(("# My function\n[CmdletBinding()]\nparam($Name)", ""))
|
||||
);
|
||||
|
||||
// Test with include_attributes = false (should only get param block, not attributes)
|
||||
let code6 = "[CmdletBinding()]\nparam($Name)";
|
||||
@@ -1224,7 +1327,10 @@ param(
|
||||
// Test with code after param
|
||||
let code7 = "[CmdletBinding()]\nparam($Name)\nWrite-Host 'Hello'";
|
||||
let result7 = extract_powershell_param_block_with_attributes(code7, true);
|
||||
assert_eq!(result7, Some(("[CmdletBinding()]\nparam($Name)", "\nWrite-Host 'Hello'")));
|
||||
assert_eq!(
|
||||
result7,
|
||||
Some(("[CmdletBinding()]\nparam($Name)", "\nWrite-Host 'Hello'"))
|
||||
);
|
||||
|
||||
// Test with code after param (without attributes)
|
||||
let code8 = "[CmdletBinding()]\nparam($Name)\nWrite-Host 'Hello'";
|
||||
@@ -1392,10 +1498,85 @@ param(
|
||||
}
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_detect_cmdlet_binding() {
|
||||
// Basic CmdletBinding
|
||||
let (has_cb, has_ssp) = detect_cmdlet_binding("[CmdletBinding()]\nparam($Name)");
|
||||
assert!(has_cb);
|
||||
assert!(!has_ssp);
|
||||
|
||||
// CmdletBinding with SupportsShouldProcess
|
||||
let (has_cb, has_ssp) =
|
||||
detect_cmdlet_binding("[CmdletBinding(SupportsShouldProcess=$true)]\nparam($Path)");
|
||||
assert!(has_cb);
|
||||
assert!(has_ssp);
|
||||
|
||||
// CmdletBinding with SupportsShouldProcess=false
|
||||
let (has_cb, has_ssp) =
|
||||
detect_cmdlet_binding("[CmdletBinding(SupportsShouldProcess=$false)]\nparam($Path)");
|
||||
assert!(has_cb);
|
||||
assert!(!has_ssp);
|
||||
|
||||
// No CmdletBinding
|
||||
let (has_cb, has_ssp) = detect_cmdlet_binding("param($Name)");
|
||||
assert!(!has_cb);
|
||||
assert!(!has_ssp);
|
||||
|
||||
// Case insensitive
|
||||
let (has_cb, has_ssp) =
|
||||
detect_cmdlet_binding("[cmdletbinding(supportsshouldprocess=$true)]\nparam($X)");
|
||||
assert!(has_cb);
|
||||
assert!(has_ssp);
|
||||
|
||||
// Commented out CmdletBinding should NOT be detected
|
||||
let (has_cb, has_ssp) =
|
||||
detect_cmdlet_binding("# [CmdletBinding(SupportsShouldProcess=$true)]\nparam($Path)");
|
||||
assert!(!has_cb);
|
||||
assert!(!has_ssp);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_powershell_common_param_filtering() -> anyhow::Result<()> {
|
||||
// Common parameters declared in param() should be filtered out
|
||||
let code = r#"[CmdletBinding()]
|
||||
param(
|
||||
[string]$Name,
|
||||
[switch]$Verbose,
|
||||
[string]$ErrorAction,
|
||||
[int]$Age
|
||||
)"#;
|
||||
let sig = parse_powershell_sig(code)?;
|
||||
assert_eq!(sig.args.len(), 2);
|
||||
assert_eq!(sig.args[0].name, "Name");
|
||||
assert_eq!(sig.args[1].name, "Age");
|
||||
assert_eq!(sig.has_cmd_binding, Some(true));
|
||||
assert_eq!(sig.supports_should_process, None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_powershell_sig_cmdlet_binding_metadata() -> anyhow::Result<()> {
|
||||
// Script without CmdletBinding
|
||||
let code = "param([string]$Name)";
|
||||
let sig = parse_powershell_sig(code)?;
|
||||
assert_eq!(sig.has_cmd_binding, None);
|
||||
assert_eq!(sig.supports_should_process, None);
|
||||
|
||||
// Script with CmdletBinding + SupportsShouldProcess
|
||||
let code = "[CmdletBinding(SupportsShouldProcess=$true)]\nparam([string]$Path)";
|
||||
let sig = parse_powershell_sig(code)?;
|
||||
assert_eq!(sig.has_cmd_binding, Some(true));
|
||||
assert_eq!(sig.supports_should_process, Some(true));
|
||||
assert_eq!(sig.args.len(), 1);
|
||||
assert_eq!(sig.args[0].name, "Path");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ pub fn parse_csharp_sig_meta(code: &str) -> anyhow::Result<CsharpMainSigMeta> {
|
||||
args,
|
||||
has_preprocessor: None,
|
||||
auto_kind,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(CsharpMainSigMeta { is_async, returns_void, class_name, main_sig, is_public })
|
||||
|
||||
@@ -43,6 +43,7 @@ pub fn parse_go_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
args,
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Ok(MainArgSignature {
|
||||
@@ -51,6 +52,7 @@ pub fn parse_go_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
args: vec![],
|
||||
auto_kind: Some("lib".to_string()),
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -244,7 +246,8 @@ func main(x int, y string, z bool, l []string, o struct { Name string `json:"nam
|
||||
},
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ pub fn parse_graphql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
args,
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
@@ -126,7 +127,8 @@ query($i: Int, $arr: [String]!, $wahoo: String = "wahoo") {
|
||||
}
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ pub fn parse_java_sig_meta(code: &str) -> anyhow::Result<JavaMainSigMeta> {
|
||||
args,
|
||||
has_preprocessor: None,
|
||||
auto_kind,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(JavaMainSigMeta { returns_void, class_name, main_sig, is_public })
|
||||
|
||||
@@ -56,6 +56,7 @@ mod test {
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
},
|
||||
sig
|
||||
);
|
||||
@@ -83,6 +84,7 @@ mod test {
|
||||
},],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
},
|
||||
sig
|
||||
);
|
||||
@@ -120,6 +122,7 @@ mod test {
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
},
|
||||
sig
|
||||
);
|
||||
@@ -232,6 +235,7 @@ mod test {
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
},
|
||||
sig
|
||||
);
|
||||
@@ -279,6 +283,7 @@ mod test {
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
},
|
||||
sig
|
||||
);
|
||||
@@ -343,6 +348,7 @@ mod test {
|
||||
// },],
|
||||
// auto_kind: None,
|
||||
// has_preprocessor: None,
|
||||
// ..Default::default()
|
||||
// },
|
||||
// sig
|
||||
// );
|
||||
@@ -373,6 +379,7 @@ mod test {
|
||||
},],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
},
|
||||
sig
|
||||
);
|
||||
@@ -420,6 +427,7 @@ mod test {
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
},
|
||||
sig
|
||||
);
|
||||
@@ -448,6 +456,7 @@ mod test {
|
||||
},],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
},
|
||||
sig
|
||||
);
|
||||
@@ -480,6 +489,7 @@ mod test {
|
||||
// },],
|
||||
// auto_kind: None,
|
||||
// has_preprocessor: None,
|
||||
// ..Default::default()
|
||||
// },
|
||||
// sig
|
||||
// );
|
||||
@@ -542,6 +552,7 @@ mod test {
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
},
|
||||
sig
|
||||
);
|
||||
@@ -635,6 +646,7 @@ mod test {
|
||||
// ],
|
||||
// auto_kind: None,
|
||||
// has_preprocessor: None,
|
||||
// ..Default::default()
|
||||
// },
|
||||
// sig
|
||||
// );
|
||||
|
||||
@@ -101,6 +101,7 @@ pub fn parse_php_signature(
|
||||
args,
|
||||
auto_kind: None,
|
||||
has_preprocessor,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Ok(MainArgSignature {
|
||||
@@ -109,6 +110,7 @@ pub fn parse_php_signature(
|
||||
args: vec![],
|
||||
auto_kind: Some("lib".to_string()),
|
||||
has_preprocessor,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -180,7 +182,8 @@ function main(string $input1 = \"hey\", bool $input2 = false, int $input3 = 3, f
|
||||
}
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -366,6 +366,7 @@ pub fn parse_python_signature(
|
||||
Some("lib".to_string())
|
||||
},
|
||||
has_preprocessor: Some(has_preprocessor),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -477,6 +478,7 @@ pub fn parse_python_signature(
|
||||
.collect(),
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(has_preprocessor),
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Ok(MainArgSignature {
|
||||
@@ -489,6 +491,7 @@ pub fn parse_python_signature(
|
||||
None
|
||||
},
|
||||
has_preprocessor: Some(has_preprocessor),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -755,7 +758,8 @@ def main(test1: str, name: datetime.datetime = datetime.now(), byte: bytes = byt
|
||||
},
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false)
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -820,7 +824,8 @@ def main(test1: str,
|
||||
}
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false)
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -880,7 +885,8 @@ def main(test1: str,
|
||||
}
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false)
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -924,7 +930,8 @@ def main(test1: Literal["foo", "bar"], test2: List[Literal["foo", "bar"]]): retu
|
||||
}
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false)
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -955,7 +962,8 @@ def main(test1: DynSelect_foo): return
|
||||
oidx: None
|
||||
}],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false)
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -979,7 +987,8 @@ def hello(): return
|
||||
star_kwargs: false,
|
||||
args: vec![],
|
||||
auto_kind: Some("lib".to_string()),
|
||||
has_preprocessor: Some(false)
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1007,7 +1016,8 @@ def main(): return
|
||||
star_kwargs: false,
|
||||
args: vec![],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(true)
|
||||
has_preprocessor: Some(true),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1072,7 +1082,8 @@ def main(a: list, e: List[int], b: list = [1,2,3,4], c = [1,2,3,4], d = ["a", "b
|
||||
}
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false)
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1121,7 +1132,8 @@ def main(a: str, b: Optional[str], c: str | None): return
|
||||
},
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false)
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
17
backend/parsers/windmill-parser-r/Cargo.toml
Normal file
17
backend/parsers/windmill-parser-r/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "windmill-parser-r"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "windmill_parser_r"
|
||||
path = "./src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
windmill-parser.workspace = true
|
||||
tree-sitter.workspace = true
|
||||
tree-sitter-r.workspace = true
|
||||
anyhow.workspace = true
|
||||
wasm-bindgen.workspace = true
|
||||
serde_json.workspace = true
|
||||
364
backend/parsers/windmill-parser-r/src/lib.rs
Normal file
364
backend/parsers/windmill-parser-r/src/lib.rs
Normal file
@@ -0,0 +1,364 @@
|
||||
#![cfg_attr(target_arch = "wasm32", feature(c_variadic))]
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod wasm_libc;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use serde_json::Value;
|
||||
use tree_sitter::Node;
|
||||
use tree_sitter::Range;
|
||||
use windmill_parser::json_to_typ;
|
||||
use windmill_parser::Arg;
|
||||
use windmill_parser::MainArgSignature;
|
||||
|
||||
pub fn parse_r_sig_meta(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let mut parser = tree_sitter::Parser::new();
|
||||
let language = tree_sitter_r::LANGUAGE;
|
||||
parser
|
||||
.set_language(&language.into())
|
||||
.map_err(|e| anyhow!("Error setting R as language: {e}"))?;
|
||||
|
||||
let tree = parser
|
||||
.parse(code, None)
|
||||
.ok_or(anyhow!("Failed to parse code"))?;
|
||||
let root_node = tree.root_node();
|
||||
|
||||
let args = find_main_signature(root_node, code)?;
|
||||
let main_sig = MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args: args.unwrap_or_default(),
|
||||
has_preprocessor: None,
|
||||
auto_kind: None,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(main_sig)
|
||||
}
|
||||
|
||||
pub fn parse_r_signature(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
Ok(parse_r_sig_meta(code)?)
|
||||
}
|
||||
|
||||
/// Extract package names from `library(...)` and `require(...)` calls in R code.
|
||||
/// Returns a newline-separated list of package names.
|
||||
pub fn parse_r_requirements(code: &str) -> anyhow::Result<String> {
|
||||
let mut parser = tree_sitter::Parser::new();
|
||||
let language = tree_sitter_r::LANGUAGE;
|
||||
parser
|
||||
.set_language(&language.into())
|
||||
.map_err(|e| anyhow!("Error setting R as language: {e}"))?;
|
||||
|
||||
let tree = parser
|
||||
.parse(code, None)
|
||||
.ok_or(anyhow!("Failed to parse code"))?;
|
||||
let root_node = tree.root_node();
|
||||
|
||||
let mut packages = vec![];
|
||||
find_library_calls(root_node, code, &mut packages);
|
||||
|
||||
// Deduplicate and exclude base packages
|
||||
packages.sort();
|
||||
packages.dedup();
|
||||
packages.retain(|p| !is_base_package(p));
|
||||
|
||||
Ok(packages.join("\n"))
|
||||
}
|
||||
|
||||
fn find_library_calls(node: Node, code: &str, packages: &mut Vec<String>) {
|
||||
let mut cursor = node.walk();
|
||||
for child in node.children(&mut cursor) {
|
||||
if child.kind() == "call" {
|
||||
// call node: child 0 is the function name, child 1 is arguments
|
||||
if let (Some(func_node), Some(args_node)) = (child.child(0), child.child(1)) {
|
||||
let func_name = func_node.utf8_text(code.as_bytes()).unwrap_or("");
|
||||
if func_name == "library" || func_name == "require" {
|
||||
// AST: arguments → ( + argument → identifier/string + )
|
||||
if args_node.kind() == "arguments" {
|
||||
let mut args_cursor = args_node.walk();
|
||||
for arg in args_node.children(&mut args_cursor) {
|
||||
if arg.kind() == "argument" {
|
||||
// The argument node wraps the actual value
|
||||
if let Some(value_node) = arg.child(0) {
|
||||
let pkg = value_node
|
||||
.utf8_text(code.as_bytes())
|
||||
.unwrap_or("")
|
||||
.trim_matches('"')
|
||||
.trim_matches('\'');
|
||||
if !pkg.is_empty() {
|
||||
packages.push(pkg.to_string());
|
||||
}
|
||||
}
|
||||
break; // only first arg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Recurse into children to find nested library() calls
|
||||
find_library_calls(child, code, packages);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_base_package(pkg: &str) -> bool {
|
||||
matches!(
|
||||
pkg,
|
||||
"base"
|
||||
| "compiler"
|
||||
| "datasets"
|
||||
| "grDevices"
|
||||
| "graphics"
|
||||
| "grid"
|
||||
| "methods"
|
||||
| "parallel"
|
||||
| "splines"
|
||||
| "stats"
|
||||
| "stats4"
|
||||
| "tcltk"
|
||||
| "tools"
|
||||
| "utils"
|
||||
)
|
||||
}
|
||||
|
||||
/// Find the main function signature in R code.
|
||||
/// R function definitions look like: `main <- function(x, y = 10) { ... }`
|
||||
/// In the tree-sitter-r AST, this is a `binary_operator` node with:
|
||||
/// - child 0: identifier "main"
|
||||
/// - child 1: "<-" or "="
|
||||
/// - child 2: function_definition node
|
||||
fn find_main_signature<'a>(root_node: Node<'a>, code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
|
||||
let mut cursor = root_node.walk();
|
||||
for x in root_node.children(&mut cursor) {
|
||||
if x.kind() == "binary_operator" {
|
||||
let child_count = x.child_count();
|
||||
if child_count < 3 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// First child should be identifier "main"
|
||||
let ident_node = x.child(0).unwrap();
|
||||
if ident_node.kind() != "identifier" {
|
||||
continue;
|
||||
}
|
||||
let ident = ident_node.utf8_text(code.as_bytes()).unwrap_or("");
|
||||
if ident != "main" {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Second child should be "<-" or "="
|
||||
let op_node = x.child(1).unwrap();
|
||||
let op = op_node.utf8_text(code.as_bytes()).unwrap_or("");
|
||||
if op != "<-" && op != "=" {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Third child should be the function_definition
|
||||
let func_node = x.child(2).unwrap();
|
||||
if func_node.kind() != "function_definition" {
|
||||
continue;
|
||||
}
|
||||
|
||||
return Ok(Some(parse_function_params(func_node, code)?));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Parse parameters from a function_definition node.
|
||||
/// function_definition has children: "function", parameters, body
|
||||
/// Each parameter node has:
|
||||
/// - 1 child (identifier) for positional args
|
||||
/// - 3 children (identifier, "=", value) for default args
|
||||
fn parse_function_params(func_node: Node, code: &str) -> anyhow::Result<Vec<Arg>> {
|
||||
let mut args = vec![];
|
||||
let mut func_cursor = func_node.walk();
|
||||
|
||||
for child in func_node.children(&mut func_cursor) {
|
||||
if child.kind() == "parameters" {
|
||||
let mut param_cursor = child.walk();
|
||||
for param in child.children(&mut param_cursor) {
|
||||
if param.kind() != "parameter" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let param_child_count = param.child_count();
|
||||
if param_child_count == 1 {
|
||||
// Simple parameter: just identifier
|
||||
let ident_node = param.child(0).unwrap();
|
||||
let name = ident_node.utf8_text(code.as_bytes())?;
|
||||
args.push(Arg { name: name.to_owned(), ..Default::default() });
|
||||
} else if param_child_count >= 3 {
|
||||
// Default parameter: identifier = value
|
||||
let ident_node = param.child(0).unwrap();
|
||||
let value_node = param.child(2).unwrap();
|
||||
let name = ident_node.utf8_text(code.as_bytes())?;
|
||||
|
||||
let Range { start_byte, end_byte, .. } = value_node.range();
|
||||
let raw = &code[start_byte..end_byte];
|
||||
// Convert R literals to JSON
|
||||
let unparsed = raw
|
||||
.replace("NULL", "null")
|
||||
.replace("TRUE", "true")
|
||||
.replace("FALSE", "false");
|
||||
match serde_json::from_str::<Value>(&unparsed) {
|
||||
Ok(default) => {
|
||||
args.push(Arg {
|
||||
name: name.to_owned(),
|
||||
typ: json_to_typ(&default, true),
|
||||
default: Some(default),
|
||||
has_default: true,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
Err(_) => {
|
||||
args.push(Arg {
|
||||
name: name.to_owned(),
|
||||
has_default: true,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use serde_json::json;
|
||||
use windmill_parser::Typ;
|
||||
|
||||
use super::parse_r_sig_meta as parse;
|
||||
|
||||
#[test]
|
||||
fn test_parse_r_no_main() {
|
||||
let code = r#"
|
||||
not_main <- function() {}
|
||||
helper <- function(x) { x + 1 }
|
||||
"#;
|
||||
let sig = parse(code).unwrap();
|
||||
assert_eq!(
|
||||
sig,
|
||||
windmill_parser::MainArgSignature { auto_kind: None, ..Default::default() }
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_r_no_args() {
|
||||
let code = r#"
|
||||
main <- function() {
|
||||
return(42)
|
||||
}
|
||||
"#;
|
||||
let sig = parse(code).unwrap();
|
||||
assert_eq!(
|
||||
sig,
|
||||
windmill_parser::MainArgSignature { auto_kind: None, ..Default::default() }
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_r_positional_args() {
|
||||
let code = r#"main <- function(a, b, c) { a + b + c }"#;
|
||||
let sig = parse(code).unwrap();
|
||||
assert_eq!(
|
||||
sig,
|
||||
windmill_parser::MainArgSignature {
|
||||
args: vec![
|
||||
windmill_parser::Arg { name: "a".into(), ..Default::default() },
|
||||
windmill_parser::Arg { name: "b".into(), ..Default::default() },
|
||||
windmill_parser::Arg { name: "c".into(), ..Default::default() },
|
||||
],
|
||||
auto_kind: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_r_default_args() {
|
||||
let code = r#"main <- function(a = 10, b = "hey", c = FALSE) { }"#;
|
||||
let sig = parse(code).unwrap();
|
||||
assert_eq!(sig.args.len(), 3);
|
||||
assert_eq!(sig.args[0].name, "a");
|
||||
assert_eq!(sig.args[0].default, Some(json!(10)));
|
||||
assert_eq!(sig.args[0].typ, Typ::Int);
|
||||
assert_eq!(sig.args[1].name, "b");
|
||||
assert_eq!(sig.args[1].default, Some(json!("hey")));
|
||||
assert_eq!(sig.args[1].typ, Typ::Str(None));
|
||||
assert_eq!(sig.args[2].name, "c");
|
||||
assert_eq!(sig.args[2].default, Some(json!(false)));
|
||||
assert_eq!(sig.args[2].typ, Typ::Bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_r_equals_assignment() {
|
||||
let code = r#"main = function(x, y = 5) { x + y }"#;
|
||||
let sig = parse(code).unwrap();
|
||||
assert_eq!(sig.args.len(), 2);
|
||||
assert_eq!(sig.args[0].name, "x");
|
||||
assert_eq!(sig.args[1].name, "y");
|
||||
assert_eq!(sig.args[1].default, Some(json!(5)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_r_null_default() {
|
||||
let code = r#"main <- function(x = NULL) { x }"#;
|
||||
let sig = parse(code).unwrap();
|
||||
assert_eq!(sig.args.len(), 1);
|
||||
assert_eq!(sig.args[0].name, "x");
|
||||
assert_eq!(sig.args[0].default, Some(json!(null)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_r_requirements() {
|
||||
use super::parse_r_requirements;
|
||||
|
||||
let code = r#"
|
||||
library(dplyr)
|
||||
library(ggplot2)
|
||||
require(tidyr)
|
||||
library(stats)
|
||||
|
||||
main <- function(x) {
|
||||
library(stringr)
|
||||
x
|
||||
}
|
||||
"#;
|
||||
let reqs = parse_r_requirements(code).unwrap();
|
||||
let pkgs: Vec<&str> = reqs.lines().collect();
|
||||
assert!(pkgs.contains(&"dplyr"));
|
||||
assert!(pkgs.contains(&"ggplot2"));
|
||||
assert!(pkgs.contains(&"tidyr"));
|
||||
assert!(pkgs.contains(&"stringr"));
|
||||
assert!(!pkgs.contains(&"stats")); // base package excluded
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_r_requirements_string_args() {
|
||||
use super::parse_r_requirements;
|
||||
|
||||
let code = r#"
|
||||
library("data.table")
|
||||
require("jsonlite")
|
||||
|
||||
main <- function() { }
|
||||
"#;
|
||||
let reqs = parse_r_requirements(code).unwrap();
|
||||
let pkgs: Vec<&str> = reqs.lines().collect();
|
||||
assert!(pkgs.contains(&"data.table"));
|
||||
assert!(pkgs.contains(&"jsonlite"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_r_requirements_no_deps() {
|
||||
use super::parse_r_requirements;
|
||||
|
||||
let code = r#"main <- function(x) { x + 1 }"#;
|
||||
let reqs = parse_r_requirements(code).unwrap();
|
||||
assert!(reqs.is_empty());
|
||||
}
|
||||
}
|
||||
293
backend/parsers/windmill-parser-r/src/wasm_libc.rs
Normal file
293
backend/parsers/windmill-parser-r/src/wasm_libc.rs
Normal file
@@ -0,0 +1,293 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
use std::{
|
||||
alloc::{self, Layout},
|
||||
ffi::{c_char, c_int, c_void},
|
||||
mem::align_of,
|
||||
ptr,
|
||||
};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
/* -------------------------------- stdlib.h -------------------------------- */
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn abort() {
|
||||
panic!("Aborted from C");
|
||||
}
|
||||
|
||||
macro_rules! console_log {
|
||||
($($t:tt)*) => (unsafe { log(&format_args!($($t)*).to_string()) })
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(a: &str);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn malloc(size: usize) -> *mut c_void {
|
||||
if size == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let (layout, offset_to_data) = layout_for_size_prepended(size);
|
||||
let buf = alloc::alloc(layout);
|
||||
store_layout(buf, layout, offset_to_data)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn calloc(count: usize, size: usize) -> *mut c_void {
|
||||
if count == 0 || size == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let (layout, offset_to_data) = layout_for_size_prepended(size * count);
|
||||
let buf = alloc::alloc_zeroed(layout);
|
||||
store_layout(buf, layout, offset_to_data)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn realloc(buf: *mut c_void, new_size: usize) -> *mut c_void {
|
||||
if buf.is_null() {
|
||||
malloc(new_size)
|
||||
} else if new_size == 0 {
|
||||
free(buf);
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
let (old_buf, old_layout) = retrieve_layout(buf);
|
||||
let (new_layout, offset_to_data) = layout_for_size_prepended(new_size);
|
||||
let new_buf = alloc::realloc(old_buf, old_layout, new_layout.size());
|
||||
store_layout(new_buf, new_layout, offset_to_data)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn free(buf: *mut c_void) {
|
||||
if buf.is_null() {
|
||||
return;
|
||||
}
|
||||
let (buf, layout) = retrieve_layout(buf);
|
||||
alloc::dealloc(buf, layout);
|
||||
}
|
||||
|
||||
// In all these allocations, we store the layout before the data for later retrieval.
|
||||
// This is because we need to know the layout when deallocating the memory.
|
||||
// Here are some helper methods for that:
|
||||
|
||||
/// Given a pointer to the data, retrieve the layout and the pointer to the layout.
|
||||
unsafe fn retrieve_layout(buf: *mut c_void) -> (*mut u8, Layout) {
|
||||
let (_, layout_offset) = Layout::new::<Layout>()
|
||||
.extend(Layout::from_size_align(0, align_of::<*const u8>() * 2).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let buf = (buf as *mut u8).offset(-(layout_offset as isize));
|
||||
let layout = *(buf as *mut Layout);
|
||||
|
||||
(buf, layout)
|
||||
}
|
||||
|
||||
/// Calculate a layout for a given size with space for storing a layout at the start.
|
||||
/// Returns the layout and the offset to the data.
|
||||
fn layout_for_size_prepended(size: usize) -> (Layout, usize) {
|
||||
Layout::new::<Layout>()
|
||||
.extend(Layout::from_size_align(size, align_of::<*const u8>() * 2).unwrap())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Store a layout in the pointer, returning a pointer to where the data should be stored.
|
||||
unsafe fn store_layout(buf: *mut u8, layout: Layout, offset_to_data: usize) -> *mut c_void {
|
||||
*(buf as *mut Layout) = layout;
|
||||
(buf as *mut u8).offset(offset_to_data as isize) as *mut c_void
|
||||
}
|
||||
|
||||
/* -------------------------------- string.h -------------------------------- */
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strncmp(ptr1: *const c_void, ptr2: *const c_void, n: usize) -> c_int {
|
||||
let s1 = std::slice::from_raw_parts(ptr1 as *const u8, n);
|
||||
let s2 = std::slice::from_raw_parts(ptr2 as *const u8, n);
|
||||
|
||||
for (a, b) in s1.iter().zip(s2.iter()) {
|
||||
if *a != *b || *a == 0 {
|
||||
return (*a as i32) - (*b as i32);
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
// Implementation by AI:
|
||||
pub type size_t = usize;
|
||||
use std::slice;
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn memchr(haystack: *const c_void, needle: c_int, len: usize) -> *mut c_void {
|
||||
if haystack.is_null() || len == 0 {
|
||||
return ptr::null_mut(); // Return null if the input pointer is null or length is zero
|
||||
}
|
||||
|
||||
let needle_byte = needle as u8; // Convert needle to a byte
|
||||
|
||||
// Create a pointer to the start of the haystack
|
||||
let mut current = haystack as *const u8;
|
||||
|
||||
// Iterate through the memory block
|
||||
for _ in 0..len {
|
||||
if *current == needle_byte {
|
||||
return current as *mut c_void; // Return the pointer to the found byte
|
||||
}
|
||||
current = current.add(1); // Move to the next byte
|
||||
}
|
||||
|
||||
ptr::null_mut() // Return null if the byte was not found
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strchr(mut s: *const c_char, c: c_int) -> *mut c_char {
|
||||
if s.is_null() {
|
||||
return std::ptr::null_mut(); // Return null if the input string is null
|
||||
}
|
||||
|
||||
let target = c as u8 as char; // Convert c to a char
|
||||
let mut current = s;
|
||||
|
||||
// Iterate through the string until we find the character or reach the end
|
||||
while *current != 0 {
|
||||
if *current as u8 as char == target {
|
||||
return current as *mut c_char; // Return the pointer to the found character
|
||||
}
|
||||
current = current.add(1); // Move to the next character
|
||||
}
|
||||
|
||||
std::ptr::null_mut() // Return null if the character was not found
|
||||
}
|
||||
// End of AI implemetation
|
||||
/* -------------------------------- wctype.h -------------------------------- */
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn iswspace(c: c_int) -> bool {
|
||||
char::from_u32(c as u32).map_or(false, |c| c.is_whitespace())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn iswalnum(c: c_int) -> bool {
|
||||
char::from_u32(c as u32).map_or(false, |c| c.is_alphanumeric())
|
||||
}
|
||||
|
||||
// Implementation by AI:
|
||||
pub type wint_t = u32;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn iswdigit(wc: wint_t) -> c_int {
|
||||
// Check if the character is a digit ('0' to '9')
|
||||
if wc >= '0' as wint_t && wc <= '9' as wint_t {
|
||||
return 1; // Return true (1)
|
||||
}
|
||||
0 // Return false (0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn iswupper(wc: wint_t) -> c_int {
|
||||
// Check if the character is an uppercase letter ('A' to 'Z')
|
||||
if wc >= 'A' as wint_t && wc <= 'Z' as wint_t {
|
||||
return 1; // Return true (1)
|
||||
}
|
||||
0 // Return false (0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn iswalpha(wc: wint_t) -> c_int {
|
||||
// Check if the character is an alphabetic character ('A' to 'Z' or 'a' to 'z')
|
||||
if (wc >= 'A' as wint_t && wc <= 'Z' as wint_t) || (wc >= 'a' as wint_t && wc <= 'z' as wint_t)
|
||||
{
|
||||
return 1; // Return true (1)
|
||||
}
|
||||
0 // Return false (0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn iswlower(wc: wint_t) -> c_int {
|
||||
// Check if the character is a lowercase letter ('a' to 'z')
|
||||
if wc >= 'a' as wint_t && wc <= 'z' as wint_t {
|
||||
return 1; // Return true (1)
|
||||
}
|
||||
0 // Return false (0)
|
||||
}
|
||||
// End of AI implemetation
|
||||
|
||||
/* --------------------------------- time.h --------------------------------- */
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn clock() -> u64 {
|
||||
panic!("clock is not supported");
|
||||
}
|
||||
|
||||
/* --------------------------------- ctype.h -------------------------------- */
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn isprint(c: c_int) -> bool {
|
||||
c >= 32 && c <= 126
|
||||
}
|
||||
|
||||
/* --------------------------------- stdio.h -------------------------------- */
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fprintf(_file: *mut c_void, _format: *const c_void, _args: ...) -> c_int {
|
||||
panic!("fprintf is not supported");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fputs(_s: *const c_void, _file: *mut c_void) -> c_int {
|
||||
panic!("fputs is not supported");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fputc(_c: c_int, _file: *mut c_void) -> c_int {
|
||||
panic!("fputc is not supported");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fdopen(_fd: c_int, _mode: *const c_void) -> *mut c_void {
|
||||
panic!("fdopen is not supported");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fclose(_file: *mut c_void) -> c_int {
|
||||
panic!("fclose is not supported");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fwrite(
|
||||
_ptr: *const c_void,
|
||||
_size: usize,
|
||||
_nmemb: usize,
|
||||
_stream: *mut c_void,
|
||||
) -> usize {
|
||||
panic!("fwrite is not supported");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn vsnprintf(
|
||||
_buf: *mut c_char,
|
||||
_size: usize,
|
||||
_format: *const c_char,
|
||||
_args: ...
|
||||
) -> c_int {
|
||||
panic!("vsnprintf is not supported");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn clock_gettime(ptr: usize, new_size: usize) {
|
||||
panic!("clock_gettime is not supported");
|
||||
}
|
||||
|
||||
// int snprintf( char* restrict buffer, size_t bufsz, const char* restrict format, ... );
|
||||
#[no_mangle]
|
||||
pub extern "C" fn snprintf() {
|
||||
panic!("snprintf is not supported");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn __assert_fail(_: *const i32, _: *const i32, _: *const i32, _: *const i32) {
|
||||
panic!("oh no");
|
||||
}
|
||||
@@ -41,6 +41,7 @@ pub fn parse_ruby_sig_meta(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
args: args.unwrap_or_default(),
|
||||
has_preprocessor: None,
|
||||
auto_kind,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(main_sig)
|
||||
|
||||
@@ -30,6 +30,7 @@ pub fn parse_rust_signature(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
args,
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Ok(MainArgSignature {
|
||||
@@ -38,6 +39,7 @@ pub fn parse_rust_signature(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
args: vec![],
|
||||
auto_kind: Some("lib".to_string()),
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ pub fn parse_mysql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
args,
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
@@ -46,6 +47,7 @@ pub fn parse_oracledb_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
args,
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
@@ -67,6 +69,7 @@ pub fn parse_pgsql_sig_with_typed_schema(code: &str) -> anyhow::Result<(MainArgS
|
||||
args,
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
},
|
||||
typed_schema,
|
||||
))
|
||||
@@ -85,6 +88,7 @@ pub fn parse_bigquery_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
args,
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
@@ -100,6 +104,7 @@ pub fn parse_duckdb_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
args,
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
@@ -116,6 +121,7 @@ pub fn parse_snowflake_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
args,
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
@@ -132,6 +138,7 @@ pub fn parse_mssql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
args,
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
@@ -944,7 +951,8 @@ SELECT * FROM table WHERE token=$1::TEXT AND image=$2::BIGINT
|
||||
},
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -993,7 +1001,8 @@ SELECT $2::TEXT;
|
||||
},
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1120,7 +1129,8 @@ SELECT ?, ?;
|
||||
},
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1168,7 +1178,8 @@ SELECT :param2;
|
||||
},
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1208,7 +1219,8 @@ SELECT @token;
|
||||
},
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1256,7 +1268,8 @@ SELECT ?;
|
||||
}
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1304,7 +1317,8 @@ SELECT @P2;
|
||||
},
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1353,7 +1367,8 @@ SELECT * FROM table_name WHERE thing = :name4;
|
||||
},
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1391,7 +1406,8 @@ SELECT * FROM users WHERE id = $1 AND email = $2::text;
|
||||
},
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1429,7 +1445,8 @@ SELECT * FROM users LIMIT $1 OFFSET $2;
|
||||
},
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1479,7 +1496,8 @@ WHERE id = $1
|
||||
},
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1506,7 +1524,8 @@ SELECT * FROM users WHERE id = ANY($1);
|
||||
oidx: Some(1),
|
||||
},],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1535,7 +1554,8 @@ SELECT $1::integer;
|
||||
oidx: Some(1),
|
||||
},],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1567,7 +1587,8 @@ SELECT x
|
||||
oidx: None,
|
||||
},],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -106,6 +106,28 @@ fn extract_wmill_datatable_call(expr: &Expr) -> Option<(AssetKind, String, Optio
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Check if an expression is `tag.raw(...)` where `tag` matches the given tag name.
|
||||
/// Returns true for patterns like `sql.raw(x)`.
|
||||
fn is_raw_call(expr: &Expr, tag_name: &str) -> bool {
|
||||
if let Expr::Call(call_expr) = expr {
|
||||
if let Some(Expr::Member(member)) = call_expr.callee.as_expr().map(AsRef::as_ref) {
|
||||
let is_tag = matches!(
|
||||
member.obj.as_ref(),
|
||||
Expr::Ident(ident) if ident.sym.as_str() == tag_name
|
||||
);
|
||||
if is_tag {
|
||||
if let MemberProp::Ident(prop) = &member.prop {
|
||||
return prop.sym.as_str() == "raw";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
const WM_SQL_RAW_PLACEHOLDER: &str = "__WM_SQL_RAW__";
|
||||
|
||||
impl Visit for AssetsFinder {
|
||||
// visit_call_expr will not recurse if it detects an asset,
|
||||
// so this will only be called when no further context was found
|
||||
@@ -230,21 +252,31 @@ impl Visit for AssetsFinder {
|
||||
return;
|
||||
};
|
||||
|
||||
// Extract the SQL query from the template quasis (string parts)
|
||||
// Substitute ${} with $1, $2, etc.
|
||||
let sql: String = node
|
||||
// Determine which interpolations are sql.raw() calls
|
||||
let raw_flags: Vec<bool> = node
|
||||
.tpl
|
||||
.quasis
|
||||
.exprs
|
||||
.iter()
|
||||
.map(|quasi| quasi.raw.as_str())
|
||||
.enumerate()
|
||||
.fold(String::new(), |acc, (i, s)| {
|
||||
if i == 0 {
|
||||
s.to_string()
|
||||
.map(|expr| is_raw_call(expr.as_ref(), tag_name))
|
||||
.collect();
|
||||
let has_raw_interpolation = raw_flags.iter().any(|&r| r);
|
||||
|
||||
// Extract the SQL query from the template quasis (string parts)
|
||||
// Substitute ${} with $N for normal args, __WM_SQL_RAW__ for raw args
|
||||
let mut sql = String::new();
|
||||
let mut arg_index = 0usize;
|
||||
for (i, quasi) in node.tpl.quasis.iter().enumerate() {
|
||||
if i > 0 {
|
||||
let is_raw = raw_flags.get(i - 1).copied().unwrap_or(false);
|
||||
if is_raw {
|
||||
sql.push_str(WM_SQL_RAW_PLACEHOLDER);
|
||||
} else {
|
||||
format!("{}${}{}", acc, i, s)
|
||||
arg_index += 1;
|
||||
sql.push_str(&format!("${}", arg_index));
|
||||
}
|
||||
});
|
||||
}
|
||||
sql.push_str(quasi.raw.as_str());
|
||||
}
|
||||
|
||||
// Capture SQL query details before transforming for SQL parser
|
||||
let span = node.span();
|
||||
@@ -256,6 +288,7 @@ impl Visit for AssetsFinder {
|
||||
source_kind: *kind,
|
||||
source_name: asset_name.clone(),
|
||||
source_schema: schema.clone(),
|
||||
has_raw_interpolation,
|
||||
});
|
||||
|
||||
// We use the SQL parser to detect RW, specific tables, etc.
|
||||
@@ -266,7 +299,11 @@ impl Visit for AssetsFinder {
|
||||
&sql,
|
||||
);
|
||||
match sql_assets {
|
||||
Ok(Some(sql_assets)) => self.assets.extend(sql_assets),
|
||||
Ok(Some(sql_assets)) => self.assets.extend(
|
||||
sql_assets
|
||||
.into_iter()
|
||||
.filter(|a| !a.path.contains(WM_SQL_RAW_PLACEHOLDER)),
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -629,6 +666,7 @@ mod tests {
|
||||
assert_eq!(query_detail.source_kind, AssetKind::DataTable);
|
||||
assert_eq!(query_detail.source_name, "dt");
|
||||
assert_eq!(query_detail.source_schema, None);
|
||||
assert_eq!(query_detail.has_raw_interpolation, false);
|
||||
// Span should be non-zero
|
||||
assert!(query_detail.span.0 > 0);
|
||||
assert!(query_detail.span.1 > query_detail.span.0);
|
||||
@@ -692,5 +730,86 @@ mod tests {
|
||||
assert_eq!(query_detail.source_kind, AssetKind::Ducklake);
|
||||
assert_eq!(query_detail.source_name, "my_lake");
|
||||
assert_eq!(query_detail.source_schema, None);
|
||||
assert_eq!(query_detail.has_raw_interpolation, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ts_asset_parser_sql_raw_basic() {
|
||||
let input = r#"
|
||||
import * as wmill from "windmill-client"
|
||||
export async function main(table: string) {
|
||||
let sql = wmill.datatable('dt')
|
||||
return await sql`SELECT * FROM ${sql.raw(table)}`.fetch()
|
||||
}
|
||||
"#;
|
||||
let result = parse_assets(input).unwrap();
|
||||
|
||||
// sql.raw in table position => the __WM_SQL_RAW__ asset gets filtered out,
|
||||
// but the datatable itself is still tracked as "used" (without specific table info)
|
||||
assert_eq!(
|
||||
result.assets,
|
||||
vec![ParseAssetsResult {
|
||||
kind: AssetKind::DataTable,
|
||||
path: "dt".to_string(),
|
||||
access_type: None,
|
||||
columns: None,
|
||||
}]
|
||||
);
|
||||
|
||||
// Check SQL query details
|
||||
assert_eq!(result.sql_queries.len(), 1);
|
||||
let q = &result.sql_queries[0];
|
||||
assert!(q.query_string.contains("__WM_SQL_RAW__"));
|
||||
assert!(!q.query_string.contains("$1"));
|
||||
assert_eq!(q.has_raw_interpolation, true);
|
||||
assert_eq!(q.source_kind, AssetKind::DataTable);
|
||||
assert_eq!(q.source_name, "dt");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ts_asset_parser_sql_raw_mixed() {
|
||||
let input = r#"
|
||||
import * as wmill from "windmill-client"
|
||||
export async function main(name: string, col: string, val: number) {
|
||||
let sql = wmill.datatable('dt')
|
||||
return await sql`SELECT * FROM users WHERE name = ${name} AND ${sql.raw(col)} = ${val}`.fetch()
|
||||
}
|
||||
"#;
|
||||
let result = parse_assets(input).unwrap();
|
||||
|
||||
// "users" table should still be detected
|
||||
assert_eq!(result.assets.len(), 1);
|
||||
assert_eq!(result.assets[0].path, "dt/users");
|
||||
assert_eq!(result.assets[0].access_type, Some(R));
|
||||
|
||||
// Check SQL query details — arg numbering skips the raw interpolation
|
||||
assert_eq!(result.sql_queries.len(), 1);
|
||||
let q = &result.sql_queries[0];
|
||||
assert_eq!(
|
||||
q.query_string,
|
||||
"SELECT * FROM users WHERE name = $1 AND __WM_SQL_RAW__ = $2"
|
||||
);
|
||||
assert_eq!(q.has_raw_interpolation, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ts_asset_parser_sql_raw_no_false_positive() {
|
||||
// Ensure that a normal query (no sql.raw) still has has_raw_interpolation=false
|
||||
let input = r#"
|
||||
import * as wmill from "windmill-client"
|
||||
export async function main(x: number) {
|
||||
let sql = wmill.datatable('dt')
|
||||
return await sql`SELECT * FROM friends WHERE age = ${x}`.fetch()
|
||||
}
|
||||
"#;
|
||||
let result = parse_assets(input).unwrap();
|
||||
assert_eq!(result.sql_queries.len(), 1);
|
||||
assert_eq!(result.sql_queries[0].has_raw_interpolation, false);
|
||||
assert_eq!(
|
||||
result.sql_queries[0].query_string,
|
||||
"SELECT * FROM friends WHERE age = $1"
|
||||
);
|
||||
assert_eq!(result.assets.len(), 1);
|
||||
assert_eq!(result.assets[0].path, "dt/friends");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,6 +462,7 @@ pub fn parse_deno_signature(
|
||||
},
|
||||
auto_kind,
|
||||
has_preprocessor: Some(has_preprocessor),
|
||||
..Default::default()
|
||||
};
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ mod tests {
|
||||
args: vec![],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -105,6 +106,7 @@ mod tests {
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -154,6 +156,7 @@ mod tests {
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -203,6 +206,7 @@ mod tests {
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -234,6 +238,7 @@ mod tests {
|
||||
},],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -265,6 +270,7 @@ mod tests {
|
||||
},],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -305,6 +311,7 @@ mod tests {
|
||||
}],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -343,6 +350,7 @@ mod tests {
|
||||
}],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -401,6 +409,7 @@ mod tests {
|
||||
}],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -459,6 +468,7 @@ mod tests {
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -508,6 +518,7 @@ mod tests {
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -557,6 +568,7 @@ mod tests {
|
||||
args: vec![],
|
||||
auto_kind: Some("lib".to_string()),
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -584,6 +596,7 @@ mod tests {
|
||||
}],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -613,6 +626,7 @@ mod tests {
|
||||
}],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -642,6 +656,7 @@ mod tests {
|
||||
}],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -691,6 +706,7 @@ mod tests {
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(false),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -731,6 +747,7 @@ mod tests {
|
||||
}],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(true),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -761,6 +778,7 @@ mod tests {
|
||||
}],
|
||||
auto_kind: None,
|
||||
has_preprocessor: Some(true),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user