Compare commits
75 Commits
rf/strip2
...
rf/fixGrap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fceae4f85 | ||
|
|
c1cfed840a | ||
|
|
0921ba0085 | ||
|
|
14b32be8b2 | ||
|
|
b67550a790 | ||
|
|
06347746c0 | ||
|
|
0b4288ad9c | ||
|
|
a6dcb218ff | ||
|
|
6fe93dac58 | ||
|
|
202b04bb08 | ||
|
|
df8cc1f248 | ||
|
|
61e3214f9e | ||
|
|
d663884a99 | ||
|
|
a2ed69f87f | ||
|
|
f377c84f5a | ||
|
|
772c768cda | ||
|
|
a4f8f9e1cf | ||
|
|
dfae34d9b4 | ||
|
|
0bc995bd07 | ||
|
|
161e76c500 | ||
|
|
c186db4e3b | ||
|
|
652eb65082 | ||
|
|
a98ba6dc41 | ||
|
|
080ecb04d7 | ||
|
|
8de3804d4a | ||
|
|
008bd32ec1 | ||
|
|
a2f679ffc9 | ||
|
|
2d3fe64bc4 | ||
|
|
b7db4c78c4 | ||
|
|
d0fc40b483 | ||
|
|
1efae55d36 | ||
|
|
791a772199 | ||
|
|
285b896a1d | ||
|
|
098cd96655 | ||
|
|
4eee71a8fb | ||
|
|
c01bf70f62 | ||
|
|
3ed16b88a4 | ||
|
|
18f30c8286 | ||
|
|
f221a6c17f | ||
|
|
51a710ffa0 | ||
|
|
ad0c836129 | ||
|
|
be6707b461 | ||
|
|
c227ba2794 | ||
|
|
d60a770eb7 | ||
|
|
30d83a942c | ||
|
|
98bf9355dc | ||
|
|
a3928101af | ||
|
|
6e77757939 | ||
|
|
66c5d6e400 | ||
|
|
b0c184767c | ||
|
|
1165cf6e24 | ||
|
|
a9640107f3 | ||
|
|
73bc13bb7d | ||
|
|
e98966283d | ||
|
|
8ad2344ec6 | ||
|
|
b77c239f30 | ||
|
|
15cb83936b | ||
|
|
e2157ee1c0 | ||
|
|
bdfd50b970 | ||
|
|
4debfd68dc | ||
|
|
410032060a | ||
|
|
22b8fed9d9 | ||
|
|
0172587b12 | ||
|
|
f05f9e4edb | ||
|
|
24b1fa0ae3 | ||
|
|
fedb9fe74c | ||
|
|
5408da829c | ||
|
|
448e45b7e4 | ||
|
|
156f257619 | ||
|
|
7f40373fd6 | ||
|
|
f2ec03ba1b | ||
|
|
9a79c5537e | ||
|
|
cf156b0591 | ||
|
|
832ddabdf2 | ||
|
|
22080312f0 |
12
.github/uffizzi/docker-compose.uffizzi.yml
vendored
12
.github/uffizzi/docker-compose.uffizzi.yml
vendored
@@ -14,13 +14,13 @@ services:
|
||||
|
||||
windmill:
|
||||
image: '${WINDMILL_IMAGE}'
|
||||
privileged: false
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 8000:8000
|
||||
entrypoint: ['/bin/sh', '-c']
|
||||
command: 'echo ${OAUTH_JSON_BASE64} | base64 --decode > /usr/src/app/oauth.json && ./windmill'
|
||||
environment:
|
||||
- DATABASE_URL=postgres://postgres:changeme@localhost/windmill?sslmode=disable
|
||||
- BASE_URL=http://localhost
|
||||
- BASE_URL=${EXPECTED_URL}
|
||||
- BASE_INTERNAL_URL=http://localhost:8000
|
||||
- RUST_LOG=info
|
||||
- NUM_WORKERS=3
|
||||
@@ -28,12 +28,16 @@ services:
|
||||
- DENO_PATH=/usr/bin/deno
|
||||
- PYTHON_PATH=/usr/local/bin/python3
|
||||
- METRICS_ADDR=false
|
||||
- OAUTH_JSON_BASE64=${OAUTH_JSON_BASE64}
|
||||
volumes:
|
||||
- worker_dependency_cache:/tmp/windmill/cache
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 250M
|
||||
|
||||
lsp:
|
||||
image: '${LSP_IMAGE}'
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3001:3001
|
||||
|
||||
|
||||
2
.github/workflows/automerge-dependabot.yml
vendored
2
.github/workflows/automerge-dependabot.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
steps:
|
||||
- name: Dependabot metadata
|
||||
id: metadata
|
||||
uses: dependabot/fetch-metadata@v1.3.5
|
||||
uses: dependabot/fetch-metadata@v1.3.6
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: Enable auto-merge for Dependabot PRs
|
||||
|
||||
47
.github/workflows/docker-310.yml
vendored
Normal file
47
.github/workflows/docker-310.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
name: Build and push windmill with python 3.10 and openbb
|
||||
on: workflow_dispatch
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}-openbb
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build_ee:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# - name: Set up Docker Buildx
|
||||
# uses: docker/setup-buildx-action@v2
|
||||
|
||||
- uses: depot/setup-action@v1
|
||||
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push publicly ee
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: ./docker/DockerfileOpenbb
|
||||
build-args: |
|
||||
features=enterprise
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:openbb
|
||||
labels: |
|
||||
org.opencontainers.image.licenses=Windmill-Enterprise-License
|
||||
2
.github/workflows/uffizzi-build.yml
vendored
2
.github/workflows/uffizzi-build.yml
vendored
@@ -4,6 +4,8 @@ on:
|
||||
types: [opened,synchronize,reopened,closed]
|
||||
paths:
|
||||
- "backend/**"
|
||||
- ".github/uffizzi/**"
|
||||
- ".github/workflows/**"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
27
.github/workflows/uffizzi-preview.yml
vendored
27
.github/workflows/uffizzi-preview.yml
vendored
@@ -47,22 +47,35 @@ jobs:
|
||||
echo 'EVENT_JSON<<EOF' >> $GITHUB_ENV
|
||||
cat event.json >> $GITHUB_ENV
|
||||
echo 'EOF' >> $GITHUB_ENV
|
||||
- name: Read PR Number From Event Object
|
||||
id: pr
|
||||
run: echo "PR_NUMBER=${{ fromJSON(env.EVENT_JSON).number }}" >> $GITHUB_ENV
|
||||
- name: Predict Deployment URL
|
||||
id: url
|
||||
# Replace dots in the repo name with the plus sign
|
||||
run: |
|
||||
REPO=$(echo ${{ github.repository }} | sed 's/\./+/g')
|
||||
echo "EXPECTED_URL=https://app.uffizzi.com/github.com/$REPO/pull/$PR_NUMBER" >> $GITHUB_ENV
|
||||
|
||||
- name: Re-Render Compose File
|
||||
run: |
|
||||
OAUTH_JSON_BASE64=${{ secrets.OAUTH_JSON_BASE64 }}
|
||||
export OAUTH_JSON_BASE64
|
||||
envsubst '${OAUTH_JSON_BASE64} ${EXPECTED_URL}' < docker-compose.rendered.yml > docker-compose.uffizzi.yml
|
||||
# cat docker-compose.uffizzi.yml
|
||||
|
||||
- name: Hash Rendered Compose File
|
||||
id: hash
|
||||
# If the previous workflow was triggered by a PR close event, we will not have a compose file artifact.
|
||||
if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }}
|
||||
run: echo "COMPOSE_FILE_HASH=$(md5sum docker-compose.rendered.yml | awk '{ print $1 }')" >> $GITHUB_ENV
|
||||
run: echo "COMPOSE_FILE_HASH=$(md5sum docker-compose.uffizzi.yml | awk '{ print $1 }')" >> $GITHUB_ENV
|
||||
- name: Cache Rendered Compose File
|
||||
if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: docker-compose.rendered.yml
|
||||
path: docker-compose.uffizzi.yml
|
||||
key: ${{ env.COMPOSE_FILE_HASH }}
|
||||
|
||||
- name: Read PR Number From Event Object
|
||||
id: pr
|
||||
run: echo "PR_NUMBER=${{ fromJSON(env.EVENT_JSON).number }}" >> $GITHUB_ENV
|
||||
|
||||
- name: DEBUG - Print Job Outputs
|
||||
if: ${{ runner.debug }}
|
||||
run: |
|
||||
@@ -80,7 +93,7 @@ jobs:
|
||||
# If this workflow was triggered by a PR close event, cache-key will be an empty string
|
||||
# and this reusable workflow will delete the preview deployment.
|
||||
compose-file-cache-key: ${{ needs.cache-compose-file.outputs.compose-file-cache-key }}
|
||||
compose-file-cache-path: docker-compose.rendered.yml
|
||||
compose-file-cache-path: docker-compose.uffizzi.yml
|
||||
server: https://app.uffizzi.com
|
||||
pr-number: ${{ needs.cache-compose-file.outputs.pr-number }}
|
||||
permissions:
|
||||
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"python.analysis.typeCheckingMode": "basic"
|
||||
}
|
||||
65
CHANGELOG.md
65
CHANGELOG.md
@@ -1,6 +1,71 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
## [1.61.1](https://github.com/windmill-labs/windmill/compare/v1.61.0...v1.61.1) (2023-01-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **backend:** compile issue ([df8cc1f](https://github.com/windmill-labs/windmill/commit/df8cc1f2482b3d8b1530cdaef1361303ff5cadff))
|
||||
|
||||
## [1.61.0](https://github.com/windmill-labs/windmill/compare/v1.60.0...v1.61.0) (2023-01-31)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add openapi viewer ([#1094](https://github.com/windmill-labs/windmill/issues/1094)) ([1337811](https://github.com/windmill-labs/windmill/commit/1337811438d48e23133f68e9157bd185d5fe4a82))
|
||||
* add PIP_LOCAL_DEPENDENCIES ([b7db4c7](https://github.com/windmill-labs/windmill/commit/b7db4c78c4629f1fd2dfd7a338f783b16f07b24d))
|
||||
* add QUEUE_LIMIT_WAIT_RESULT ([51a8810](https://github.com/windmill-labs/windmill/commit/51a8810aa0a9ab7702df459dd270278d42bd3899))
|
||||
* add resource and resource type from json ([080ecb0](https://github.com/windmill-labs/windmill/commit/080ecb04d7a08d035fe07f179975b52bc0f77297))
|
||||
* add sql as a valid type in Python ([0172587](https://github.com/windmill-labs/windmill/commit/0172587b129ce54d96dc99336a1f56c66ebdbef5))
|
||||
* add sync webhook for flows ([f377c84](https://github.com/windmill-labs/windmill/commit/f377c84f5a2148a2bbb7c16e93f13e1d85ceb17e))
|
||||
* **backend:** add queue_limit + configurable timeout + fix timeout cancel ([eef3bab](https://github.com/windmill-labs/windmill/commit/eef3bab6e4d9f1af1435db868c707a692558ab74))
|
||||
* **deno:** add support for DENO_AUTH_TOKENS ([832ddab](https://github.com/windmill-labs/windmill/commit/832ddabdf2239521368e5f96df144abce0db31c2))
|
||||
* **deno:** allow overriding deno sandboxing with DENO_FLAGS' ([7f40373](https://github.com/windmill-labs/windmill/commit/7f40373fd64005d87972854a565c6cf521232982))
|
||||
* **frontend:** Add app inputs configurations ([#1142](https://github.com/windmill-labs/windmill/issues/1142)) ([3ed16b8](https://github.com/windmill-labs/windmill/commit/3ed16b88a42e4db6e12f8557c5bbaa2d832b1c17))
|
||||
* **frontend:** Add app preview lock ([#1127](https://github.com/windmill-labs/windmill/issues/1127)) ([6a88e8c](https://github.com/windmill-labs/windmill/commit/6a88e8c4f4d6fa5c393ce27b2040784a74a73b06))
|
||||
* **frontend:** Add copy button option to app text display component ([#1090](https://github.com/windmill-labs/windmill/issues/1090)) ([bdfc38d](https://github.com/windmill-labs/windmill/commit/bdfc38d954a3c5548fb7f9ee6f80f741eff8cb67))
|
||||
* **frontend:** Add default codes to app editor ([#1099](https://github.com/windmill-labs/windmill/issues/1099)) ([c50c740](https://github.com/windmill-labs/windmill/commit/c50c7406f267b480af2a01b47e3fcfa1d763db7a))
|
||||
* **frontend:** Add HTML result rendering ([#1160](https://github.com/windmill-labs/windmill/issues/1160)) ([c01bf70](https://github.com/windmill-labs/windmill/commit/c01bf70f62680a4b77812ac6eb64aca2b15d9a8d))
|
||||
* **frontend:** Add more integration icons ([#1097](https://github.com/windmill-labs/windmill/issues/1097)) ([2191e85](https://github.com/windmill-labs/windmill/commit/2191e852318f069489f77a4f1c44aadf248c7f53))
|
||||
* **frontend:** add plotly support ([a4f8f9e](https://github.com/windmill-labs/windmill/commit/a4f8f9e1cf80395d5cd1229c8dd5dda244e2ba7f))
|
||||
* **frontend:** add selectedRowIndex to the table outputs ([#1145](https://github.com/windmill-labs/windmill/issues/1145)) ([f05f9e4](https://github.com/windmill-labs/windmill/commit/f05f9e4edb928e7a8e3e66a62de9c6487684a14b))
|
||||
* **frontend:** Add Supabase resource ([#1107](https://github.com/windmill-labs/windmill/issues/1107)) ([12b00a8](https://github.com/windmill-labs/windmill/commit/12b00a808d1f12827a7bc26518cc6f972bdde917))
|
||||
* **frontend:** add support for background scripts + add FormButtonCo… ([#1124](https://github.com/windmill-labs/windmill/issues/1124)) ([e969af9](https://github.com/windmill-labs/windmill/commit/e969af9e44d1b4409064080e8662552ee3e262e8))
|
||||
* **frontend:** Add surreal db logo ([#1102](https://github.com/windmill-labs/windmill/issues/1102)) ([d811675](https://github.com/windmill-labs/windmill/commit/d81167588227f2cc433aab64551d96d21a589c5b))
|
||||
* **frontend:** Add tooltip to app recompute ([#1122](https://github.com/windmill-labs/windmill/issues/1122)) ([4dfdf37](https://github.com/windmill-labs/windmill/commit/4dfdf374af358ef46ee8057373546719c6570067))
|
||||
* **frontend:** add vega-lite component ([bd79938](https://github.com/windmill-labs/windmill/commit/bd79938bed6da3875a4a2dd72dad14dedbf25ddf))
|
||||
* **frontend:** Display error as an icon in order to avoid clutter wh… ([#1143](https://github.com/windmill-labs/windmill/issues/1143)) ([22b8fed](https://github.com/windmill-labs/windmill/commit/22b8fed9d904a37aae66f6d957f4987f6ca9955c))
|
||||
* **frontend:** Open debug runs from component ([#1155](https://github.com/windmill-labs/windmill/issues/1155)) ([73bc13b](https://github.com/windmill-labs/windmill/commit/73bc13bb7d4b1eb25a3a726ac9e6bb80120a495f))
|
||||
* **frontend:** Update app table component styles ([#1100](https://github.com/windmill-labs/windmill/issues/1100)) ([172b5db](https://github.com/windmill-labs/windmill/commit/172b5dba8f4c3aaf11569c72313ad74845c668a6))
|
||||
* **python:** add support for extra args in python ([772c768](https://github.com/windmill-labs/windmill/commit/772c768cda094f208a5efb7aab03eee3a8f38f68))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** Add default value for text, number and date input + fix issues with number input + add date input in the settings panel ([#1135](https://github.com/windmill-labs/windmill/issues/1135)) ([8f90602](https://github.com/windmill-labs/windmill/commit/8f906026b3203702c3b6a30bcac9fb2aca985c29))
|
||||
* **frontend:** Add highlight to selected workspace ([#1159](https://github.com/windmill-labs/windmill/issues/1159)) ([f221a6c](https://github.com/windmill-labs/windmill/commit/f221a6c17f145d0c42f7faf785c37f4037308973))
|
||||
* **frontend:** add missing condition to properly select first row ([#1128](https://github.com/windmill-labs/windmill/issues/1128)) ([3d873ed](https://github.com/windmill-labs/windmill/commit/3d873ed51c769005981a8d8dfb95faa3ca33bb83))
|
||||
* **frontend:** App form component display ([#1096](https://github.com/windmill-labs/windmill/issues/1096)) ([339742c](https://github.com/windmill-labs/windmill/commit/339742ca77dd0fda19d5a262617e42c341ef5871))
|
||||
* **frontend:** App script list panel overflow ([#1101](https://github.com/windmill-labs/windmill/issues/1101)) ([7bc59d9](https://github.com/windmill-labs/windmill/commit/7bc59d9d2650b623a2b481a727ffc495b4216f22))
|
||||
* **frontend:** App table action button cell ([#1149](https://github.com/windmill-labs/windmill/issues/1149)) ([e989662](https://github.com/windmill-labs/windmill/commit/e98966283dd9b57cc07da34876a90d19210c2927))
|
||||
* **frontend:** App table header z-index ([#1120](https://github.com/windmill-labs/windmill/issues/1120)) ([59c4cc2](https://github.com/windmill-labs/windmill/commit/59c4cc2058f86deea793b61de59e2936e50e5577))
|
||||
* **frontend:** Check if hiddenInlineScripts are undefined before iterating over them ([#1134](https://github.com/windmill-labs/windmill/issues/1134)) ([71a443e](https://github.com/windmill-labs/windmill/commit/71a443e3c56d2b8c951de6e3701a411ad1a0ce34))
|
||||
* **frontend:** fix first row selection ([#1125](https://github.com/windmill-labs/windmill/issues/1125)) ([6c9daf7](https://github.com/windmill-labs/windmill/commit/6c9daf70021859dcd7cef717bc3acdfa88cffd02))
|
||||
* **frontend:** Fix id generation when a second action ([#1110](https://github.com/windmill-labs/windmill/issues/1110)) ([4f86981](https://github.com/windmill-labs/windmill/commit/4f869811fee73826b2b10965241d2d8dba59dc2a))
|
||||
* **frontend:** Make sure AppSelect items are an array ([#1144](https://github.com/windmill-labs/windmill/issues/1144)) ([24b1fa0](https://github.com/windmill-labs/windmill/commit/24b1fa0ae327c984841f9ed8b163b3fccc6da258))
|
||||
* **frontend:** Make sure that old apps are rendering properly ([#1132](https://github.com/windmill-labs/windmill/issues/1132)) ([a78486d](https://github.com/windmill-labs/windmill/commit/a78486d7e08f76e22406063288b35e9030974d7a))
|
||||
* **frontend:** Playwright ([#1108](https://github.com/windmill-labs/windmill/issues/1108)) ([f0435f5](https://github.com/windmill-labs/windmill/commit/f0435f5f81941c5b49500003aa27956d627daadb))
|
||||
* **frontend:** Prepare app scripts code for export ([#1123](https://github.com/windmill-labs/windmill/issues/1123)) ([173093a](https://github.com/windmill-labs/windmill/commit/173093a40321f6ad35bf766a5554b21cea388771))
|
||||
* **frontend:** Prevent modal from hijacking all keypress event ([#1136](https://github.com/windmill-labs/windmill/issues/1136)) ([aa6de3b](https://github.com/windmill-labs/windmill/commit/aa6de3bb5746b9d99c8e3a52e6a9fff10d97bc6a))
|
||||
* **frontend:** Revert component input panel change ([#1092](https://github.com/windmill-labs/windmill/issues/1092)) ([0419e7e](https://github.com/windmill-labs/windmill/commit/0419e7e1c9239fd3cbc49acf82a73e9c01938153))
|
||||
* **frontend:** Runnable table overflow ([#1119](https://github.com/windmill-labs/windmill/issues/1119)) ([462adbe](https://github.com/windmill-labs/windmill/commit/462adbe42f823646413a5003fd71f3dd473c0728))
|
||||
* **frontend:** Select the first row by default, and remove the abilit… ([#1121](https://github.com/windmill-labs/windmill/issues/1121)) ([3c483f5](https://github.com/windmill-labs/windmill/commit/3c483f533759b9b4e589055dbddb31f294bea8fa))
|
||||
* **frontend:** Show app builder header always on top ([#1118](https://github.com/windmill-labs/windmill/issues/1118)) ([631a3da](https://github.com/windmill-labs/windmill/commit/631a3da17f05a3d29defdf96a50d7e96a9f8baad))
|
||||
* **frontend:** Update app scripts pane ([#1146](https://github.com/windmill-labs/windmill/issues/1146)) ([18f30c8](https://github.com/windmill-labs/windmill/commit/18f30c8286f8240158643ade8b0ef4607a80fbb0))
|
||||
* **frontend:** Use absolute path on connect images ([#1095](https://github.com/windmill-labs/windmill/issues/1095)) ([43e069e](https://github.com/windmill-labs/windmill/commit/43e069eb96c0af7d3a1fe1db4f4b69f8e31e7438))
|
||||
* improvements for error handling as first step of flow ([b77c239](https://github.com/windmill-labs/windmill/commit/b77c239f307a37777acb083b0cdb5c0d214a9dd8))
|
||||
|
||||
## [1.60.0](https://github.com/windmill-labs/windmill/compare/v1.59.0...v1.60.0) (2023-01-11)
|
||||
|
||||
|
||||
|
||||
@@ -306,6 +306,11 @@ upcoming CLI tool.
|
||||
| SUPERADMIN_SECRET | None | A token that would let the caller act as a virtual superadmin superadmin@windmill.dev | Server |
|
||||
| TIMEOUT_WAIT_RESULT | 20 | The number of seconds to wait before timeout on the 'run_wait_result' endpoint | Worker |
|
||||
| QUEUE_LIMIT_WAIT_RESULT | None | The number of max jobs in the queue before rejecting immediately the request in 'run_wait_result' endpoint. Takes precedence on the query arg. If none is specified, there are no limit. | Worker |
|
||||
| DENO_AUTH_TOKENS | None | Custom DENO_AUTH_TOKENS to pass to worker to allow the use of private modules | Worker |
|
||||
| DENO_FLAGS | None | Override the flags passed to deno (default --allow-all) to tighten permissions. Minimum permissions needed are "--allow-read=args.json --allow-write=result.json" | Worker |
|
||||
| PIP_LOCAL_DEPENDENCIES | None | Specify dependencies that are installed locally and do not need to be solved nor installed again |
|
||||
| ADDITIONAL_PYTHON_PATHS | None | Specify python paths (separated by a :) to be appended to the PYTHONPATH of the python jobs. To be used with PIP_LOCAL_DEPENDENCIES to use python codebases within Windmill | Worker |
|
||||
| INCLUDE_HEADERS | None | Whitelist of headers that are passed to jobs as args (separated by a comma) | Server |
|
||||
|
||||
|
||||
|
||||
|
||||
377
backend/Cargo.lock
generated
377
backend/Cargo.lock
generated
@@ -135,9 +135,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-recursion"
|
||||
version = "1.0.0"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea"
|
||||
checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -180,9 +180,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.61"
|
||||
version = "0.1.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282"
|
||||
checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -217,9 +217,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.6.2"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1304eab461cf02bd70b083ed8273388f9724c549b316ba3d1e213ce0e9e7fb7e"
|
||||
checksum = "e5694b64066a2459918d8074c2ce0d5a88f409431994c2356617c8ae0c4721fc"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
@@ -251,9 +251,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f487e40dc9daee24d8a1779df88522f159a54a980f99cfbe43db0be0bd3444a8"
|
||||
checksum = "1cae3e661676ffbacb30f1a824089a8c9150e71017f7e1e38f2aa32009188d34"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
@@ -385,10 +385,20 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.11.1"
|
||||
name = "built"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
||||
checksum = "5b9c056b9ed43aee5e064b683aa1ec783e19c6acec7559e3ae931b7490472fbe"
|
||||
dependencies = [
|
||||
"cargo-lock",
|
||||
"git2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@@ -403,10 +413,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.78"
|
||||
name = "cargo-lock"
|
||||
version = "8.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
checksum = "031718ddb8f78aa5def78a09e90defe30151d1f6c672f937af4dd916429ed996"
|
||||
dependencies = [
|
||||
"semver 1.0.16",
|
||||
"serde",
|
||||
"toml",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -441,9 +466,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.1.1"
|
||||
version = "4.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ec7a4128863c188deefe750ac1d1dfe66c236909f845af04beed823638dc1b2"
|
||||
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
@@ -566,9 +591,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.0.0"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3"
|
||||
checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
|
||||
dependencies = [
|
||||
"crc-catalog",
|
||||
]
|
||||
@@ -636,9 +661,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.86"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579"
|
||||
checksum = "322296e2f2e5af4270b54df9e85a02ff037e271af20ba3e7fe1575515dc840b8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@@ -648,9 +673,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.86"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70"
|
||||
checksum = "017a1385b05d631e7875b1f151c9f012d37b53491e2a87f65bff5c262b2111d8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@@ -663,15 +688,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.86"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c"
|
||||
checksum = "c26bbb078acf09bc1ecda02d4223f03bdd28bd4874edcb0379138efc499ce971"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.86"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5"
|
||||
checksum = "357f40d1f06a24b60ae1fe122542c1fb05d28d32acb2aed064e84bc2ad1e252e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -885,9 +910,9 @@ checksum = "131726693bce13b09331bee70734fe266666332b6ddfef23e9dca5b8bf6dea66"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -1030,9 +1055,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
|
||||
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -1045,9 +1070,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
||||
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -1055,15 +1080,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
|
||||
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@@ -1083,9 +1108,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
@@ -1104,9 +1129,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
||||
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1115,21 +1140,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
||||
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -1206,6 +1231,19 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"libgit2-sys",
|
||||
"log",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.15"
|
||||
@@ -1554,6 +1592,15 @@ version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.60"
|
||||
@@ -1698,12 +1745,36 @@ version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.14.2+1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.8"
|
||||
@@ -1873,6 +1944,15 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom8"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
@@ -1914,9 +1994,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
|
||||
checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"serde",
|
||||
@@ -1965,18 +2045,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.5.7"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
|
||||
checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.5.7"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
|
||||
checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -2152,9 +2232,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.5.3"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4257b4a04d91f7e9e6290be5d3da4804dd5784fafde3a497d73eb2b4a158c30a"
|
||||
checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"ucd-trie",
|
||||
@@ -2170,15 +2250,6 @@ dependencies = [
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
|
||||
dependencies = [
|
||||
"phf_shared 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.1"
|
||||
@@ -2191,12 +2262,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.10.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
|
||||
checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770"
|
||||
dependencies = [
|
||||
"phf_generator 0.10.0",
|
||||
"phf_shared 0.10.0",
|
||||
"phf_generator 0.11.1",
|
||||
"phf_shared 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2341,13 +2412,12 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.2.1"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9"
|
||||
checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"thiserror",
|
||||
"toml",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2382,9 +2452,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.49"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
|
||||
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -2392,14 +2462,16 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#46d2df0e2a3dcbaea70669df6afb75ae27dea348"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#8a09b98645276ef3738d3e904e1c32e5de1d657e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"built",
|
||||
"clap",
|
||||
"openapiv3",
|
||||
"progenitor-client",
|
||||
"progenitor-impl",
|
||||
"progenitor-macro",
|
||||
"project-root",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
@@ -2408,7 +2480,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor-client"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#46d2df0e2a3dcbaea70669df6afb75ae27dea348"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#8a09b98645276ef3738d3e904e1c32e5de1d657e"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -2422,7 +2494,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor-impl"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#46d2df0e2a3dcbaea70669df6afb75ae27dea348"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#8a09b98645276ef3738d3e904e1c32e5de1d657e"
|
||||
dependencies = [
|
||||
"getopts",
|
||||
"heck",
|
||||
@@ -2444,7 +2516,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "progenitor-macro"
|
||||
version = "0.2.1-dev"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#46d2df0e2a3dcbaea70669df6afb75ae27dea348"
|
||||
source = "git+https://github.com/oxidecomputer/progenitor#8a09b98645276ef3738d3e904e1c32e5de1d657e"
|
||||
dependencies = [
|
||||
"openapiv3",
|
||||
"proc-macro2",
|
||||
@@ -2458,6 +2530,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "project-root"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bccbff07d5ed689c4087d20d7307a52ab6141edeedf487c3876a55b86cf63df"
|
||||
|
||||
[[package]]
|
||||
name = "prometheus"
|
||||
version = "0.13.3"
|
||||
@@ -2618,11 +2696,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.13"
|
||||
version = "0.11.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
|
||||
checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"base64 0.21.0",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
@@ -2650,6 +2728,7 @@ dependencies = [
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-streams",
|
||||
"web-sys",
|
||||
"winreg",
|
||||
]
|
||||
@@ -2775,9 +2854,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.6"
|
||||
version = "0.36.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549"
|
||||
checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -2811,7 +2890,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#59b191c789c891a500659d2c3b29a645883f780a"
|
||||
source = "git+https://github.com/RustPython/RustPython#b6e447145800ec7888f035bf266329e4b457e5f9"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-compiler-core",
|
||||
@@ -2820,7 +2899,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#59b191c789c891a500659d2c3b29a645883f780a"
|
||||
source = "git+https://github.com/RustPython/RustPython#b6e447145800ec7888f035bf266329e4b457e5f9"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
@@ -2837,7 +2916,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython#59b191c789c891a500659d2c3b29a645883f780a"
|
||||
source = "git+https://github.com/RustPython/RustPython#b6e447145800ec7888f035bf266329e4b457e5f9"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -2847,7 +2926,7 @@ dependencies = [
|
||||
"log",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"phf 0.10.1",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
"rustc-hash",
|
||||
"rustpython-ast",
|
||||
@@ -2945,9 +3024,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.7.0"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
|
||||
checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
@@ -2958,9 +3037,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.6.1"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
|
||||
checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@@ -2989,6 +3068,9 @@ name = "semver"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
@@ -3138,9 +3220,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.16"
|
||||
version = "0.9.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92b5b431e8907b50339b51223b97d102db8d987ced36f6e4d03621db9316c834"
|
||||
checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
@@ -3489,9 +3571,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "swc_atoms"
|
||||
version = "0.4.33"
|
||||
version = "0.4.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93a49e93996e1c1cfdb641cd94888da545d440ff6cada04155ef118adee620c1"
|
||||
checksum = "731cf66bd8e11030f056f91f9d8af77f83ec4377ff04d1670778a57d1607402a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"rustc-hash",
|
||||
@@ -3503,9 +3585,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_common"
|
||||
version = "0.29.27"
|
||||
version = "0.29.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90a2c285d33b47a5e532a662c178dc91956534ff52207892918d3034a534ae12"
|
||||
checksum = "a97e491d31418cd33fea58e9f893316fc04b30e2b5d0e750c066e2ba4907ae54"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ast_node",
|
||||
@@ -3530,9 +3612,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_ast"
|
||||
version = "0.96.1"
|
||||
version = "0.96.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "708345b44bedd33ff5dedfc66263070af0cad788d4bb19b51007b4583a64c43f"
|
||||
checksum = "a887102d5595b557261aa4bde35f3d71906fba674d4b79cd5c59b4155b12ee2d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"is-macro",
|
||||
@@ -3547,9 +3629,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_parser"
|
||||
version = "0.124.2"
|
||||
version = "0.124.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ec6b89544ad2242a8e19d966a6c3ac8e6eb5ec5060e08bdcb01062c479b41ca"
|
||||
checksum = "9e75888eabf1ad8a8968e3befc7cd20c10e4721254d3344285bd5c3a42f58dc1"
|
||||
dependencies = [
|
||||
"either",
|
||||
"enum_kind",
|
||||
@@ -3590,9 +3672,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_visit"
|
||||
version = "0.5.3"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82f2bcb7223e185c4c7cbf5e0c1207dec6d2bfd5e72e3fb7b3e8d179747e9130"
|
||||
checksum = "470a1963cf182fdcbbac46e3a7fd2caf7329da0e568d3668202da9501c880e16"
|
||||
dependencies = [
|
||||
"either",
|
||||
"swc_visit_macros",
|
||||
@@ -3600,9 +3682,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_visit_macros"
|
||||
version = "0.5.4"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fb1f3561674d84947694d41fb6d5737d19539222779baeac1b3a071a2b29428"
|
||||
checksum = "6098b717cfd4c85f5cddec734af191dbce461c39975ed567c32ac6d0c6d61a6d"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"pmutil",
|
||||
@@ -3767,9 +3849,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.24.1"
|
||||
version = "1.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae"
|
||||
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
@@ -3871,13 +3953,30 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.10"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
|
||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"nom8",
|
||||
"toml_datetime",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toolchain_find"
|
||||
version = "0.2.0"
|
||||
@@ -4073,7 +4172,7 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
[[package]]
|
||||
name = "typify"
|
||||
version = "0.0.11-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#0f173877fa12c7e53c351fd1084e7cab0c6a7be1"
|
||||
source = "git+https://github.com/oxidecomputer/typify#cd1f95b66b0d0fbb33537d66043f26512ee3268b"
|
||||
dependencies = [
|
||||
"typify-impl",
|
||||
"typify-macro",
|
||||
@@ -4082,7 +4181,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "typify-impl"
|
||||
version = "0.0.11-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#0f173877fa12c7e53c351fd1084e7cab0c6a7be1"
|
||||
source = "git+https://github.com/oxidecomputer/typify#cd1f95b66b0d0fbb33537d66043f26512ee3268b"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"log",
|
||||
@@ -4100,7 +4199,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "typify-macro"
|
||||
version = "0.0.11-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#0f173877fa12c7e53c351fd1084e7cab0c6a7be1"
|
||||
source = "git+https://github.com/oxidecomputer/typify#cd1f95b66b0d0fbb33537d66043f26512ee3268b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4191,9 +4290,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.8"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-general-category"
|
||||
@@ -4224,9 +4323,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.10.0"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
|
||||
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
@@ -4443,6 +4542,19 @@ version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-streams"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.60"
|
||||
@@ -4474,9 +4586,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.3.0"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
|
||||
checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
|
||||
dependencies = [
|
||||
"either",
|
||||
"libc",
|
||||
@@ -4526,7 +4638,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -4553,7 +4665,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -4570,6 +4682,7 @@ dependencies = [
|
||||
"hmac",
|
||||
"hyper",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"magic-crypt",
|
||||
"mime_guess",
|
||||
"rand 0.8.5",
|
||||
@@ -4605,7 +4718,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"chrono",
|
||||
@@ -4620,7 +4733,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
@@ -4633,7 +4746,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -4657,7 +4770,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4665,12 +4778,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"phf 0.11.1",
|
||||
"phf",
|
||||
"regex",
|
||||
"unicode-general-category",
|
||||
"windmill-common",
|
||||
@@ -4679,11 +4792,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"phf 0.11.1",
|
||||
"phf",
|
||||
"unicode-general-category",
|
||||
"windmill-common",
|
||||
"windmill-parser",
|
||||
@@ -4691,12 +4804,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"phf 0.11.1",
|
||||
"phf",
|
||||
"regex",
|
||||
"rustpython-parser",
|
||||
"serde_json",
|
||||
@@ -4706,7 +4819,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deno_core",
|
||||
@@ -4720,7 +4833,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -4743,7 +4856,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -19,7 +19,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.60.0"
|
||||
version = "1.61.1"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -83,7 +83,7 @@ chrono = { version = "^0", features = ["serde"] }
|
||||
tracing = "^0"
|
||||
tracing-subscriber = { version = "^0", features = ["env-filter", "json"] }
|
||||
prometheus = { version = "^0", default-features = false }
|
||||
cookie = { version = "^0" }
|
||||
cookie = { version = "0.16.2" }
|
||||
phf = { version = "0.11", features = ["macros"] }
|
||||
rust-embed = "^6"
|
||||
mime_guess = "^2"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add up migration script here
|
||||
ALTER TABLE account ALTER COLUMN refresh_token TYPE VARCHAR(1500);
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Add up migration script here
|
||||
|
||||
GRANT ALL PRIVILEGES ON TABLE favorite TO windmill_admin;
|
||||
GRANT ALL PRIVILEGES ON TABLE favorite TO windmill_user;
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,25 @@
|
||||
-- Add up migration script here
|
||||
|
||||
|
||||
CREATE POLICY see_folder_extra_perms_user ON capture FOR ALL
|
||||
USING (SPLIT_PART(capture.path, '/', 1) = 'f' AND SPLIT_PART(capture.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_read'), ',')::text[]))
|
||||
WITH CHECK (SPLIT_PART(capture.path, '/', 1) = 'f' AND SPLIT_PART(capture.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
|
||||
|
||||
DO
|
||||
$do$
|
||||
DECLARE
|
||||
i text;
|
||||
arr text[] := array['queue', 'completed_job'];
|
||||
BEGIN
|
||||
FOREACH i IN ARRAY arr
|
||||
LOOP
|
||||
EXECUTE FORMAT(
|
||||
$$
|
||||
CREATE POLICY see_folder_extra_perms_user ON %1$I FOR ALL
|
||||
USING (%1$I.visible_to_owner IS true AND SPLIT_PART(%1$I.script_path, '/', 1) = 'f' AND SPLIT_PART(%1$I.script_path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_read'), ',')::text[]));
|
||||
$$,
|
||||
i
|
||||
);
|
||||
END LOOP;
|
||||
END
|
||||
$do$;
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Add up migration script here
|
||||
|
||||
CREATE POLICY see_folder_extra_perms_user ON account FOR ALL
|
||||
USING (SPLIT_PART(account.owner, '/', 1) = 'f' AND SPLIT_PART(account.owner, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_read'), ',')::text[]))
|
||||
WITH CHECK (SPLIT_PART(account.owner, '/', 1) = 'f' AND SPLIT_PART(account.owner, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
|
||||
@@ -80,7 +80,7 @@ pub fn parse_python_signature(code: &str) -> error::Result<MainArgSignature> {
|
||||
let def_arg_start = params.args.len() - params.defaults.len();
|
||||
Ok(MainArgSignature {
|
||||
star_args: params.vararg.is_some(),
|
||||
star_kwargs: params.vararg.is_some(),
|
||||
star_kwargs: params.kwarg.is_some(),
|
||||
args: params
|
||||
.args
|
||||
.into_iter()
|
||||
@@ -104,6 +104,7 @@ pub fn parse_python_signature(code: &str) -> error::Result<MainArgSignature> {
|
||||
"bytes" => Typ::Bytes,
|
||||
"datetime" => Typ::Datetime,
|
||||
"datetime.datetime" => Typ::Datetime,
|
||||
"Sql" | "sql" => Typ::Sql,
|
||||
_ => Typ::Resource(id),
|
||||
},
|
||||
_ => Typ::Unknown,
|
||||
@@ -135,7 +136,10 @@ fn to_value(et: &ExprKind) -> Option<serde_json::Value> {
|
||||
.into_iter()
|
||||
.zip(values)
|
||||
.map(|(k, v)| {
|
||||
let key = to_value(&k.node)
|
||||
let key = k
|
||||
.as_ref()
|
||||
.map(|x| x.node.clone())
|
||||
.and_then(|n| to_value(&n))
|
||||
.and_then(|x| match x {
|
||||
serde_json::Value::String(s) => Some(s),
|
||||
_ => None,
|
||||
@@ -181,6 +185,7 @@ static PYTHON_IMPORTS_REPLACEMENT: phf::Map<&'static str, &'static str> = phf_ma
|
||||
"f" => "requests",
|
||||
"shopify" => "ShopifyAPI",
|
||||
"seleniumwire" => "selenium-wire",
|
||||
"openbb-terminal" => "openbb[all]",
|
||||
};
|
||||
|
||||
fn replace_import(x: String) -> String {
|
||||
@@ -192,13 +197,13 @@ fn replace_import(x: String) -> String {
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new(r"^\#(\S+)$").unwrap();
|
||||
static ref RE: Regex = Regex::new(r"^\#\s?(\S+)$").unwrap();
|
||||
}
|
||||
|
||||
pub fn parse_python_imports(code: &str) -> error::Result<Vec<String>> {
|
||||
let find_requirements = code
|
||||
.lines()
|
||||
.find_position(|x| x.starts_with("#requirements:"));
|
||||
.find_position(|x| x.starts_with("#requirements:") || x.starts_with("# requirements:"));
|
||||
if let Some((pos, _)) = find_requirements {
|
||||
let lines = code
|
||||
.lines()
|
||||
|
||||
@@ -548,6 +548,16 @@
|
||||
"name": "auto_invite_operator",
|
||||
"ordinal": 6,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "customer_id",
|
||||
"ordinal": 7,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "plan",
|
||||
"ordinal": 8,
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
@@ -557,6 +567,8 @@
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
@@ -1759,23 +1771,6 @@
|
||||
},
|
||||
"query": "\n UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2),\n ARRAY['step'], $3)\n WHERE id = $4\n "
|
||||
},
|
||||
"53f2836a652d34aafe6ffb266a092d7390212bc5532dcc226529a61a53deec42": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO token\n (workspace_id, token, owner, label, expiration, super_admin)\n VALUES ($1, $2, $3, $4, now() + ($5 || ' seconds')::interval, $6)"
|
||||
},
|
||||
"541ebd3bac65431237cf3b882dfdcd61ca97c253d9754d05bba59fda89841067": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -1845,6 +1840,16 @@
|
||||
"name": "auto_invite_operator",
|
||||
"ordinal": 6,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "customer_id",
|
||||
"ordinal": 7,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "plan",
|
||||
"ordinal": 8,
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
@@ -1854,6 +1859,8 @@
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
@@ -2064,6 +2071,26 @@
|
||||
},
|
||||
"query": "DELETE FROM group_ WHERE name = $1 AND workspace_id = $2"
|
||||
},
|
||||
"5ba4b87528ad49f17d72b53c3db30f5ca4b3b0b0afbd5d9721c8b5d692af601b": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "count",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
null
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT COUNT(id) FROM queue WHERE email = $1"
|
||||
},
|
||||
"5c377fffc224a06f693c125f4c13b0a9ccfc217190ba6cf78246294bbc6c93bc": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -4077,6 +4104,26 @@
|
||||
},
|
||||
"query": "\n SELECT resume_id, approver\n FROM resume_job\n WHERE job = $1\n "
|
||||
},
|
||||
"b053117536c067095e2fb2864ce5af33f84b22c24c92fcb870f37f0501f8ea9a": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "count",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
null
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT COUNT(id) FROM queue WHERE running = true AND email = $1"
|
||||
},
|
||||
"b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -4564,6 +4611,26 @@
|
||||
},
|
||||
"query": "UPDATE schedule SET script_path = $1 WHERE script_path = $2 AND workspace_id = $3 AND is_flow IS false RETURNING *"
|
||||
},
|
||||
"c07c9276945663d062cf0ff5b3323be681a0e2cb07a457ea9aede2daeff551cc": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "customer_id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT customer_id FROM workspace_settings WHERE workspace_id = $1"
|
||||
},
|
||||
"c1149f74ef0cca4a48be80f523d339d78e8e20e2bfacac9ad1b6b75c5ae370fb": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -5556,6 +5623,24 @@
|
||||
},
|
||||
"query": "DELETE FROM password WHERE email = $1"
|
||||
},
|
||||
"e9c0e331c16312bf086b17c91466c5389d41454fd3f18d73c2e9554845ee9a72": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Bool",
|
||||
"Varchar"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO token\n (workspace_id, token, owner, label, expiration, super_admin, email)\n VALUES ($1, $2, $3, $4, now() + ($5 || ' seconds')::interval, $6, $7)"
|
||||
},
|
||||
"ea8ebb8d972fe99c960b5a69f794ee2b57bfb1914bf370c5b10313e45fa9b65f": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
|
||||
@@ -262,7 +262,7 @@ mod suspend_resume {
|
||||
// print_job(second, &db).await;
|
||||
|
||||
let tx = db.begin().await.unwrap();
|
||||
let (tx, token) = windmill_worker::create_token_for_owner(tx, "test-workspace", "u/test-user", "", 100).await.unwrap();
|
||||
let (tx, token) = windmill_worker::create_token_for_owner(tx, "test-workspace", "u/test-user", "", 100, "").await.unwrap();
|
||||
tx.commit().await.unwrap();
|
||||
let secret = reqwest::get(format!(
|
||||
"http://localhost:{port}/api/w/test-workspace/jobs/job_signature/{second}/0?token={token}&approver=ruben"
|
||||
@@ -365,7 +365,7 @@ mod suspend_resume {
|
||||
let second = completed.next().await.unwrap();
|
||||
|
||||
let tx = db.begin().await.unwrap();
|
||||
let (tx, token) = windmill_worker::create_token_for_owner(tx, "test-workspace", "u/test-user", "", 100).await.unwrap();
|
||||
let (tx, token) = windmill_worker::create_token_for_owner(tx, "test-workspace", "u/test-user", "", 100, "").await.unwrap();
|
||||
tx.commit().await.unwrap();
|
||||
let secret = reqwest::get(format!(
|
||||
"http://localhost:{port}/api/w/test-workspace/jobs/job_signature/{second}/0?token={token}"
|
||||
@@ -395,7 +395,7 @@ mod suspend_resume {
|
||||
let result = completed_job(flow, &db).await.result.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
json!( {"error": {"name": "InternalErr", "message": "{\"message\":\"Job canceled: approval request disapproved by unknown\",\"name\":\"Canceled\",\"reason\":\"approval request disapproved\",\"canceler\":\"unknown\"}"}}),
|
||||
json!( {"error": {"name": "Canceled", "reason": "approval request disapproved", "message": "Job canceled: approval request disapproved by unknown", "canceler": "unknown"}}),
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,3 +70,4 @@ cookie.workspace = true
|
||||
sha2.workspace = true
|
||||
urlencoding.workspace = true
|
||||
async-stripe.workspace = true
|
||||
lazy_static.workspace = true
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.60.0
|
||||
version: 1.61.1
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -2445,6 +2445,44 @@ paths:
|
||||
application/json:
|
||||
schema: {}
|
||||
|
||||
/w/{workspace}/jobs/run_wait_result/f/{path}:
|
||||
post:
|
||||
summary: run flow by path and wait until completion
|
||||
operationId: runWaitResultFlowByPath
|
||||
tags:
|
||||
- job
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
- name: scheduled_for
|
||||
description: when to schedule this job (leave empty for immediate run)
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- name: scheduled_in_secs
|
||||
description: schedule the script to execute in the number of seconds starting now
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
- $ref: "#/components/parameters/IncludeHeader"
|
||||
- $ref: "#/components/parameters/QueueLimit"
|
||||
|
||||
requestBody:
|
||||
description: script args
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
|
||||
responses:
|
||||
"200":
|
||||
description: job result
|
||||
content:
|
||||
application/json:
|
||||
schema: {}
|
||||
|
||||
/w/{workspace}/jobs/result_by_id/{flow_job_id}/{node_id}:
|
||||
get:
|
||||
summary: get job result by id
|
||||
|
||||
@@ -53,6 +53,10 @@ pub fn workspaced_service() -> Router {
|
||||
"/run_wait_result/h/:hash",
|
||||
post(run_wait_result_job_by_hash),
|
||||
)
|
||||
.route(
|
||||
"/run_wait_result/f/*script_path",
|
||||
post(run_wait_result_flow_by_path),
|
||||
)
|
||||
.route("/run/h/:hash", post(run_job_by_hash))
|
||||
.route("/run/preview", post(run_preview_job))
|
||||
.route("/run/preview_flow", post(run_preview_flow_job))
|
||||
@@ -266,6 +270,14 @@ pub struct RunJobQuery {
|
||||
queue_limit: Option<i64>,
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref INCLUDE_HEADERS: Vec<String> = std::env::var("INCLUDE_HEADERS")
|
||||
.ok().map(|x| x
|
||||
.split(',')
|
||||
.map(|s| s.to_string())
|
||||
.collect()).unwrap_or_default();
|
||||
}
|
||||
|
||||
impl RunJobQuery {
|
||||
async fn get_scheduled_for<'c>(
|
||||
&self,
|
||||
@@ -286,11 +298,14 @@ impl RunJobQuery {
|
||||
headers: HeaderMap,
|
||||
mut args: serde_json::Map<String, serde_json::Value>,
|
||||
) -> serde_json::Map<String, serde_json::Value> {
|
||||
self.include_header
|
||||
let whitelist = self
|
||||
.include_header
|
||||
.as_ref()
|
||||
.map(|s| s.split(",").map(|s| s.to_string()).collect::<Vec<_>>())
|
||||
.unwrap_or_default()
|
||||
.unwrap_or_default();
|
||||
whitelist
|
||||
.iter()
|
||||
.chain(INCLUDE_HEADERS.iter())
|
||||
.for_each(|h| {
|
||||
if let Some(v) = headers.get(h) {
|
||||
args.insert(
|
||||
@@ -1388,6 +1403,53 @@ pub async fn run_wait_result_job_by_hash(
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn run_wait_result_flow_by_path(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(timeout): Extension<Arc<TimeoutWaitResult>>,
|
||||
Path((w_id, flow_path)): Path<(String, StripPath)>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
headers: HeaderMap,
|
||||
Json(args): Json<Option<serde_json::Map<String, serde_json::Value>>>,
|
||||
) -> error::JsonResult<serde_json::Value> {
|
||||
check_queue_too_long(db, run_query.queue_limit).await?;
|
||||
|
||||
let flow_path = flow_path.to_path();
|
||||
let mut tx = user_db.clone().begin(&authed).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(&mut tx).await?;
|
||||
let args = run_query.add_include_headers(headers, args.unwrap_or_default());
|
||||
|
||||
let (uuid, tx) = push(
|
||||
tx,
|
||||
&w_id,
|
||||
JobPayload::Flow(flow_path.to_string()),
|
||||
args,
|
||||
&authed.username,
|
||||
&authed.email,
|
||||
username_to_permissioned_as(&authed.username),
|
||||
scheduled_for,
|
||||
None,
|
||||
run_query.parent_job,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
!run_query.invisible_to_owner.unwrap_or(false),
|
||||
)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
run_wait_result(
|
||||
authed,
|
||||
Extension(user_db),
|
||||
timeout.0,
|
||||
uuid,
|
||||
Path((w_id, flow_path)),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
// a similar function exists on the worker
|
||||
pub async fn script_path_to_payload<'c>(
|
||||
script_path: &str,
|
||||
|
||||
@@ -233,28 +233,38 @@ async fn stripe_checkout(
|
||||
params.line_items = match plan.plan.as_str() {
|
||||
"team" => Some(vec![
|
||||
stripe::CreateCheckoutSessionLineItems {
|
||||
quantity: None,
|
||||
price: Some("price_1MSdSyGU3NdFi9eLMdV6cS6F".to_string()),
|
||||
quantity: Some(1),
|
||||
price: Some("price_1MUlrWGU3NdFi9eLE9GBZhoY".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
stripe::CreateCheckoutSessionLineItems {
|
||||
quantity: None,
|
||||
price: Some("price_1MShsNGU3NdFi9eLJMEZUW8b".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
]),
|
||||
"enterprise" => Some(vec![
|
||||
stripe::CreateCheckoutSessionLineItems {
|
||||
quantity: None,
|
||||
price: Some("price_1MSdf6GU3NdFi9eLJFRkntlx".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
stripe::CreateCheckoutSessionLineItems {
|
||||
quantity: None,
|
||||
price: Some("price_1MShsNGU3NdFi9eLJMEZUW8b".to_string()),
|
||||
price: Some("price_1MUlreGU3NdFi9eLi6sOyvVa".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
stripe::CreateCheckoutSessionLineItems {
|
||||
quantity: None,
|
||||
price: Some("price_1MUlrlGU3NdFi9eLFLggSXZV".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
stripe::CreateCheckoutSessionLineItems {
|
||||
quantity: None,
|
||||
price: Some("price_1MUlr3GU3NdFi9eLbZYFjR9p".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
]),
|
||||
// "enterprise" => Some(vec![
|
||||
// stripe::CreateCheckoutSessionLineItems {
|
||||
// quantity: None,
|
||||
// price: Some("price_1MSdf6GU3NdFi9eLJFRkntlx".to_string()),
|
||||
// ..Default::default()
|
||||
// },
|
||||
// stripe::CreateCheckoutSessionLineItems {
|
||||
// quantity: None,
|
||||
// price: Some("price_1MShsNGU3NdFi9eLJMEZUW8b".to_string()),
|
||||
// ..Default::default()
|
||||
// },
|
||||
// ]),
|
||||
_ => Err(Error::BadRequest("invalid plan".to_string()))?,
|
||||
};
|
||||
params.customer_email = Some(&authed.email);
|
||||
|
||||
@@ -58,6 +58,8 @@ pub enum Error {
|
||||
ExitStatus(i32),
|
||||
#[error(transparent)]
|
||||
Anyhow(#[from] anyhow::Error),
|
||||
#[error("Error: {0:#?}")]
|
||||
JsonErr(serde_json::Value),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{Pool, Postgres, Transaction};
|
||||
use tracing::{instrument, Instrument};
|
||||
@@ -44,6 +45,7 @@ lazy_static::lazy_static! {
|
||||
}
|
||||
|
||||
const MAX_FREE_EXECS: i32 = 1000;
|
||||
const MAX_FREE_CONCURRENT_RUNS: i32 = 3;
|
||||
|
||||
pub async fn cancel_job<'c>(
|
||||
username: &str,
|
||||
@@ -317,10 +319,38 @@ pub async fn push<'c>(
|
||||
.fetch_optional(&mut tx)
|
||||
.await?
|
||||
.unwrap_or(false);
|
||||
if !is_super_admin && usage > MAX_FREE_EXECS {
|
||||
return Err(error::Error::BadRequest(format!(
|
||||
|
||||
if !is_super_admin {
|
||||
if usage > MAX_FREE_EXECS {
|
||||
return Err(error::Error::BadRequest(format!(
|
||||
"User {email} has exceeded the free usage limit of {MAX_FREE_EXECS} that applies outside of premium workspaces."
|
||||
)));
|
||||
}
|
||||
let in_queue =
|
||||
sqlx::query_scalar!("SELECT COUNT(id) FROM queue WHERE email = $1", email)
|
||||
.fetch_one(&mut tx)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
if in_queue > MAX_FREE_EXECS.into() {
|
||||
return Err(error::Error::BadRequest(format!(
|
||||
"User {email} has exceeded the jobs in queue limit of {MAX_FREE_EXECS} that applies outside of premium workspaces."
|
||||
)));
|
||||
}
|
||||
|
||||
let concurrent_runs = sqlx::query_scalar!(
|
||||
"SELECT COUNT(id) FROM queue WHERE running = true AND email = $1",
|
||||
email
|
||||
)
|
||||
.fetch_one(&mut tx)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
if concurrent_runs > MAX_FREE_CONCURRENT_RUNS.into() {
|
||||
return Err(error::Error::BadRequest(format!(
|
||||
"User {email} has exceeded the concurrent runs limit of {MAX_FREE_CONCURRENT_RUNS} that applies outside of premium workspaces."
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -351,7 +381,9 @@ pub async fn push<'c>(
|
||||
)
|
||||
}
|
||||
JobPayload::ScriptHub { path } => {
|
||||
let script = get_hub_script(path.clone(), email).await?;
|
||||
let script = get_hub_script(path.clone(), email)
|
||||
.await
|
||||
.context("error fetching hub script")?;
|
||||
(
|
||||
None,
|
||||
Some(path),
|
||||
|
||||
@@ -72,6 +72,12 @@ mount {
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "{JOB_DIR}/import_map.json"
|
||||
dst: "/tmp/import_map.json"
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "{JOB_DIR}/lock.json"
|
||||
dst: "/tmp/lock.json"
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE-AGPL for a copy of the license.
|
||||
*/
|
||||
|
||||
use serde_json::{Map, Value};
|
||||
use sqlx::{Pool, Postgres, Transaction};
|
||||
use tracing::instrument;
|
||||
use uuid::Uuid;
|
||||
@@ -27,16 +26,6 @@ pub async fn add_completed_job_error(
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn error_to_result<E: ToString + std::fmt::Debug>(
|
||||
output_map: &mut Map<String, Value>,
|
||||
err: &E,
|
||||
) {
|
||||
output_map.insert(
|
||||
"error".to_string(),
|
||||
serde_json::Value::String(err.to_string()),
|
||||
);
|
||||
}
|
||||
|
||||
fn flatten_jobs(modules: Vec<FlowStatusModule>) -> Vec<Uuid> {
|
||||
modules
|
||||
.into_iter()
|
||||
|
||||
@@ -49,7 +49,7 @@ use futures::{
|
||||
use async_recursion::async_recursion;
|
||||
|
||||
use crate::{
|
||||
jobs::{add_completed_job, add_completed_job_error, error_to_result},
|
||||
jobs::{add_completed_job, add_completed_job_error},
|
||||
worker_flow::{
|
||||
handle_flow, update_flow_status_after_job_completion, update_flow_status_in_progress,
|
||||
},
|
||||
@@ -133,7 +133,7 @@ async fn copy_cache_to_bucket_as_tar(bucket: &str) {
|
||||
.current_dir(ROOT_CACHE_DIR)
|
||||
.arg("-c")
|
||||
.arg("-f")
|
||||
.arg(format!("{ROOT_CACHE_DIR}/{TAR_CACHE_FILENAME}"))
|
||||
.arg(format!("{ROOT_CACHE_DIR}{TAR_CACHE_FILENAME}"))
|
||||
.args(&["pip", "go", "deno"])
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
@@ -181,7 +181,7 @@ async fn copy_cache_from_bucket_as_tar(bucket: &str) -> bool {
|
||||
match Command::new("rclone")
|
||||
.arg("copyto")
|
||||
.arg(format!(":s3,env_auth=true:{bucket}/{TAR_CACHE_FILENAME}"))
|
||||
.arg(format!("{ROOT_CACHE_DIR}/{TAR_CACHE_FILENAME}"))
|
||||
.arg(format!("{ROOT_CACHE_DIR}{TAR_CACHE_FILENAME}"))
|
||||
.arg("--size-only")
|
||||
.arg("--fast-list")
|
||||
.stdin(Stdio::null())
|
||||
@@ -234,6 +234,7 @@ pub async fn create_token_for_owner<'c>(
|
||||
owner: &str,
|
||||
label: &str,
|
||||
expires_in: i32,
|
||||
email: &str,
|
||||
) -> error::Result<(Transaction<'c, Postgres>, String)> {
|
||||
// TODO: Bad implementation. We should not have access to this DB here.
|
||||
let token: String = rd_string(30);
|
||||
@@ -245,14 +246,15 @@ pub async fn create_token_for_owner<'c>(
|
||||
|
||||
sqlx::query_scalar!(
|
||||
"INSERT INTO token
|
||||
(workspace_id, token, owner, label, expiration, super_admin)
|
||||
VALUES ($1, $2, $3, $4, now() + ($5 || ' seconds')::interval, $6)",
|
||||
(workspace_id, token, owner, label, expiration, super_admin, email)
|
||||
VALUES ($1, $2, $3, $4, now() + ($5 || ' seconds')::interval, $6, $7)",
|
||||
&w_id,
|
||||
token,
|
||||
owner,
|
||||
label,
|
||||
expires_in.to_string(),
|
||||
is_super_admin
|
||||
is_super_admin,
|
||||
email
|
||||
)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
@@ -414,10 +416,26 @@ pub async fn run_worker(
|
||||
let pip_index_url = std::env::var("PIP_INDEX_URL").ok();
|
||||
let pip_extra_index_url = std::env::var("PIP_EXTRA_INDEX_URL").ok();
|
||||
let pip_trusted_host = std::env::var("PIP_TRUSTED_HOST").ok();
|
||||
let deno_auth_tokens = std::env::var("DENO_AUTH_TOKENS")
|
||||
.ok()
|
||||
.map(|x| format!(";{x}"))
|
||||
.unwrap_or_else(|| String::new());
|
||||
let max_log_size = std::env::var("MAX_LOG_SIZE")
|
||||
.ok()
|
||||
.and_then(|x| x.parse::<i64>().ok())
|
||||
.unwrap_or(500000);
|
||||
let deno_flags = std::env::var("DENO_FLAGS")
|
||||
.ok()
|
||||
.map(|x| x.split(' ').map(|x| x.to_string()).collect());
|
||||
let pip_local_dependencies = std::env::var("PIP_LOCAL_DEPENDENCIES")
|
||||
.ok()
|
||||
.map(|x| x.split(',').map(|x| x.to_string()).collect());
|
||||
|
||||
let pip_local_dependencies = if pip_local_dependencies == Some(vec!["".to_string()]) {
|
||||
None
|
||||
} else {
|
||||
pip_local_dependencies
|
||||
};
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
let tar_cache_rate = std::env::var("TAR_CACHE_RATE")
|
||||
@@ -425,6 +443,10 @@ pub async fn run_worker(
|
||||
.and_then(|x| x.parse::<i32>().ok())
|
||||
.unwrap_or(100);
|
||||
|
||||
let additional_python_paths = std::env::var("ADDITIONAL_PYTHON_PATHS")
|
||||
.ok()
|
||||
.map(|x| x.split(':').map(|x| x.to_string()).collect());
|
||||
|
||||
let envs = Envs {
|
||||
deno_path,
|
||||
go_path,
|
||||
@@ -436,6 +458,10 @@ pub async fn run_worker(
|
||||
pip_extra_index_url,
|
||||
pip_trusted_host,
|
||||
max_log_size,
|
||||
deno_flags,
|
||||
deno_auth_tokens,
|
||||
pip_local_dependencies,
|
||||
additional_python_paths,
|
||||
};
|
||||
WORKER_STARTED.inc();
|
||||
|
||||
@@ -570,10 +596,11 @@ pub async fn run_worker(
|
||||
&job.permissioned_as,
|
||||
"ephemeral-script",
|
||||
timeout * 2,
|
||||
&job.email,
|
||||
)
|
||||
.await.expect("could not create job token");
|
||||
tx.commit().await.expect("could not commit job token");
|
||||
let job_client = windmill_api_client::create_client(&worker_config.base_url, token.clone());
|
||||
let job_client = windmill_api_client::create_client(&worker_config.base_internal_url, token.clone());
|
||||
let is_flow = job.job_kind == JobKind::Flow || job.job_kind == JobKind::FlowPreview || job.job_kind == JobKind::FlowDependencies;
|
||||
|
||||
if let Some(err) = handle_queued_job(
|
||||
@@ -649,24 +676,29 @@ async fn handle_job_error(
|
||||
keep_job_dir: bool,
|
||||
base_internal_url: &str,
|
||||
) {
|
||||
let _ = add_completed_job_error(
|
||||
db,
|
||||
&job,
|
||||
format!("Unexpected error during job execution:\n{err}"),
|
||||
json!({"message": err.to_string(), "name": "InternalErr"}),
|
||||
metrics.clone(),
|
||||
)
|
||||
.await;
|
||||
let err = match err {
|
||||
Error::JsonErr(err) => err,
|
||||
_ => json!({"message": err.to_string(), "name": "InternalErr"}),
|
||||
};
|
||||
|
||||
let update_job_future = || {
|
||||
add_completed_job_error(
|
||||
db,
|
||||
&job,
|
||||
format!("Unexpected error during job execution:\n{err}"),
|
||||
err.clone(),
|
||||
metrics.clone(),
|
||||
)
|
||||
};
|
||||
|
||||
if job.is_flow_step || job.job_kind == JobKind::FlowPreview || job.job_kind == JobKind::Flow {
|
||||
let (flow, job_status_to_update) = if let Some(parent_job_id) = job.parent_job {
|
||||
let _ = update_job_future().await;
|
||||
(parent_job_id, job.id)
|
||||
} else {
|
||||
(job.id, Uuid::nil())
|
||||
};
|
||||
|
||||
let mut output_map = serde_json::Map::new();
|
||||
error_to_result(&mut output_map, &err);
|
||||
let updated_flow = update_flow_status_after_job_completion(
|
||||
db,
|
||||
client,
|
||||
@@ -674,7 +706,7 @@ async fn handle_job_error(
|
||||
&job_status_to_update,
|
||||
&job.workspace_id,
|
||||
false,
|
||||
serde_json::Value::Object(output_map),
|
||||
json!({ "error": err }),
|
||||
metrics.clone(),
|
||||
unrecoverable,
|
||||
same_worker_tx,
|
||||
@@ -684,9 +716,8 @@ async fn handle_job_error(
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
if let Err(err) = updated_flow {
|
||||
println!("error updating flow status: {}", err);
|
||||
|
||||
if let Err(err) = updated_flow {
|
||||
if let Some(parent_job_id) = job.parent_job {
|
||||
if let Ok(mut tx) = db.begin().await {
|
||||
if let Ok(Some(parent_job)) =
|
||||
@@ -705,7 +736,10 @@ async fn handle_job_error(
|
||||
}
|
||||
}
|
||||
}
|
||||
tracing::error!(job_id = %job.id, err = err.alt(), "error handling job: {} {} {}", job.id, job.workspace_id, job.created_by);
|
||||
if job.parent_job.is_none() {
|
||||
let _ = update_job_future().await;
|
||||
}
|
||||
tracing::error!(job_id = %job.id, "error handling job: {err:#?} {} {} {}", job.id, job.workspace_id, job.created_by);
|
||||
}
|
||||
|
||||
async fn insert_initial_ping(
|
||||
@@ -735,7 +769,11 @@ struct Envs {
|
||||
pip_index_url: Option<String>,
|
||||
pip_extra_index_url: Option<String>,
|
||||
pip_trusted_host: Option<String>,
|
||||
deno_auth_tokens: String,
|
||||
deno_flags: Option<Vec<String>>,
|
||||
max_log_size: i64,
|
||||
pip_local_dependencies: Option<Vec<String>>,
|
||||
additional_python_paths: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
fn extract_error_value(log_lines: &str) -> serde_json::Value {
|
||||
@@ -758,9 +796,7 @@ async fn handle_queued_job(
|
||||
base_internal_url: &str,
|
||||
) -> windmill_common::error::Result<()> {
|
||||
if job.canceled {
|
||||
return Err(Error::ExecutionErr(
|
||||
canceled_job_to_result(&job).to_string(),
|
||||
))?;
|
||||
return Err(Error::JsonErr(canceled_job_to_result(&job)))?;
|
||||
}
|
||||
if let Some(e) = job.pre_run_error {
|
||||
return Err(Error::ExecutionErr(e));
|
||||
@@ -886,7 +922,7 @@ async fn handle_queued_job(
|
||||
}
|
||||
}
|
||||
err @ _ => {
|
||||
json!({"message": format!("error before termination: {err:#?}"), "name": "ExecutionErr"})
|
||||
json!({"message": format!("error during execution of the script:\n{}", err), "name": "ExecutionErr"})
|
||||
}
|
||||
};
|
||||
|
||||
@@ -932,6 +968,7 @@ async fn write_file(dir: &str, path: &str, content: &str) -> error::Result<File>
|
||||
|
||||
#[async_recursion]
|
||||
async fn transform_json_value(
|
||||
name: &str,
|
||||
client: &windmill_api_client::Client,
|
||||
workspace: &str,
|
||||
v: Value,
|
||||
@@ -942,7 +979,7 @@ async fn transform_json_value(
|
||||
let v = client
|
||||
.get_variable(workspace, path, Some(true))
|
||||
.await
|
||||
.map_err(|_| Error::NotFound(format!("Variable {path} not found")))
|
||||
.map_err(|_| Error::NotFound(format!("Variable {path} not found for `{name}`")))
|
||||
.map(|v| v.into_inner())?
|
||||
.value
|
||||
.unwrap_or_else(|| String::new());
|
||||
@@ -951,20 +988,23 @@ async fn transform_json_value(
|
||||
Value::String(y) if y.starts_with("$res:") => {
|
||||
let path = y.strip_prefix("$res:").unwrap();
|
||||
if path.split("/").count() < 2 {
|
||||
return Err(Error::InternalErr(
|
||||
format!("invalid resource path: {path}",),
|
||||
));
|
||||
return Err(Error::InternalErr(format!(
|
||||
"Argument `{name}` is an invalid resource path: {path}",
|
||||
)));
|
||||
}
|
||||
let v = client
|
||||
.get_resource_value(workspace, path)
|
||||
.await
|
||||
.map_err(|_| Error::NotFound(format!("Resource {path} not found")))?
|
||||
.map_err(|_| Error::NotFound(format!("Resource {path} not found for `{name}`")))?
|
||||
.into_inner();
|
||||
transform_json_value(client, workspace, v).await
|
||||
transform_json_value(name, client, workspace, v).await
|
||||
}
|
||||
Value::Object(mut m) => {
|
||||
for (a, b) in m.clone().into_iter() {
|
||||
m.insert(a, transform_json_value(client, workspace, b).await?);
|
||||
m.insert(
|
||||
a.clone(),
|
||||
transform_json_value(&a, client, workspace, b).await?,
|
||||
);
|
||||
}
|
||||
Ok(Value::Object(m))
|
||||
}
|
||||
@@ -1411,7 +1451,7 @@ fn capitalize(s: &str) -> String {
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
async fn handle_deno_job(
|
||||
WorkerConfig { base_internal_url, base_url, disable_nuser, disable_nsjail, .. }: &WorkerConfig,
|
||||
Envs { nsjail_path, deno_path, path_env, max_log_size, .. }: &Envs,
|
||||
Envs { nsjail_path, deno_path, path_env, max_log_size, deno_auth_tokens, deno_flags, .. }: &Envs,
|
||||
logs: &mut String,
|
||||
job: &QueuedJob,
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
@@ -1461,12 +1501,23 @@ run().catch(async (e) => {{
|
||||
"#,
|
||||
);
|
||||
write_file(job_dir, "main.ts", &wrapper_content).await?;
|
||||
let w_id = job.workspace_id.clone();
|
||||
let import_map = format!(
|
||||
r#"{{
|
||||
"imports": {{
|
||||
"/": "{base_internal_url}/api/w/{w_id}/scripts/raw/p/",
|
||||
"./": "./"
|
||||
}}
|
||||
}}"#
|
||||
);
|
||||
write_file(job_dir, "import_map.json", &import_map).await?;
|
||||
let mut reserved_variables = get_reserved_variables(job, &token, &base_url, db).await?;
|
||||
reserved_variables.insert("RUST_LOG".to_string(), "info".to_string());
|
||||
|
||||
let hostname_base = base_url.split("://").last().unwrap_or("localhost");
|
||||
let hostname_internal = base_internal_url.split("://").last().unwrap_or("localhost");
|
||||
let deno_auth_tokens = format!("{token}@{hostname_base};{token}@{hostname_internal}");
|
||||
let deno_auth_tokens =
|
||||
format!("{token}@{hostname_base};{token}@{hostname_internal}{deno_auth_tokens}",);
|
||||
let child = async {
|
||||
Ok(if !disable_nsjail {
|
||||
let _ = write_file(
|
||||
@@ -1488,9 +1539,16 @@ run().catch(async (e) => {{
|
||||
if lockfile.is_some() {
|
||||
args.push("--lock=/tmp/lock.json");
|
||||
}
|
||||
args.push("--import-map");
|
||||
args.push("/tmp/import_map.json");
|
||||
args.push("--unstable");
|
||||
args.push("--v8-flags=--max-heap-size=2048");
|
||||
args.push("-A");
|
||||
if let Some(deno_flags) = deno_flags {
|
||||
for flag in deno_flags {
|
||||
args.push(flag);
|
||||
}
|
||||
} else {
|
||||
args.push("-A");
|
||||
}
|
||||
args.push("/tmp/main.ts");
|
||||
|
||||
Command::new(nsjail_path)
|
||||
@@ -1507,13 +1565,18 @@ run().catch(async (e) => {{
|
||||
} else {
|
||||
let mut args = Vec::new();
|
||||
let script_path = format!("{job_dir}/main.ts");
|
||||
let import_map_path = format!("{job_dir}/import_map.json");
|
||||
args.push("run");
|
||||
if lockfile.is_some() {
|
||||
args.push("--lock=/tmp/lock.json");
|
||||
}
|
||||
args.push("--import-map");
|
||||
args.push(&import_map_path);
|
||||
args.push("--unstable");
|
||||
args.push("--v8-flags=--max-heap-size=2048");
|
||||
args.push("-A");
|
||||
if let Some(deno_flags) = deno_flags {
|
||||
for flag in deno_flags {
|
||||
args.push(flag);
|
||||
}
|
||||
} else {
|
||||
args.push("-A");
|
||||
}
|
||||
args.push(&script_path);
|
||||
Command::new(deno_path)
|
||||
.current_dir(job_dir)
|
||||
@@ -1543,7 +1606,7 @@ async fn create_args_and_out_file(
|
||||
job_dir: &str,
|
||||
) -> Result<(), Error> {
|
||||
let args = if let Some(args) = &job.args {
|
||||
Some(transform_json_value(client, &job.workspace_id, args.clone()).await?)
|
||||
Some(transform_json_value("args", client, &job.workspace_id, args.clone()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -1560,7 +1623,9 @@ lazy_static! {
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
async fn handle_python_job(
|
||||
WorkerConfig { base_internal_url, base_url, disable_nuser, disable_nsjail, .. }: &WorkerConfig,
|
||||
envs @ Envs { nsjail_path, python_path, path_env, max_log_size, .. }: &Envs,
|
||||
envs @ Envs {
|
||||
nsjail_path, python_path, path_env, max_log_size, additional_python_paths, ..
|
||||
}: &Envs,
|
||||
requirements_o: Option<String>,
|
||||
job_dir: &str,
|
||||
worker_dir: &str,
|
||||
@@ -1576,7 +1641,8 @@ async fn handle_python_job(
|
||||
) -> error::Result<serde_json::Value> {
|
||||
create_dependencies_dir(job_dir).await;
|
||||
|
||||
let mut additional_python_paths: Vec<String> = vec![];
|
||||
let mut additional_python_paths: Vec<String> =
|
||||
additional_python_paths.to_owned().unwrap_or_else(|| vec![]);
|
||||
|
||||
let requirements = match requirements_o {
|
||||
Some(r) => r,
|
||||
@@ -1683,6 +1749,15 @@ async fn handle_python_job(
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let spread = if sig.star_kwargs {
|
||||
"args = kwargs".to_string()
|
||||
} else {
|
||||
sig.args
|
||||
.into_iter()
|
||||
.map(|x| format!("args[\"{}\"] = kwargs[\"{}\"]", x.name, x.name))
|
||||
.join("\n")
|
||||
};
|
||||
|
||||
let wrapper_content: String = format!(
|
||||
r#"
|
||||
import json
|
||||
@@ -1699,9 +1774,11 @@ with open("args.json") as f:
|
||||
for k, v in list(kwargs.items()):
|
||||
if v == '<function call>':
|
||||
del kwargs[k]
|
||||
args = {{}}
|
||||
{spread}
|
||||
{transforms}
|
||||
try:
|
||||
res = inner_script.main(**kwargs)
|
||||
res = inner_script.main(**args)
|
||||
res_json = json.dumps(res, separators=(',', ':'), default=str).replace('\n', '')
|
||||
with open("result.json", 'w') as f:
|
||||
f.write(res_json)
|
||||
@@ -2055,7 +2132,14 @@ async fn pip_compile(
|
||||
requirements: &str,
|
||||
logs: &mut String,
|
||||
job_dir: &str,
|
||||
Envs { pip_extra_index_url, pip_index_url, pip_trusted_host, max_log_size, .. }: &Envs,
|
||||
Envs {
|
||||
pip_extra_index_url,
|
||||
pip_index_url,
|
||||
pip_trusted_host,
|
||||
max_log_size,
|
||||
pip_local_dependencies,
|
||||
..
|
||||
}: &Envs,
|
||||
db: &Pool<Postgres>,
|
||||
timeout: i32,
|
||||
) -> error::Result<String> {
|
||||
@@ -2063,6 +2147,15 @@ async fn pip_compile(
|
||||
set_logs(logs, job_id, db).await;
|
||||
logs.push_str(&format!("\ncontent of requirements:\n{}", requirements));
|
||||
let file = "requirements.in";
|
||||
let requirements = if let Some(pip_local_dependencies) = pip_local_dependencies {
|
||||
let deps = pip_local_dependencies.clone();
|
||||
requirements
|
||||
.lines()
|
||||
.filter(|s| !deps.contains(&s.to_string()))
|
||||
.join("\n")
|
||||
} else {
|
||||
requirements.to_string()
|
||||
};
|
||||
write_file(job_dir, file, &requirements).await?;
|
||||
|
||||
let mut args = vec!["-q", "--no-header", file, "--resolver=backtracking"];
|
||||
@@ -2595,6 +2688,7 @@ async fn handle_zombie_jobs(db: &Pool<Postgres>, timeout: i32, base_url: &str) {
|
||||
&job.permissioned_as,
|
||||
"ephemeral-zombie-jobs",
|
||||
timeout * 2,
|
||||
&job.email,
|
||||
)
|
||||
.await
|
||||
.expect("could not create job token");
|
||||
|
||||
@@ -427,8 +427,10 @@ pub async fn update_flow_status_after_job_completion(
|
||||
"Flow job canceled".to_string()
|
||||
} else if stop_early {
|
||||
format!("Flow job stopped early because of a stop early predicate returning true")
|
||||
} else if success {
|
||||
"Flow job completed with success".to_string()
|
||||
} else {
|
||||
"Flow job completed".to_string()
|
||||
"Flow job completed with error".to_string()
|
||||
};
|
||||
if flow_job.canceled {
|
||||
add_completed_job_error(
|
||||
@@ -986,7 +988,7 @@ async fn push_next_flow_job(
|
||||
let success = false;
|
||||
let skipped = false;
|
||||
let logs = "Timed out waiting to be resumed".to_string();
|
||||
let result = json!({ "error": logs });
|
||||
let result = json!({ "error": {"message": logs, "name": "SuspendedTimeout"}});
|
||||
let _uuid =
|
||||
add_completed_job(db, &flow_job, success, skipped, result, logs).await?;
|
||||
|
||||
@@ -1881,6 +1883,7 @@ async fn get_transform_context(
|
||||
&flow_job.permissioned_as,
|
||||
"transform-input",
|
||||
10,
|
||||
&flow_job.email,
|
||||
)
|
||||
.await?;
|
||||
//we need to commit asap otherwise the token won't be valid for auth to check outside of this transaction
|
||||
|
||||
@@ -12,7 +12,7 @@ import hub from "./hub.ts";
|
||||
import { tryResolveVersion } from "./context.ts";
|
||||
import { GlobalOptions } from "./types.ts";
|
||||
|
||||
const VERSION = "v1.60.0";
|
||||
const VERSION = "v1.61.1";
|
||||
|
||||
const command = new Command()
|
||||
.name("wmill")
|
||||
|
||||
@@ -7,6 +7,12 @@ export {
|
||||
UserService, WorkspaceService
|
||||
} from './windmill-api/index.ts'
|
||||
|
||||
|
||||
// @ts-ignore: Otherwise BigInt is not supported for export
|
||||
BigInt.prototype.toJSON = function () {
|
||||
return this.toString();
|
||||
};
|
||||
|
||||
export { pgSql, pgClient } from './pg.ts'
|
||||
export { mySql, mysqlClient } from './mysql.ts'
|
||||
|
||||
|
||||
140
docker/DockerfileOpenbb
Normal file
140
docker/DockerfileOpenbb
Normal file
@@ -0,0 +1,140 @@
|
||||
FROM debian:buster-slim as nsjail
|
||||
|
||||
WORKDIR /nsjail
|
||||
|
||||
ARG nsjail=""
|
||||
|
||||
RUN if [ "$nsjail" = "true" ]; then apt-get -y update \
|
||||
&& apt-get install -y \
|
||||
bison=2:3.3.* \
|
||||
flex=2.6.* \
|
||||
g++=4:8.3.* \
|
||||
gcc=4:8.3.* \
|
||||
git=1:2.20.* \
|
||||
libprotobuf-dev=3.6.* \
|
||||
libnl-route-3-dev=3.4.* \
|
||||
make=4.2.* \
|
||||
pkg-config=0.29-6 \
|
||||
protobuf-compiler=3.6.*; fi
|
||||
|
||||
|
||||
RUN if [ "$nsjail" = "true" ]; then git clone -b master --single-branch https://github.com/google/nsjail.git . \
|
||||
&& git checkout dccf911fd2659e7b08ce9507c25b2b38ec2c5800; fi
|
||||
RUN if [ "$nsjail" = "true" ]; then make; else touch nsjail; fi
|
||||
|
||||
FROM rust:slim-buster AS rust_base
|
||||
|
||||
RUN apt-get update && apt-get install -y git libssl-dev pkg-config npm
|
||||
|
||||
RUN apt-get -y update \
|
||||
&& apt-get install -y \
|
||||
curl lld nodejs npm
|
||||
|
||||
RUN rustup component add rustfmt
|
||||
|
||||
RUN CARGO_NET_GIT_FETCH_WITH_CLI=true cargo install cargo-chef
|
||||
|
||||
WORKDIR /windmill
|
||||
|
||||
ENV SQLX_OFFLINE=true
|
||||
ENV CARGO_INCREMENTAL=1
|
||||
|
||||
FROM node:19-alpine as frontend
|
||||
|
||||
# install dependencies
|
||||
WORKDIR /frontend
|
||||
COPY ./frontend/package.json ./frontend/package-lock.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Copy all local files into the image.
|
||||
COPY frontend .
|
||||
RUN mkdir /backend
|
||||
COPY /backend/windmill-api/openapi.yaml /backend/windmill-api/openapi.yaml
|
||||
COPY /openflow.openapi.yaml /openflow.openapi.yaml
|
||||
COPY /backend/windmill-api/build_openapi.sh /backend/windmill-api/build_openapi.sh
|
||||
RUN cd /backend/windmill-api && . ./build_openapi.sh
|
||||
|
||||
RUN npm run generate-backend-client
|
||||
ENV NODE_OPTIONS "--max-old-space-size=8192"
|
||||
RUN npm run build
|
||||
RUN npm run check
|
||||
|
||||
|
||||
|
||||
FROM rust_base AS planner
|
||||
|
||||
COPY ./openflow.openapi.yaml /openflow.openapi.yaml
|
||||
COPY ./backend ./
|
||||
|
||||
RUN CARGO_NET_GIT_FETCH_WITH_CLI=true cargo chef prepare --recipe-path recipe.json
|
||||
|
||||
FROM rust_base AS builder
|
||||
ARG features=""
|
||||
|
||||
COPY --from=planner /windmill/recipe.json recipe.json
|
||||
|
||||
RUN CARGO_NET_GIT_FETCH_WITH_CLI=true cargo chef cook --release --features "$features" --recipe-path recipe.json
|
||||
|
||||
COPY ./openflow.openapi.yaml /openflow.openapi.yaml
|
||||
COPY ./backend ./
|
||||
|
||||
COPY --from=frontend /frontend /frontend
|
||||
COPY --from=frontend /backend/windmill-api/openapi-deref.yaml ./windmill-api/openapi-deref.yaml
|
||||
COPY .git/ .git/
|
||||
|
||||
RUN CARGO_NET_GIT_FETCH_WITH_CLI=true cargo build --release --features "$features"
|
||||
|
||||
|
||||
FROM python:3.10.8-slim-buster
|
||||
|
||||
ARG APP=/usr/src/app
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y ca-certificates wget curl git jq libprotobuf-dev libnl-route-3-dev unzip \
|
||||
&& apt-get install -y ca-certificates wget curl git jq libprotobuf-dev libnl-route-3-dev unzip build-essential \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
|
||||
curl -o rclone.zip "https://downloads.rclone.org/v1.60.1/rclone-v1.60.1-linux-$arch.zip"; \
|
||||
unzip -p rclone.zip rclone-v1.60.1-linux-$arch/rclone > /usr/bin/rclone; rm rclone.zip; \
|
||||
chown root:root /usr/bin/rclone; chmod 755 /usr/bin/rclone
|
||||
|
||||
RUN set -eux; \
|
||||
arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
|
||||
url=; \
|
||||
case "$arch" in \
|
||||
'amd64') \
|
||||
targz='go1.19.3.linux-amd64.tar.gz'; \
|
||||
;; \
|
||||
'arm64') \
|
||||
targz='go1.19.3.linux-arm64.tar.gz'; \
|
||||
;; \
|
||||
'armhf') \
|
||||
targz='go1.19.3.linux-armv6l.tar.gz'; \
|
||||
;; \
|
||||
*) echo >&2 "error: unsupported architecture '$arch' (likely packaging update needed)"; exit 1 ;; \
|
||||
esac; \
|
||||
wget "https://golang.org/dl/$targz" -nv && tar -C /usr/local -xzf "$targz" && rm "$targz";
|
||||
|
||||
ENV PATH="${PATH}:/usr/local/go/bin"
|
||||
ENV GO_PATH=/usr/local/go/bin/go
|
||||
|
||||
ENV TZ=Etc/UTC
|
||||
|
||||
RUN /usr/local/bin/python3 -m pip install pip-tools
|
||||
|
||||
COPY --from=builder /windmill/target/release/windmill ${APP}/windmill
|
||||
|
||||
COPY --from=nsjail /nsjail/nsjail /bin/nsjail
|
||||
|
||||
COPY --from=denoland/deno:latest /usr/bin/deno /usr/bin/deno
|
||||
|
||||
RUN /usr/local/bin/python3 -m pip install openbb[all]
|
||||
|
||||
RUN mkdir -p ${APP}
|
||||
|
||||
WORKDIR ${APP}
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["./windmill"]
|
||||
231
frontend/package-lock.json
generated
231
frontend/package-lock.json
generated
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"name": "windmill",
|
||||
"version": "1.60.0",
|
||||
"version": "1.61.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill",
|
||||
"version": "1.60.0",
|
||||
"version": "1.61.1",
|
||||
"dependencies": {
|
||||
"@fortawesome/free-brands-svg-icons": "^6.2.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
||||
"@leeoniya/ufuzzy": "^0.9.0",
|
||||
"@leeoniya/ufuzzy": "^0.9.1",
|
||||
"@redocly/json-to-json-schema": "^0.0.1",
|
||||
"@tanstack/svelte-table": "^8.7.6",
|
||||
"async-mutex": "^0.4.0",
|
||||
"chartjs-adapter-date-fns": "^2.0.0",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"chartjs-plugin-zoom": "^2.0.0",
|
||||
"date-fns": "^2.29.3",
|
||||
"highlight.js": "^11.7.0",
|
||||
@@ -32,12 +32,12 @@
|
||||
"@playwright/test": "^1.29.2",
|
||||
"@sveltejs/adapter-static": "^1.0.0",
|
||||
"@sveltejs/kit": "^1.0.0-next.589",
|
||||
"@sveltejs/package": "^1.0.1",
|
||||
"@sveltejs/package": "^1.0.2",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/typography": "^0.5.8",
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/vscode": "~1.74.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.46.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"@windmill-labs/svelvet": "^4.0.20",
|
||||
"@zerodevx/svelte-toast": "^0.8.1",
|
||||
@@ -71,7 +71,7 @@
|
||||
"tailwindcss": "^3.2.2",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^4.0.3"
|
||||
"vite": "^4.0.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.53.1"
|
||||
@@ -709,9 +709,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@leeoniya/ufuzzy": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@leeoniya/ufuzzy/-/ufuzzy-0.9.0.tgz",
|
||||
"integrity": "sha512-p2zWsX0GwO1x723Yhb3KLAoSwp1geQvzRPHgIoOR/0qn8Ptpsb3b01+W47iAYR/NWo0pX36XQoTU0alVRykMAg=="
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@leeoniya/ufuzzy/-/ufuzzy-0.9.1.tgz",
|
||||
"integrity": "sha512-xlL3HMhHdZX/UVTyjiLB0hpUa71HERhegvrfkUD9NxGnYh9kk9+/llynEWS+NmGRB2KlRKoY+HmWZzt/c0whJQ=="
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
@@ -828,15 +828,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/package": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/package/-/package-1.0.1.tgz",
|
||||
"integrity": "sha512-iYoDz4AEJQUfdKUfBcwtYEGYkf4NMByQL3Sl2ESnu+IXsLNsHvhH0zUDhAmUmAgcrH8fVjiR7FuJeyh+7EQtiw==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/package/-/package-1.0.2.tgz",
|
||||
"integrity": "sha512-VY9U+05d9uNFDj7ScKRlHORYlfPSHwJewBjV+V2RsnViexpLFPUrboC9SiPYDCpLnbeqwXerxhO6twGHUBGeIA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chokidar": "^3.5.3",
|
||||
"kleur": "^4.1.5",
|
||||
"sade": "^1.8.1",
|
||||
"svelte2tsx": "~0.5.20"
|
||||
"svelte2tsx": "~0.6.0"
|
||||
},
|
||||
"bin": {
|
||||
"svelte-package": "svelte-package.js"
|
||||
@@ -848,6 +848,20 @@
|
||||
"svelte": "^3.44.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/package/node_modules/svelte2tsx": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.6.0.tgz",
|
||||
"integrity": "sha512-TrxfQkO7CKi8Pu2eC/FyteDCdk3OOeQV5u6z7OjYAsOhsd0ClzAKqxJdvp6xxNQLrbFzf/XvCi9Fy8MQ1MleFA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"dedent-js": "^1.0.1",
|
||||
"pascal-case": "^3.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.55",
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/vite-plugin-svelte": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.0.2.tgz",
|
||||
@@ -1001,14 +1015,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.1.tgz",
|
||||
"integrity": "sha512-YpzNv3aayRBwjs4J3oz65eVLXc9xx0PDbIRisHj+dYhvBn02MjYOD96P8YGiWEIFBrojaUjxvkaUpakD82phsA==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz",
|
||||
"integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.46.1",
|
||||
"@typescript-eslint/type-utils": "5.46.1",
|
||||
"@typescript-eslint/utils": "5.46.1",
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/type-utils": "5.49.0",
|
||||
"@typescript-eslint/utils": "5.49.0",
|
||||
"debug": "^4.3.4",
|
||||
"ignore": "^5.2.0",
|
||||
"natural-compare-lite": "^1.4.0",
|
||||
@@ -1135,13 +1149,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.1.tgz",
|
||||
"integrity": "sha512-iOChVivo4jpwUdrJZyXSMrEIM/PvsbbDOX1y3UCKjSgWn+W89skxWaYXACQfxmIGhPVpRWK/VWPYc+bad6smIA==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
|
||||
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.46.1",
|
||||
"@typescript-eslint/visitor-keys": "5.46.1"
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/visitor-keys": "5.49.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@@ -1152,13 +1166,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.46.1.tgz",
|
||||
"integrity": "sha512-V/zMyfI+jDmL1ADxfDxjZ0EMbtiVqj8LUGPAGyBkXXStWmCUErMpW873zEHsyguWCuq2iN4BrlWUkmuVj84yng==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
|
||||
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "5.46.1",
|
||||
"@typescript-eslint/utils": "5.46.1",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"@typescript-eslint/utils": "5.49.0",
|
||||
"debug": "^4.3.4",
|
||||
"tsutils": "^3.21.0"
|
||||
},
|
||||
@@ -1179,9 +1193,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.1.tgz",
|
||||
"integrity": "sha512-Z5pvlCaZgU+93ryiYUwGwLl9AQVB/PQ1TsJ9NZ/gHzZjN7g9IAn6RSDkpCV8hqTwAiaj6fmCcKSQeBPlIpW28w==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
|
||||
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@@ -1192,13 +1206,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.1.tgz",
|
||||
"integrity": "sha512-j9W4t67QiNp90kh5Nbr1w92wzt+toiIsaVPnEblB2Ih2U9fqBTyqV9T3pYWZBRt6QoMh/zVWP59EpuCjc4VRBg==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz",
|
||||
"integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.46.1",
|
||||
"@typescript-eslint/visitor-keys": "5.46.1",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/visitor-keys": "5.49.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -1219,16 +1233,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.1.tgz",
|
||||
"integrity": "sha512-RBdBAGv3oEpFojaCYT4Ghn4775pdjvwfDOfQ2P6qzNVgQOVrnSPe5/Pb88kv7xzYQjoio0eKHKB9GJ16ieSxvA==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
|
||||
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@types/semver": "^7.3.12",
|
||||
"@typescript-eslint/scope-manager": "5.46.1",
|
||||
"@typescript-eslint/types": "5.46.1",
|
||||
"@typescript-eslint/typescript-estree": "5.46.1",
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"semver": "^7.3.7"
|
||||
@@ -1245,12 +1259,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.1.tgz",
|
||||
"integrity": "sha512-jczZ9noovXwy59KjRTk1OftT78pwygdcmCuBf8yMoWt/8O8l+6x2LSEze0E4TeepXK4MezW3zGSyoDRZK7Y9cg==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz",
|
||||
"integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.46.1",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1868,11 +1882,12 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/chartjs-adapter-date-fns": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-2.0.0.tgz",
|
||||
"integrity": "sha512-rmZINGLe+9IiiEB0kb57vH3UugAtYw33anRiw5kS2Tu87agpetDDoouquycWc9pRsKtQo5j+vLsYHyr8etAvFw==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz",
|
||||
"integrity": "sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==",
|
||||
"peerDependencies": {
|
||||
"chart.js": "^3.0.0"
|
||||
"chart.js": ">=2.8.0",
|
||||
"date-fns": ">=2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chartjs-plugin-zoom": {
|
||||
@@ -6786,9 +6801,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.0.3.tgz",
|
||||
"integrity": "sha512-HvuNv1RdE7deIfQb8mPk51UKjqptO/4RXZ5yXSAvurd5xOckwS/gg8h9Tky3uSbnjYTgUm0hVCet1cyhKd73ZA==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.0.4.tgz",
|
||||
"integrity": "sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.16.3",
|
||||
@@ -7537,9 +7552,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@leeoniya/ufuzzy": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@leeoniya/ufuzzy/-/ufuzzy-0.9.0.tgz",
|
||||
"integrity": "sha512-p2zWsX0GwO1x723Yhb3KLAoSwp1geQvzRPHgIoOR/0qn8Ptpsb3b01+W47iAYR/NWo0pX36XQoTU0alVRykMAg=="
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@leeoniya/ufuzzy/-/ufuzzy-0.9.1.tgz",
|
||||
"integrity": "sha512-xlL3HMhHdZX/UVTyjiLB0hpUa71HERhegvrfkUD9NxGnYh9kk9+/llynEWS+NmGRB2KlRKoY+HmWZzt/c0whJQ=="
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
@@ -7624,15 +7639,27 @@
|
||||
}
|
||||
},
|
||||
"@sveltejs/package": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/package/-/package-1.0.1.tgz",
|
||||
"integrity": "sha512-iYoDz4AEJQUfdKUfBcwtYEGYkf4NMByQL3Sl2ESnu+IXsLNsHvhH0zUDhAmUmAgcrH8fVjiR7FuJeyh+7EQtiw==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/package/-/package-1.0.2.tgz",
|
||||
"integrity": "sha512-VY9U+05d9uNFDj7ScKRlHORYlfPSHwJewBjV+V2RsnViexpLFPUrboC9SiPYDCpLnbeqwXerxhO6twGHUBGeIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chokidar": "^3.5.3",
|
||||
"kleur": "^4.1.5",
|
||||
"sade": "^1.8.1",
|
||||
"svelte2tsx": "~0.5.20"
|
||||
"svelte2tsx": "~0.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"svelte2tsx": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.6.0.tgz",
|
||||
"integrity": "sha512-TrxfQkO7CKi8Pu2eC/FyteDCdk3OOeQV5u6z7OjYAsOhsd0ClzAKqxJdvp6xxNQLrbFzf/XvCi9Fy8MQ1MleFA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dedent-js": "^1.0.1",
|
||||
"pascal-case": "^3.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@sveltejs/vite-plugin-svelte": {
|
||||
@@ -7755,14 +7782,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.1.tgz",
|
||||
"integrity": "sha512-YpzNv3aayRBwjs4J3oz65eVLXc9xx0PDbIRisHj+dYhvBn02MjYOD96P8YGiWEIFBrojaUjxvkaUpakD82phsA==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz",
|
||||
"integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "5.46.1",
|
||||
"@typescript-eslint/type-utils": "5.46.1",
|
||||
"@typescript-eslint/utils": "5.46.1",
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/type-utils": "5.49.0",
|
||||
"@typescript-eslint/utils": "5.49.0",
|
||||
"debug": "^4.3.4",
|
||||
"ignore": "^5.2.0",
|
||||
"natural-compare-lite": "^1.4.0",
|
||||
@@ -7827,41 +7854,41 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.1.tgz",
|
||||
"integrity": "sha512-iOChVivo4jpwUdrJZyXSMrEIM/PvsbbDOX1y3UCKjSgWn+W89skxWaYXACQfxmIGhPVpRWK/VWPYc+bad6smIA==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
|
||||
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.46.1",
|
||||
"@typescript-eslint/visitor-keys": "5.46.1"
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/visitor-keys": "5.49.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/type-utils": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.46.1.tgz",
|
||||
"integrity": "sha512-V/zMyfI+jDmL1ADxfDxjZ0EMbtiVqj8LUGPAGyBkXXStWmCUErMpW873zEHsyguWCuq2iN4BrlWUkmuVj84yng==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
|
||||
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/typescript-estree": "5.46.1",
|
||||
"@typescript-eslint/utils": "5.46.1",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"@typescript-eslint/utils": "5.49.0",
|
||||
"debug": "^4.3.4",
|
||||
"tsutils": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.1.tgz",
|
||||
"integrity": "sha512-Z5pvlCaZgU+93ryiYUwGwLl9AQVB/PQ1TsJ9NZ/gHzZjN7g9IAn6RSDkpCV8hqTwAiaj6fmCcKSQeBPlIpW28w==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
|
||||
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.1.tgz",
|
||||
"integrity": "sha512-j9W4t67QiNp90kh5Nbr1w92wzt+toiIsaVPnEblB2Ih2U9fqBTyqV9T3pYWZBRt6QoMh/zVWP59EpuCjc4VRBg==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz",
|
||||
"integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.46.1",
|
||||
"@typescript-eslint/visitor-keys": "5.46.1",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/visitor-keys": "5.49.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -7870,28 +7897,28 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/utils": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.1.tgz",
|
||||
"integrity": "sha512-RBdBAGv3oEpFojaCYT4Ghn4775pdjvwfDOfQ2P6qzNVgQOVrnSPe5/Pb88kv7xzYQjoio0eKHKB9GJ16ieSxvA==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
|
||||
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@types/semver": "^7.3.12",
|
||||
"@typescript-eslint/scope-manager": "5.46.1",
|
||||
"@typescript-eslint/types": "5.46.1",
|
||||
"@typescript-eslint/typescript-estree": "5.46.1",
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"semver": "^7.3.7"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "5.46.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.1.tgz",
|
||||
"integrity": "sha512-jczZ9noovXwy59KjRTk1OftT78pwygdcmCuBf8yMoWt/8O8l+6x2LSEze0E4TeepXK4MezW3zGSyoDRZK7Y9cg==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz",
|
||||
"integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.46.1",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
}
|
||||
},
|
||||
@@ -8314,9 +8341,9 @@
|
||||
"peer": true
|
||||
},
|
||||
"chartjs-adapter-date-fns": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-2.0.0.tgz",
|
||||
"integrity": "sha512-rmZINGLe+9IiiEB0kb57vH3UugAtYw33anRiw5kS2Tu87agpetDDoouquycWc9pRsKtQo5j+vLsYHyr8etAvFw==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz",
|
||||
"integrity": "sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==",
|
||||
"requires": {}
|
||||
},
|
||||
"chartjs-plugin-zoom": {
|
||||
@@ -11910,9 +11937,9 @@
|
||||
"optional": true
|
||||
},
|
||||
"vite": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.0.3.tgz",
|
||||
"integrity": "sha512-HvuNv1RdE7deIfQb8mPk51UKjqptO/4RXZ5yXSAvurd5xOckwS/gg8h9Tky3uSbnjYTgUm0hVCet1cyhKd73ZA==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.0.4.tgz",
|
||||
"integrity": "sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esbuild": "^0.16.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill",
|
||||
"version": "1.60.0",
|
||||
"version": "1.61.1",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
@@ -18,12 +18,12 @@
|
||||
"@playwright/test": "^1.29.2",
|
||||
"@sveltejs/adapter-static": "^1.0.0",
|
||||
"@sveltejs/kit": "^1.0.0-next.589",
|
||||
"@sveltejs/package": "^1.0.1",
|
||||
"@sveltejs/package": "^1.0.2",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/typography": "^0.5.8",
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/vscode": "~1.74.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.46.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"@windmill-labs/svelvet": "^4.0.20",
|
||||
"@zerodevx/svelte-toast": "^0.8.1",
|
||||
@@ -57,17 +57,17 @@
|
||||
"tailwindcss": "^3.2.2",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^4.0.3"
|
||||
"vite": "^4.0.4"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@fortawesome/free-brands-svg-icons": "^6.2.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
||||
"@leeoniya/ufuzzy": "^0.9.0",
|
||||
"@leeoniya/ufuzzy": "^0.9.1",
|
||||
"@redocly/json-to-json-schema": "^0.0.1",
|
||||
"@tanstack/svelte-table": "^8.7.6",
|
||||
"async-mutex": "^0.4.0",
|
||||
"chartjs-adapter-date-fns": "^2.0.0",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"chartjs-plugin-zoom": "^2.0.0",
|
||||
"date-fns": "^2.29.3",
|
||||
"highlight.js": "^11.7.0",
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface SchemaProperty {
|
||||
}
|
||||
|
||||
export type Schema = {
|
||||
$schema: string
|
||||
$schema: string | undefined
|
||||
type: string
|
||||
properties: { [name: string]: SchemaProperty }
|
||||
required: string[]
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { emptySchema, emptyString } from '$lib/utils'
|
||||
import SchemaForm from './SchemaForm.svelte'
|
||||
import SimpleEditor from './SimpleEditor.svelte'
|
||||
import Toggle from './Toggle.svelte'
|
||||
|
||||
export let resource_type: string
|
||||
export let args: Record<string, any> | any = {}
|
||||
@@ -12,7 +13,10 @@
|
||||
|
||||
let schema = emptySchema()
|
||||
let notFound = false
|
||||
|
||||
async function loadSchema() {
|
||||
rawCode = '{}'
|
||||
viewJsonSchema = false
|
||||
try {
|
||||
const rt = await ResourceService.getResourceType({
|
||||
workspace: $workspaceStore!,
|
||||
@@ -21,8 +25,6 @@
|
||||
schema = rt.schema
|
||||
notFound = false
|
||||
} catch (e) {
|
||||
rawCode = '{}'
|
||||
|
||||
notFound = true
|
||||
}
|
||||
}
|
||||
@@ -43,17 +45,40 @@
|
||||
}
|
||||
let error = ''
|
||||
let rawCode = ''
|
||||
let viewJsonSchema = false
|
||||
|
||||
$: rawCode && parseJson()
|
||||
|
||||
function switchTab(asJson: boolean) {
|
||||
viewJsonSchema = asJson
|
||||
if (asJson) {
|
||||
rawCode = JSON.stringify(args, null, 2)
|
||||
} else {
|
||||
parseJson()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if notFound}
|
||||
{#if !notFound}
|
||||
<div class="w-full flex flex-row-reverse">
|
||||
<Toggle
|
||||
on:change={(e) => switchTab(e.detail)}
|
||||
options={{
|
||||
right: 'As JSON'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="italic text-gray-500 text-xs mb-4"
|
||||
>No corresponding resource type found in your workspace for {resource_type}. Define the value in
|
||||
JSON directly</p
|
||||
>
|
||||
{/if}
|
||||
{#if notFound || viewJsonSchema}
|
||||
{#if !emptyString(error)}<span class="text-red-400 text-xs mb-1 flex flex-row-reverse"
|
||||
>{error}</span
|
||||
>{:else}<div class="py-2" />{/if}
|
||||
<SimpleEditor autoHeight lang="json" bind:code={rawCode} fixedOverflowWidgets={false} />
|
||||
{:else}
|
||||
<SchemaForm {password} isValid {schema} bind:args />
|
||||
<SchemaForm noDelete {password} isValid {schema} bind:args />
|
||||
{/if}
|
||||
|
||||
@@ -155,6 +155,7 @@
|
||||
async function next() {
|
||||
if (step == 1 && manual) {
|
||||
step += 1
|
||||
args = {}
|
||||
} else if (step == 1 && !manual) {
|
||||
const url = new URL(`/api/oauth/connect/${resource_type}`, $page.url.origin)
|
||||
url.searchParams.append('scopes', scopes.join('+'))
|
||||
@@ -410,19 +411,20 @@
|
||||
</ol>
|
||||
</div>
|
||||
{#if apiTokenApps[resource_type].img}
|
||||
<div class="mt-4 w-full">
|
||||
<img class="max-h-96 m-auto" alt="connect" src={apiTokenApps[resource_type].img} />
|
||||
<div class="mt-4 w-full overflow-hidden">
|
||||
<img class="m-auto max-h-60" alt="connect" src={apiTokenApps[resource_type].img} />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<h2 class="mt-4">Value</h2>
|
||||
<div class="mt-4">
|
||||
<ApiConnectForm password={key ?? ''} {resource_type} bind:args bind:isValid />
|
||||
{#key resource_type}
|
||||
<ApiConnectForm password={key ?? ''} {resource_type} bind:args bind:isValid />
|
||||
{/key}
|
||||
</div>
|
||||
|
||||
<h2 class="mt-4 mb-2">Description</h2>
|
||||
|
||||
<textarea type="text" autocomplete="off" use:autosize bind:value={description} />
|
||||
{:else}
|
||||
<Path
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
import autosize from 'svelte-autosize'
|
||||
|
||||
export let result: any
|
||||
export let requireHtmlApproval = false
|
||||
|
||||
let resultKind:
|
||||
| 'json'
|
||||
| 'table-col'
|
||||
| 'table-row'
|
||||
| 'html'
|
||||
| 'png'
|
||||
| 'file'
|
||||
| 'jpeg'
|
||||
@@ -19,9 +21,11 @@
|
||||
| 'error'
|
||||
| 'approval'
|
||||
| undefined
|
||||
|
||||
$: resultKind = inferResultKind(result)
|
||||
|
||||
let forceJson = false
|
||||
let enableHtml = false
|
||||
|
||||
function isRectangularArray(obj: any) {
|
||||
if (!Array.isArray(obj) || obj.length == 0) {
|
||||
@@ -53,6 +57,8 @@
|
||||
return 'table-row'
|
||||
} else if (keys.map((k) => Array.isArray(result[k])).reduce((a, b) => a && b)) {
|
||||
return 'table-col'
|
||||
} else if (keys.length == 1 && keys[0] == 'html') {
|
||||
return 'html'
|
||||
} else if (keys.length == 1 && keys[0] == 'png') {
|
||||
return 'png'
|
||||
} else if (keys.length == 1 && keys[0] == 'jpeg') {
|
||||
@@ -119,6 +125,34 @@
|
||||
</tbody>
|
||||
</TableCustom>
|
||||
</div>
|
||||
{:else if !forceJson && resultKind == 'html'}
|
||||
<div class="h-full">
|
||||
{#if !requireHtmlApproval || enableHtml}
|
||||
{@html result.html}
|
||||
{:else}
|
||||
<div class="font-main text-sm">
|
||||
<div class="flex flex-col">
|
||||
<div class="bg-red-400 py-1 rounded-t text-white font-bold text-center">
|
||||
Warning
|
||||
</div>
|
||||
<p
|
||||
class="text-gray-600 mb-2 text-left border-2 !border-t-0 rounded-b border-red-400 overflow-auto p-1"
|
||||
>Rendering HTML can expose you to <a href="https://owasp.org/www-community/attacks/xss/" target="_blank" rel="noreferrer" class="hover:underline">XSS attacks</a>.
|
||||
Only enable it if you trust the author of the script.
|
||||
</p>
|
||||
</div>
|
||||
<div class="center-center">
|
||||
<Button
|
||||
size="sm"
|
||||
color="dark"
|
||||
on:click={() => enableHtml = true}
|
||||
>
|
||||
Enable HTML rendering
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if !forceJson && resultKind == 'png'}
|
||||
<div class="h-full"
|
||||
><img alt="png rendered" class="w-auto h-full" src="data:image/png;base64,{result.png}" />
|
||||
@@ -142,7 +176,7 @@
|
||||
>
|
||||
</div>
|
||||
{:else if !forceJson && resultKind == 'error'}<div>
|
||||
<span class="text-red-500 font-semibold text-sm"
|
||||
<span class="text-red-500 font-semibold text-sm whitespace-pre-wrap"
|
||||
>{result.error.name}: {result.error.message}</span
|
||||
>
|
||||
<pre class="text-sm whitespace-pre-wrap text-gray-900">{result.error.stack ?? ''}</pre>
|
||||
|
||||
@@ -411,7 +411,8 @@
|
||||
|
||||
model.updateOptions(updateOptions)
|
||||
editor = meditor.create(divEl as HTMLDivElement, {
|
||||
...editorConfig(model, code, lang, automaticLayout, fixedOverflowWidgets)
|
||||
...editorConfig(model, code, lang, automaticLayout, fixedOverflowWidgets),
|
||||
tabSize: lang == 'python' ? 4 : 2
|
||||
})
|
||||
|
||||
let timeoutModel: NodeJS.Timeout | undefined = undefined
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import DisplayResult from './DisplayResult.svelte'
|
||||
import LogViewer from './LogViewer.svelte'
|
||||
|
||||
@@ -6,6 +7,7 @@
|
||||
export let logs: string
|
||||
export let col: boolean = false
|
||||
export let noBorder = false
|
||||
export let loading
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -14,7 +16,13 @@
|
||||
>
|
||||
<div class="bg-white max-h-80 h-full p-1 overflow-auto relative">
|
||||
<span class="text-gray-500">Result</span>
|
||||
<DisplayResult {result} />
|
||||
{#if result}
|
||||
<DisplayResult {result} />
|
||||
{:else if loading}
|
||||
<Loader2 class="animate-spin" />
|
||||
{:else}
|
||||
<div class="text-gray-400">No result (result is undefined)</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="overflow-auto max-h-80 h-full relative">
|
||||
<LogViewer content={logs ?? ''} isLoading={false} />
|
||||
|
||||
@@ -149,8 +149,8 @@
|
||||
</div>
|
||||
<FlowProgressBar {job} bind:reset={jobProgressReset} />
|
||||
|
||||
<div class="overflow-y-auto grow flex-col flex divide-y divide-gray-600 ">
|
||||
<div class="max-h-1/2 overflow-auto">
|
||||
<div class="overflow-y-auto grow divide-y divide-gray-600 ">
|
||||
<div class="max-h-1/2 overflow-auto border-b border-gray-700">
|
||||
<SchemaForm
|
||||
noVariablePicker
|
||||
compact
|
||||
@@ -160,8 +160,7 @@
|
||||
bind:args={$previewArgs}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="h-full pt-4 grow">
|
||||
<div class="pt-4 grow">
|
||||
{#if jobId}
|
||||
<FlowStatusViewer bind:flowState={$flowStateStore} {jobId} bind:job />
|
||||
{:else}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import Tabs from './common/tabs/Tabs.svelte'
|
||||
import { FlowGraph, type GraphModuleState } from './graph'
|
||||
import ModuleStatus from './ModuleStatus.svelte'
|
||||
import { displayDate, isOwner, pluralize, truncateRev } from '$lib/utils'
|
||||
import { displayDate, emptyString, isOwner, pluralize, truncateRev } from '$lib/utils'
|
||||
import JobArgs from './JobArgs.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import SimpleEditor from './SimpleEditor.svelte'
|
||||
@@ -180,7 +180,11 @@
|
||||
<FlowPreviewStatus {job} />
|
||||
{#if `result` in job}
|
||||
<div class="w-full h-full">
|
||||
<FlowJobResult result={job.result} logs={job.logs ?? ''} />
|
||||
<FlowJobResult
|
||||
loading={job['running'] == true}
|
||||
result={job.result}
|
||||
logs={job.logs ?? ''}
|
||||
/>
|
||||
</div>
|
||||
{:else if job.flow_status?.modules?.[job?.flow_status?.step]?.type === FlowStatusModule.type.WAITING_FOR_EVENTS}
|
||||
<div class="w-full h-full mt-2 text-sm text-gray-600">
|
||||
@@ -226,14 +230,21 @@
|
||||
<div class="flex flex-col gap-1">
|
||||
{#each innerModules as mod, i (mod.id)}
|
||||
{#if mod.type == FlowStatusModule.type.IN_PROGRESS}
|
||||
{@const rawMod = job.raw_flow?.modules[i]}
|
||||
|
||||
<div
|
||||
><span class="inline-flex gap-1"
|
||||
><Badge color="indigo">{mod.id}</Badge>
|
||||
{#if job.raw_flow?.modules[i]?.summary}
|
||||
<span class="font-medium text-gray-900">
|
||||
{job.raw_flow?.modules[i]?.summary ?? ''}
|
||||
</span>
|
||||
{/if}
|
||||
<span class="font-medium text-gray-900">
|
||||
{#if !emptyString(rawMod?.summary)}
|
||||
{rawMod?.summary ?? ''}
|
||||
{:else if rawMod?.value.type == 'script'}
|
||||
{rawMod.value.path ?? ''}
|
||||
{:else if rawMod?.value.type}
|
||||
{rawMod?.value.type}
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<Loader2 class="animate-spin" /></span
|
||||
></div
|
||||
>
|
||||
@@ -444,7 +455,13 @@
|
||||
{#if selectedNode}
|
||||
{@const node = localFlowModuleStates[selectedNode]}
|
||||
{#if selectedNode == 'end'}
|
||||
<FlowJobResult noBorder col result={job['result'] ?? {}} logs={job.logs ?? ''} />
|
||||
<FlowJobResult
|
||||
loading={job['running']}
|
||||
noBorder
|
||||
col
|
||||
result={job['result']}
|
||||
logs={job.logs ?? ''}
|
||||
/>
|
||||
{:else if selectedNode == 'start'}
|
||||
{#if job.args}
|
||||
<div class="p-2">
|
||||
@@ -471,7 +488,13 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<FlowJobResult noBorder col result={node.result ?? {}} logs={node.logs ?? ''} />
|
||||
<FlowJobResult
|
||||
loading={job['running'] == true}
|
||||
noBorder
|
||||
col
|
||||
result={node.result}
|
||||
logs={node.logs ?? ''}
|
||||
/>
|
||||
{:else}
|
||||
<p class="p-2 text-gray-600 italic"
|
||||
>The execution of this node has no information attached to it. The job likely did
|
||||
|
||||
@@ -60,7 +60,11 @@
|
||||
}
|
||||
|
||||
if (isCodeInjection(rawValue)) {
|
||||
arg.expr = getDefaultExpr(argName, previousModuleId, `\`${rawValue}\``)
|
||||
arg.expr = getDefaultExpr(
|
||||
argName,
|
||||
previousModuleId,
|
||||
`\`${rawValue.toString().replaceAll('`', '\\`')}\``
|
||||
)
|
||||
arg.type = 'javascript'
|
||||
propertyType = 'static'
|
||||
} else {
|
||||
@@ -117,9 +121,13 @@
|
||||
const openBracket = '${'
|
||||
const closeBracket = '}'
|
||||
|
||||
$: schema.properties[argName].default &&
|
||||
!arg?.value &&
|
||||
monacoTemplate?.setCode(schema.properties[argName].default)
|
||||
function setDefaultCode() {
|
||||
if (!arg?.value) {
|
||||
monacoTemplate?.setCode(schema.properties[argName].default)
|
||||
}
|
||||
}
|
||||
|
||||
$: schema.properties[argName].default && setDefaultCode()
|
||||
</script>
|
||||
|
||||
{#if arg != undefined}
|
||||
@@ -157,7 +165,7 @@
|
||||
argName,
|
||||
previousModuleId,
|
||||
staticTemplate
|
||||
? `\`${arg?.value ?? ''}\``
|
||||
? `\`${arg?.value.toString().replaceAll('`', '\\`') ?? ''}\``
|
||||
: arg.value
|
||||
? JSON.stringify(arg?.value, null, 4)
|
||||
: ''
|
||||
@@ -172,6 +180,7 @@
|
||||
if (staticTemplate) {
|
||||
if (arg) {
|
||||
arg.value = codeToStaticTemplate(arg.expr)
|
||||
arg.expr = undefined
|
||||
}
|
||||
setPropertyType(arg?.value)
|
||||
} else {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import SimpleEditor from './SimpleEditor.svelte'
|
||||
import { faSave } from '@fortawesome/free-solid-svg-icons'
|
||||
import SchemaForm from './SchemaForm.svelte'
|
||||
import Toggle from './Toggle.svelte'
|
||||
|
||||
let path = ''
|
||||
let initialPath = ''
|
||||
@@ -38,6 +39,7 @@
|
||||
path = p
|
||||
resourceToEdit = undefined
|
||||
resourceSchema = undefined
|
||||
viewJsonSchema = false
|
||||
loadingSchema = true
|
||||
drawer.openDrawer?.()
|
||||
resourceToEdit = await ResourceService.getResource({ workspace: $workspaceStore!, path: p })
|
||||
@@ -111,6 +113,16 @@
|
||||
args[k] = `$var:${path}`
|
||||
})
|
||||
}
|
||||
let viewJsonSchema = false
|
||||
|
||||
function switchTab(asJson: boolean) {
|
||||
viewJsonSchema = asJson
|
||||
if (asJson) {
|
||||
rawCode = JSON.stringify(args, null, 2)
|
||||
} else {
|
||||
parseJson()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Drawer bind:this={drawer} size="800px">
|
||||
@@ -136,12 +148,21 @@
|
||||
kind="resource"
|
||||
/>
|
||||
</div>
|
||||
<h3 class="mt-4">Value</h3>
|
||||
<div class="flex w-full justify-between max-w-lg items-center mt-4">
|
||||
<h3>Value</h3>
|
||||
<Toggle
|
||||
on:change={(e) => switchTab(e.detail)}
|
||||
options={{
|
||||
right: 'As JSON'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
{#if loadingSchema}
|
||||
<Skeleton layout={[[4]]} />
|
||||
{:else if resourceSchema && resourceSchema?.properties}
|
||||
{:else if !viewJsonSchema && resourceSchema && resourceSchema?.properties}
|
||||
<SchemaForm
|
||||
noDelete
|
||||
disabled={!can_write}
|
||||
compact
|
||||
schema={resourceSchema}
|
||||
@@ -151,10 +172,13 @@
|
||||
{:else if !can_write}
|
||||
<input type="text" disabled value={rawCode} />
|
||||
{:else}
|
||||
<p class="italic text-gray-500 text-xs mb-4"
|
||||
>No corresponding resource type found in your workspace for {selectedResourceType}.
|
||||
Define the value in JSON directly</p
|
||||
>
|
||||
{#if !viewJsonSchema}
|
||||
<p class="italic text-gray-500 text-xs mb-4"
|
||||
>No corresponding resource type found in your workspace for {selectedResourceType}.
|
||||
Define the value in JSON directly</p
|
||||
>
|
||||
{/if}
|
||||
|
||||
{#if !emptyString(jsonError)}<span
|
||||
class="text-red-400 text-xs mb-1 flex flex-row-reverse">{jsonError}</span
|
||||
>{:else}<div class="py-2" />{/if}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
import Select from 'svelte-select'
|
||||
import AppConnect from './AppConnect.svelte'
|
||||
import ResourceEditor from './ResourceEditor.svelte'
|
||||
import { truncate } from '$lib/utils'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let resources: Resource[] = []
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
export let filter: string[] | undefined = undefined
|
||||
export let noDynamicToggle = false
|
||||
export let flexWrap = false
|
||||
export let noDelete = false
|
||||
|
||||
let clazz: string = ''
|
||||
export { clazz as class }
|
||||
@@ -43,23 +44,32 @@
|
||||
|
||||
function removeExtraKey() {
|
||||
Object.keys(args ?? {}).forEach((key) => {
|
||||
if (!Object.keys(schema?.properties ?? {}).includes(key)) {
|
||||
if (!keys.includes(key)) {
|
||||
delete args[key]
|
||||
delete inputCheck[key]
|
||||
console.log('DELETED', key)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$: schema?.properties && removeExtraKey()
|
||||
|
||||
let pickForField: string | undefined
|
||||
let itemPicker: ItemPicker | undefined = undefined
|
||||
let variableEditor: VariableEditor | undefined = undefined
|
||||
let keys: string[] = []
|
||||
$: {
|
||||
let lkeys = Object.keys(schema?.properties ?? {})
|
||||
if (schema?.properties && JSON.stringify(lkeys) != JSON.stringify(keys)) {
|
||||
keys = lkeys
|
||||
if (!noDelete) {
|
||||
removeExtraKey()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-full {clazz} {flexWrap ? 'flex flex-row flex-wrap gap-x-6 gap-y-2' : ''}">
|
||||
{#if Object.keys(schema?.properties ?? {}).length > 0}
|
||||
{#each Object.keys(schema?.properties ?? {}) as argName, i (argName)}
|
||||
{#if keys.length > 0}
|
||||
{#each keys as argName, i (argName)}
|
||||
{#if !filter || filter.includes(argName)}
|
||||
<div transition:slide|local>
|
||||
{#if inputTransform}
|
||||
|
||||
@@ -60,9 +60,15 @@
|
||||
localStorage.removeItem(script.path)
|
||||
|
||||
script.schema = script.schema ?? emptySchema()
|
||||
if (!script.schema) {
|
||||
try {
|
||||
await inferArgs(script.language, script.content, script.schema)
|
||||
} catch (error) {
|
||||
sendUserToast(
|
||||
`Impossible to infer the schema. Assuming this is a script without main function`,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
const newHash = await ScriptService.createScript({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
@@ -87,7 +93,13 @@
|
||||
async function changeStep(step: number) {
|
||||
if (step > 1) {
|
||||
script.schema = script.schema ?? emptySchema()
|
||||
await inferArgs(script.language, script.content, script.schema)
|
||||
try {
|
||||
await inferArgs(script.language, script.content, script.schema)
|
||||
} catch (error) {
|
||||
console.info(
|
||||
'Impossible to infer the schema. Assuming this is a script without main function'
|
||||
)
|
||||
}
|
||||
}
|
||||
goto(`?step=${step}`)
|
||||
}
|
||||
|
||||
16
frontend/src/lib/components/Slider.svelte
Normal file
16
frontend/src/lib/components/Slider.svelte
Normal file
@@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { ChevronDown, ChevronUp } from 'lucide-svelte'
|
||||
import { slide } from 'svelte/transition'
|
||||
import Button from './common/button/Button.svelte'
|
||||
|
||||
export let text: string
|
||||
export let view = false
|
||||
</script>
|
||||
|
||||
<Button color="light" on:click={() => (view = !view)} variant="border"
|
||||
>{text}
|
||||
{#if !view}<ChevronDown />{:else}<ChevronUp />{/if}</Button
|
||||
>
|
||||
{#if view}
|
||||
<div class="my-4 px-2" transition:slide><slot /></div>
|
||||
{/if}
|
||||
@@ -1,11 +1,14 @@
|
||||
<script lang="ts">
|
||||
import DisplayResult from '$lib/components/DisplayResult.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../inputType'
|
||||
import { IS_APP_PUBLIC_CONTEXT_KEY } from '../types'
|
||||
import RunnableWrapper from './helpers/RunnableWrapper.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
|
||||
const requireHtmlApproval = getContext<boolean | undefined>(IS_APP_PUBLIC_CONTEXT_KEY)
|
||||
let result: any = undefined
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
@@ -16,6 +19,6 @@
|
||||
Results
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<DisplayResult {result} />
|
||||
<DisplayResult {result} {requireHtmlApproval} />
|
||||
</div>
|
||||
</RunnableWrapper>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { AppInput } from '../../inputType'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<script lang="ts">
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { onMount } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
|
||||
let result: object | undefined = undefined
|
||||
let divEl: HTMLDivElement | null = null
|
||||
|
||||
let Plotly
|
||||
onMount(async () => {
|
||||
if (divEl) {
|
||||
//@ts-ignore
|
||||
await import('https://cdn.plot.ly/plotly-2.18.0.min.js')
|
||||
|
||||
Plotly = window['Plotly']
|
||||
}
|
||||
})
|
||||
|
||||
let h: number | undefined = undefined
|
||||
let w: number | undefined = undefined
|
||||
|
||||
$: Plotly &&
|
||||
result &&
|
||||
divEl &&
|
||||
h &&
|
||||
w &&
|
||||
Plotly.newPlot(
|
||||
divEl,
|
||||
[result],
|
||||
{ width: w, height: h, margin: { l: 40, r: 40, b: 40, t: 40, pad: 4 } },
|
||||
{ responsive: true, displayModeBar: false }
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
|
||||
<RunnableWrapper flexWrap bind:componentInput {id} bind:result>
|
||||
{#if !Plotly}
|
||||
<div class="p-2">
|
||||
<Loader2 class="animate-spin" />
|
||||
</div>
|
||||
{/if}
|
||||
<div on:pointerdown bind:this={divEl} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
@@ -1,9 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { parse } from 'path'
|
||||
import { onMount } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
|
||||
<RunnableWrapper
|
||||
defaultUserInput
|
||||
noMinH
|
||||
bind:runnableComponent
|
||||
bind:componentInput
|
||||
{id}
|
||||
|
||||
@@ -89,7 +89,6 @@
|
||||
>
|
||||
<RunnableWrapper
|
||||
defaultUserInput
|
||||
noMinH
|
||||
bind:runnableComponent
|
||||
bind:componentInput
|
||||
{id}
|
||||
|
||||
@@ -21,13 +21,21 @@
|
||||
export let autoRefresh: boolean = true
|
||||
export let result: any = undefined
|
||||
export let forceSchemaDisplay: boolean = false
|
||||
export let noMinH = false
|
||||
export let defaultUserInput = false
|
||||
export let flexWrap = false
|
||||
export let wrapperClass = ''
|
||||
|
||||
const { worldStore, runnableComponents, workspace, appPath, isEditor, jobs, noBackend } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
const {
|
||||
worldStore,
|
||||
runnableComponents,
|
||||
workspace,
|
||||
appPath,
|
||||
isEditor,
|
||||
jobs,
|
||||
noBackend,
|
||||
errorByComponent,
|
||||
mode
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
onMount(() => {
|
||||
if (autoRefresh) {
|
||||
@@ -41,7 +49,6 @@
|
||||
let args: Record<string, any> = {}
|
||||
let testIsLoading = false
|
||||
let runnableInputValues: Record<string, any> = {}
|
||||
|
||||
let executeTimeout: NodeJS.Timeout | undefined = undefined
|
||||
|
||||
function setDebouncedExecute() {
|
||||
@@ -245,7 +252,19 @@
|
||||
export async function runComponent() {
|
||||
await executeComponent()
|
||||
}
|
||||
|
||||
let lastStartedAt: number = Date.now()
|
||||
|
||||
function recordError(error: string) {
|
||||
if (testJob) {
|
||||
$errorByComponent[testJob.id] = {
|
||||
error: error,
|
||||
componentId: id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: result?.error && recordError(result.error)
|
||||
</script>
|
||||
|
||||
{#each Object.entries(fields ?? {}) as [key, v]}
|
||||
@@ -268,6 +287,15 @@
|
||||
lastStartedAt = startedAt
|
||||
outputs.result?.set(testJob?.result)
|
||||
result = testJob.result
|
||||
|
||||
const previousJobId = Object.keys($errorByComponent).find(
|
||||
(key) => $errorByComponent[key].componentId === id
|
||||
)
|
||||
|
||||
if (previousJobId && !result?.error) {
|
||||
delete $errorByComponent[previousJobId]
|
||||
$errorByComponent = $errorByComponent
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -299,19 +327,21 @@
|
||||
<Alert type="warning" size="xs" class="mt-2 px-1" title="Missing runnable">
|
||||
Please select a runnable
|
||||
</Alert>
|
||||
{:else if result?.error}
|
||||
{:else if result?.error && $mode === 'preview'}
|
||||
<div class="p-2">
|
||||
<Alert type="error" title="Error during execution">
|
||||
See "Debug Runs" on the top right for more details
|
||||
<pre
|
||||
title={JSON.stringify(result.error, null, 4)}
|
||||
class=" mt-2 text-2xs whitespace-pre-wrap">{JSON.stringify(result.error, null, 4)}</pre
|
||||
>
|
||||
<div class="flex flex-col gap-2">
|
||||
An error occured, please contact the app author.
|
||||
<span class="font-semibold">Job id: {testJob?.id}</span>
|
||||
<pre class=" whitespace-pre-wrap text-gray-900 bg-white border w-full p-4 text-xs"
|
||||
>{JSON.stringify(result.error, null, 4)}
|
||||
</pre>
|
||||
</div>
|
||||
</Alert>
|
||||
<slot />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="block w-full h-full">
|
||||
<div class="block grow max-h-full">
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
export let componentInput: AppInput | undefined
|
||||
export let id: string
|
||||
export let result: any = undefined
|
||||
export let noMinH = false
|
||||
|
||||
export let extraQueryParams: Record<string, any> = {}
|
||||
export let autoRefresh: boolean = true
|
||||
@@ -47,7 +46,6 @@
|
||||
{id}
|
||||
{extraQueryParams}
|
||||
{forceSchemaDisplay}
|
||||
{noMinH}
|
||||
wrapperClass={runnableClass}
|
||||
>
|
||||
<slot />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import Select from 'svelte-select'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
@@ -46,7 +46,7 @@
|
||||
e?.stopPropagation()
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
}}
|
||||
{items}
|
||||
items={Array.isArray(items) ? items : []}
|
||||
{value}
|
||||
placeholder="Select an item"
|
||||
on:click={() => {
|
||||
|
||||
@@ -19,7 +19,13 @@
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let actionButtons: (BaseAppComponent & ButtonComponent)[]
|
||||
|
||||
export const staticOutputs: string[] = ['selectedRow', 'loading', 'result', 'search']
|
||||
export const staticOutputs: string[] = [
|
||||
'selectedRow',
|
||||
'loading',
|
||||
'result',
|
||||
'search',
|
||||
'selectedRowIndex'
|
||||
]
|
||||
|
||||
type T = Record<string, any>
|
||||
|
||||
@@ -52,6 +58,7 @@
|
||||
if (selectedRowIndex !== rowIndex) {
|
||||
selectedRowIndex = rowIndex
|
||||
outputs?.selectedRow.set(row.original)
|
||||
outputs?.selectedRowIndex.set(rowIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +121,7 @@
|
||||
$: search === 'By Component' && (filteredResult = searchInResult(result ?? [], searchValue))
|
||||
$: (search === 'By Runnable' || search === 'Disabled') && (filteredResult = result ?? [])
|
||||
$: outputs = $worldStore?.outputsById[id] as {
|
||||
selectedRowIndex: Output<number>
|
||||
selectedRow: Output<any>
|
||||
search: Output<string>
|
||||
}
|
||||
@@ -203,18 +211,20 @@
|
||||
|
||||
{#if actionButtons.length > 0}
|
||||
<td
|
||||
class="flex w-full flex-row gap-2 p-4"
|
||||
class="p-2 "
|
||||
on:click={() => toggleRow(row, rowIndex)}
|
||||
>
|
||||
{#each actionButtons as actionButton, actionIndex (actionIndex)}
|
||||
<AppButton
|
||||
noWFull
|
||||
{...actionButton}
|
||||
extraQueryParams={{ row: row.original }}
|
||||
bind:componentInput={actionButton.componentInput}
|
||||
bind:staticOutputs={$staticOutputsStore[actionButton.id]}
|
||||
/>
|
||||
{/each}
|
||||
<div class="center-center h-full w-full flex-wrap gap-1">
|
||||
{#each actionButtons as actionButton, actionIndex (actionIndex)}
|
||||
<AppButton
|
||||
noWFull
|
||||
{...actionButton}
|
||||
extraQueryParams={{ row: row.original }}
|
||||
bind:componentInput={actionButton.componentInput}
|
||||
bind:staticOutputs={$staticOutputsStore[actionButton.id]}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import type { AppComponent } from '../types'
|
||||
import { displayData } from '../utils'
|
||||
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
|
||||
|
||||
export let component: AppComponent
|
||||
export let resourceOnly = false
|
||||
</script>
|
||||
|
||||
{#if component?.componentInput?.type === 'runnable' && Object.keys(component?.componentInput?.fields ?? {}).length > 0}
|
||||
<div class="mb-8">
|
||||
<div class="flex justify-between mb-4">
|
||||
<span class="text-sm font-bold">{component.id}</span>
|
||||
<span class="text-sm font-bold">{displayData[component.type].name}</span>
|
||||
</div>
|
||||
|
||||
{#if resourceOnly && Object.keys(component.componentInput.fields).filter((fieldKey) => {
|
||||
if (component?.componentInput?.type === 'runnable') {
|
||||
const fields = component.componentInput?.fields
|
||||
const field = fields[fieldKey]
|
||||
return field.fieldType === 'object' && field.format?.startsWith('resource-')
|
||||
}
|
||||
return false
|
||||
}).length === 0}
|
||||
<span class="text-sm">No resource input</span>
|
||||
{:else}
|
||||
<InputsSpecsEditor
|
||||
id={component.id}
|
||||
shouldCapitalize={false}
|
||||
bind:inputSpecs={component.componentInput.fields}
|
||||
userInputEnabled={component.type !== 'buttoncomponent'}
|
||||
{resourceOnly}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
import type { ConnectedAppInput, RowAppInput, StaticAppInput, UserAppInput } from '../inputType'
|
||||
import type { AppComponent } from '../types'
|
||||
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
|
||||
|
||||
export let fields: Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
|
||||
export let component: AppComponent
|
||||
export let resourceOnly: boolean = false
|
||||
|
||||
$: fields = resourceOnly ? fields : fields
|
||||
</script>
|
||||
|
||||
<InputsSpecsEditor
|
||||
id={component.id}
|
||||
shouldCapitalize={false}
|
||||
bind:inputSpecs={fields}
|
||||
userInputEnabled={component.type !== 'buttoncomponent'}
|
||||
/>
|
||||
@@ -39,6 +39,7 @@
|
||||
export let initialMode: EditorMode = 'dnd'
|
||||
export let policy: Policy
|
||||
export let summary: string
|
||||
export let fromHub: boolean = false
|
||||
|
||||
const appStore = writable<App>(app)
|
||||
const worldStore = writable<World | undefined>(undefined)
|
||||
@@ -54,6 +55,7 @@
|
||||
})
|
||||
|
||||
const runnableComponents = writable<Record<string, () => Promise<void>>>({})
|
||||
const errorByComponent = writable<Record<string, { error: string; componentId: string }>>({})
|
||||
|
||||
setContext<AppEditorContext>('AppEditorContext', {
|
||||
worldStore,
|
||||
@@ -72,7 +74,9 @@
|
||||
isEditor: true,
|
||||
jobs: writable([]),
|
||||
staticExporter: writable({}),
|
||||
noBackend: false
|
||||
noBackend: false,
|
||||
errorByComponent,
|
||||
openDebugRun: writable(undefined)
|
||||
})
|
||||
|
||||
let timeout: NodeJS.Timeout | undefined = undefined
|
||||
@@ -122,7 +126,7 @@
|
||||
{#if !$userStore?.operator}
|
||||
<UnsavedConfirmationModal />
|
||||
{#if initialMode !== 'preview'}
|
||||
<AppEditorHeader {policy} />
|
||||
<AppEditorHeader {policy} {fromHub} />
|
||||
{/if}
|
||||
|
||||
{#if previewing}
|
||||
|
||||
@@ -35,16 +35,24 @@
|
||||
Loader2,
|
||||
MoreVertical,
|
||||
Pencil,
|
||||
SlidersHorizontal,
|
||||
Smartphone
|
||||
} from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import { Icon } from 'svelte-awesome'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import { appToHubUrl, classNames, copyToClipboard, sendUserToast } from '../../../utils'
|
||||
import type { AppInput } from '../inputType'
|
||||
import type {
|
||||
AppInput,
|
||||
ConnectedAppInput,
|
||||
RowAppInput,
|
||||
StaticAppInput,
|
||||
UserAppInput
|
||||
} from '../inputType'
|
||||
import type { AppComponent, AppEditorContext } from '../types'
|
||||
import { toStatic } from '../utils'
|
||||
import AppExportButton from './AppExportButton.svelte'
|
||||
import AppInputs from './AppInputs.svelte'
|
||||
import PanelSection from './settingsPanel/common/PanelSection.svelte'
|
||||
|
||||
async function hash(message) {
|
||||
@@ -56,27 +64,60 @@
|
||||
}
|
||||
|
||||
export let policy: Policy
|
||||
export let fromHub: boolean = false
|
||||
|
||||
const {
|
||||
app,
|
||||
summary,
|
||||
mode,
|
||||
breakpoint,
|
||||
appPath,
|
||||
jobs,
|
||||
staticExporter,
|
||||
errorByComponent,
|
||||
openDebugRun
|
||||
} = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
const { app, summary, mode, breakpoint, appPath, jobs, staticExporter } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
const loading = {
|
||||
publish: false,
|
||||
save: false
|
||||
}
|
||||
|
||||
$: if ($openDebugRun == undefined) {
|
||||
$openDebugRun = (componentId: string) => {
|
||||
jobsDrawerOpen = true
|
||||
|
||||
const job = $jobs.find((job) => job.component === componentId)
|
||||
if (job) {
|
||||
selectedJobId = job.job
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let newPath: string = ''
|
||||
let pathError: string | undefined = undefined
|
||||
|
||||
let appExport: AppExportButton
|
||||
|
||||
let saveDrawerOpen = false
|
||||
let jobsDrawerOpen = false
|
||||
let publishDrawerOpen = false
|
||||
let inputsDrawerOpen = fromHub
|
||||
|
||||
function closeSaveDrawer() {
|
||||
saveDrawerOpen = false
|
||||
}
|
||||
|
||||
function collectStaticFields(
|
||||
fields: Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
|
||||
) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(fields ?? {})
|
||||
.filter(([k, v]) => v.type == 'static')
|
||||
.map(([k, v]) => {
|
||||
return [k, v['value']]
|
||||
})
|
||||
)
|
||||
}
|
||||
async function computeTriggerables() {
|
||||
const allTriggers = await Promise.all(
|
||||
$app.grid
|
||||
@@ -90,13 +131,7 @@
|
||||
})
|
||||
.map(async (input) => {
|
||||
if (input?.type == 'runnable') {
|
||||
const staticInputs = Object.fromEntries(
|
||||
Object.entries(input.fields ?? {})
|
||||
.filter(([k, v]) => v.type == 'static')
|
||||
.map(([k, v]) => {
|
||||
return [k, v['value']]
|
||||
})
|
||||
)
|
||||
const staticInputs = collectStaticFields(input.fields)
|
||||
if (input.runnable?.type == 'runnableByName') {
|
||||
let hex = await hash(input.runnable.inlineScript?.content)
|
||||
return [`rawscript/${hex}`, staticInputs]
|
||||
@@ -108,8 +143,16 @@
|
||||
}
|
||||
return []
|
||||
})
|
||||
.concat(
|
||||
Object.values($app.hiddenInlineScripts).map(async (v) => {
|
||||
let hex = await hash(v.inlineScript?.content)
|
||||
const staticInputs = collectStaticFields(v.fields)
|
||||
return [`rawscript/${hex}`, staticInputs]
|
||||
})
|
||||
)
|
||||
)
|
||||
policy.triggerables = Object.fromEntries(allTriggers)
|
||||
policy.triggerables = Object.fromEntries(allTriggers.filter((x) => x.length > 0))
|
||||
|
||||
policy.on_behalf_of = `u/${$userStore?.username}`
|
||||
policy.on_behalf_of_email = $userStore?.email
|
||||
}
|
||||
@@ -183,6 +226,7 @@
|
||||
let testIsLoading = false
|
||||
|
||||
$: selectedJobId && testJobLoader?.watchJob(selectedJobId)
|
||||
$: hasErrors = Object.keys($errorByComponent).length > 0
|
||||
</script>
|
||||
|
||||
<TestJobLoader bind:this={testJobLoader} bind:isLoading={testIsLoading} bind:job />
|
||||
@@ -207,6 +251,12 @@
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
<Drawer bind:open={inputsDrawerOpen} size="600px">
|
||||
<DrawerContent title="App inputs configuration" on:close={() => (inputsDrawerOpen = false)}>
|
||||
<AppInputs />
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
<Drawer bind:open={jobsDrawerOpen} size="900px">
|
||||
<DrawerContent noPadding title="Debug Runs" on:close={() => (jobsDrawerOpen = false)}>
|
||||
<Splitpanes class="!overflow-visible">
|
||||
@@ -218,10 +268,13 @@
|
||||
{#each $jobs ?? [] as { job, component } (job)}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="{classNames(
|
||||
class={classNames(
|
||||
'border flex gap-1 truncate justify-between flex-row w-full items-center p-2 rounded-md cursor-pointer hover:bg-blue-50 hover:text-blue-400',
|
||||
selectedJobId == job ? 'bg-blue-100 text-blue-600' : ''
|
||||
)},"
|
||||
$errorByComponent[job] ? 'border border-red-500 bg-red-100' : '',
|
||||
selectedJobId == job && !$errorByComponent[component]
|
||||
? 'bg-blue-100 text-blue-600'
|
||||
: ''
|
||||
)}
|
||||
on:click={() => (selectedJobId = job)}
|
||||
>
|
||||
<span class="text-xs truncate">{job}</span>
|
||||
@@ -243,12 +296,14 @@
|
||||
{:else}
|
||||
<div class="flex flex-col h-full w-full gap-4 mb-4">
|
||||
{#if job?.['running']}
|
||||
<div class="flex flex-row-reverse w-full"
|
||||
><Button
|
||||
<div class="flex flex-row-reverse w-full">
|
||||
<Button
|
||||
color="red"
|
||||
variant="border"
|
||||
on:click={() => testJobLoader?.cancelJob()}>Cancel</Button
|
||||
on:click={() => testJobLoader?.cancelJob()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="p-2">
|
||||
@@ -433,10 +488,18 @@
|
||||
>
|
||||
<MoreVertical size={20} />
|
||||
</Dropdown>
|
||||
<span class="hidden md:inline">
|
||||
<Button on:click={() => (inputsDrawerOpen = true)} color="light" size="xs" variant="border">
|
||||
<span class="flex gap-2">
|
||||
<SlidersHorizontal size={14} />
|
||||
App inputs
|
||||
</span>
|
||||
</Button>
|
||||
</span>
|
||||
<span class="hidden md:inline">
|
||||
<Button
|
||||
on:click={() => (jobsDrawerOpen = true)}
|
||||
color="light"
|
||||
color={hasErrors ? 'red' : 'light'}
|
||||
size="xs"
|
||||
variant="border"
|
||||
startIcon={{ icon: faBug }}
|
||||
|
||||
56
frontend/src/lib/components/apps/editor/AppInputs.svelte
Normal file
56
frontend/src/lib/components/apps/editor/AppInputs.svelte
Normal file
@@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
import { Alert } from '$lib/components/common'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../types'
|
||||
import AppComponentInput from './AppComponentInput.svelte'
|
||||
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
|
||||
|
||||
const { app, lazyGrid } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let resourceOnly: boolean = true
|
||||
</script>
|
||||
|
||||
<Alert type="info" title="Configurations">
|
||||
In order to properly configure the app, you need to fill in the inputs below.
|
||||
</Alert>
|
||||
<Toggle bind:checked={resourceOnly} options={{ right: 'Resource only' }} />
|
||||
{#each $lazyGrid as gridItem (gridItem.data.id)}
|
||||
{#if gridItem.data.type === 'tablecomponent'}
|
||||
<div>
|
||||
<AppComponentInput bind:component={gridItem.data} {resourceOnly} />
|
||||
<div class="ml-4 mt-4">
|
||||
{#each gridItem.data.actionButtons as actionButton (actionButton.id)}
|
||||
<AppComponentInput bind:component={actionButton.data} {resourceOnly} />
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<AppComponentInput bind:component={gridItem.data} {resourceOnly} />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
{#if $app?.hiddenInlineScripts?.length > 0}
|
||||
<span class="font-bold text-sm">Background script inputs</span>
|
||||
<div class="gap-4 flex flex-col">
|
||||
{#each $app?.hiddenInlineScripts ?? [] as script, index (script.name)}
|
||||
<span class="text-sm">{script.name}</span>
|
||||
|
||||
{#if resourceOnly && Object.keys(script.fields).filter((fieldKey) => {
|
||||
const fields = script.fields
|
||||
const field = fields[fieldKey]
|
||||
return field.fieldType === 'object' && field.format?.startsWith('resource-')
|
||||
}).length === 0}
|
||||
<span class="text-sm">No resource input</span>
|
||||
{:else}
|
||||
<InputsSpecsEditor
|
||||
id={`bg_${index}`}
|
||||
shouldCapitalize={false}
|
||||
bind:inputSpecs={script.fields}
|
||||
userInputEnabled={false}
|
||||
{resourceOnly}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -59,7 +59,9 @@
|
||||
isEditor,
|
||||
jobs: writable([]),
|
||||
staticExporter: writable({}),
|
||||
noBackend
|
||||
noBackend,
|
||||
errorByComponent: writable({}),
|
||||
openDebugRun: writable(undefined)
|
||||
})
|
||||
|
||||
let mounted = false
|
||||
@@ -74,8 +76,10 @@
|
||||
</script>
|
||||
|
||||
<div class="relative">
|
||||
<div class="{$$props.class} {lockedClasses} h-full max-h-[calc(100%-41px)] overflow-auto
|
||||
w-full {app.fullscreen ? '' : 'max-w-6xl'} mx-auto">
|
||||
<div
|
||||
class="{$$props.class} {lockedClasses} h-full max-h-[calc(100%-41px)] overflow-auto
|
||||
w-full {app.fullscreen ? '' : 'max-w-6xl'} mx-auto"
|
||||
>
|
||||
{#if $appStore.grid}
|
||||
<div class={classNames('mx-auto pb-4', width)}>
|
||||
<GridEditor {policy} />
|
||||
@@ -86,10 +90,10 @@
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
transition:fade|local={{ duration: 200, easing: cubicOut }}
|
||||
on:click={() => isLocked = false}
|
||||
on:click={() => (isLocked = false)}
|
||||
class="absolute inset-0 center-center bg-black/20 z-50 backdrop-blur-[1px] cursor-pointer"
|
||||
>
|
||||
<Button on:click={() => isLocked = false}>
|
||||
<Button on:click={() => (isLocked = false)}>
|
||||
Unlock preview
|
||||
<Unlock size={18} class="ml-1" strokeWidth={2.5} />
|
||||
</Button>
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
import AppSliderInputs from '../components/numberInputs/AppSliderInputs.svelte'
|
||||
import AppFormButton from '../components/form/AppFormButton.svelte'
|
||||
import VegaLiteHtml from '../components/dataDisplay/VegaLiteHtml.svelte'
|
||||
import PlotlyHtml from '../components/dataDisplay/PlotlyHtml.svelte'
|
||||
|
||||
export let component: AppComponent
|
||||
export let selected: boolean
|
||||
@@ -86,6 +87,12 @@
|
||||
bind:componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'plotlycomponent'}
|
||||
<PlotlyHtml
|
||||
{...component}
|
||||
bind:componentInput={component.componentInput}
|
||||
bind:staticOutputs={$staticOutputs[component.id]}
|
||||
/>
|
||||
{:else if component.type === 'scatterchartcomponent'}
|
||||
<AppScatterChart
|
||||
{...component}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { classNames } from '$lib/utils'
|
||||
import type { AppComponent } from '../types'
|
||||
import { Anchor, Move } from 'lucide-svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { AppComponent, AppEditorContext } from '../types'
|
||||
import { Anchor, Bug, Move } from 'lucide-svelte'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
import { Alert, Button } from '$lib/components/common'
|
||||
|
||||
export let component: AppComponent
|
||||
export let selected: boolean
|
||||
@@ -11,6 +13,16 @@
|
||||
export let hover: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const { errorByComponent, openDebugRun } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
$: error = Object.values($errorByComponent).find((e) => e.componentId === component.id)
|
||||
|
||||
function openDebugRuns() {
|
||||
if ($openDebugRun) {
|
||||
$openDebugRun(component.id)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<span
|
||||
@@ -56,3 +68,32 @@
|
||||
<Move size={14} />
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
{#if error}
|
||||
{@const json = JSON.parse(JSON.stringify(error.error))}
|
||||
<span
|
||||
title="Error"
|
||||
class={classNames(
|
||||
'text-red-500 px-1 text-2xs py-0.5 font-bold w-fit absolute border border-red-500 -bottom-1 shadow left-1/2 transform -translate-x-1/2 z-50 cursor-pointer',
|
||||
'bg-red-100/80'
|
||||
)}
|
||||
>
|
||||
<Popover notClickable placement="bottom" popupClass="!bg-white border w-96">
|
||||
<Bug size={14} />
|
||||
<span slot="text">
|
||||
<div class="bg-white">
|
||||
<Alert type="error" title={`${json?.name}: ${json?.message}`}>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div>
|
||||
<pre class=" whitespace-pre-wrap text-gray-900 bg-white border w-full p-4 text-xs"
|
||||
>{json?.stack ?? ''}
|
||||
</pre>
|
||||
</div>
|
||||
<Button color="red" variant="border" on:click={openDebugRuns}>Open Debug Runs</Button>
|
||||
</div>
|
||||
</Alert>
|
||||
</div>
|
||||
</span>
|
||||
</Popover>
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
@@ -39,7 +39,8 @@
|
||||
'piechartcomponent',
|
||||
'displaycomponent',
|
||||
'scatterchartcomponent',
|
||||
'vegalitecomponent'
|
||||
'vegalitecomponent',
|
||||
'plotlycomponent'
|
||||
],
|
||||
'3:10-6:10': ['tablecomponent']
|
||||
}
|
||||
@@ -55,7 +56,10 @@
|
||||
function addComponent(appComponent: AppComponent) {
|
||||
$dirtyStore = true
|
||||
const grid = $app.grid ?? []
|
||||
const id = getNextId(grid.map((gridItem) => gridItem.data.id))
|
||||
const id = getNextId(
|
||||
grid.map((gridItem) => gridItem.data.id),
|
||||
true
|
||||
)
|
||||
|
||||
appComponent.id = id
|
||||
|
||||
|
||||
@@ -328,6 +328,28 @@ const display: ComponentSet = {
|
||||
},
|
||||
card: false
|
||||
},
|
||||
{
|
||||
softWrap: false,
|
||||
id: 'plotlycomponent',
|
||||
type: 'plotlycomponent',
|
||||
componentInput: {
|
||||
type: 'static',
|
||||
fieldType: 'object',
|
||||
value: {
|
||||
type: 'bar',
|
||||
x: [1, 2, 3, 4],
|
||||
y: [5, 10, 2, 8],
|
||||
marker: {
|
||||
color: '#C8A2C8',
|
||||
line: {
|
||||
width: 2.5
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
configuration: {},
|
||||
card: false
|
||||
},
|
||||
{
|
||||
softWrap: true,
|
||||
horizontalAlignment: 'left',
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Script, type Preview } from '$lib/gen'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import { initialCode } from '$lib/script_helpers'
|
||||
import { emptySchema } from '$lib/utils'
|
||||
import { capitalize, emptySchema } from '$lib/utils'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import { fly } from 'svelte/transition'
|
||||
import { defaultCode } from '../../editorUtils'
|
||||
@@ -61,7 +61,7 @@
|
||||
<div class="flex gap-2 flex-row flex-wrap">
|
||||
{#each langs as lang}
|
||||
<FlowScriptPicker
|
||||
label={lang}
|
||||
label={capitalize(lang)}
|
||||
{lang}
|
||||
on:click={() => {
|
||||
createInlineScriptByLanguage(lang, name)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { faTrash } from '@fortawesome/free-solid-svg-icons'
|
||||
import { createEventDispatcher, getContext, onMount } from 'svelte'
|
||||
import type { AppEditorContext, InlineScript } from '../../types'
|
||||
import { CheckCircle, Code2, X } from 'lucide-svelte'
|
||||
import { CheckCircle, Code2, Maximize2, Trash2, X } from 'lucide-svelte'
|
||||
import InlineScriptEditorDrawer from './InlineScriptEditorDrawer.svelte'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import type { Schema } from '$lib/common'
|
||||
@@ -13,6 +13,7 @@
|
||||
import Editor from '$lib/components/Editor.svelte'
|
||||
import { emptySchema, scriptLangToEditorLang } from '$lib/utils'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import Popover from '../../../Popover.svelte'
|
||||
|
||||
let inlineScriptEditorDrawer: InlineScriptEditorDrawer
|
||||
|
||||
@@ -57,21 +58,51 @@
|
||||
<InlineScriptEditorDrawer {editor} bind:this={inlineScriptEditorDrawer} bind:inlineScript />
|
||||
|
||||
<div class="h-full flex flex-col gap-1" transition:fly|local={{ duration: 50 }}>
|
||||
<div class="flex justify-between w-full gap-1 px-2 pt-1 flex-row items-center">
|
||||
<div class="flex justify-between w-full gap-2 px-2 pt-1 flex-row items-center">
|
||||
{#if name !== undefined}
|
||||
<input bind:value={name} placeholder="Inline script name" />
|
||||
{/if}
|
||||
<div class="flex w-full flex-row gap-2 items-center justify-end">
|
||||
{#if validCode}
|
||||
<Badge color="green">
|
||||
<Badge color="green" baseClass="!p-0 !h-[30px] aspect-square center-center">
|
||||
<CheckCircle size={16} />
|
||||
</Badge>
|
||||
{:else}
|
||||
<Badge color="red">
|
||||
<Badge color="red" baseClass="!p-0 !h-[30px] aspect-square center-center">
|
||||
<X size={16} />
|
||||
</Badge>
|
||||
{/if}
|
||||
|
||||
{#if id.startsWith('unused-') || id.startsWith('bg_')}
|
||||
<Popover notClickable placement="bottom">
|
||||
<Button
|
||||
size="xs"
|
||||
color="red"
|
||||
variant="border"
|
||||
btnClasses="!px-1.5"
|
||||
aria-label="Delete"
|
||||
on:click={() => dispatch('delete')}
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</Button>
|
||||
<svelte:fragment slot="text">Delete</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
<Popover notClickable placement="bottom">
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
variant="border"
|
||||
btnClasses="!px-1.5"
|
||||
aria-label="Open full editor"
|
||||
on:click={() => {
|
||||
inlineScriptEditorDrawer?.openDrawer()
|
||||
}}
|
||||
>
|
||||
<Maximize2 size={16} />
|
||||
</Button>
|
||||
<svelte:fragment slot="text">Open full editor</svelte:fragment>
|
||||
</Popover>
|
||||
<Button
|
||||
variant="border"
|
||||
size="xs"
|
||||
@@ -80,18 +111,8 @@
|
||||
editor.format()
|
||||
}}
|
||||
>
|
||||
Format <Tooltip>Ctrl+S</Tooltip>
|
||||
Format <Tooltip placement="bottom">Ctrl+S</Tooltip>
|
||||
</Button>
|
||||
{#if id.startsWith('unused-') || id.startsWith('bg_')}
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
iconOnly
|
||||
startIcon={{ icon: faTrash }}
|
||||
on:click={() => dispatch('delete')}
|
||||
/>
|
||||
{/if}
|
||||
{#if $runnableComponents[id] != undefined}
|
||||
<Button
|
||||
loading={runLoading}
|
||||
@@ -104,22 +125,9 @@
|
||||
}}
|
||||
>
|
||||
Run
|
||||
<Tooltip light>Ctrl+Enter</Tooltip>
|
||||
<Tooltip light placement="bottom">Ctrl+Enter</Tooltip>
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
on:click={() => {
|
||||
inlineScriptEditorDrawer?.openDrawer()
|
||||
}}
|
||||
>
|
||||
<div class="flex gap-1 items-center">
|
||||
<Code2 size={16} />
|
||||
Open full editor
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Badge, Button } from '$lib/components/common'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
@@ -9,8 +7,11 @@
|
||||
import PanelSection from '../settingsPanel/common/PanelSection.svelte'
|
||||
import { getAppScripts } from './utils'
|
||||
|
||||
const PREFIX = 'script-selector-' as const
|
||||
|
||||
export let selectedScriptComponentId: string | undefined = undefined
|
||||
const { app, selectedComponent, lazyGrid } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let list: HTMLElement
|
||||
|
||||
function selectInlineScript(id: string) {
|
||||
selectedScriptComponentId = id
|
||||
@@ -21,13 +22,20 @@
|
||||
|
||||
$: runnables = getAppScripts($lazyGrid)
|
||||
|
||||
// When seleced component changes, update selectedScriptComponentId
|
||||
$: {
|
||||
if (selectedComponent) {
|
||||
selectedScriptComponentId = $selectedComponent
|
||||
}
|
||||
// When selected component changes, update selectedScriptComponentId
|
||||
$: if (selectedComponent) {
|
||||
selectedScriptComponentId = $selectedComponent
|
||||
}
|
||||
|
||||
// Doesn't work for some reason
|
||||
// $: if (selectedScriptComponentId) {
|
||||
// const selected = document.getElementById(PREFIX + selectedScriptComponentId)
|
||||
// if(selected) {
|
||||
// const top = selected.getBoundingClientRect().top - list.getBoundingClientRect().top + list.scrollTop
|
||||
// list.scrollTo({ top, behavior: 'smooth' })
|
||||
// }
|
||||
// }
|
||||
|
||||
function createBackgroundScript() {
|
||||
let index = 0
|
||||
let newScriptPath = `Background Script ${index}`
|
||||
@@ -49,28 +57,27 @@
|
||||
fields: {}
|
||||
})
|
||||
$app.hiddenInlineScripts = $app.hiddenInlineScripts
|
||||
selectInlineScript(`bg_${$app.hiddenInlineScripts.length - 1}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="min-h-full flex flex-col gap-4">
|
||||
<div bind:this={list} class="min-h-full flex flex-col gap-4">
|
||||
<PanelSection title="Inline scripts" smallPadding>
|
||||
<div class="flex flex-col gap-2 w-full">
|
||||
{#if runnables.inline.length > 0}
|
||||
<div class="flex gap-2 flex-col ">
|
||||
{#each runnables.inline as { name, id }, index (index)}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="{classNames(
|
||||
'border flex gap-1 truncate justify-between flex-row w-full items-center p-2 rounded-sm cursor-pointer hover:bg-blue-50 hover:text-blue-400',
|
||||
selectedScriptComponentId === id ? 'border-blue-500 border' : ''
|
||||
)},"
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center p-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectInlineScript(id)}
|
||||
>
|
||||
<span class="text-xs truncate">{name}</span>
|
||||
<div>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -78,17 +85,16 @@
|
||||
{#if $app.unusedInlineScripts?.length > 0}
|
||||
<div class="flex gap-2 flex-col ">
|
||||
{#each $app.unusedInlineScripts as unusedInlineScript, index (index)}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="{classNames(
|
||||
'border flex gap-1 truncate justify-between flex-row w-full items-center p-2 rounded-md cursor-pointer hover:bg-blue-50 hover:text-blue-400',
|
||||
selectedScriptComponentId === `unused-${index}` ? 'bg-blue-100 text-blue-600' : ''
|
||||
)},"
|
||||
on:click={() => selectInlineScript(`unused-${index}`)}
|
||||
{@const id = `unused-${index}`}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center p-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectInlineScript(id)}
|
||||
>
|
||||
<span class="text-xs truncate">{unusedInlineScript.name}</span>
|
||||
<Badge color="red">Detached</Badge>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -104,17 +110,15 @@
|
||||
{#if runnables.imported.length > 0}
|
||||
<div class="flex gap-2 flex-col ">
|
||||
{#each runnables.imported as { name, id }, index (index)}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="{classNames(
|
||||
'border flex gap-1 truncate justify-between flex-row w-full items-center p-2 rounded-md cursor-pointer hover:bg-blue-50 hover:text-blue-400',
|
||||
selectedScriptComponentId === id ? 'bg-blue-100 text-blue-600' : ''
|
||||
)},"
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center p-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectInlineScript(id)}
|
||||
>
|
||||
<span class="text-xs truncate">{name}</span>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
@@ -123,31 +127,31 @@
|
||||
</div>
|
||||
</PanelSection>
|
||||
|
||||
<PanelSection title="Background scripts" smallPadding>
|
||||
<PanelSection
|
||||
title="Background scripts"
|
||||
tooltip="Background scripts are triggered upon global refresh or when their input changes. The result
|
||||
of a background script can be shared among many components."
|
||||
smallPadding
|
||||
>
|
||||
<svelte:fragment slot="action">
|
||||
<Button size="xs" color="dark" startIcon={{ icon: faPlus }} on:click={createBackgroundScript}>
|
||||
Add
|
||||
</Button>
|
||||
<Tooltip>
|
||||
Background scripts are triggered upon global refresh or when their input changes. The result
|
||||
of a background script can be shared among many components.
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
<div class="flex flex-col gap-2 w-full">
|
||||
{#if $app.hiddenInlineScripts?.length > 0}
|
||||
<div class="flex gap-2 flex-col ">
|
||||
{#each $app.hiddenInlineScripts as { name }, index (index)}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="{classNames(
|
||||
'border flex gap-1 truncate justify-between flex-row w-full items-center p-2 rounded-md cursor-pointer hover:bg-blue-50 hover:text-blue-400',
|
||||
selectedScriptComponentId === `bg_${index}` ? 'bg-blue-100 text-blue-600' : ''
|
||||
)},"
|
||||
on:click={() => selectInlineScript(`bg_${index}`)}
|
||||
{@const id = `bg_${index}`}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center p-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectInlineScript(id)}
|
||||
>
|
||||
<span class="text-xs truncate">{name}</span>
|
||||
<Badge color="yellow">Background</Badge>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
export let userInputEnabled: boolean = true
|
||||
export let shouldCapitalize: boolean = true
|
||||
export let rowColumns = false
|
||||
export let resourceOnly = false
|
||||
|
||||
const { connectingInput } = getContext<AppEditorContext>('AppEditorContext')
|
||||
</script>
|
||||
@@ -22,97 +23,99 @@
|
||||
<div class="w-full flex flex-col gap-4">
|
||||
{#each Object.keys(inputSpecs) as inputSpecKey (inputSpecKey)}
|
||||
{@const input = inputSpecs[inputSpecKey]}
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex justify-between items-end gap-1">
|
||||
<span class="text-sm font-semibold truncate">
|
||||
{shouldCapitalize
|
||||
? capitalize(addWhitespaceBeforeCapitals(inputSpecKey))
|
||||
: inputSpecKey}
|
||||
{#if input.tooltip}
|
||||
<Tooltip>
|
||||
{input.tooltip}
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</span>
|
||||
{#if !(resourceOnly && (input.fieldType !== 'object' || !input.format?.startsWith('resource-')))}
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex justify-between items-end gap-1">
|
||||
<span class="text-sm font-semibold truncate">
|
||||
{shouldCapitalize
|
||||
? capitalize(addWhitespaceBeforeCapitals(inputSpecKey))
|
||||
: inputSpecKey}
|
||||
{#if input.tooltip}
|
||||
<Tooltip>
|
||||
{input.tooltip}
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<div class="flex gap-x-2 gap-y-1 flex-wrap justify-end items-center">
|
||||
<Badge color="blue">
|
||||
{input.fieldType === 'array' && input.subFieldType
|
||||
? `${capitalize(fieldTypeToTsType(input.subFieldType))}[]`
|
||||
: capitalize(fieldTypeToTsType(input.fieldType))}
|
||||
</Badge>
|
||||
<div class="flex gap-x-2 gap-y-1 flex-wrap justify-end items-center">
|
||||
<Badge color="blue">
|
||||
{input.fieldType === 'array' && input.subFieldType
|
||||
? `${capitalize(fieldTypeToTsType(input.subFieldType))}[]`
|
||||
: capitalize(fieldTypeToTsType(input.fieldType))}
|
||||
</Badge>
|
||||
|
||||
{#if !inputSpecs[inputSpecKey].onlyStatic && inputSpecs[inputSpecKey].type != 'eval'}
|
||||
<ToggleButtonGroup
|
||||
bind:selected={inputSpecs[inputSpecKey].type}
|
||||
on:selected={(e) => {
|
||||
if (e.detail == 'connected' && !inputSpecs[inputSpecKey]['connection']) {
|
||||
$connectingInput = {
|
||||
opened: true,
|
||||
input: undefined,
|
||||
hoveredComponent: undefined
|
||||
{#if !inputSpecs[inputSpecKey].onlyStatic && inputSpecs[inputSpecKey].type != 'eval'}
|
||||
<ToggleButtonGroup
|
||||
bind:selected={inputSpecs[inputSpecKey].type}
|
||||
on:selected={(e) => {
|
||||
if (e.detail == 'connected' && !inputSpecs[inputSpecKey]['connection']) {
|
||||
$connectingInput = {
|
||||
opened: true,
|
||||
input: undefined,
|
||||
hoveredComponent: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="left"
|
||||
value="static"
|
||||
startIcon={{ icon: faPen }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Static</svelte:fragment>
|
||||
</Popover>
|
||||
{#if rowColumns}
|
||||
}}
|
||||
>
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="row"
|
||||
startIcon={{ icon: faTableCells }}
|
||||
size="xs"
|
||||
>
|
||||
<Tooltip scale={0.6} placement="top-end" wrapperClass="center-center">
|
||||
Use the column name to have the value of the cell be passed to the action
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
<svelte:fragment slot="text">Column</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if userInputEnabled && !input.format?.startsWith('resource-')}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="user"
|
||||
startIcon={{ icon: faUser }}
|
||||
position="left"
|
||||
value="static"
|
||||
startIcon={{ icon: faPen }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">User Input</svelte:fragment>
|
||||
<svelte:fragment slot="text">Static</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value="connected"
|
||||
startIcon={{ icon: faArrowRight }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Connect</svelte:fragment>
|
||||
</Popover>
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
{#if rowColumns}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="row"
|
||||
startIcon={{ icon: faTableCells }}
|
||||
size="xs"
|
||||
>
|
||||
<Tooltip scale={0.6} placement="top-end" wrapperClass="center-center">
|
||||
Use the column name to have the value of the cell be passed to the action
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
<svelte:fragment slot="text">Column</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if userInputEnabled && !input.format?.startsWith('resource-')}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="user"
|
||||
startIcon={{ icon: faUser }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">User Input</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value="connected"
|
||||
startIcon={{ icon: faArrowRight }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Connect</svelte:fragment>
|
||||
</Popover>
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InputsSpecEditor
|
||||
hasRows={rowColumns}
|
||||
{id}
|
||||
bind:componentInput={inputSpecs[inputSpecKey]}
|
||||
/>
|
||||
</div>
|
||||
<InputsSpecEditor
|
||||
hasRows={rowColumns}
|
||||
{id}
|
||||
bind:componentInput={inputSpecs[inputSpecKey]}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
|
||||
@@ -147,6 +147,39 @@ def main():
|
||||
"y": { "field": "b", "type": "quantitative" },
|
||||
},
|
||||
}
|
||||
`
|
||||
},
|
||||
plotlycomponent: {
|
||||
deno:
|
||||
`
|
||||
export async function main() {
|
||||
return {
|
||||
type: 'bar',
|
||||
x: [1, 2, 3, 4],
|
||||
y: [5, 10, 2, 8],
|
||||
marker: {
|
||||
color: '#C8A2C8',
|
||||
line: {
|
||||
width: 2.5
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
`,
|
||||
python3:
|
||||
`
|
||||
def main():
|
||||
return {
|
||||
"type": "bar",
|
||||
"x": [1, 2, 3, 4],
|
||||
"y": [5, 10, 2, 8],
|
||||
"marker": {
|
||||
"color": "#C8A2C8",
|
||||
"line": {
|
||||
"width": 2.5
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
},
|
||||
piechartcomponent: {
|
||||
|
||||
@@ -25,6 +25,7 @@ export type NumberInputComponent = BaseComponent<'numberinputcomponent'>
|
||||
export type SliderComponent = BaseComponent<'slidercomponent'>
|
||||
export type HtmlComponent = BaseComponent<'htmlcomponent'>
|
||||
export type VegaLiteComponent = BaseComponent<'vegalitecomponent'>
|
||||
export type PlotlyComponent = BaseComponent<'plotlycomponent'>
|
||||
export type TimeseriesComponent = BaseComponent<'timeseriescomponent'>
|
||||
export type ButtonComponent = BaseComponent<'buttoncomponent'> & {
|
||||
recomputeIds: string[] | undefined
|
||||
@@ -107,6 +108,7 @@ export type AppComponent = BaseAppComponent &
|
||||
| FormComponent
|
||||
| FormButtonComponent
|
||||
| VegaLiteComponent
|
||||
| PlotlyComponent
|
||||
)
|
||||
|
||||
export type ComponentSet = {
|
||||
@@ -172,9 +174,13 @@ export type AppEditorContext = {
|
||||
isEditor: boolean
|
||||
jobs: Writable<{ job: string; component: string }[]>
|
||||
noBackend: boolean
|
||||
errorByComponent: Writable<Record<string, { error: string; componentId: string }>>
|
||||
openDebugRun: Writable<((componentID: string) => void) | undefined>
|
||||
}
|
||||
|
||||
export type EditorMode = 'dnd' | 'preview'
|
||||
export type EditorBreakpoint = 'sm' | 'lg'
|
||||
|
||||
export const IS_APP_PUBLIC_CONTEXT_KEY = 'isAppPublicContext' as const
|
||||
|
||||
type ComponentID = string
|
||||
|
||||
@@ -122,6 +122,10 @@ export const displayData: Record<AppComponent['type'], { name: string; icon: any
|
||||
name: 'Vega Lite',
|
||||
icon: PieChart
|
||||
},
|
||||
plotlycomponent: {
|
||||
name: 'Plotly',
|
||||
icon: PieChart
|
||||
},
|
||||
timeseriescomponent: {
|
||||
name: 'Timeseries',
|
||||
icon: GripHorizontal
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
colorVariants?.[color]?.[variant],
|
||||
variant === 'border' ? 'border' : '',
|
||||
ButtonType.FontSizeClasses[size],
|
||||
ButtonType.SpacingClasses[spacingSize],
|
||||
ButtonType.SpacingClasses[spacingSize][variant],
|
||||
'focus:ring-2 font-semibold',
|
||||
'duration-200 rounded-md',
|
||||
'justify-center items-center text-center whitespace-nowrap inline-flex',
|
||||
@@ -79,7 +79,8 @@
|
||||
target,
|
||||
tabindex: disabled ? -1 : 0,
|
||||
type: buttonType,
|
||||
title
|
||||
title,
|
||||
...$$restProps
|
||||
}
|
||||
|
||||
async function onClick(event: MouseEvent) {
|
||||
|
||||
@@ -20,12 +20,27 @@ export namespace ButtonType {
|
||||
xl: 'text-xl'
|
||||
} as const
|
||||
|
||||
export const SpacingClasses: Record<ButtonType.Size, string> = {
|
||||
xs: 'px-3 py-1.5',
|
||||
sm: 'px-3 py-1.5',
|
||||
md: 'px-3 py-1.5',
|
||||
lg: 'px-4 py-2',
|
||||
xl: 'px-4 py-2'
|
||||
export const SpacingClasses: Record<ButtonType.Size, Record<ButtonType.Variant, string>> = {
|
||||
xs: {
|
||||
border: 'px-3 py-[6px]',
|
||||
contained: 'px-3 py-[7px]'
|
||||
},
|
||||
sm: {
|
||||
border: 'px-3 py-[6px]',
|
||||
contained: 'px-3 py-[7px]'
|
||||
},
|
||||
md: {
|
||||
border: 'px-3 py-[6px]',
|
||||
contained: 'px-3 py-[7px]'
|
||||
},
|
||||
lg: {
|
||||
border: 'px-4 py-[8px]',
|
||||
contained: 'px-4 py-[9px]'
|
||||
},
|
||||
xl: {
|
||||
border: 'px-4 py-[8px]',
|
||||
contained: 'px-4 py-[9px]'
|
||||
},
|
||||
} as const
|
||||
|
||||
export const IconScale: Record<ButtonType.Size, number> = {
|
||||
|
||||
@@ -55,8 +55,10 @@
|
||||
if (open) {
|
||||
switch (event.key) {
|
||||
case 'Escape':
|
||||
open = false
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
event.stopImmediatePropagation()
|
||||
open = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import Drawer from '$lib/components/common/drawer/Drawer.svelte'
|
||||
import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte'
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
import { flowStore, initFlow } from '$lib/components/flows/flowStore'
|
||||
import { importFlowStore } from '$lib/components/flows/flowStore'
|
||||
import { Icon } from 'svelte-awesome'
|
||||
|
||||
let drawer: Drawer | undefined = undefined
|
||||
@@ -14,9 +14,7 @@
|
||||
|
||||
async function importJson() {
|
||||
await goto('/flows/add')
|
||||
Object.assign($flowStore, JSON.parse(pendingJson))
|
||||
|
||||
initFlow($flowStore)
|
||||
$importFlowStore = JSON.parse(pendingJson)
|
||||
drawer?.closeDrawer?.()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -39,12 +39,14 @@
|
||||
editor?.insertAtCursor(detail)
|
||||
}}
|
||||
>
|
||||
<SimpleEditor
|
||||
bind:this={editor}
|
||||
lang="javascript"
|
||||
bind:code={branch.expr}
|
||||
class="small-editor"
|
||||
shouldBindKey={false}
|
||||
extraLib={stepPropPicker.extraLib}
|
||||
/>
|
||||
<div class="border border-gray-400">
|
||||
<SimpleEditor
|
||||
bind:this={editor}
|
||||
lang="javascript"
|
||||
bind:code={branch.expr}
|
||||
class="small-editor border "
|
||||
shouldBindKey={false}
|
||||
extraLib={stepPropPicker.extraLib}
|
||||
/>
|
||||
</div>
|
||||
</PropPickerWrapper>
|
||||
|
||||
@@ -27,133 +27,143 @@
|
||||
</script>
|
||||
|
||||
<div class="p-4 h-full flex flex-col">
|
||||
{#if !failureModule}
|
||||
<div class="center-center">
|
||||
<div class="max-w-min">
|
||||
<ToggleButtonGroup bind:selected={kind}>
|
||||
<ToggleButton position="left" value="script" size="sm" startIcon={{ icon: faCode }}>
|
||||
Action <Tooltip>
|
||||
An action script is simply a script that is neither a trigger nor an approval script.
|
||||
Those are the majority of the scripts.
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
{#if !shouldDisableTriggerScripts}
|
||||
<ToggleButton position="center" value="trigger" size="sm" startIcon={{ icon: faBolt }}>
|
||||
Trigger <Tooltip>
|
||||
Used as a first step most commonly with a state and a schedule to watch for changes
|
||||
on an external system, compute the diff since last time, set the new state. The
|
||||
diffs are then treated one by one with a for-loop.
|
||||
{#if summary == 'Terminate flow'}
|
||||
<Alert role="info" title="The flow stops here"
|
||||
>This is an identity step with an early stop that has 'true' for expression</Alert
|
||||
>
|
||||
{:else}{#if !failureModule}
|
||||
<div class="center-center">
|
||||
<div class="max-w-min">
|
||||
<ToggleButtonGroup bind:selected={kind}>
|
||||
<ToggleButton position="left" value="script" size="sm" startIcon={{ icon: faCode }}>
|
||||
Action <Tooltip>
|
||||
An action script is simply a script that is neither a trigger nor an approval
|
||||
script. Those are the majority of the scripts.
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
{/if}
|
||||
<ToggleButton position="right" value="approval" size="sm" startIcon={{ icon: faCheck }}>
|
||||
Approval <Tooltip>
|
||||
An approval step will suspend the execution of a flow until it has been approved
|
||||
through the resume endpoints or the approval page by and solely by the recipients of
|
||||
those secret urls. Use `wmill.getResumeUrls()` in Typescript or
|
||||
`wmill.get_resume_urls()` in Python from the wmill client to generate those URLs.
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
{#if !shouldDisableTriggerScripts}
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="trigger"
|
||||
size="sm"
|
||||
startIcon={{ icon: faBolt }}
|
||||
>
|
||||
Trigger <Tooltip>
|
||||
Used as a first step most commonly with a state and a schedule to watch for
|
||||
changes on an external system, compute the diff since last time, set the new
|
||||
state. The diffs are then treated one by one with a for-loop.
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
{/if}
|
||||
<ToggleButton position="right" value="approval" size="sm" startIcon={{ icon: faCheck }}>
|
||||
Approval <Tooltip>
|
||||
An approval step will suspend the execution of a flow until it has been approved
|
||||
through the resume endpoints or the approval page by and solely by the recipients of
|
||||
those secret urls. Use `wmill.getResumeUrls()` in Typescript or
|
||||
`wmill.get_resume_urls()` in Python from the wmill client to generate those URLs.
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if kind == 'trigger'}
|
||||
<div class="mt-2" />
|
||||
<Alert title="Trigger script automatic schedule" role="info">
|
||||
A schedule will be automatically attached to this flow to run every 15 minutes. Adjust
|
||||
frequency in 'Settings -> Schedule'</Alert
|
||||
>
|
||||
{/if}
|
||||
<h3 class="pb-2 pt-4">
|
||||
Inline new <span class="text-blue-500">{kind == 'script' ? 'action' : kind}</span> script
|
||||
<Tooltip>
|
||||
Embed a script directly inside a flow instead of saving the script into your workspace for
|
||||
reuse. You can always save an inline script to your workspace later.
|
||||
</Tooltip>
|
||||
</h3>
|
||||
<div class="flex flex-row">
|
||||
<div class="flex flex-row flex-wrap gap-2">
|
||||
<FlowScriptPicker
|
||||
label="Typescript"
|
||||
lang={Script.language.DENO}
|
||||
on:click={() => {
|
||||
dispatch('new', {
|
||||
language: RawScript.language.DENO,
|
||||
kind,
|
||||
subkind: 'flow'
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
||||
<FlowScriptPicker
|
||||
label="Python"
|
||||
lang={Script.language.PYTHON3}
|
||||
on:click={() => {
|
||||
dispatch('new', {
|
||||
language: RawScript.language.PYTHON3,
|
||||
kind,
|
||||
subkind: 'flow'
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if kind != 'approval'}
|
||||
{/if}
|
||||
{#if kind == 'trigger'}
|
||||
<div class="mt-2" />
|
||||
<Alert title="Trigger script automatic schedule" role="info">
|
||||
A schedule will be automatically attached to this flow to run every 15 minutes. Adjust
|
||||
frequency in 'Settings -> Schedule'</Alert
|
||||
>
|
||||
{/if}
|
||||
<h3 class="pb-2 pt-4">
|
||||
Inline new <span class="text-blue-500">{kind == 'script' ? 'action' : kind}</span> script
|
||||
<Tooltip>
|
||||
Embed a script directly inside a flow instead of saving the script into your workspace for
|
||||
reuse. You can always save an inline script to your workspace later.
|
||||
</Tooltip>
|
||||
</h3>
|
||||
<div class="flex flex-row">
|
||||
<div class="flex flex-row flex-wrap gap-2">
|
||||
<FlowScriptPicker
|
||||
label="Go"
|
||||
lang={Script.language.GO}
|
||||
label="Typescript"
|
||||
lang={Script.language.DENO}
|
||||
on:click={() => {
|
||||
dispatch('new', {
|
||||
language: RawScript.language.GO,
|
||||
kind,
|
||||
subkind: 'flow'
|
||||
})
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if kind == 'script'}
|
||||
<FlowScriptPicker
|
||||
label="Bash"
|
||||
lang={Script.language.BASH}
|
||||
on:click={() => {
|
||||
dispatch('new', {
|
||||
language: RawScript.language.BASH,
|
||||
language: RawScript.language.DENO,
|
||||
kind,
|
||||
subkind: 'flow'
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if !failureModule}
|
||||
<FlowScriptPicker
|
||||
label={`PostgreSQL`}
|
||||
lang="pgsql"
|
||||
on:click={() =>
|
||||
dispatch('new', { language: RawScript.language.DENO, kind, subkind: 'pgsql' })}
|
||||
/>
|
||||
<FlowScriptPicker
|
||||
label="Python"
|
||||
lang={Script.language.PYTHON3}
|
||||
on:click={() => {
|
||||
dispatch('new', {
|
||||
language: RawScript.language.PYTHON3,
|
||||
kind,
|
||||
subkind: 'flow'
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if kind != 'approval'}
|
||||
<FlowScriptPicker
|
||||
label={`MySQL`}
|
||||
lang="mysql"
|
||||
on:click={() =>
|
||||
dispatch('new', { language: RawScript.language.DENO, kind, subkind: 'mysql' })}
|
||||
label="Go"
|
||||
lang={Script.language.GO}
|
||||
on:click={() => {
|
||||
dispatch('new', {
|
||||
language: RawScript.language.GO,
|
||||
kind,
|
||||
subkind: 'flow'
|
||||
})
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="mb-2 mt-6"
|
||||
>Use pre-made <span class="text-blue-500">{kind == 'script' ? 'action' : kind}</span> script</h3
|
||||
>
|
||||
{#if pick_existing == 'hub'}
|
||||
<PickHubScript bind:filter {kind} on:pick>
|
||||
<ToggleHubWorkspace bind:selected={pick_existing} />
|
||||
</PickHubScript>
|
||||
{:else}
|
||||
<WorkspaceScriptPicker displayLock bind:filter {kind} on:pick>
|
||||
<ToggleHubWorkspace bind:selected={pick_existing} />
|
||||
</WorkspaceScriptPicker>
|
||||
{#if kind == 'script'}
|
||||
<FlowScriptPicker
|
||||
label="Bash"
|
||||
lang={Script.language.BASH}
|
||||
on:click={() => {
|
||||
dispatch('new', {
|
||||
language: RawScript.language.BASH,
|
||||
kind,
|
||||
subkind: 'flow'
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if !failureModule}
|
||||
<FlowScriptPicker
|
||||
label={`PostgreSQL`}
|
||||
lang="pgsql"
|
||||
on:click={() =>
|
||||
dispatch('new', { language: RawScript.language.DENO, kind, subkind: 'pgsql' })}
|
||||
/>
|
||||
|
||||
<FlowScriptPicker
|
||||
label={`MySQL`}
|
||||
lang="mysql"
|
||||
on:click={() =>
|
||||
dispatch('new', { language: RawScript.language.DENO, kind, subkind: 'mysql' })}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="mb-2 mt-6"
|
||||
>Use pre-made <span class="text-blue-500">{kind == 'script' ? 'action' : kind}</span> script</h3
|
||||
>
|
||||
{#if pick_existing == 'hub'}
|
||||
<PickHubScript bind:filter {kind} on:pick>
|
||||
<ToggleHubWorkspace bind:selected={pick_existing} />
|
||||
</PickHubScript>
|
||||
{:else}
|
||||
<WorkspaceScriptPicker displayLock bind:filter {kind} on:pick>
|
||||
<ToggleHubWorkspace bind:selected={pick_existing} />
|
||||
</WorkspaceScriptPicker>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -35,9 +35,8 @@
|
||||
<h2 class="mt-2"
|
||||
>Retries <Tooltip>
|
||||
If defined, upon error this step will be retried with a delay and a maximum number of attempts
|
||||
as defined below. If none of the retries succeed, the step job is a failure and the error will
|
||||
propagate up in the case of a branch, and the error handler will be called ultimately if not
|
||||
handled prior.</Tooltip
|
||||
as defined below. If both static and exponential delay is defined, the static delay attempts
|
||||
are tried before the exponential ones.</Tooltip
|
||||
></h2
|
||||
>
|
||||
|
||||
@@ -76,22 +75,25 @@
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
right: 'Exponential retry enabled'
|
||||
right: 'Exponential backoff enabled'
|
||||
}}
|
||||
/>
|
||||
{#if flowModule.retry?.exponential}
|
||||
<span class="text-xs font-bold">Attempts</span>
|
||||
<input bind:value={flowModule.retry.exponential.attempts} type="number" />
|
||||
<span class="text-xs font-bold">Mulitplier</span>
|
||||
<span class="text-xs text-gray-500">additional delay = multiplier * base ^ (# attempts)</span>
|
||||
<input bind:value={flowModule.retry.exponential.multiplier} type="number" />
|
||||
<span class="text-xs font-bold">Initial delay (seconds)</span>
|
||||
<span class="text-xs font-bold">Base</span>
|
||||
<input bind:value={flowModule.retry.exponential.seconds} type="number" />
|
||||
{:else}
|
||||
<span class="text-xs font-bold">Attempts</span>
|
||||
<input type="number" disabled />
|
||||
<span class="text-xs font-bold">Mulitplier</span>
|
||||
<span class="text-xs text-gray-500">additional delay = multiplier * seconds ^ (# attempts)</span
|
||||
>
|
||||
<input type="number" disabled />
|
||||
<span class="text-xs font-bold">Initial delay (seconds)</span>
|
||||
<span class="text-xs font-bold">Base</span>
|
||||
<input type="number" disabled />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -14,12 +14,20 @@
|
||||
import { getContext } from 'svelte'
|
||||
import type { FlowEditorContext } from '../types'
|
||||
import autosize from 'svelte-autosize'
|
||||
import Slider from '$lib/components/Slider.svelte'
|
||||
import { page } from '$app/stores'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { copyToClipboard } from '$lib/utils'
|
||||
import { Icon } from 'svelte-awesome'
|
||||
import { faClipboard } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
const { selectedId } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
export let initialPath: string
|
||||
|
||||
let topHeight = 0
|
||||
|
||||
$: url = `${$page.url.hostname}/api/w/${$workspaceStore}/jobs/run/f/${$flowStore?.path}`
|
||||
</script>
|
||||
|
||||
<div class="h-full overflow-hidden">
|
||||
@@ -32,34 +40,78 @@
|
||||
<Tab value="settings-graph">Graph</Tab>
|
||||
|
||||
<svelte:fragment slot="content">
|
||||
<TabContent value="settings-metadata" class="p-4">
|
||||
<Path bind:path={$flowStore.path} {initialPath} namePlaceholder="my_flow" kind="flow" />
|
||||
|
||||
<label class="block my-4">
|
||||
<span class="text-gray-700 text-sm">Summary <Required required={false} /></span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={$flowStore.summary}
|
||||
placeholder="A short summary of the flow displayed when the flow is listed"
|
||||
id="flow-summary"
|
||||
<TabContent value="settings-metadata" class="p-4 h-full">
|
||||
<div class="overflow-auto h-full">
|
||||
<Path
|
||||
bind:path={$flowStore.path}
|
||||
{initialPath}
|
||||
namePlaceholder="my_flow"
|
||||
kind="flow"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="block my-4" for="inp">
|
||||
<span class="text-gray-700 text-sm">
|
||||
Description
|
||||
<Required required={false} />
|
||||
<textarea
|
||||
use:autosize
|
||||
<label class="block my-4">
|
||||
<span class="text-gray-700 text-sm">Summary <Required required={false} /></span>
|
||||
<input
|
||||
type="text"
|
||||
class="text-sm"
|
||||
id="inp"
|
||||
bind:value={$flowStore.description}
|
||||
placeholder="A description to help users understand what this flow does and how to use it."
|
||||
rows="3"
|
||||
bind:value={$flowStore.summary}
|
||||
placeholder="A short summary of the flow displayed when the flow is listed"
|
||||
id="flow-summary"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</label>
|
||||
|
||||
<label class="block my-4" for="inp">
|
||||
<span class="text-gray-700 text-sm">
|
||||
Description
|
||||
<Required required={false} />
|
||||
<textarea
|
||||
use:autosize
|
||||
type="text"
|
||||
class="text-sm"
|
||||
id="inp"
|
||||
bind:value={$flowStore.description}
|
||||
placeholder="A description to help users understand what this flow does and how to use it."
|
||||
rows="3"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<Slider text="How to trigger from external events?">
|
||||
<div class="text-sm text-gray-600 border p-4">
|
||||
There are 2 ways to trigger a flow based on external events:
|
||||
<ul class="pt-4">
|
||||
<li
|
||||
>1. Send a webhook after each event: <a
|
||||
on:click={(e) => {
|
||||
e.preventDefault()
|
||||
copyToClipboard(url)
|
||||
}}
|
||||
href={$page.url.protocol + '//' + url}
|
||||
class="whitespace-nowrap text-ellipsis overflow-hidden mr-1"
|
||||
>
|
||||
{url}
|
||||
<span class="text-gray-700 ml-2">
|
||||
<Icon data={faClipboard} />
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="mt-2">
|
||||
<div class="flex flex-row gap-2">
|
||||
2. Use a trigger script and schedule this flow to run as frequently as
|
||||
needed and compare a state persisted in Windmill to the state of the
|
||||
external system. If a difference is detected, then the rest of the flow is
|
||||
triggered. Oftentimes, the second step of a flow is a for-loop that will
|
||||
iterate over every elements. When using a trigger, a default schedule will
|
||||
be created.
|
||||
<img
|
||||
class="shadow-lg border rounded"
|
||||
alt="static button"
|
||||
src="/trigger_button.png"
|
||||
/>
|
||||
</div>
|
||||
</li></ul
|
||||
>
|
||||
</div>
|
||||
</Slider>
|
||||
</div>
|
||||
</TabContent>
|
||||
<TabContent value="settings-schedule" class="p-4">
|
||||
<Alert type="info" title="Primary Schedule">
|
||||
|
||||
@@ -32,7 +32,7 @@ export async function loadFlowModuleState(flowModule: FlowModule): Promise<FlowM
|
||||
|
||||
export const idMutex = new Mutex()
|
||||
|
||||
export function getNextId(currentKeys: string[]): string {
|
||||
export function getNextId(currentKeys: string[], skipTilde: boolean = false): string {
|
||||
const max = currentKeys.reduce((acc, key) => {
|
||||
if (key === 'failure' || key.includes('branch') || key.includes('loop')) {
|
||||
return acc
|
||||
|
||||
@@ -6,6 +6,7 @@ import { numberToChars } from './utils'
|
||||
|
||||
export type FlowMode = 'push' | 'pull'
|
||||
|
||||
export const importFlowStore = writable<Flow | undefined>(undefined)
|
||||
export const flowStore = writable<Flow>({
|
||||
summary: '',
|
||||
value: { modules: [] },
|
||||
@@ -53,7 +54,6 @@ export async function initFlow(flow: Flow) {
|
||||
}
|
||||
|
||||
await initFlowState(flow)
|
||||
|
||||
flowStore.set(flow)
|
||||
|
||||
function migrateFlowModule(mod: FlowModule) {
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FlowModuleSchemaMap bind:modules={branch.modules} />
|
||||
<FlowModuleSchemaMap parentType="branchall" bind:modules={branch.modules} />
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<FlowModuleSchemaMap bind:modules={module.value.default} />
|
||||
<FlowModuleSchemaMap parentType="branchone" bind:modules={module.value.default} />
|
||||
</div>
|
||||
|
||||
{#each module.value.branches ?? [] as branch, branchIndex (branchIndex)}
|
||||
@@ -108,7 +108,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FlowModuleSchemaMap bind:modules={branch.modules} />
|
||||
<FlowModuleSchemaMap parentType="branchone" bind:modules={branch.modules} />
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
@@ -23,13 +23,14 @@
|
||||
|
||||
export let root: boolean = false
|
||||
export let modules: FlowModule[] | undefined
|
||||
export let parentType: 'flow' | 'forloop' | 'branchall' | 'branchone' | undefined = undefined
|
||||
|
||||
let indexToRemove: number | undefined = undefined
|
||||
const { select, selectedId } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
async function insertNewModuleAtIndex(
|
||||
index: number,
|
||||
kind: 'script' | 'forloop' | 'branchone' | 'branchall' | 'flow' | 'trigger' | 'approval'
|
||||
kind: 'script' | 'forloop' | 'branchone' | 'branchall' | 'flow' | 'trigger' | 'approval' | 'end'
|
||||
): Promise<void> {
|
||||
await idMutex.runExclusive(async () => {
|
||||
var module = emptyModule(kind == 'flow')
|
||||
@@ -50,6 +51,9 @@
|
||||
flowModule.summary = 'Trigger'
|
||||
} else if (kind == 'approval') {
|
||||
flowModule.summary = 'Approval'
|
||||
} else if (kind == 'end') {
|
||||
flowModule.summary = 'Terminate flow'
|
||||
flowModule.stop_after_if = { skip_if_stopped: false, expr: 'true' }
|
||||
}
|
||||
select(flowModule.id)
|
||||
})
|
||||
@@ -136,10 +140,15 @@
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
<InsertModuleButton
|
||||
trigger={modules?.length == 0}
|
||||
on:new={(e) => insertNewModuleAtIndex(modules?.length ?? 0, e.detail)}
|
||||
/>
|
||||
{#if !modules || modules[modules?.length - 1]?.summary != 'Terminate flow'}
|
||||
<InsertModuleButton
|
||||
stop={!root && parentType == 'branchone'}
|
||||
trigger={root && modules?.length == 0}
|
||||
on:new={(e) => insertNewModuleAtIndex(modules?.length ?? 0, e.detail)}
|
||||
/>
|
||||
{:else}
|
||||
<div class="my-2" />
|
||||
{/if}
|
||||
</ul>
|
||||
{#if root}
|
||||
<div class="sticky bottom-0 bg-gray-50 flex-none px-4 py-1 pb-2 border-t">
|
||||
|
||||
@@ -10,10 +10,11 @@
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Icon from 'svelte-awesome'
|
||||
import { Repeat } from 'lucide-svelte'
|
||||
import { Repeat, Square } from 'lucide-svelte'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
export let trigger = false
|
||||
export let stop = false
|
||||
</script>
|
||||
|
||||
<Menu noMinW placement="bottom-start" let:close>
|
||||
@@ -113,5 +114,18 @@
|
||||
<Icon data={faBarsStaggered} scale={0.8} class="mr-2" />
|
||||
Flow
|
||||
</button>
|
||||
{#if stop}
|
||||
<button
|
||||
class="w-full text-left p-2 hover:bg-gray-100 inline-flex gap-2.5"
|
||||
on:click={() => {
|
||||
close()
|
||||
dispatch('new', 'end')
|
||||
}}
|
||||
role="menuitem"
|
||||
>
|
||||
<Square size={14} />
|
||||
End Flow
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</Menu>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import Icon from 'svelte-awesome'
|
||||
import IconedResourceType from '$lib/components/IconedResourceType.svelte'
|
||||
import LanguageIcon from '$lib/components/common/languageIcons/LanguageIcon.svelte'
|
||||
import { Building, Repeat } from 'lucide-svelte'
|
||||
import { Building, Repeat, Square } from 'lucide-svelte'
|
||||
|
||||
export let mod: FlowModule
|
||||
export let index: number
|
||||
@@ -107,6 +107,8 @@
|
||||
<div slot="icon">
|
||||
{#if mod.value.type === 'rawscript'}
|
||||
<LanguageIcon lang={mod.value.language} width={16} height={16} />
|
||||
{:else if mod.summary == 'Terminate flow'}
|
||||
<Square size={16} />
|
||||
{:else if mod.value.type === 'identity'}
|
||||
<Icon data={faLongArrowDown} scale={1.1} />
|
||||
{:else if mod.value.type === 'script'}
|
||||
|
||||
@@ -162,7 +162,7 @@ export function codeToStaticTemplate(code?: string): string | undefined {
|
||||
if (lines.length == 1) {
|
||||
const line = lines[0].trim()
|
||||
if (line[0] == '`' && line.charAt(line.length - 1) == '`') {
|
||||
return line.slice(1, line.length - 1)
|
||||
return line.slice(1, line.length - 1).replaceAll('\\`', '`')
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
@@ -179,7 +179,10 @@ export function emptyFlowModuleState(): FlowModuleState {
|
||||
|
||||
const aCharCode = 'a'.charCodeAt(0)
|
||||
|
||||
export function numberToChars(n: number) {
|
||||
export function numberToChars(n: number, skipTilde: boolean = false) {
|
||||
if (n < 0) {
|
||||
return "-" + numberToChars(-n, skipTilde)
|
||||
}
|
||||
var b = [n],
|
||||
sp,
|
||||
out,
|
||||
@@ -187,11 +190,12 @@ export function numberToChars(n: number) {
|
||||
div
|
||||
|
||||
sp = 0
|
||||
const basisInc = skipTilde ? -1 : 0
|
||||
while (sp < b.length) {
|
||||
if (b[sp] > 25) {
|
||||
div = Math.floor(b[sp] / 27)
|
||||
if (b[sp] > (25 + basisInc)) {
|
||||
div = Math.floor(b[sp] / (27 + basisInc))
|
||||
b[sp + 1] = div - 1
|
||||
b[sp] %= 27
|
||||
b[sp] %= (27 + basisInc)
|
||||
}
|
||||
sp += 1
|
||||
}
|
||||
@@ -199,17 +203,20 @@ export function numberToChars(n: number) {
|
||||
out = ''
|
||||
for (i = 0; i < b.length; i += 1) {
|
||||
let charCode = aCharCode + b[i]
|
||||
out = (charCode == 26 ? '-' : String.fromCharCode(charCode)) + out
|
||||
out = (b[i] == 26 ? "-" : String.fromCharCode(charCode)) + out
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
export function charsToNumber(n: string): number {
|
||||
if (n.charAt(0) == '-') {
|
||||
return charsToNumber(n.slice(1)) * -1
|
||||
}
|
||||
let b = Math.pow(27, n.length - 1)
|
||||
let res = 0
|
||||
for (let c of n) {
|
||||
let charCode = c == '-' ? aCharCode + 26 : c.charCodeAt(0)
|
||||
let charCode = (c == '-' || c == '_') ? aCharCode + 26 : c.charCodeAt(0)
|
||||
res += (charCode - aCharCode + 1) * b
|
||||
b = b / 27
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
return flowModuleToBranch(
|
||||
module,
|
||||
branches,
|
||||
['Default', ...module.value.branches.map((x) => `If ${truncateRev(x.expr, 20)}`)],
|
||||
['Default', ...module.value.branches.map((x) => `${truncateRev(x.expr, 20)}`)],
|
||||
parent,
|
||||
insideLoop
|
||||
)
|
||||
@@ -328,11 +328,11 @@
|
||||
if (!modules.length) {
|
||||
items.push(createVirtualNode(branchParent, 'Empty branch', edgesLabel[i]))
|
||||
} else {
|
||||
modules.forEach((module) => {
|
||||
modules.forEach((module, j) => {
|
||||
const item = getConvertedFlowModule(
|
||||
module,
|
||||
items.length ? items : numberToChars(branch.node.id),
|
||||
edgesLabel[i],
|
||||
j == 0 ? edgesLabel[i] : undefined,
|
||||
insideLoop
|
||||
)
|
||||
item && items.push(item)
|
||||
@@ -434,8 +434,9 @@
|
||||
edgesLabel?: string,
|
||||
offset?: number
|
||||
): Node {
|
||||
const id = -idGenerator.next().value - 1 + (offset ?? 0)
|
||||
return {
|
||||
id: -idGenerator.next().value - 1 + (offset ?? 0),
|
||||
id,
|
||||
position: { x: -1, y: -1 },
|
||||
data: {
|
||||
html: `
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
faBarsStaggered
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { CalendarClock } from 'lucide-svelte'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import Icon from 'svelte-awesome'
|
||||
import { check } from 'svelte-awesome/icons'
|
||||
import { Badge } from '../common'
|
||||
@@ -32,6 +33,24 @@
|
||||
|
||||
export let job: Job
|
||||
let scheduleEditor: ScheduleEditor
|
||||
|
||||
let time = Date.now()
|
||||
let interval
|
||||
onMount(() => {
|
||||
interval = setInterval(() => {
|
||||
time = Date.now()
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
interval && clearInterval(interval)
|
||||
})
|
||||
|
||||
function endedDate(started_at: string, duration_ms: number): string {
|
||||
const started = new Date(started_at)
|
||||
started.setMilliseconds(started.getMilliseconds() + duration_ms)
|
||||
return displayDaysAgo(started.toString())
|
||||
}
|
||||
</script>
|
||||
|
||||
<ScheduleEditor on:update={() => goto('/schedules')} bind:this={scheduleEditor} />
|
||||
@@ -152,9 +171,17 @@
|
||||
<Icon class="text-gray-700" data={faClock} scale={SMALL_ICON_SCALE} /><span
|
||||
class="mx-1.5"
|
||||
>
|
||||
Started {displayDaysAgo(job.started_at ?? '')}</span
|
||||
>
|
||||
</div>
|
||||
<span>
|
||||
{#if job?.['duration_ms']}
|
||||
Ended {#key time}
|
||||
{endedDate(job.started_at, job?.['duration_ms'])}{/key}
|
||||
{:else}
|
||||
Started {#key time}
|
||||
{displayDaysAgo(job.started_at ?? '')}{/key}
|
||||
{/if}</span
|
||||
>
|
||||
</span></div
|
||||
>
|
||||
{/if}
|
||||
{#if 'scheduled_for' in job && !job.running && job.scheduled_for && forLater(job.scheduled_for)}
|
||||
<div class="inline-flex gap-1">
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import { Badge } from '../common'
|
||||
import { NEVER_TESTED_THIS_FAR } from '../flows/utils'
|
||||
import { getTypeAsString } from '../flows/utils'
|
||||
import Popover from '../Popover.svelte'
|
||||
import { computeKey } from './utils'
|
||||
import WarningMessage from './WarningMessage.svelte'
|
||||
|
||||
@@ -18,6 +17,7 @@
|
||||
export let rawKey = false
|
||||
export let topBrackets = false
|
||||
export let topLevelNode = false
|
||||
export let allowCopy = true
|
||||
|
||||
const collapsedSymbol = '...'
|
||||
let keys: string | any[]
|
||||
@@ -39,7 +39,7 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function selectProp(key: string, value: any) {
|
||||
if (pureViewer) {
|
||||
if (pureViewer && allowCopy) {
|
||||
copyToClipboard(value)
|
||||
}
|
||||
dispatch('select', rawKey ? key : computeKey(key, isArray, currentPath))
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
const EMPTY_STRING = ''
|
||||
let search = ''
|
||||
|
||||
const { propPickerConfig, clearFocus } = getContext<PropPickerWrapperContext>('PropPickerWrapper')
|
||||
const { propPickerConfig } = getContext<PropPickerWrapperContext>('PropPickerWrapper')
|
||||
|
||||
$: flowInputsFiltered =
|
||||
search === EMPTY_STRING
|
||||
@@ -89,6 +89,7 @@
|
||||
</div>
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<ObjectViewer
|
||||
allowCopy={false}
|
||||
pureViewer={!$propPickerConfig}
|
||||
json={flowInputsFiltered}
|
||||
on:select={(e) => {
|
||||
@@ -100,6 +101,7 @@
|
||||
<span class="font-bold text-sm">Error</span>
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<ObjectViewer
|
||||
allowCopy={false}
|
||||
pureViewer={!$propPickerConfig}
|
||||
json={{
|
||||
error: {
|
||||
@@ -116,6 +118,7 @@
|
||||
<span class="font-bold text-sm">Previous Result</span>
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<ObjectViewer
|
||||
allowCopy={false}
|
||||
topLevelNode
|
||||
pureViewer={!$propPickerConfig}
|
||||
json={Object.fromEntries(
|
||||
@@ -131,6 +134,7 @@
|
||||
<span class="font-bold text-sm">Resume payloads</span>
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<ObjectViewer
|
||||
allowCopy={false}
|
||||
topLevelNode
|
||||
pureViewer={!$propPickerConfig}
|
||||
json={{
|
||||
@@ -148,6 +152,7 @@
|
||||
<span class="font-bold text-sm">All Results</span>
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<ObjectViewer
|
||||
allowCopy={false}
|
||||
topLevelNode
|
||||
pureViewer={!$propPickerConfig}
|
||||
collapsed={true}
|
||||
@@ -173,6 +178,7 @@
|
||||
}}>-</Button
|
||||
>
|
||||
<ObjectViewer
|
||||
allowCopy={false}
|
||||
pureViewer={!$propPickerConfig}
|
||||
rawKey={true}
|
||||
json={variables}
|
||||
@@ -200,6 +206,7 @@
|
||||
}}>-</Button
|
||||
>
|
||||
<ObjectViewer
|
||||
allowCopy={false}
|
||||
pureViewer={!$propPickerConfig}
|
||||
rawKey={true}
|
||||
json={resources}
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
<div class="w-full px-2">
|
||||
<span class="font-bold text-sm">Result</span>
|
||||
<div class="overflow-y-auto mb-2 w-full">
|
||||
<ObjectViewer json={{ result }} on:select />
|
||||
<ObjectViewer allowCopy={false} json={{ result }} on:select />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
Sign out
|
||||
</button>
|
||||
</div>
|
||||
{#if isCloudHosted()}
|
||||
{#if isCloudHosted() && $premiumStore}
|
||||
{#if !$premiumStore.premium}
|
||||
<div class="py-1" role="none">
|
||||
<span class="text-gray-700 block w-full text-left px-4 py-2 text-sm"
|
||||
|
||||
@@ -30,8 +30,10 @@
|
||||
<table class="w-full">
|
||||
{#each $userWorkspaces as workspace}
|
||||
<tr
|
||||
class="text-xs cursor-pointer hover:bg-gray-100"
|
||||
class="text-xs
|
||||
{$workspaceStore === workspace.id ? 'cursor-default bg-blue-50' : 'cursor-pointer hover:bg-gray-100'}"
|
||||
on:click={() => {
|
||||
if($workspaceStore === workspace.id) { return }
|
||||
switchWorkspace(workspace.id)
|
||||
close()
|
||||
}}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user