Compare commits

..

4 Commits

Author SHA1 Message Date
Ruben Fiszel
ece4573076 all 2023-02-01 19:46:48 +01:00
Kai Jellinghaus
833d0df965 Add webhook request histogram 2023-01-26 07:03:49 +01:00
Kai Jellinghaus
1e7b7cc8d6 Update SQLX 2023-01-26 06:59:29 +01:00
Kai Jellinghaus
1de8eefe96 Add workspace webhook 2023-01-26 06:42:10 +01:00
91 changed files with 989 additions and 1594 deletions

View File

@@ -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=${EXPECTED_URL}
- BASE_URL=http://localhost
- BASE_INTERNAL_URL=http://localhost:8000
- RUST_LOG=info
- NUM_WORKERS=3
@@ -28,16 +28,12 @@ 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

View File

@@ -13,7 +13,7 @@ jobs:
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v1.3.6
uses: dependabot/fetch-metadata@v1.3.5
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Enable auto-merge for Dependabot PRs

View File

@@ -1,47 +0,0 @@
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

View File

@@ -4,8 +4,6 @@ on:
types: [opened,synchronize,reopened,closed]
paths:
- "backend/**"
- ".github/uffizzi/**"
- ".github/workflows/**"
workflow_dispatch:
jobs:

View File

@@ -47,35 +47,22 @@ 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.uffizzi.yml | awk '{ print $1 }')" >> $GITHUB_ENV
run: echo "COMPOSE_FILE_HASH=$(md5sum docker-compose.rendered.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.uffizzi.yml
path: docker-compose.rendered.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: |
@@ -93,7 +80,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.uffizzi.yml
compose-file-cache-path: docker-compose.rendered.yml
server: https://app.uffizzi.com
pr-number: ${{ needs.cache-compose-file.outputs.pr-number }}
permissions:

View File

@@ -1,3 +0,0 @@
{
"python.analysis.typeCheckingMode": "basic"
}

View File

@@ -1,71 +1,6 @@
# 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)

View File

@@ -308,9 +308,7 @@ upcoming CLI tool.
| 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 |
| |

373
backend/Cargo.lock generated
View File

@@ -135,9 +135,9 @@ dependencies = [
[[package]]
name = "async-recursion"
version = "1.0.2"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796"
checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea"
dependencies = [
"proc-macro2",
"quote",
@@ -180,9 +180,9 @@ dependencies = [
[[package]]
name = "async-trait"
version = "0.1.64"
version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282"
dependencies = [
"proc-macro2",
"quote",
@@ -217,9 +217,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "axum"
version = "0.6.4"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5694b64066a2459918d8074c2ce0d5a88f409431994c2356617c8ae0c4721fc"
checksum = "1304eab461cf02bd70b083ed8273388f9724c549b316ba3d1e213ce0e9e7fb7e"
dependencies = [
"async-trait",
"axum-core",
@@ -251,9 +251,9 @@ dependencies = [
[[package]]
name = "axum-core"
version = "0.3.2"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cae3e661676ffbacb30f1a824089a8c9150e71017f7e1e38f2aa32009188d34"
checksum = "f487e40dc9daee24d8a1779df88522f159a54a980f99cfbe43db0be0bd3444a8"
dependencies = [
"async-trait",
"bytes",
@@ -384,21 +384,11 @@ dependencies = [
"regex-automata",
]
[[package]]
name = "built"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b9c056b9ed43aee5e064b683aa1ec783e19c6acec7559e3ae931b7490472fbe"
dependencies = [
"cargo-lock",
"git2",
]
[[package]]
name = "bumpalo"
version = "3.12.0"
version = "3.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]]
name = "byteorder"
@@ -412,26 +402,11 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
[[package]]
name = "cargo-lock"
version = "8.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "031718ddb8f78aa5def78a09e90defe30151d1f6c672f937af4dd916429ed996"
dependencies = [
"semver 1.0.16",
"serde",
"toml",
"url",
]
[[package]]
name = "cc"
version = "1.0.79"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
dependencies = [
"jobserver",
]
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
[[package]]
name = "cfg-if"
@@ -466,9 +441,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.1.4"
version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
checksum = "4ec7a4128863c188deefe750ac1d1dfe66c236909f845af04beed823638dc1b2"
dependencies = [
"bitflags",
"clap_derive",
@@ -591,9 +566,9 @@ dependencies = [
[[package]]
name = "crc"
version = "3.0.1"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3"
dependencies = [
"crc-catalog",
]
@@ -661,9 +636,9 @@ dependencies = [
[[package]]
name = "cxx"
version = "1.0.88"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322296e2f2e5af4270b54df9e85a02ff037e271af20ba3e7fe1575515dc840b8"
checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579"
dependencies = [
"cc",
"cxxbridge-flags",
@@ -673,9 +648,9 @@ dependencies = [
[[package]]
name = "cxx-build"
version = "1.0.88"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "017a1385b05d631e7875b1f151c9f012d37b53491e2a87f65bff5c262b2111d8"
checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70"
dependencies = [
"cc",
"codespan-reporting",
@@ -688,15 +663,15 @@ dependencies = [
[[package]]
name = "cxxbridge-flags"
version = "1.0.88"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c26bbb078acf09bc1ecda02d4223f03bdd28bd4874edcb0379138efc499ce971"
checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c"
[[package]]
name = "cxxbridge-macro"
version = "1.0.88"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357f40d1f06a24b60ae1fe122542c1fb05d28d32acb2aed064e84bc2ad1e252e"
checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5"
dependencies = [
"proc-macro2",
"quote",
@@ -910,9 +885,9 @@ checksum = "131726693bce13b09331bee70734fe266666332b6ddfef23e9dca5b8bf6dea66"
[[package]]
name = "either"
version = "1.8.1"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
dependencies = [
"serde",
]
@@ -1055,9 +1030,9 @@ dependencies = [
[[package]]
name = "futures"
version = "0.3.26"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
dependencies = [
"futures-channel",
"futures-core",
@@ -1070,9 +1045,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.26"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
dependencies = [
"futures-core",
"futures-sink",
@@ -1080,15 +1055,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.26"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
[[package]]
name = "futures-executor"
version = "0.3.26"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
dependencies = [
"futures-core",
"futures-task",
@@ -1108,9 +1083,9 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.26"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
[[package]]
name = "futures-lite"
@@ -1129,9 +1104,9 @@ dependencies = [
[[package]]
name = "futures-macro"
version = "0.3.26"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
dependencies = [
"proc-macro2",
"quote",
@@ -1140,21 +1115,21 @@ dependencies = [
[[package]]
name = "futures-sink"
version = "0.3.26"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
[[package]]
name = "futures-task"
version = "0.3.26"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
[[package]]
name = "futures-util"
version = "0.3.26"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
dependencies = [
"futures-channel",
"futures-core",
@@ -1231,19 +1206,6 @@ 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"
@@ -1592,15 +1554,6 @@ 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"
@@ -1745,36 +1698,12 @@ 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"
@@ -1944,15 +1873,6 @@ 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"
@@ -1994,9 +1914,9 @@ dependencies = [
[[package]]
name = "num-complex"
version = "0.4.3"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
dependencies = [
"num-traits",
"serde",
@@ -2045,18 +1965,18 @@ dependencies = [
[[package]]
name = "num_enum"
version = "0.5.9"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b"
checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.9"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e"
checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
dependencies = [
"proc-macro-crate",
"proc-macro2",
@@ -2232,9 +2152,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "pest"
version = "2.5.4"
version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f"
checksum = "4257b4a04d91f7e9e6290be5d3da4804dd5784fafde3a497d73eb2b4a158c30a"
dependencies = [
"thiserror",
"ucd-trie",
@@ -2250,6 +2170,15 @@ 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"
@@ -2262,12 +2191,12 @@ dependencies = [
[[package]]
name = "phf_codegen"
version = "0.11.1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770"
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
dependencies = [
"phf_generator 0.11.1",
"phf_shared 0.11.1",
"phf_generator 0.10.0",
"phf_shared 0.10.0",
]
[[package]]
@@ -2412,12 +2341,13 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "proc-macro-crate"
version = "1.3.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34"
checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9"
dependencies = [
"once_cell",
"toml_edit",
"thiserror",
"toml",
]
[[package]]
@@ -2452,9 +2382,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
version = "1.0.50"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
dependencies = [
"unicode-ident",
]
@@ -2462,16 +2392,14 @@ dependencies = [
[[package]]
name = "progenitor"
version = "0.2.1-dev"
source = "git+https://github.com/oxidecomputer/progenitor#8a09b98645276ef3738d3e904e1c32e5de1d657e"
source = "git+https://github.com/oxidecomputer/progenitor#46d2df0e2a3dcbaea70669df6afb75ae27dea348"
dependencies = [
"anyhow",
"built",
"clap",
"openapiv3",
"progenitor-client",
"progenitor-impl",
"progenitor-macro",
"project-root",
"serde",
"serde_json",
"serde_yaml",
@@ -2480,7 +2408,7 @@ dependencies = [
[[package]]
name = "progenitor-client"
version = "0.2.1-dev"
source = "git+https://github.com/oxidecomputer/progenitor#8a09b98645276ef3738d3e904e1c32e5de1d657e"
source = "git+https://github.com/oxidecomputer/progenitor#46d2df0e2a3dcbaea70669df6afb75ae27dea348"
dependencies = [
"bytes",
"futures-core",
@@ -2494,7 +2422,7 @@ dependencies = [
[[package]]
name = "progenitor-impl"
version = "0.2.1-dev"
source = "git+https://github.com/oxidecomputer/progenitor#8a09b98645276ef3738d3e904e1c32e5de1d657e"
source = "git+https://github.com/oxidecomputer/progenitor#46d2df0e2a3dcbaea70669df6afb75ae27dea348"
dependencies = [
"getopts",
"heck",
@@ -2516,7 +2444,7 @@ dependencies = [
[[package]]
name = "progenitor-macro"
version = "0.2.1-dev"
source = "git+https://github.com/oxidecomputer/progenitor#8a09b98645276ef3738d3e904e1c32e5de1d657e"
source = "git+https://github.com/oxidecomputer/progenitor#46d2df0e2a3dcbaea70669df6afb75ae27dea348"
dependencies = [
"openapiv3",
"proc-macro2",
@@ -2530,12 +2458,6 @@ 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"
@@ -2696,11 +2618,11 @@ dependencies = [
[[package]]
name = "reqwest"
version = "0.11.14"
version = "0.11.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9"
checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
dependencies = [
"base64 0.21.0",
"base64 0.13.1",
"bytes",
"encoding_rs",
"futures-core",
@@ -2728,7 +2650,6 @@ dependencies = [
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"winreg",
]
@@ -2854,9 +2775,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.36.7"
version = "0.36.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549"
dependencies = [
"bitflags",
"errno",
@@ -2890,7 +2811,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython#b6e447145800ec7888f035bf266329e4b457e5f9"
source = "git+https://github.com/RustPython/RustPython#59b191c789c891a500659d2c3b29a645883f780a"
dependencies = [
"num-bigint",
"rustpython-compiler-core",
@@ -2899,7 +2820,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython#b6e447145800ec7888f035bf266329e4b457e5f9"
source = "git+https://github.com/RustPython/RustPython#59b191c789c891a500659d2c3b29a645883f780a"
dependencies = [
"bincode",
"bitflags",
@@ -2916,7 +2837,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython#b6e447145800ec7888f035bf266329e4b457e5f9"
source = "git+https://github.com/RustPython/RustPython#59b191c789c891a500659d2c3b29a645883f780a"
dependencies = [
"ahash",
"anyhow",
@@ -2926,7 +2847,7 @@ dependencies = [
"log",
"num-bigint",
"num-traits",
"phf",
"phf 0.10.1",
"phf_codegen",
"rustc-hash",
"rustpython-ast",
@@ -3024,9 +2945,9 @@ dependencies = [
[[package]]
name = "security-framework"
version = "2.8.2"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
dependencies = [
"bitflags",
"core-foundation",
@@ -3037,9 +2958,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
version = "2.8.0"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4"
checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
dependencies = [
"core-foundation-sys",
"libc",
@@ -3068,9 +2989,6 @@ name = "semver"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
dependencies = [
"serde",
]
[[package]]
name = "semver-parser"
@@ -3220,9 +3138,9 @@ dependencies = [
[[package]]
name = "serde_yaml"
version = "0.9.17"
version = "0.9.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567"
checksum = "92b5b431e8907b50339b51223b97d102db8d987ced36f6e4d03621db9316c834"
dependencies = [
"indexmap",
"itoa",
@@ -3571,9 +3489,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "swc_atoms"
version = "0.4.34"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "731cf66bd8e11030f056f91f9d8af77f83ec4377ff04d1670778a57d1607402a"
checksum = "93a49e93996e1c1cfdb641cd94888da545d440ff6cada04155ef118adee620c1"
dependencies = [
"once_cell",
"rustc-hash",
@@ -3585,9 +3503,9 @@ dependencies = [
[[package]]
name = "swc_common"
version = "0.29.29"
version = "0.29.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97e491d31418cd33fea58e9f893316fc04b30e2b5d0e750c066e2ba4907ae54"
checksum = "90a2c285d33b47a5e532a662c178dc91956534ff52207892918d3034a534ae12"
dependencies = [
"ahash",
"ast_node",
@@ -3612,9 +3530,9 @@ dependencies = [
[[package]]
name = "swc_ecma_ast"
version = "0.96.3"
version = "0.96.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a887102d5595b557261aa4bde35f3d71906fba674d4b79cd5c59b4155b12ee2d"
checksum = "708345b44bedd33ff5dedfc66263070af0cad788d4bb19b51007b4583a64c43f"
dependencies = [
"bitflags",
"is-macro",
@@ -3629,9 +3547,9 @@ dependencies = [
[[package]]
name = "swc_ecma_parser"
version = "0.124.5"
version = "0.124.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e75888eabf1ad8a8968e3befc7cd20c10e4721254d3344285bd5c3a42f58dc1"
checksum = "1ec6b89544ad2242a8e19d966a6c3ac8e6eb5ec5060e08bdcb01062c479b41ca"
dependencies = [
"either",
"enum_kind",
@@ -3672,9 +3590,9 @@ dependencies = [
[[package]]
name = "swc_visit"
version = "0.5.4"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "470a1963cf182fdcbbac46e3a7fd2caf7329da0e568d3668202da9501c880e16"
checksum = "82f2bcb7223e185c4c7cbf5e0c1207dec6d2bfd5e72e3fb7b3e8d179747e9130"
dependencies = [
"either",
"swc_visit_macros",
@@ -3682,9 +3600,9 @@ dependencies = [
[[package]]
name = "swc_visit_macros"
version = "0.5.5"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6098b717cfd4c85f5cddec734af191dbce461c39975ed567c32ac6d0c6d61a6d"
checksum = "8fb1f3561674d84947694d41fb6d5737d19539222779baeac1b3a071a2b29428"
dependencies = [
"Inflector",
"pmutil",
@@ -3849,9 +3767,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.25.0"
version = "1.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae"
dependencies = [
"autocfg",
"bytes",
@@ -3953,30 +3871,13 @@ dependencies = [
[[package]]
name = "toml"
version = "0.5.11"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
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"
@@ -4172,7 +4073,7 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "typify"
version = "0.0.11-dev"
source = "git+https://github.com/oxidecomputer/typify#cd1f95b66b0d0fbb33537d66043f26512ee3268b"
source = "git+https://github.com/oxidecomputer/typify#0f173877fa12c7e53c351fd1084e7cab0c6a7be1"
dependencies = [
"typify-impl",
"typify-macro",
@@ -4181,7 +4082,7 @@ dependencies = [
[[package]]
name = "typify-impl"
version = "0.0.11-dev"
source = "git+https://github.com/oxidecomputer/typify#cd1f95b66b0d0fbb33537d66043f26512ee3268b"
source = "git+https://github.com/oxidecomputer/typify#0f173877fa12c7e53c351fd1084e7cab0c6a7be1"
dependencies = [
"heck",
"log",
@@ -4199,7 +4100,7 @@ dependencies = [
[[package]]
name = "typify-macro"
version = "0.0.11-dev"
source = "git+https://github.com/oxidecomputer/typify#cd1f95b66b0d0fbb33537d66043f26512ee3268b"
source = "git+https://github.com/oxidecomputer/typify#0f173877fa12c7e53c351fd1084e7cab0c6a7be1"
dependencies = [
"proc-macro2",
"quote",
@@ -4290,9 +4191,9 @@ dependencies = [
[[package]]
name = "unicode-bidi"
version = "0.3.10"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-general-category"
@@ -4323,9 +4224,9 @@ dependencies = [
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
[[package]]
name = "unicode-width"
@@ -4542,19 +4443,6 @@ 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"
@@ -4586,9 +4474,9 @@ dependencies = [
[[package]]
name = "which"
version = "4.4.0"
version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
dependencies = [
"either",
"libc",
@@ -4638,7 +4526,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
version = "1.61.1"
version = "1.60.0"
dependencies = [
"anyhow",
"axum",
@@ -4665,7 +4553,7 @@ dependencies = [
[[package]]
name = "windmill-api"
version = "1.61.1"
version = "1.60.0"
dependencies = [
"anyhow",
"argon2",
@@ -4685,6 +4573,7 @@ dependencies = [
"lazy_static",
"magic-crypt",
"mime_guess",
"prometheus",
"rand 0.8.5",
"reqwest",
"retainer",
@@ -4718,7 +4607,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
version = "1.61.1"
version = "1.60.0"
dependencies = [
"base64 0.21.0",
"chrono",
@@ -4733,7 +4622,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
version = "1.61.1"
version = "1.60.0"
dependencies = [
"chrono",
"serde",
@@ -4746,7 +4635,7 @@ dependencies = [
[[package]]
name = "windmill-common"
version = "1.61.1"
version = "1.60.0"
dependencies = [
"anyhow",
"axum",
@@ -4770,7 +4659,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
version = "1.61.1"
version = "1.60.0"
dependencies = [
"serde",
"serde_json",
@@ -4778,12 +4667,12 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
version = "1.61.1"
version = "1.60.0"
dependencies = [
"anyhow",
"itertools",
"lazy_static",
"phf",
"phf 0.11.1",
"regex",
"unicode-general-category",
"windmill-common",
@@ -4792,11 +4681,11 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
version = "1.61.1"
version = "1.60.0"
dependencies = [
"anyhow",
"itertools",
"phf",
"phf 0.11.1",
"unicode-general-category",
"windmill-common",
"windmill-parser",
@@ -4804,12 +4693,12 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
version = "1.61.1"
version = "1.60.0"
dependencies = [
"anyhow",
"itertools",
"lazy_static",
"phf",
"phf 0.11.1",
"regex",
"rustpython-parser",
"serde_json",
@@ -4819,7 +4708,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
version = "1.61.1"
version = "1.60.0"
dependencies = [
"anyhow",
"deno_core",
@@ -4833,7 +4722,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
version = "1.61.1"
version = "1.60.0"
dependencies = [
"anyhow",
"chrono",
@@ -4856,7 +4745,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
version = "1.61.1"
version = "1.60.0"
dependencies = [
"anyhow",
"async-recursion",

View File

@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.61.1"
version = "1.60.0"
authors.workspace = true
edition.workspace = true
@@ -19,7 +19,7 @@ members = [
]
[workspace.package]
version = "1.61.1"
version = "1.60.0"
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.16.2" }
cookie = { version = "^0" }
phf = { version = "0.11", features = ["macros"] }
rust-embed = "^6"
mime_guess = "^2"

View File

@@ -0,0 +1,5 @@
-- Add up migration script here
ALTER TABLE
workspace_settings
ADD
COLUMN webhook text;

View File

@@ -1,2 +0,0 @@
-- Add up migration script here
ALTER TABLE account ALTER COLUMN refresh_token TYPE VARCHAR(1500);

View File

@@ -1 +0,0 @@
-- Add down migration script here

View File

@@ -1,4 +0,0 @@
-- Add up migration script here
GRANT ALL PRIVILEGES ON TABLE favorite TO windmill_admin;
GRANT ALL PRIVILEGES ON TABLE favorite TO windmill_user;

View File

@@ -1 +0,0 @@
-- Add down migration script here

View File

@@ -1,25 +0,0 @@
-- 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$;

View File

@@ -1 +0,0 @@
-- Add down migration script here

View File

@@ -1,5 +0,0 @@
-- 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[]));

View File

@@ -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.kwarg.is_some(),
star_kwargs: params.vararg.is_some(),
args: params
.args
.into_iter()
@@ -136,10 +136,7 @@ fn to_value(et: &ExprKind) -> Option<serde_json::Value> {
.into_iter()
.zip(values)
.map(|(k, v)| {
let key = k
.as_ref()
.map(|x| x.node.clone())
.and_then(|n| to_value(&n))
let key = to_value(&k.node)
.and_then(|x| match x {
serde_json::Value::String(s) => Some(s),
_ => None,
@@ -185,7 +182,6 @@ 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 {
@@ -197,13 +193,13 @@ fn replace_import(x: String) -> String {
}
lazy_static! {
static ref RE: Regex = Regex::new(r"^\#\s?(\S+)$").unwrap();
static ref RE: Regex = Regex::new(r"^\#(\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:") || x.starts_with("# requirements:"));
.find_position(|x| x.starts_with("#requirements:"));
if let Some((pos, _)) = find_requirements {
let lines = code
.lines()

View File

@@ -558,6 +558,11 @@
"name": "plan",
"ordinal": 8,
"type_info": "Varchar"
},
{
"name": "webhook",
"ordinal": 9,
"type_info": "Text"
}
],
"nullable": [
@@ -569,6 +574,7 @@
true,
true,
true,
true,
true
],
"parameters": {
@@ -1231,6 +1237,18 @@
},
"query": "INSERT INTO usr\n (workspace_id, email, username, is_admin, operator)\n VALUES ($1, $2, $3, $4, $5)"
},
"33d69b3915ddfde40323ace65c14e39fa4bbc8b5dd50a34e165765eaea1f4966": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Text"
]
}
},
"query": "UPDATE workspace_settings SET webhook = NULL WHERE workspace_id = $1"
},
"355dcb2cbebd13f0e3bdd4929b9e431b0e6d72716d1c4f9ab6af6adce5b5e4b3": {
"describe": {
"columns": [
@@ -1850,6 +1868,11 @@
"name": "plan",
"ordinal": 8,
"type_info": "Varchar"
},
{
"name": "webhook",
"ordinal": 9,
"type_info": "Text"
}
],
"nullable": [
@@ -1861,6 +1884,7 @@
true,
true,
true,
true,
true
],
"parameters": {
@@ -2710,6 +2734,19 @@
},
"query": "UPDATE script SET archived = true WHERE path = $1 AND workspace_id = $2 RETURNING hash"
},
"8292b7b2cce5319575bc09ad18f29b63270872b6e5c6df1f0a326370058f13b0": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Text",
"Text"
]
}
},
"query": "UPDATE workspace_settings SET webhook = $1 WHERE workspace_id = $2"
},
"82f3c4cd1c1f6aea86d66f675442587684391bc32be9ab55ae20aab549b7bba5": {
"describe": {
"columns": [],
@@ -3765,6 +3802,26 @@
},
"query": "SELECT email FROM usr where username = $1 AND workspace_id = $2"
},
"a34b79872766941cae2d62c99d80e28b7214dd2fcbb68020a63325bbcb34f417": {
"describe": {
"columns": [
{
"name": "webhook",
"ordinal": 0,
"type_info": "Text"
}
],
"nullable": [
true
],
"parameters": {
"Left": [
"Text"
]
}
},
"query": "SELECT webhook FROM workspace_settings WHERE workspace_id = $1"
},
"a38059dc3574da498ce986c916b6d385b1f18d5bd659ef13c43fafa9daff6bda": {
"describe": {
"columns": [

View File

@@ -70,4 +70,5 @@ cookie.workspace = true
sha2.workspace = true
urlencoding.workspace = true
async-stripe.workspace = true
lazy_static.workspace = true
lazy_static.workspace = true
prometheus.workspace = true

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.61.1
version: 1.60.0
title: Windmill API
contact:
@@ -883,6 +883,8 @@ paths:
type: string
customer_id:
type: string
webhook:
type: string
/w/{workspace}/workspaces/premium_info:
get:
@@ -962,6 +964,33 @@ paths:
schema:
type: string
/w/{workspace}/workspaces/edit_webhook:
post:
summary: edit webhook
operationId: editWebhook
tags:
- workspace
parameters:
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: WorkspaceWebhook
required: true
content:
application/json:
schema:
type: object
properties:
webhook:
type: string
responses:
"200":
description: status
content:
text/plain:
schema:
type: string
/w/{workspace}/users/list:
get:
summary: list users
@@ -2445,44 +2474,6 @@ 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

View File

@@ -12,6 +12,7 @@ use crate::{
jobs::script_path_to_payload,
users::{require_owner_of_path, Authed, OptAuthed},
variables::build_crypt,
webhook_util::{WebhookMessage, WebhookUtil},
};
use axum::{
extract::{Extension, Json, Path, Query},
@@ -310,6 +311,7 @@ async fn get_secret_id(
async fn create_app(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path(w_id): Path<String>,
Json(app): Json<CreateApp>,
) -> Result<(StatusCode, String)> {
@@ -356,7 +358,12 @@ async fn create_app(
None,
)
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::CreateApp {
workspace: w_id.clone(),
path: app.path.clone(),
});
Ok((StatusCode::CREATED, app.path))
}
@@ -395,6 +402,7 @@ pub async fn get_hub_app_by_id(
async fn delete_app(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path((w_id, path)): Path<(String, StripPath)>,
) -> Result<String> {
let path = path.to_path();
@@ -418,6 +426,8 @@ async fn delete_app(
)
.await?;
tx.commit().await?;
webhook
.send_message(WebhookMessage::DeleteApp { workspace: w_id.clone(), path: path.to_owned() });
Ok(format!("app {} deleted", path))
}
@@ -425,6 +435,7 @@ async fn delete_app(
async fn update_app(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
Json(ns): Json<EditApp>,
@@ -514,6 +525,11 @@ async fn update_app(
)
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::UpdateApp {
workspace: w_id.clone(),
old_path: path.to_owned(),
new_path: npath.clone(),
});
Ok(format!("app {} updated (npath: {:?})", path, npath))
}

View File

@@ -32,6 +32,7 @@ use crate::{
db::{UserDB, DB},
schedule::clear_schedule,
users::{require_owner_of_path, Authed},
webhook_util::{WebhookMessage, WebhookUtil},
};
pub fn workspaced_service() -> Router {
@@ -181,6 +182,7 @@ async fn check_path_conflict<'c>(
async fn create_flow(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path(w_id): Path<String>,
Json(nf): Json<NewFlow>,
) -> Result<(StatusCode, String)> {
@@ -221,6 +223,10 @@ async fn create_flow(
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::CreateFlow {
workspace: w_id.clone(),
path: nf.path.clone(),
});
let tx = user_db.begin(&authed).await?;
let (dependency_job_uuid, mut tx) = push(
@@ -280,6 +286,7 @@ async fn update_flow(
authed: Authed,
Extension(user_db): Extension<UserDB>,
Extension(db): Extension<DB>,
webhook: WebhookUtil,
Path((w_id, flow_path)): Path<(String, StripPath)>,
Json(nf): Json<NewFlow>,
) -> Result<String> {
@@ -368,6 +375,11 @@ async fn update_flow(
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::UpdateFlow {
workspace: w_id.clone(),
old_path: flow_path.to_owned(),
new_path: nf.path.clone(),
});
let tx = user_db.begin(&authed).await?;
let (dependency_job_uuid, mut tx) = push(
@@ -450,6 +462,7 @@ async fn exists_flow_by_path(
async fn archive_flow_by_path(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path((w_id, path)): Path<(String, StripPath)>,
) -> Result<String> {
let path = path.to_path();
@@ -474,6 +487,10 @@ async fn archive_flow_by_path(
)
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::ArchiveFlow {
workspace: w_id.clone(),
path: path.to_owned(),
});
Ok(format!("Flow {path} archived"))
}

View File

@@ -9,6 +9,7 @@
use crate::{
db::{UserDB, DB},
users::Authed,
webhook_util::{WebhookMessage, WebhookUtil},
};
use axum::{
extract::{Extension, Path, Query},
@@ -139,6 +140,7 @@ async fn check_name_conflict<'c>(
async fn create_folder(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path(w_id): Path<String>,
Json(ng): Json<NewFolder>,
) -> Result<String> {
@@ -193,8 +195,12 @@ async fn create_folder(
None,
)
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::CreateFolder {
workspace: w_id.clone(),
name: ng.name.clone(),
});
Ok(format!("Created folder {}", ng.name))
}
@@ -245,6 +251,7 @@ pub async fn require_is_owner(
async fn update_folder(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path((w_id, name)): Path<(String, String)>,
Json(ng): Json<UpdateFolder>,
) -> Result<String> {
@@ -298,8 +305,12 @@ async fn update_folder(
None,
)
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::UpdateFolder {
workspace: w_id.clone(),
name: name.to_owned(),
});
Ok(format!("Updated folder {}", name))
}
@@ -416,6 +427,7 @@ async fn get_folder_usage(
async fn delete_folder(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path((w_id, name)): Path<(String, String)>,
) -> Result<String> {
let mut tx = user_db.begin(&authed).await?;
@@ -440,6 +452,10 @@ async fn delete_folder(
)
.await?;
tx.commit().await?;
webhook
.send_message(WebhookMessage::DeleteFolder { workspace: w_id.clone(), name: name.clone() });
Ok(format!("delete folder at name {}", name))
}
@@ -447,6 +463,7 @@ async fn add_owner(
authed: Authed,
Extension(db): Extension<DB>,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path((w_id, name)): Path<(String, String)>,
Json(Owner { owner }): Json<Owner>,
) -> Result<String> {
@@ -477,6 +494,10 @@ async fn add_owner(
)
.await?;
tx.commit().await?;
webhook
.send_message(WebhookMessage::UpdateFolder { workspace: w_id.clone(), name: name.clone() });
Ok(format!("Added {} to folder {}", owner, name))
}
@@ -510,6 +531,7 @@ async fn remove_owner(
authed: Authed,
Extension(db): Extension<DB>,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path((w_id, name)): Path<(String, String)>,
Json(Owner { owner }): Json<Owner>,
) -> Result<String> {
@@ -540,5 +562,9 @@ async fn remove_owner(
)
.await?;
tx.commit().await?;
webhook
.send_message(WebhookMessage::UpdateFolder { workspace: w_id.clone(), name: name.clone() });
Ok(format!("Removed {} to folder {}", owner, name))
}

View File

@@ -53,10 +53,6 @@ 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))
@@ -107,7 +103,9 @@ async fn get_result_by_id(
Query(ResultByIdQuery { skip_direct }): Query<ResultByIdQuery>,
Path((w_id, flow_id, node_id)): Path<(String, String, String)>,
) -> windmill_common::error::JsonResult<serde_json::Value> {
tracing::error!("get_result_by_id_bef: {:?} {:?}", flow_id, node_id);
let res = windmill_queue::get_result_by_id(db, skip_direct, w_id, flow_id, node_id).await?;
tracing::error!("get_result_by_id: {:?}", res);
Ok(Json(res))
}
@@ -270,14 +268,6 @@ 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,
@@ -298,14 +288,11 @@ impl RunJobQuery {
headers: HeaderMap,
mut args: serde_json::Map<String, serde_json::Value>,
) -> serde_json::Map<String, serde_json::Value> {
let whitelist = self
.include_header
self.include_header
.as_ref()
.map(|s| s.split(",").map(|s| s.to_string()).collect::<Vec<_>>())
.unwrap_or_default();
whitelist
.unwrap_or_default()
.iter()
.chain(INCLUDE_HEADERS.iter())
.for_each(|h| {
if let Some(v) = headers.get(h) {
args.insert(
@@ -1403,53 +1390,6 @@ 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,

View File

@@ -21,6 +21,7 @@ use crate::{
oauth2::{build_oauth_clients, SlackVerifier},
tracing_init::{MyMakeSpan, MyOnResponse},
users::{Authed, OptAuthed},
webhook_util::{WebhookShared, WebhookUtil},
};
mod apps;
@@ -42,6 +43,7 @@ mod tracing_init;
mod users;
mod utils;
mod variables;
mod webhook_util;
mod worker_ping;
mod workspaces;
@@ -106,7 +108,8 @@ pub async fn run_server(
std::env::var("COOKIE_DOMAIN").ok(),
))))
.layer(Extension(http_client))
.layer(CookieManagerLayer::new());
.layer(CookieManagerLayer::new())
.layer(Extension(WebhookShared::new(rx.resubscribe())));
// build our application with a route
let app = Router::new()
.nest(
@@ -147,7 +150,8 @@ pub async fn run_server(
.nest("/flows", flows::workspaced_service())
.nest("/capture", capture::workspaced_service())
.nest("/favorites", favorite::workspaced_service())
.nest("/folders", folders::workspaced_service()),
.nest("/folders", folders::workspaced_service())
.route_layer(from_extractor::<WebhookUtil>()),
)
.nest("/workspaces", workspaces::global_service())
.nest(

View File

@@ -9,6 +9,7 @@
use crate::{
db::{UserDB, DB},
users::{require_owner_of_path, Authed},
webhook_util::{WebhookMessage, WebhookUtil},
};
use axum::{
extract::{Extension, Path, Query},
@@ -263,6 +264,7 @@ async fn check_path_conflict<'c>(
async fn create_resource(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path(w_id): Path<String>,
Json(resource): Json<CreateResource>,
) -> Result<(StatusCode, String)> {
@@ -293,6 +295,11 @@ async fn create_resource(
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::CreateResource {
workspace: w_id.clone(),
path: resource.path.clone(),
});
Ok((
StatusCode::CREATED,
format!("resource {} created", resource.path),
@@ -302,6 +309,7 @@ async fn create_resource(
async fn delete_resource(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path((w_id, path)): Path<(String, StripPath)>,
) -> Result<String> {
let path = path.to_path();
@@ -333,12 +341,18 @@ async fn delete_resource(
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::DeleteResource {
workspace: w_id.clone(),
path: path.to_owned(),
});
Ok(format!("resource {} deleted", path))
}
async fn update_resource(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
Json(ns): Json<EditResource>,
@@ -400,6 +414,12 @@ async fn update_resource(
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::UpdateResource {
workspace: w_id.clone(),
old_path: path.to_owned(),
new_path: npath.clone(),
});
Ok(format!("resource {} updated (npath: {:?})", path, npath))
}
@@ -411,6 +431,7 @@ struct UpdateResource {
async fn update_resource_value(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path((w_id, path)): Path<(String, StripPath)>,
Json(nv): Json<UpdateResource>,
) -> Result<String> {
@@ -436,6 +457,11 @@ async fn update_resource_value(
)
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::UpdateResource {
workspace: w_id.clone(),
old_path: path.to_owned(),
new_path: path.to_owned(),
});
Ok(format!("value of resource {} updated", path))
}
@@ -513,6 +539,7 @@ async fn exists_resource_type(
async fn create_resource_type(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path(w_id): Path<String>,
Json(resource_type): Json<CreateResourceType>,
) -> Result<(StatusCode, String)> {
@@ -543,6 +570,8 @@ async fn create_resource_type(
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::CreateResourceType { name: resource_type.name.clone() });
Ok((
StatusCode::CREATED,
format!("resource_type {} created", resource_type.name),
@@ -574,6 +603,7 @@ async fn check_rt_path_conflict<'c>(
async fn delete_resource_type(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path((w_id, name)): Path<(String, String)>,
) -> Result<String> {
require_admin(authed.is_admin, &authed.username)?;
@@ -598,6 +628,7 @@ async fn delete_resource_type(
)
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::DeleteResourceType { name: name.clone() });
Ok(format!("resource_type {} deleted", name))
}
@@ -605,6 +636,7 @@ async fn delete_resource_type(
async fn update_resource_type(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path((w_id, name)): Path<(String, String)>,
Json(ns): Json<EditResourceType>,
) -> Result<String> {
@@ -634,6 +666,7 @@ async fn update_resource_type(
)
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::UpdateResourceType { name: name.clone() });
Ok(format!("resource_type {} updated", name))
}

View File

@@ -14,6 +14,7 @@ use crate::{
db::{UserDB, DB},
schedule::clear_schedule,
users::{require_owner_of_path, Authed},
webhook_util::{WebhookMessage, WebhookUtil},
};
use axum::{
extract::{Extension, Path, Query},
@@ -184,6 +185,7 @@ fn hash_script(ns: &NewScript) -> i64 {
async fn create_script(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Extension(db): Extension<DB>,
Path(w_id): Path<String>,
Json(ns): Json<NewScript>,
@@ -400,6 +402,11 @@ async fn create_script(
Some([("hash", hash.to_string().as_str())].into()),
)
.await?;
webhook.send_message(WebhookMessage::UpdateScript {
workspace: w_id.clone(),
path: ns.path.clone(),
hash: hash.to_string(),
});
} else {
audit_log(
&mut tx,
@@ -417,6 +424,11 @@ async fn create_script(
),
)
.await?;
webhook.send_message(WebhookMessage::CreateScript {
workspace: w_id.clone(),
path: ns.path.clone(),
hash: hash.to_string(),
});
}
tx.commit().await?;
@@ -600,6 +612,7 @@ async fn get_deployment_status(
async fn archive_script_by_path(
authed: Authed,
webhook: WebhookUtil,
Extension(user_db): Extension<UserDB>,
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
@@ -626,6 +639,10 @@ async fn archive_script_by_path(
)
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::DeleteScript {
workspace: w_id.clone(),
hash: hash.to_string(),
});
Ok(())
}
@@ -633,6 +650,7 @@ async fn archive_script_by_path(
async fn archive_script_by_hash(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path((w_id, hash)): Path<(String, ScriptHash)>,
) -> JsonResult<Script> {
let mut tx = user_db.begin(&authed).await?;
@@ -657,12 +675,18 @@ async fn archive_script_by_hash(
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::DeleteScript {
workspace: w_id.clone(),
hash: hash.to_string(),
});
Ok(Json(script))
}
async fn delete_script_by_hash(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Extension(db): Extension<DB>,
Path((w_id, hash)): Path<(String, ScriptHash)>,
) -> JsonResult<Script> {
@@ -691,6 +715,11 @@ async fn delete_script_by_hash(
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::DeleteScript {
workspace: w_id.clone(),
hash: hash.to_string(),
});
Ok(Json(script))
}

View File

@@ -12,6 +12,7 @@ use crate::{
db::{UserDB, DB},
oauth2::{AllClients, _refresh_token},
users::{require_owner_of_path, Authed},
webhook_util::{WebhookMessage, WebhookUtil},
BaseUrl,
};
/*
@@ -224,6 +225,7 @@ async fn check_path_conflict<'c>(
async fn create_variable(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path(w_id): Path<String>,
Json(variable): Json<CreateVariable>,
) -> Result<(StatusCode, String)> {
@@ -265,6 +267,11 @@ async fn create_variable(
tx.commit().await?;
webhook.send_message(WebhookMessage::CreateVariable {
workspace: w_id.clone(),
path: variable.path.clone(),
});
Ok((
StatusCode::CREATED,
format!("variable {} created", variable.path),
@@ -274,6 +281,7 @@ async fn create_variable(
async fn delete_variable(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Path((w_id, path)): Path<(String, StripPath)>,
) -> Result<String> {
let path = path.to_path();
@@ -306,6 +314,11 @@ async fn delete_variable(
tx.commit().await?;
webhook.send_message(WebhookMessage::DeleteVariable {
workspace: w_id.clone(),
path: path.to_owned(),
});
Ok(format!("variable {} deleted", path))
}
@@ -320,6 +333,7 @@ struct EditVariable {
async fn update_variable(
authed: Authed,
Extension(user_db): Extension<UserDB>,
webhook: WebhookUtil,
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
Json(ns): Json<EditVariable>,
@@ -405,6 +419,12 @@ async fn update_variable(
.await?;
tx.commit().await?;
webhook.send_message(WebhookMessage::UpdateVariable {
workspace: w_id.clone(),
old_path: path.to_owned(),
new_path: npath.clone(),
});
Ok(format!("variable {} updated (npath: {:?})", path, npath))
}

View File

@@ -0,0 +1,156 @@
use std::time::Duration;
use axum::{
async_trait,
extract::{FromRequestParts, OriginalUri},
http::request::Parts,
Extension,
};
use hyper::StatusCode;
use serde::Serialize;
use tokio::{select, sync::mpsc};
use crate::db::DB;
lazy_static::lazy_static! {
// TODO: these aren't synced, they should be moved into the queue abstraction once/if that happens.
static ref WEBHOOK_REQUEST_COUNT: prometheus::Histogram = prometheus::register_histogram!(
"webhook_request",
"Histogram of webhook requests made"
)
.unwrap();
}
#[derive(Serialize)]
#[serde(tag = "type")]
pub enum WebhookMessage {
// See https://serde.rs/enum-representations.html#internally-tagged for how this looks in JSON
CreateApp { workspace: String, path: String },
DeleteApp { workspace: String, path: String },
UpdateApp { workspace: String, old_path: String, new_path: String },
CreateFlow { workspace: String, path: String },
UpdateFlow { workspace: String, old_path: String, new_path: String },
ArchiveFlow { workspace: String, path: String },
CreateFolder { workspace: String, name: String },
UpdateFolder { workspace: String, name: String },
DeleteFolder { workspace: String, name: String },
DeleteResource { workspace: String, path: String },
CreateResource { workspace: String, path: String },
UpdateResource { workspace: String, old_path: String, new_path: String },
CreateResourceType { name: String },
DeleteResourceType { name: String },
UpdateResourceType { name: String },
CreateScript { workspace: String, path: String, hash: String },
UpdateScript { workspace: String, path: String, hash: String },
DeleteScript { workspace: String, hash: String },
CreateVariable { workspace: String, path: String },
UpdateVariable { workspace: String, old_path: String, new_path: String },
DeleteVariable { workspace: String, path: String },
}
#[derive(Clone)]
pub struct WebhookShared {
pub channel: mpsc::UnboundedSender<(String, WebhookMessage)>,
}
impl WebhookShared {
pub fn new(mut shutdown_rx: tokio::sync::broadcast::Receiver<()>) -> Self {
let (tx, mut rx) = mpsc::unbounded_channel::<(String, WebhookMessage)>();
let _process = tokio::spawn(async move {
let client = reqwest::Client::builder()
// TODO: investigate pool timeouts and such if TCP load is high
.timeout(Duration::from_secs(5))
.build()
.unwrap();
loop {
select! {
biased;
_ = shutdown_rx.recv() => break,
r = rx.recv() => match r {
Some((url, message)) => {
let timer = WEBHOOK_REQUEST_COUNT.start_timer();
let _ = client.post(url).json(&message).send().await;
timer.stop_and_record();
},
None => break,
}
}
}
});
Self { channel: tx }
}
}
#[derive(Clone)]
pub struct WebhookUtil {
webhook: Option<String>,
shared: Extension<WebhookShared>,
}
impl WebhookUtil {
pub fn send_message(&self, message: WebhookMessage) {
let Some(webhook) = &self.webhook else {
return;
};
let _ = self.shared.channel.send((webhook.clone(), message));
}
}
#[async_trait]
impl<S> FromRequestParts<S> for WebhookUtil
where
S: Send + Sync,
{
type Rejection = (StatusCode, String);
async fn from_request_parts(
parts: &mut Parts,
state: &S,
) -> std::result::Result<Self, Self::Rejection> {
let original_uri = OriginalUri::from_request_parts(parts, state)
.await
.ok()
.map(|x| x.0)
.unwrap_or_default();
let path_vec: Vec<&str> = original_uri.path().split("/").collect();
let workspace_id = if path_vec.len() >= 4 && path_vec[0] == "" && path_vec[2] == "w" {
Some(path_vec[3].to_owned())
} else {
None
};
let webhook = sqlx::query_scalar!(
"SELECT webhook FROM workspace_settings WHERE workspace_id = $1",
workspace_id
)
.fetch_one(
&Extension::<DB>::from_request_parts(parts, state)
.await
.map_err(|_| {
(
StatusCode::INTERNAL_SERVER_ERROR,
"Could not aquire DB while retrieving webhook".to_owned(),
)
})?
.0,
)
.await
.map_err(|e| {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Could not execute DB query {:?}", e),
)
})?;
let shared = Extension::<WebhookShared>::from_request_parts(parts, state)
.await
.map_err(|_| {
(
StatusCode::INTERNAL_SERVER_ERROR,
"Could not aquire shared process while retrieving webhook".to_owned(),
)
})?;
Ok(Self { webhook, shared })
}
}

View File

@@ -51,6 +51,7 @@ pub fn workspaced_service() -> Router {
.route("/delete_invite", post(delete_invite))
.route("/get_settings", get(get_settings))
.route("/edit_slack_command", post(edit_slack_command))
.route("/edit_webhook", post(edit_webhook))
.route("/edit_auto_invite", post(edit_auto_invite))
.route("/tarball", get(tarball_workspace))
.route("/premium_info", get(premium_info))
@@ -90,6 +91,7 @@ pub struct WorkspaceSettings {
pub auto_invite_operator: Option<bool>,
pub customer_id: Option<String>,
pub plan: Option<String>,
pub webhook: Option<String>,
}
#[derive(FromRow, Serialize, Debug)]
@@ -117,6 +119,11 @@ struct EditAutoInvite {
operator: Option<bool>,
}
#[derive(Deserialize)]
struct EditWebhook {
webhook: Option<String>,
}
#[derive(Deserialize)]
struct CreateWorkspace {
id: String,
@@ -233,38 +240,28 @@ async fn stripe_checkout(
params.line_items = match plan.plan.as_str() {
"team" => Some(vec![
stripe::CreateCheckoutSessionLineItems {
quantity: Some(1),
price: Some("price_1MUlrWGU3NdFi9eLE9GBZhoY".to_string()),
quantity: None,
price: Some("price_1MSdSyGU3NdFi9eLMdV6cS6F".to_string()),
..Default::default()
},
stripe::CreateCheckoutSessionLineItems {
quantity: None,
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()),
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()),
..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);
@@ -519,6 +516,48 @@ async fn edit_auto_invite(
))
}
async fn edit_webhook(
authed: Authed,
Extension(db): Extension<DB>,
Path(w_id): Path<String>,
Authed { is_admin, username, .. }: Authed,
Json(ew): Json<EditWebhook>,
) -> Result<String> {
require_admin(is_admin, &username)?;
let mut tx = db.begin().await?;
if let Some(webhook) = &ew.webhook {
sqlx::query!(
"UPDATE workspace_settings SET webhook = $1 WHERE workspace_id = $2",
webhook,
&w_id
)
.execute(&mut tx)
.await?;
} else {
sqlx::query!(
"UPDATE workspace_settings SET webhook = NULL WHERE workspace_id = $1",
&w_id,
)
.execute(&mut tx)
.await?;
}
audit_log(
&mut tx,
&authed.username,
"workspaces.edit_webhook",
ActionKind::Update,
&w_id,
Some(&authed.email),
Some([("webhook", &format!("{:?}", ew.webhook)[..])].into()),
)
.await?;
tx.commit().await?;
Ok(format!("Edit webhook for workspace {}", &w_id))
}
async fn list_workspaces_as_super_admin(
authed: Authed,
Extension(user_db): Extension<UserDB>,

View File

@@ -307,6 +307,19 @@ async fn op_get_id(args: Vec<String>) -> Result<Option<serde_json::Value>, anyho
let node_id = &args[4];
let client = windmill_api_client::create_client(base_url, token.clone());
let err = client
.result_by_id(workspace, flow_job_id, node_id, Some(true))
.await
.err()
.unwrap();
let res = match err {
windmill_api_client::Error::UnexpectedResponse(e) => {
tracing::error!("{:?}", e.text().await);
anyhow::anyhow!("bar")
}
_ => anyhow::anyhow!("foo"),
};
tracing::error!("{:?}", res);
let result = client
.result_by_id(workspace, flow_job_id, node_id, Some(true))
.await

View File

@@ -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())
@@ -427,15 +427,6 @@ pub async fn run_worker(
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")
@@ -443,10 +434,6 @@ 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,
@@ -460,8 +447,6 @@ pub async fn run_worker(
max_log_size,
deno_flags,
deno_auth_tokens,
pip_local_dependencies,
additional_python_paths,
};
WORKER_STARTED.inc();
@@ -772,8 +757,6 @@ struct Envs {
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 {
@@ -1623,9 +1606,7 @@ 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, additional_python_paths, ..
}: &Envs,
envs @ Envs { nsjail_path, python_path, path_env, max_log_size, .. }: &Envs,
requirements_o: Option<String>,
job_dir: &str,
worker_dir: &str,
@@ -1641,8 +1622,7 @@ async fn handle_python_job(
) -> error::Result<serde_json::Value> {
create_dependencies_dir(job_dir).await;
let mut additional_python_paths: Vec<String> =
additional_python_paths.to_owned().unwrap_or_else(|| vec![]);
let mut additional_python_paths: Vec<String> = vec![];
let requirements = match requirements_o {
Some(r) => r,
@@ -1749,15 +1729,6 @@ 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
@@ -1774,11 +1745,9 @@ 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(**args)
res = inner_script.main(**kwargs)
res_json = json.dumps(res, separators=(',', ':'), default=str).replace('\n', '')
with open("result.json", 'w') as f:
f.write(res_json)
@@ -2132,14 +2101,7 @@ 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,
pip_local_dependencies,
..
}: &Envs,
Envs { pip_extra_index_url, pip_index_url, pip_trusted_host, max_log_size, .. }: &Envs,
db: &Pool<Postgres>,
timeout: i32,
) -> error::Result<String> {
@@ -2147,15 +2109,6 @@ 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"];

View File

@@ -12,7 +12,7 @@ import hub from "./hub.ts";
import { tryResolveVersion } from "./context.ts";
import { GlobalOptions } from "./types.ts";
const VERSION = "v1.61.1";
const VERSION = "v1.60.0";
const command = new Command()
.name("wmill")

View File

@@ -1,140 +0,0 @@
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"]

View File

@@ -1,12 +1,12 @@
{
"name": "windmill",
"version": "1.61.1",
"version": "1.60.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "windmill",
"version": "1.61.1",
"version": "1.60.0",
"dependencies": {
"@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1",
@@ -32,7 +32,7 @@
"@playwright/test": "^1.29.2",
"@sveltejs/adapter-static": "^1.0.0",
"@sveltejs/kit": "^1.0.0-next.589",
"@sveltejs/package": "^1.0.2",
"@sveltejs/package": "^1.0.1",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/typography": "^0.5.8",
"@types/node": "^18.11.18",
@@ -71,7 +71,7 @@
"tailwindcss": "^3.2.2",
"tslib": "^2.4.1",
"typescript": "^4.9.3",
"vite": "^4.0.4"
"vite": "^4.0.3"
},
"peerDependencies": {
"svelte": "^3.53.1"
@@ -828,15 +828,15 @@
}
},
"node_modules/@sveltejs/package": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@sveltejs/package/-/package-1.0.2.tgz",
"integrity": "sha512-VY9U+05d9uNFDj7ScKRlHORYlfPSHwJewBjV+V2RsnViexpLFPUrboC9SiPYDCpLnbeqwXerxhO6twGHUBGeIA==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@sveltejs/package/-/package-1.0.1.tgz",
"integrity": "sha512-iYoDz4AEJQUfdKUfBcwtYEGYkf4NMByQL3Sl2ESnu+IXsLNsHvhH0zUDhAmUmAgcrH8fVjiR7FuJeyh+7EQtiw==",
"dev": true,
"dependencies": {
"chokidar": "^3.5.3",
"kleur": "^4.1.5",
"sade": "^1.8.1",
"svelte2tsx": "~0.6.0"
"svelte2tsx": "~0.5.20"
},
"bin": {
"svelte-package": "svelte-package.js"
@@ -848,20 +848,6 @@
"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",
@@ -6801,9 +6787,9 @@
}
},
"node_modules/vite": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.0.4.tgz",
"integrity": "sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.0.3.tgz",
"integrity": "sha512-HvuNv1RdE7deIfQb8mPk51UKjqptO/4RXZ5yXSAvurd5xOckwS/gg8h9Tky3uSbnjYTgUm0hVCet1cyhKd73ZA==",
"dev": true,
"dependencies": {
"esbuild": "^0.16.3",
@@ -7639,27 +7625,15 @@
}
},
"@sveltejs/package": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@sveltejs/package/-/package-1.0.2.tgz",
"integrity": "sha512-VY9U+05d9uNFDj7ScKRlHORYlfPSHwJewBjV+V2RsnViexpLFPUrboC9SiPYDCpLnbeqwXerxhO6twGHUBGeIA==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@sveltejs/package/-/package-1.0.1.tgz",
"integrity": "sha512-iYoDz4AEJQUfdKUfBcwtYEGYkf4NMByQL3Sl2ESnu+IXsLNsHvhH0zUDhAmUmAgcrH8fVjiR7FuJeyh+7EQtiw==",
"dev": true,
"requires": {
"chokidar": "^3.5.3",
"kleur": "^4.1.5",
"sade": "^1.8.1",
"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"
}
}
"svelte2tsx": "~0.5.20"
}
},
"@sveltejs/vite-plugin-svelte": {
@@ -11937,9 +11911,9 @@
"optional": true
},
"vite": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.0.4.tgz",
"integrity": "sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.0.3.tgz",
"integrity": "sha512-HvuNv1RdE7deIfQb8mPk51UKjqptO/4RXZ5yXSAvurd5xOckwS/gg8h9Tky3uSbnjYTgUm0hVCet1cyhKd73ZA==",
"dev": true,
"requires": {
"esbuild": "^0.16.3",

View File

@@ -1,6 +1,6 @@
{
"name": "windmill",
"version": "1.61.1",
"version": "1.60.0",
"scripts": {
"dev": "vite dev",
"build": "vite build",
@@ -18,7 +18,7 @@
"@playwright/test": "^1.29.2",
"@sveltejs/adapter-static": "^1.0.0",
"@sveltejs/kit": "^1.0.0-next.589",
"@sveltejs/package": "^1.0.2",
"@sveltejs/package": "^1.0.1",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/typography": "^0.5.8",
"@types/node": "^18.11.18",
@@ -57,7 +57,7 @@
"tailwindcss": "^3.2.2",
"tslib": "^2.4.1",
"typescript": "^4.9.3",
"vite": "^4.0.4"
"vite": "^4.0.3"
},
"type": "module",
"dependencies": {

View File

@@ -19,7 +19,7 @@ export interface SchemaProperty {
}
export type Schema = {
$schema: string | undefined
$schema: string
type: string
properties: { [name: string]: SchemaProperty }
required: string[]

View File

@@ -4,7 +4,6 @@
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 = {}
@@ -13,10 +12,7 @@
let schema = emptySchema()
let notFound = false
async function loadSchema() {
rawCode = '{}'
viewJsonSchema = false
try {
const rt = await ResourceService.getResourceType({
workspace: $workspaceStore!,
@@ -25,6 +21,8 @@
schema = rt.schema
notFound = false
} catch (e) {
rawCode = '{}'
notFound = true
}
}
@@ -45,40 +43,17 @@
}
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}
<div class="w-full flex flex-row-reverse">
<Toggle
on:change={(e) => switchTab(e.detail)}
options={{
right: 'As JSON'
}}
/>
</div>
{:else}
{#if notFound}
<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 noDelete {password} isValid {schema} bind:args />
<SchemaForm {password} isValid {schema} bind:args />
{/if}

View File

@@ -155,7 +155,6 @@
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('+'))
@@ -411,17 +410,15 @@
</ol>
</div>
{#if 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 class="mt-4 w-full">
<img class="max-h-96 m-auto" alt="connect" src={apiTokenApps[resource_type].img} />
</div>
{/if}
{/if}
<h2 class="mt-4">Value</h2>
<div class="mt-4">
{#key resource_type}
<ApiConnectForm password={key ?? ''} {resource_type} bind:args bind:isValid />
{/key}
<ApiConnectForm password={key ?? ''} {resource_type} bind:args bind:isValid />
</div>
<h2 class="mt-4 mb-2">Description</h2>

View File

@@ -7,13 +7,11 @@
import autosize from 'svelte-autosize'
export let result: any
export let requireHtmlApproval = false
let resultKind:
| 'json'
| 'table-col'
| 'table-row'
| 'html'
| 'png'
| 'file'
| 'jpeg'
@@ -21,11 +19,9 @@
| 'error'
| 'approval'
| undefined
$: resultKind = inferResultKind(result)
let forceJson = false
let enableHtml = false
function isRectangularArray(obj: any) {
if (!Array.isArray(obj) || obj.length == 0) {
@@ -57,8 +53,6 @@
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') {
@@ -125,34 +119,6 @@
</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}" />

View File

@@ -60,11 +60,7 @@
}
if (isCodeInjection(rawValue)) {
arg.expr = getDefaultExpr(
argName,
previousModuleId,
`\`${rawValue.toString().replaceAll('`', '\\`')}\``
)
arg.expr = getDefaultExpr(argName, previousModuleId, `\`${rawValue}\``)
arg.type = 'javascript'
propertyType = 'static'
} else {
@@ -121,13 +117,9 @@
const openBracket = '${'
const closeBracket = '}'
function setDefaultCode() {
if (!arg?.value) {
monacoTemplate?.setCode(schema.properties[argName].default)
}
}
$: schema.properties[argName].default && setDefaultCode()
$: schema.properties[argName].default &&
!arg?.value &&
monacoTemplate?.setCode(schema.properties[argName].default)
</script>
{#if arg != undefined}
@@ -165,7 +157,7 @@
argName,
previousModuleId,
staticTemplate
? `\`${arg?.value.toString().replaceAll('`', '\\`') ?? ''}\``
? `\`${arg?.value ?? ''}\``
: arg.value
? JSON.stringify(arg?.value, null, 4)
: ''
@@ -180,7 +172,6 @@
if (staticTemplate) {
if (arg) {
arg.value = codeToStaticTemplate(arg.expr)
arg.expr = undefined
}
setPropertyType(arg?.value)
} else {

View File

@@ -13,7 +13,6 @@
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 = ''
@@ -39,7 +38,6 @@
path = p
resourceToEdit = undefined
resourceSchema = undefined
viewJsonSchema = false
loadingSchema = true
drawer.openDrawer?.()
resourceToEdit = await ResourceService.getResource({ workspace: $workspaceStore!, path: p })
@@ -113,16 +111,6 @@
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">
@@ -148,21 +136,12 @@
kind="resource"
/>
</div>
<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>
<h3 class="mt-4">Value</h3>
<div class="text-sm">
{#if loadingSchema}
<Skeleton layout={[[4]]} />
{:else if !viewJsonSchema && resourceSchema && resourceSchema?.properties}
{:else if resourceSchema && resourceSchema?.properties}
<SchemaForm
noDelete
disabled={!can_write}
compact
schema={resourceSchema}
@@ -172,13 +151,10 @@
{:else if !can_write}
<input type="text" disabled value={rawCode} />
{:else}
{#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}
<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 !emptyString(jsonError)}<span
class="text-red-400 text-xs mb-1 flex flex-row-reverse">{jsonError}</span
>{:else}<div class="py-2" />{/if}

View File

@@ -30,7 +30,6 @@
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 }
@@ -60,9 +59,7 @@
let lkeys = Object.keys(schema?.properties ?? {})
if (schema?.properties && JSON.stringify(lkeys) != JSON.stringify(keys)) {
keys = lkeys
if (!noDelete) {
removeExtraKey()
}
removeExtraKey()
}
}
</script>

View File

@@ -1,14 +1,11 @@
<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']
@@ -19,6 +16,6 @@
Results
</div>
<div class="p-2">
<DisplayResult {result} {requireHtmlApproval} />
<DisplayResult {result} />
</div>
</RunnableWrapper>

View File

@@ -1,9 +1,11 @@
<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']

View File

@@ -1,52 +0,0 @@
<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>

View File

@@ -1,7 +1,9 @@
<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'

View File

@@ -1,36 +0,0 @@
<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}

View File

@@ -1,18 +0,0 @@
<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'}
/>

View File

@@ -39,7 +39,6 @@
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)
@@ -126,7 +125,7 @@
{#if !$userStore?.operator}
<UnsavedConfirmationModal />
{#if initialMode !== 'preview'}
<AppEditorHeader {policy} {fromHub} />
<AppEditorHeader {policy} />
{/if}
{#if previewing}

View File

@@ -35,7 +35,6 @@
Loader2,
MoreVertical,
Pencil,
SlidersHorizontal,
Smartphone
} from 'lucide-svelte'
import { getContext } from 'svelte'
@@ -52,7 +51,6 @@
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) {
@@ -64,7 +62,6 @@
}
export let policy: Policy
export let fromHub: boolean = false
const {
app,
@@ -96,12 +93,12 @@
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
@@ -251,12 +248,6 @@
</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">
@@ -488,14 +479,6 @@
>
<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)}

View File

@@ -1,56 +0,0 @@
<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}

View File

@@ -21,7 +21,6 @@
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
@@ -87,12 +86,6 @@
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}

View File

@@ -39,8 +39,7 @@
'piechartcomponent',
'displaycomponent',
'scatterchartcomponent',
'vegalitecomponent',
'plotlycomponent'
'vegalitecomponent'
],
'3:10-6:10': ['tablecomponent']
}
@@ -56,10 +55,7 @@
function addComponent(appComponent: AppComponent) {
$dirtyStore = true
const grid = $app.grid ?? []
const id = getNextId(
grid.map((gridItem) => gridItem.data.id),
true
)
const id = getNextId(grid.map((gridItem) => gridItem.data.id))
appComponent.id = id

View File

@@ -328,28 +328,6 @@ 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',

View File

@@ -4,7 +4,7 @@
import { Script, type Preview } from '$lib/gen'
import { inferArgs } from '$lib/infer'
import { initialCode } from '$lib/script_helpers'
import { capitalize, emptySchema } from '$lib/utils'
import { 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={capitalize(lang)}
label={lang}
{lang}
on:click={() => {
createInlineScriptByLanguage(lang, name)

View File

@@ -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, Maximize2, Trash2, X } from 'lucide-svelte'
import { CheckCircle, Code2, X } from 'lucide-svelte'
import InlineScriptEditorDrawer from './InlineScriptEditorDrawer.svelte'
import { inferArgs } from '$lib/infer'
import type { Schema } from '$lib/common'
@@ -13,7 +13,6 @@
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
@@ -58,51 +57,21 @@
<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-2 px-2 pt-1 flex-row items-center">
<div class="flex justify-between w-full gap-1 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" baseClass="!p-0 !h-[30px] aspect-square center-center">
<Badge color="green">
<CheckCircle size={16} />
</Badge>
{:else}
<Badge color="red" baseClass="!p-0 !h-[30px] aspect-square center-center">
<Badge color="red">
<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"
@@ -111,8 +80,18 @@
editor.format()
}}
>
Format&nbsp;<Tooltip placement="bottom">Ctrl+S</Tooltip>
Format&nbsp;<Tooltip>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}
@@ -125,9 +104,22 @@
}}
>
Run&nbsp;
<Tooltip light placement="bottom">Ctrl+Enter</Tooltip>
<Tooltip light>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>

View File

@@ -1,5 +1,7 @@
<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'
@@ -7,11 +9,8 @@
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
@@ -22,20 +21,13 @@
$: runnables = getAppScripts($lazyGrid)
// When selected component changes, update selectedScriptComponentId
$: if (selectedComponent) {
selectedScriptComponentId = $selectedComponent
// When seleced 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}`
@@ -57,27 +49,28 @@
fields: {}
})
$app.hiddenInlineScripts = $app.hiddenInlineScripts
selectInlineScript(`bg_${$app.hiddenInlineScripts.length - 1}`)
}
</script>
<div bind:this={list} class="min-h-full flex flex-col gap-4">
<div 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)}
<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'}"
<!-- 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' : ''
)},"
on:click={() => selectInlineScript(id)}
>
<span class="text-xs truncate">{name}</span>
<div>
<Badge color="dark-indigo">{id}</Badge>
</div>
</button>
</div>
{/each}
</div>
{/if}
@@ -85,16 +78,17 @@
{#if $app.unusedInlineScripts?.length > 0}
<div class="flex gap-2 flex-col ">
{#each $app.unusedInlineScripts as unusedInlineScript, index (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)}
<!-- 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}`)}
>
<span class="text-xs truncate">{unusedInlineScript.name}</span>
<Badge color="red">Detached</Badge>
</button>
</div>
{/each}
</div>
{/if}
@@ -110,15 +104,17 @@
{#if runnables.imported.length > 0}
<div class="flex gap-2 flex-col ">
{#each runnables.imported as { name, id }, index (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'}"
<!-- 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' : ''
)},"
on:click={() => selectInlineScript(id)}
>
<span class="text-xs truncate">{name}</span>
<Badge color="dark-indigo">{id}</Badge>
</button>
</div>
{/each}
</div>
{:else}
@@ -127,31 +123,31 @@
</div>
</PanelSection>
<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
>
<PanelSection title="Background scripts" 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)}
{@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)}
<!-- 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}`)}
>
<span class="text-xs truncate">{name}</span>
<Badge color="yellow">Background</Badge>
</button>
</div>
{/each}
</div>
{:else}

View File

@@ -14,7 +14,6 @@
export let userInputEnabled: boolean = true
export let shouldCapitalize: boolean = true
export let rowColumns = false
export let resourceOnly = false
const { connectingInput } = getContext<AppEditorContext>('AppEditorContext')
</script>
@@ -23,99 +22,97 @@
<div class="w-full flex flex-col gap-4">
{#each Object.keys(inputSpecs) as inputSpecKey (inputSpecKey)}
{@const input = inputSpecs[inputSpecKey]}
{#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 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="left"
value="static"
startIcon={{ icon: faPen }}
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">Static</svelte:fragment>
<svelte:fragment slot="text">User Input</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 }}
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>
{/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>
<InputsSpecEditor
hasRows={rowColumns}
{id}
bind:componentInput={inputSpecs[inputSpecKey]}
/>
</div>
{/if}
<InputsSpecEditor
hasRows={rowColumns}
{id}
bind:componentInput={inputSpecs[inputSpecKey]}
/>
</div>
{/each}
</div>
{:else}

View File

@@ -147,39 +147,6 @@ 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: {

View File

@@ -25,7 +25,6 @@ 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
@@ -108,7 +107,6 @@ export type AppComponent = BaseAppComponent &
| FormComponent
| FormButtonComponent
| VegaLiteComponent
| PlotlyComponent
)
export type ComponentSet = {
@@ -181,6 +179,4 @@ export type AppEditorContext = {
export type EditorMode = 'dnd' | 'preview'
export type EditorBreakpoint = 'sm' | 'lg'
export const IS_APP_PUBLIC_CONTEXT_KEY = 'isAppPublicContext' as const
type ComponentID = string

View File

@@ -122,10 +122,6 @@ 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

View File

@@ -68,7 +68,7 @@
colorVariants?.[color]?.[variant],
variant === 'border' ? 'border' : '',
ButtonType.FontSizeClasses[size],
ButtonType.SpacingClasses[spacingSize][variant],
ButtonType.SpacingClasses[spacingSize],
'focus:ring-2 font-semibold',
'duration-200 rounded-md',
'justify-center items-center text-center whitespace-nowrap inline-flex',
@@ -79,8 +79,7 @@
target,
tabindex: disabled ? -1 : 0,
type: buttonType,
title,
...$$restProps
title
}
async function onClick(event: MouseEvent) {

View File

@@ -20,27 +20,12 @@ export namespace ButtonType {
xl: 'text-xl'
} as const
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]'
},
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'
} as const
export const IconScale: Record<ButtonType.Size, number> = {

View File

@@ -55,10 +55,8 @@
if (open) {
switch (event.key) {
case 'Escape':
event.preventDefault()
event.stopPropagation()
event.stopImmediatePropagation()
open = false
event.preventDefault()
break
}
}

View File

@@ -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 { importFlowStore } from '$lib/components/flows/flowStore'
import { flowStore, initFlow } from '$lib/components/flows/flowStore'
import { Icon } from 'svelte-awesome'
let drawer: Drawer | undefined = undefined
@@ -14,7 +14,9 @@
async function importJson() {
await goto('/flows/add')
$importFlowStore = JSON.parse(pendingJson)
Object.assign($flowStore, JSON.parse(pendingJson))
initFlow($flowStore)
drawer?.closeDrawer?.()
}
</script>

View File

@@ -35,8 +35,9 @@
<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 both static and exponential delay is defined, the static delay attempts
are tried before the exponential ones.</Tooltip
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
></h2
>
@@ -75,25 +76,22 @@
}
}}
options={{
right: 'Exponential backoff enabled'
right: 'Exponential retry 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">Base</span>
<span class="text-xs font-bold">Initial delay (seconds)</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">Base</span>
<span class="text-xs font-bold">Initial delay (seconds)</span>
<input type="number" disabled />
{/if}
</div>

View File

@@ -32,7 +32,7 @@ export async function loadFlowModuleState(flowModule: FlowModule): Promise<FlowM
export const idMutex = new Mutex()
export function getNextId(currentKeys: string[], skipTilde: boolean = false): string {
export function getNextId(currentKeys: string[]): string {
const max = currentKeys.reduce((acc, key) => {
if (key === 'failure' || key.includes('branch') || key.includes('loop')) {
return acc

View File

@@ -6,7 +6,6 @@ import { numberToChars } from './utils'
export type FlowMode = 'push' | 'pull'
export const importFlowStore = writable<Flow | undefined>(undefined)
export const flowStore = writable<Flow>({
summary: '',
value: { modules: [] },
@@ -54,6 +53,7 @@ export async function initFlow(flow: Flow) {
}
await initFlowState(flow)
flowStore.set(flow)
function migrateFlowModule(mod: FlowModule) {

View File

@@ -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).replaceAll('\\`', '`')
return line.slice(1, line.length - 1)
}
}
return undefined
@@ -179,10 +179,7 @@ export function emptyFlowModuleState(): FlowModuleState {
const aCharCode = 'a'.charCodeAt(0)
export function numberToChars(n: number, skipTilde: boolean = false) {
if (n < 0) {
return "-" + numberToChars(-n, skipTilde)
}
export function numberToChars(n: number) {
var b = [n],
sp,
out,
@@ -190,12 +187,11 @@ export function numberToChars(n: number, skipTilde: boolean = false) {
div
sp = 0
const basisInc = skipTilde ? -1 : 0
while (sp < b.length) {
if (b[sp] > (25 + basisInc)) {
div = Math.floor(b[sp] / (27 + basisInc))
if (b[sp] > 25) {
div = Math.floor(b[sp] / 27)
b[sp + 1] = div - 1
b[sp] %= (27 + basisInc)
b[sp] %= 27
}
sp += 1
}
@@ -203,20 +199,17 @@ export function numberToChars(n: number, skipTilde: boolean = false) {
out = ''
for (i = 0; i < b.length; i += 1) {
let charCode = aCharCode + b[i]
out = (b[i] == 26 ? "-" : String.fromCharCode(charCode)) + out
out = (charCode == 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 == '-' || c == '_') ? aCharCode + 26 : c.charCodeAt(0)
let charCode = c == '-' ? aCharCode + 26 : c.charCodeAt(0)
res += (charCode - aCharCode + 1) * b
b = b / 27
}

View File

@@ -123,7 +123,7 @@
return flowModuleToBranch(
module,
branches,
['Default', ...module.value.branches.map((x) => `${truncateRev(x.expr, 20)}`)],
['Default', ...module.value.branches.map((x) => `If ${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, j) => {
modules.forEach((module) => {
const item = getConvertedFlowModule(
module,
items.length ? items : numberToChars(branch.node.id),
j == 0 ? edgesLabel[i] : undefined,
edgesLabel[i],
insideLoop
)
item && items.push(item)
@@ -434,9 +434,8 @@
edgesLabel?: string,
offset?: number
): Node {
const id = -idGenerator.next().value - 1 + (offset ?? 0)
return {
id,
id: -idGenerator.next().value - 1 + (offset ?? 0),
position: { x: -1, y: -1 },
data: {
html: `

View File

@@ -30,10 +30,8 @@
<table class="w-full">
{#each $userWorkspaces as workspace}
<tr
class="text-xs
{$workspaceStore === workspace.id ? 'cursor-default bg-blue-50' : 'cursor-pointer hover:bg-gray-100'}"
class="text-xs cursor-pointer hover:bg-gray-100"
on:click={() => {
if($workspaceStore === workspace.id) { return }
switchWorkspace(workspace.id)
close()
}}

View File

@@ -151,7 +151,7 @@ export interface DropdownItem {
export function emptySchema() {
return {
$schema: 'https://json-schema.org/draft/2020-12/schema' as string | undefined,
$schema: 'https://json-schema.org/draft/2020-12/schema',
properties: {},
required: [],
type: 'object'

View File

@@ -74,7 +74,6 @@
on_behalf_of_email: $userStore?.email,
execution_mode: Policy.execution_mode.PUBLISHER
}}
fromHub={hubId != null}
/>
{/key}
</div>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import AppEditor from '$lib/components/apps/editor/AppEditor.svelte'
import { AppService, AppWithLastVersion } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import { userStore, workspaceStore } from '$lib/stores'
import { page } from '$app/stores'
import { decodeState, sendUserToast } from '$lib/utils'
import { goto } from '$app/navigation'

View File

@@ -4,7 +4,7 @@
import { dirtyStore } from '$lib/components/common/confirmationModal/dirtyStore'
import FlowBuilder from '$lib/components/FlowBuilder.svelte'
import { importFlowStore, initFlow } from '$lib/components/flows/flowStore'
import { initFlow } from '$lib/components/flows/flowStore'
import { FlowService, type Flow } from '$lib/gen'
import { userStore, workspaceStore } from '$lib/stores'
import { decodeState, emptySchema, sendUserToast } from '$lib/utils'
@@ -36,11 +36,7 @@
}
let state = initialState ? decodeState(initialState) : undefined
if ($importFlowStore) {
flow = $importFlowStore
$importFlowStore = undefined
sendUserToast('Flow loaded from JSON')
} else if (!templatePath && !hubId && state) {
if (!templatePath && !hubId && state) {
sendUserToast('Flow restored from draft')
flow = state.flow
state?.selectedId && (selectedId = state?.selectedId)

View File

@@ -22,8 +22,21 @@
let selectedId: string = 'settings-graph'
let flow: Flow = {
path: $page.params.path,
summary: '',
edited_by: '',
edited_at: '',
value: { modules: [] },
archived: false,
extra_perms: {},
schema: emptySchema()
}
let initialPath: string = ''
initFlow(flow)
async function loadFlow(): Promise<void> {
loading = true
let flow: Flow

View File

@@ -99,8 +99,7 @@
can_write = canWrite(flow.path, flow.extra_perms!, $userStore)
}
$: urlAsync = `${$page.url.hostname}/api/w/${$workspaceStore}/jobs/run/f/${flow?.path}`
$: urlSync = `${$page.url.hostname}/api/w/${$workspaceStore}/jobs/run_wait_result/f/${flow?.path}`
$: url = `${$page.url.hostname}/api/w/${$workspaceStore}/jobs/run/f/${flow?.path}`
let runForm: RunForm | undefined
let isValid = true
@@ -307,39 +306,21 @@
></h2
>
<div class="box max-w-2xl">
<div class="flex w-full flex-justify-between mb-1">
<div class="flex flex-row gap-x-2 w-full">
<a
on:click={(e) => {
e.preventDefault()
copyToClipboard(urlAsync)
copyToClipboard(url)
}}
href={$page.url.protocol + '//' + urlAsync}
class="whitespace-nowrap text-ellipsis overflow-hidden mr-1 w-full"
href={$page.url.protocol + '//' + url}
class="whitespace-nowrap text-ellipsis overflow-hidden mr-1"
>
{urlAsync}
{url}
<span class="text-gray-700 ml-2">
<Icon data={faClipboard} />
</span>
</a>
<Badge>UUID/Async</Badge>
</div>
<div class="mb-2 w-full flex flex-justify-between">
<a
on:click={(e) => {
e.preventDefault()
copyToClipboard(urlSync)
}}
href={$page.url.protocol + '//' + urlSync}
class="whitespace-nowrap text-ellipsis overflow-hidden mr-1 w-full"
>
{urlSync}
<span class="text-gray-700 ml-2">
<Icon data={faClipboard} />
</span>
</a>
<Badge>Result/Sync</Badge>
</div>
<div class="flex flex-row-reverse">
<Button size="xs" on:click={userSettings.openDrawer}>Create token</Button>
</div>
</div>

View File

@@ -40,9 +40,6 @@
import ListFilters from '$lib/components/home/ListFilters.svelte'
import SearchItems from '$lib/components/SearchItems.svelte'
import autosize from 'svelte-autosize'
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
import { convert } from '@redocly/json-to-json-schema'
import Portal from 'svelte-portal'
type ResourceW = ListableResource & { canWrite: boolean }
type ResourceTypeW = ResourceType & { canWrite: boolean }
@@ -203,47 +200,10 @@
}
})
function openInferrer(): void {
inferrer?.openDrawer?.()
}
let disableCustomPrefix = false
let tab: 'workspace' | 'types' | 'states' = 'workspace'
let inferrer: Drawer | undefined = undefined
let inferrerJson = ''
function inferJson(): void {
try {
newResourceType.schema = {
$schema: undefined,
required: [],
properties: {},
...convert(JSON.parse(inferrerJson))
}
} catch (e) {
sendUserToast(`Invalid JSON: ${e}`, true)
}
inferrer?.closeDrawer?.()
}
</script>
<Portal>
<Drawer bind:this={inferrer} size="800px">
<DrawerContent title="Import app from JSON" on:close={() => inferrer?.toggleDrawer?.()}>
<SimpleEditor
bind:code={inferrerJson}
lang="json"
class="h-full"
fixedOverflowWidgets={false}
/>
<svelte:fragment slot="actions">
<Button size="sm" on:click={inferJson}>Infer</Button>
</svelte:fragment>
</DrawerContent>
</Drawer>
</Portal>
<Drawer bind:this={resourceTypeViewer} size="800px">
<DrawerContent title={resourceTypeViewerObj.rt} on:close={resourceTypeViewer.closeDrawer}>
<div>
@@ -299,11 +259,6 @@
>
<div>
<div class="mb-1 font-semibold text-gray-700">Schema</div>
<div class="mb-2 w-full flex flex-row-reverse">
<Button on:click={openInferrer} size="sm" color="dark" variant="border"
>Infer schema from a json value</Button
>
</div>
<SchemaEditor bind:schema={newResourceType.schema} />
</div>
</div>

View File

@@ -445,8 +445,8 @@
</h3>
<Skeleton {loading} layout={[[8.5]]} />
<Tabs selected="uuid">
<Tab value="uuid">UUID/Async</Tab>
<Tab value="result">Result/Sync</Tab>
<Tab value="uuid">UUID</Tab>
<Tab value="result">Result</Tab>
<svelte:fragment slot="content">
{#each Object.keys(webhooks) as key}
<TabContent value={key}>

View File

@@ -7,15 +7,9 @@
Script
} from '$lib/gen'
import type { User } from '$lib/gen'
import { sendUserToast, isCloudHosted, capitalize } from '$lib/utils'
import { sendUserToast, isCloudHosted } from '$lib/utils'
import PageHeader from '$lib/components/PageHeader.svelte'
import {
superadmin,
usageStore,
userStore,
usersWorkspaceStore,
workspaceStore
} from '$lib/stores'
import { superadmin, userStore, usersWorkspaceStore, workspaceStore } from '$lib/stores'
import CenteredPage from '$lib/components/CenteredPage.svelte'
import { faSlack } from '@fortawesome/free-brands-svg-icons'
import TableCustom from '$lib/components/TableCustom.svelte'
@@ -47,9 +41,14 @@
let nbDisplayed = 30
let plan: string | undefined = undefined
let customer_id: string | undefined = undefined
let webhook: string | undefined = undefined
let tab =
($page.url.searchParams.get('tab') as 'users' | 'slack' | 'premium' | 'export_delete') ??
'users'
($page.url.searchParams.get('tab') as
| 'users'
| 'slack'
| 'premium'
| 'export_delete'
| 'webhook') ?? 'users'
// function getDropDownItems(username: string): DropdownItem[] {
// return [
@@ -84,6 +83,14 @@
sendUserToast(`slack command script set to ${scriptPath}`)
}
async function editWebhook(): Promise<void> {
await WorkspaceService.editWebhook({
workspace: $workspaceStore!,
requestBody: { webhook }
})
sendUserToast(`webhook set to ${webhook}`)
}
async function loadSettings(): Promise<void> {
const settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! })
team_name = settings.slack_name
@@ -93,6 +100,7 @@
plan = settings.plan
customer_id = settings.customer_id
initialPath = scriptPath
webhook = settings.webhook
}
async function listUsers(): Promise<void> {
@@ -184,24 +192,25 @@
{#if $userStore?.is_admin || $superadmin}
<PageHeader title="Workspace Settings of {$workspaceStore}" />
<div class="overflow-x-auto scrollbar-hidden">
<Tabs bind:selected={tab}>
<Tab size="md" value="users">
<div class="flex gap-2 items-center my-1"> Users & Invites </div>
<Tabs bind:selected={tab}>
<Tab size="md" value="users">
<div class="flex gap-2 items-center my-1"> Users & Invites </div>
</Tab>
<Tab size="md" value="slack">
<div class="flex gap-2 items-center my-1"> Slack Command </div>
</Tab>
{#if isCloudHosted()}
<Tab size="md" value="premium">
<div class="flex gap-2 items-center my-1"> Premium Plans </div>
</Tab>
<Tab size="md" value="slack">
<div class="flex gap-2 items-center my-1"> Slack Command </div>
</Tab>
{#if isCloudHosted()}
<Tab size="md" value="premium">
<div class="flex gap-2 items-center my-1"> Premium Plans </div>
</Tab>
{/if}
<Tab size="md" value="export_delete">
<div class="flex gap-2 items-center my-1"> Export & Delete Workspace </div>
</Tab>
</Tabs>
</div>
{/if}
<Tab size="md" value="export_delete">
<div class="flex gap-2 items-center my-1"> Export & Delete Workspace </div>
</Tab>
<Tab size="md" value="webhook">
<div class="flex gap-2 items-center my-1">Webhook for CLI Sync</div>
</Tab>
</Tabs>
{#if tab == 'users'}
<PageHeader title="Members ({users?.length ?? ''})" primary={false} />
@@ -244,7 +253,7 @@
<ToggleButtonGroup
selected={is_admin ? 'admin' : operator ? 'operator' : 'author'}
on:selected={async (e) => {
if (is_admin && email == $userStore?.email && e.detail != 'admin') {
if (is_admin && e.detail != 'admin') {
sendUserToast(
'Admins cannot be demoted by themselves, ask another admin to demote you',
true
@@ -433,7 +442,7 @@
{#if isCloudHosted()}
<div class="mt-4" />
{#if customer_id}
<div class="mt-2 mb-2">
<div class="mt-2 mb-6">
<Button
endIcon={{ icon: faExternalLink }}
href="/api/w/{$workspaceStore}/workspaces/billing_portal">Customer Portal</Button
@@ -444,58 +453,33 @@
</div>
{/if}
<div class="text-sm mb-4 box p-2 max-w-3xl">
<div class="text-sm mb-4">
{#if premium_info?.premium}
<div class="flex flex-col gap-0.5">
<div class="flex flex-col gap-1">
<div
>Current plan: <div class=" inline text-2xl font-bold">{plan ?? 'Free plan'}</div
></div
>
{#if plan}
<div class="mb-2"
><div class=" inline text-2xl font-bold float-right"
>{capitalize(plan ?? 'free')} plan</div
></div
>
{:else}
<div class="inline text-2xl font-bold ">Free plan</div>
{/if}
{#if plan}
{@const team_factor = plan == 'team' ? 8 : 32}
{@const user_nb = Math.max(0, (users?.filter((x) => !x.operator).length ?? 0) - 1)}
{@const team_factor = plan == 'team' ? 10 : 50}
{@const max = (users?.length ?? 0) * team_factor}
<div>
Extra seats:
<div class="inline text-2xl font-bold float-right"
>{user_nb} * ${team_factor}
= ${(users?.length ?? 0) * team_factor}/mo</div
Current number of seats in this workspace:
<div class="inline text-2xl font-bold"
>{users?.length ?? 0} * ${team_factor} = ${(users?.length ?? 0) *
team_factor}/mo</div
>
<Tooltip
>Actual pricing is calculated on the MAXIMUM number of users in a given billing
period, see the customer portal for more info.</Tooltip
>
</div>
<div>
Extra operators:
<div class="inline text-2xl font-bold float-right"
>{Math.max(0, (users?.filter((x) => x.operator).length ?? 0) - 1)} * ${team_factor /
2} = ${((users?.length ?? 0) * team_factor) / 2}/mo</div
>
<Tooltip
>Actual pricing is calculated on the MAXIMUM number of users in a given billing
period, see the customer portal for more info.</Tooltip
>
</div>
<div>
Computations executed this month:
<div class=" inline text-2xl font-bold float-right"
>{premium_info?.usage ?? 0}
</div>
</div>
<div>
Extra computations (above the 10k included) this month:
<div class=" inline text-2xl font-bold float-right"
>{Math.max(0, (premium_info?.usage ?? 0) - 10000)} x {plan == 'team'
? '0.001'
: '0.004'} = ${Math.max(0, (premium_info?.usage ?? 0) - 10000) *
(plan == 'team' ? 0.001 : 0.004)}
Included number of executions based on your seats and plan:
<div class=" inline text-2xl font-bold"
>{users?.length ?? 0} seats x {plan == 'team' ? '10k' : '50k'} = {max}k
</div>
</div>
{/if}
@@ -539,8 +523,8 @@
{#if planTitle == 'Team'}
{#if plan != 'team'}
<div class="mt-4 mx-auto">
<Button href="/api/w/{$workspaceStore}/workspaces/checkout?plan=team"
>Upgrade to the Team plan</Button
<Button disabled href="/api/w/{$workspaceStore}/workspaces/checkout?plan=team"
>Upgrade to the Team plan (Coming soon)</Button
>
</div>
{:else}
@@ -685,6 +669,29 @@
</Button>
{/if}
</div>
{:else if tab == 'webhook'}
<PageHeader title="Webhook for CLI Sync" primary={false} />
<div class="mt-2"
><Alert type="info" title="Send events to an external service"
>Connect your windmill workspace to an external service to sync or get notified about any
changes</Alert
></div
>
<h3 class="mt-5 text-gray-700"
>URL to send requests to<Tooltip>
This URL will be POSTed to with a JSON body depending on the type of event. The type is
indicated by the <pre>type</pre> field. The other fields are dependent on the type.
</Tooltip>
</h3>
<div class="flex gap-2">
<input class="justify-start" type="text" bind:value={webhook} />
<Button color="blue" btnClasses="justify-end" size="md" on:click={editWebhook}
>Set Webhook</Button
>
</div>
{/if}
{:else}
<div class="bg-red-100 border-l-4 border-red-600 text-orange-700 p-4 m-4" role="alert">

View File

@@ -19,7 +19,6 @@
let interval = setInterval(async () => {
attempt += 1
if ((await WorkspaceService.getSettings({ workspace: $workspaceStore! })).customer_id) {
clearInterval(interval)
goto('/workspace_settings?tab=premium')
} else if (attempt > 10) {
sendUserToast('Subscription upgrade failed. Contact contact@windmill.dev', true)
@@ -42,9 +41,7 @@
You will be redirected to the workspace settings page in 5 seconds...
</p>
{:else}
<p class="text-sm my-6 text-gray-600 w-full text-center">
Waiting for your upgrade to be processed...
</p>
<p class="text-sm my-6 text-gray-600"> Waiting for your upgrade to be processed... </p>
{/if}
<div class="block m-auto w-20">

View File

@@ -2,13 +2,12 @@
import { browser } from '$app/environment'
import { page } from '$app/stores'
import AppPreview from '$lib/components/apps/editor/AppPreview.svelte'
import { IS_APP_PUBLIC_CONTEXT_KEY, type EditorBreakpoint } from '$lib/components/apps/types'
import type { EditorBreakpoint } from '$lib/components/apps/types'
import { Alert, Skeleton } from '$lib/components/common'
import { WindmillIcon } from '$lib/components/icons'
import { AppService, AppWithLastVersion, GlobalUserInfo, UserService } from '$lib/gen'
import { userStore } from '$lib/stores'
import { setContext } from 'svelte'
import github from 'svelte-highlight/styles/github'
import { writable } from 'svelte/store'
@@ -16,8 +15,6 @@
let user: GlobalUserInfo | undefined = undefined
let notExists = false
setContext(IS_APP_PUBLIC_CONTEXT_KEY, true)
async function loadApp() {
try {
app = await AppService.getPublicAppBySecret({

View File

@@ -4,8 +4,8 @@ verify_ssl = true
name = "pypi"
[packages]
wmill = ">=1.61.1"
wmill_pg = ">=1.61.1"
wmill = ">=1.60.0"
wmill_pg = ">=1.60.0"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.61.1
version: 1.60.0
title: OpenFlow Spec
contact:
name: Ruben Fiszel

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
version = "1.61.1"
version = "1.60.0"
description = "A client library for accessing Windmill server wrapping the Windmill client API"
license = "Apache-2.0"
homepage = "https://windmill.dev"
@@ -16,7 +16,7 @@ include = ["wmill/py.typed"]
[tool.poetry.dependencies]
python = "^3.7"
windmill-api = "^1.61.1"
windmill-api = "^1.60.0"
[build-system]
requires = ["poetry>=1.0.2", "poetry-dynamic-versioning"]

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill-pg"
version = "1.61.1"
version = "1.60.0"
description = "An extension client for the wmill client library focused on pg"
license = "Apache-2.0"
homepage = "https://windmill.dev"

View File

@@ -1 +1 @@
1.61.1
1.60.0