Compare commits

...

158 Commits

Author SHA1 Message Date
Ruben Fiszel
0ca4ec9d32 all 2023-12-12 18:13:48 +01:00
Ruben Fiszel
88149c6d93 autoset base_url if empty 2023-12-11 00:27:59 +01:00
Ruben Fiszel
f43e31b967 make currency input more resilient 2023-12-10 21:21:50 +01:00
Ruben Fiszel
cc805a06b5 chore(main): release 1.224.0 (#2825)
* chore(main): release 1.224.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-12-10 13:53:39 +01:00
Ruben Fiszel
58c4c0b5c2 fix: make cleanup_module backcompatible 2023-12-10 13:29:51 +01:00
Ruben Fiszel
4f4ca264ae feat: embedded server only get killed after last job of worker 2023-12-10 10:51:32 +01:00
Amir Zarrinkafsh
6fefdb5438 feat: add authelia sso support (#2824)
This change adds the ability to integrate Windmill with Authelia via OIDC.
2023-12-09 14:50:29 +01:00
Ruben Fiszel
9a79850fd4 chore(main): release 1.223.1 (#2821)
* chore(main): release 1.223.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-12-09 05:35:25 +01:00
Stephan Fitzpatrick
b2f705d01e Updated Windmill client constructor to accept workspace (#2822)
This commit introduces a change in the Windmill class constructor (in wmill/client.py) to include 'workspace' as a parameter. This addition is intended to make "workspace" universally configurable either via an argument or the 'WM_WORKSPACE' environment variable. If not provided, the program will now assert a missing workspace with an informative error message.
2023-12-09 00:22:41 +01:00
Ruben Fiszel
1fdc55c816 add selectFirstRowByDefault for tables 2023-12-08 23:57:52 +01:00
Ruben Fiszel
651d99824d fix: improve aggrid selectedRow output + make first selectedRow configurable 2023-12-08 23:42:08 +01:00
Ruben Fiszel
b85103e3b5 chore(main): release 1.223.0 (#2807) 2023-12-08 21:33:02 +01:00
Guillaume Bouvignies
b65657d0f8 feat: Add possibility to delete flow step results when the flow is complete (#2806)
* feat: Add possibility to delete flow step results when the flow is complete

* Add third layer of tabs and gate feature to EE
2023-12-08 21:30:44 +01:00
Guillaume Bouvignies
b0894492f1 fix: Fix test_complex_flow_restart (#2813) 2023-12-08 19:15:08 +01:00
Guillaume Bouvignies
8c4faa05fa chore: Update git sync scripts (#2819) 2023-12-08 18:46:39 +01:00
Guillaume Bouvignies
535202c1fe feat: Add a git sync test button (#2817) 2023-12-08 18:20:23 +01:00
Guillaume Bouvignies
993812319f chore: Updating git sync info (#2818) 2023-12-08 18:20:07 +01:00
HugoCasa
2964569962 fix: deno chromium support when nsjail enabled (#2815) 2023-12-08 18:18:20 +01:00
Henri Courdent
0ce857c4a8 Git sync frontend improvements & tooltip (#2816) 2023-12-08 17:57:47 +01:00
Ruben Fiszel
033000fed3 fix: improve sql default arg parsing + auto invite 2023-12-08 16:37:11 +01:00
Guillaume Bouvignies
4bce61f2b6 chore: bump Go to 1.21.5 (#2814) 2023-12-08 16:27:16 +01:00
HugoCasa
a9b5d6bebb feat: sql types autocomplete (#2810)
* feat: sql types autocomplete

* fix: frontend check
2023-12-08 15:20:33 +01:00
Ruben Fiszel
009a83dd9b fix: support interval in pg 2023-12-08 11:47:41 +01:00
HugoCasa
e7141c147d feat: no worker with tag warning (#2803)
* feat: no worker with tag warning

* feat: better design and flow jobs

* fix: sqlx

* fix: clear timeout on destroy
2023-12-08 00:18:41 +01:00
HugoCasa
8896a8cacf feat: add mysql datetime (#2808)
* feat: mysql datetime support

* feat: nits
2023-12-08 00:14:08 +01:00
HugoCasa
e250897237 fix: db schema autocomplete (#2809) 2023-12-08 00:13:34 +01:00
Ruben Fiszel
d26730ad34 fix: conditional component also for toggles 2023-12-07 20:38:20 +01:00
Ruben Fiszel
7c64a36624 chore(main): release 1.222.0 (#2801)
* chore(main): release 1.222.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-12-07 19:04:46 +01:00
HugoCasa
c4a1054a5a feat: mysql named params (#2805) 2023-12-07 18:55:14 +01:00
Ruben Fiszel
a3eced0029 fix reload 2023-12-07 18:40:17 +01:00
Ruben Fiszel
d42ddcbe90 fix reload 2023-12-07 18:38:59 +01:00
Ruben Fiszel
eb8e75d519 fix reload 2023-12-07 18:36:39 +01:00
Ruben Fiszel
bb93bd8b89 fix reload 2023-12-07 18:32:00 +01:00
Ruben Fiszel
f7d7746327 fix: fix extraOptions for agGrid 2023-12-07 18:16:17 +01:00
Ruben Fiszel
2f7fb9bfd4 fix: fix extraOptions for agGrid 2023-12-07 17:58:36 +01:00
Ruben Fiszel
b10df30245 feat: conditional fields on forms 2023-12-07 17:58:22 +01:00
Faton Ramadani
e11b257b2d fix(frontend): Fix chartjs options merge (#2804) 2023-12-07 17:02:26 +01:00
Ruben Fiszel
c0ed1ace54 multiple small fixes 2023-12-07 15:40:14 +01:00
Guillaume Bouvignies
da15a56a07 fix: add saml feature flag (#2802) 2023-12-07 15:04:31 +01:00
HugoCasa
f7f71302f7 fix: app reports screenshot refresh (#2800) 2023-12-07 11:03:23 +01:00
Ruben Fiszel
5aedc6832d improve evalv2 fullscreen 2023-12-07 09:40:51 +01:00
Ruben Fiszel
4bda35cd35 update ag-grid 2023-12-07 09:34:59 +01:00
Ruben Fiszel
2386ba16e4 bump ag-grid to 31.0 2023-12-07 01:04:09 +01:00
Ruben Fiszel
b75e8a55ba chore(main): release 1.221.0 (#2786)
* chore(main): release 1.221.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-12-07 00:50:26 +01:00
Ruben Fiszel
d85555d3fb handle plain custom content type in display result 2023-12-07 00:49:59 +01:00
Ruben Fiszel
9c4f1e0333 fix: linked variable already being a variable 2023-12-07 00:41:51 +01:00
Henri Courdent
4cb7a121e6 Update architecture graph (#2795) 2023-12-07 00:24:29 +01:00
HugoCasa
4f6a116e46 fix: app reports ts (#2798) 2023-12-07 00:24:08 +01:00
Ruben Fiszel
c22afa3a8b improve flow status viewer 2023-12-07 00:17:48 +01:00
HugoCasa
c58190ef78 feat: limit pro plans (#2794) 2023-12-06 17:47:32 +01:00
Faton Ramadani
e4da819cda fix(frontend): fix storage key typo (#2796) 2023-12-06 17:10:55 +01:00
Guillaume Bouvignies
4de62a9019 chore: Update git sync script version (#2793) 2023-12-06 15:29:16 +01:00
Ruben Fiszel
2b36ffb9cf revert forloop selected details 2023-12-06 15:17:51 +01:00
Guillaume Bouvignies
256ee646b4 fix: Deno allow run git (#2791) 2023-12-06 15:00:45 +01:00
Faton Ramadani
90668fb0e5 fix(frontend): use correct id for selected for loop iteration (#2787) 2023-12-06 13:57:31 +01:00
Guillaume Bouvignies
d822beba63 fix: Allow run git for Deno (#2790) 2023-12-06 13:56:00 +01:00
Ruben Fiszel
abb50fac93 fix: smtp doesn't require username/password 2023-12-06 12:25:44 +01:00
Guillaume Bouvignies
8ad8d20136 fix: Allow run git for Deno (#2789) 2023-12-06 12:18:25 +01:00
HugoCasa
0b13f2d782 fix: app reports nits (#2788) 2023-12-06 11:45:38 +01:00
Ruben Fiszel
b5f53556a9 fix: improve conditionalwrapper selection 2023-12-05 22:29:55 +01:00
Ruben Fiszel
0333ce78bd chore(main): release 1.220.0 (#2744)
* chore(main): release 1.220.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-12-05 19:52:32 +01:00
Ruben Fiszel
042504f905 fix: add run_flow_sync to python client + few nits 2023-12-05 19:49:18 +01:00
Guillaume Bouvignies
d9458e96eb feat: CLI now accepts message arg for sync push (#2784) 2023-12-05 19:01:09 +01:00
HugoCasa
f36a510025 feat: app reports v2 (#2778)
* fix: schema autocomplete

* feat: app reports v2

* fix: remove global copilot api call

* feat: nits

* fix: backward compatible slack notif

* feat: nits

* feat: log browser logs

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2023-12-05 18:55:46 +01:00
Guillaume Bouvignies
874b7a50f7 feat: History commit message can be updated from the UI (#2777)
* feat: History comit message can be updated from the UI

* Script version history

* Display commit message in left column

* Fix FE compile break

* sqlx prepare and fix openapi file
2023-12-05 15:45:36 +01:00
Guillaume Bouvignies
20b0500a28 fix: Array of S3 files now shows links to the S3 explorer (#2783)
* fix: array of s3 object result

* Add more text file extensions

* Fix bug when adding a s3object param
2023-12-05 15:44:10 +01:00
Ruben Fiszel
c728c93a3a nits 2023-12-05 15:17:10 +01:00
Faton Ramadani
b67d00540b fix(frontend): infer args if the schema is not an object (#2782) 2023-12-05 15:03:49 +01:00
Faton Ramadani
a6d1e7c79b fix(frontend): Fix dark mode observer, removed code duplication (#2770)
* fix(frontend): Fix dark mode observer, removed code duplication

* Revert
2023-12-05 14:20:21 +01:00
Ruben Fiszel
1cc54ffb9a more try catch around localStorage 2023-12-05 14:20:30 +01:00
Faton Ramadani
0c3d4a64c4 fix(frontend): fix fileinput drag and drop check (#2781)
* fix(frontend): fix fileinput drag and drop check

* fix(frontend): fix fileinput drag and drop check

* fix(frontend): fix fileinput drag and drop check
2023-12-05 14:11:49 +01:00
Ruben Fiszel
60cd14ff59 fix: add access-control-allow-origin to static assets 2023-12-05 12:15:21 +01:00
Guillaume Bouvignies
f3a0c81639 fix: strip prefix of s3 resource (#2780) 2023-12-05 09:52:49 +01:00
Ruben Fiszel
cb8bc75b30 smaller font size on inline editor 2023-12-04 21:32:54 +01:00
Ruben Fiszel
5fcdf12ef5 nit webhook panel 2023-12-04 20:59:56 +01:00
Henri Courdent
2d8bca8d38 Fixed links components (#2776) 2023-12-04 20:01:29 +01:00
Ruben Fiszel
bea0da3d6f feat: perpetual scripts 2023-12-04 19:46:43 +01:00
Guillaume Bouvignies
df865380bc fix: deployment_metadata table app_version is a nullable BIGINT (#2769) 2023-12-04 17:00:52 +01:00
Guillaume Bouvignies
1cad51b594 fix: string results with custom content types are not quoted (#2768) 2023-12-04 15:07:56 +01:00
Guillaume Bouvignies
194ee830cc feat: Add workspace settings to sync scripts/flows/apps to git repo on deployment (#2740)
* feat: Add workspace settings to sync script to git repo on deployment

* track deployment callback jobs in separate table

* sqlx prepare

* update deployment_metadata table

* Point to hub script

* Add details to the settings page and restrict to EE only

* cleanup unused impots

* Flows and Apps are now pushed to GH as well

* Ignore scripts/flows/apps located in private user folders
2023-12-04 14:26:27 +01:00
Guillaume Bouvignies
5cb3c34e44 feat: Various minor improvements for S3 DX (#2760)
* feat: Various minor improvements for S3 DX

* Fix openAPI yaml

* Remove unused imports
2023-12-04 13:20:04 +01:00
Guillaume Bouvignies
6c0f921b3a feat: Custom content type for script and flow results (#2767) 2023-12-04 13:19:40 +01:00
Bret Mogilefsky
2170f6504f Fix typo (#2765) 2023-12-04 07:36:59 +01:00
Ruben Fiszel
f50cef0367 feat: save inline scripts of apps to workspace + search by path 2023-12-03 15:13:57 +01:00
Ruben Fiszel
51d648d450 feat: result preview in app editor (#2761)
* update cli deps settings

* update cli deps settings

* all

* iterate
2023-12-02 16:01:04 +01:00
Faton Ramadani
8ca04e49b0 fix(frontend): fix sign in with Google button (#2756) 2023-12-01 18:33:00 +01:00
Faton Ramadani
2f72be36e5 App menu improvements (#2753)
* fix(frontend): add dropdown menu result + fix splitpanes seperators

* fix(frontend): remove useless border

* fix(frontend): fix arg enum

* fix(frontend): remove useless div

* fix(frontend): remove duplicated code

* fix(frontend): remove duplicated code

* fix(frontend): fix reactivity

* fix(frontend): fix reactivity
2023-12-01 17:10:50 +01:00
HugoCasa
d47d4ccf85 fix: improve azure openai readme (#2754) 2023-12-01 17:10:19 +01:00
HugoCasa
583dae6a72 fix: string or enum (#2752) 2023-12-01 16:07:16 +01:00
HugoCasa
8fc0afce71 fix: do not prorate for team plan checkouts mornings of firsts (#2751) 2023-12-01 15:16:54 +01:00
Faton Ramadani
6eaec47162 fix(frontend): fix resource search (#2748) 2023-12-01 13:32:45 +01:00
Faton Ramadani
a3f1111ca7 feat(frontend): add before and after icons for text input components (#2746) 2023-12-01 13:26:50 +01:00
Ruben Fiszel
60c4860233 fix: fix early return sync on existing flows 2023-12-01 13:18:31 +01:00
Faton Ramadani
85805bdf82 fix(frontend): fix tutorials contols (#2745) 2023-12-01 10:20:53 +01:00
Ruben Fiszel
225f675fb3 app ux nits 2023-12-01 10:00:16 +01:00
Ruben Fiszel
a122b6cab6 fix: show bg runnables currently running 2023-12-01 09:49:33 +01:00
Ruben Fiszel
dd980ee296 display more info on backend tests 2023-12-01 08:08:58 +01:00
Ruben Fiszel
45648bbf7f fix componentoutputviewer reset 2023-12-01 07:55:33 +01:00
Ruben Fiszel
1d90434f12 chore(main): release 1.219.1 (#2742)
* chore(main): release 1.219.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-12-01 07:41:18 +01:00
Ruben Fiszel
cb42f10dc6 fix: fix editorContext in preview 2023-12-01 07:38:51 +01:00
Ruben Fiszel
62de305908 fix: maps mapRegion update on move 2023-12-01 07:12:09 +01:00
Ruben Fiszel
49803e26f6 chore(main): release 1.219.0 (#2739)
* chore(main): release 1.219.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-12-01 01:20:38 +01:00
Ruben Fiszel
592d7839d1 fix: limit log pull from queued jobs 2023-12-01 01:14:01 +01:00
Ruben Fiszel
f195320e99 fix aggrid classes 2023-12-01 00:24:09 +01:00
Ruben Fiszel
fdb546ea2a fix eval preview 2023-12-01 00:13:27 +01:00
Ruben Fiszel
772e3b8a32 fix eval preview 2023-12-01 00:07:44 +01:00
Ruben Fiszel
1bef60dd50 fix eval preview 2023-12-01 00:02:39 +01:00
Ruben Fiszel
d4374a0103 feat: eval preview for apps 2023-11-30 23:54:33 +01:00
Ruben Fiszel
fc5f054b94 fix: improve stat card 2023-11-30 22:09:57 +01:00
Ruben Fiszel
4774e03be5 fix: improve quickstyle to be in static ccomponents only 2023-11-30 21:09:27 +01:00
Ruben Fiszel
007d5be23e fix: support INET in pg 2023-11-30 20:18:43 +01:00
Faton Ramadani
f816ad01d3 fix(frontend): rename menu component (#2738) 2023-11-30 17:51:21 +01:00
Ruben Fiszel
bb7f11fcda chore(main): release 1.218.0 (#2731)
* chore(main): release 1.218.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-11-30 17:22:05 +01:00
Ruben Fiszel
830dec0f90 fix: make REST language support URLSearchParams, headers, FormData 2023-11-30 16:50:00 +01:00
Guillaume Bouvignies
920cc9a576 fix: Using latest gosyn (#2737) 2023-11-30 15:57:16 +01:00
HugoCasa
1f99fcd3af fix: update deployed even when draft only (#2694)
* fix: update deployed even when draft only

* feat: create new when draft only and saving draft

* fix: nit

* fix: final fixes
2023-11-30 15:46:44 +01:00
Roman Grazhdan
52a2746e50 Adding build options for images, utilities and utilities' versions (#2736)
* Adding build options for images, utilities and utilities' versions

* Adding build options for images, utilities and utilities' versions

---------

Co-authored-by: r.grazdhan <r.grazdhan@team.bumble.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2023-11-30 15:44:11 +01:00
Faton Ramadani
ec25856b7c feat(frontend): resource rework (#2725)
* feat(frontend): resource rework wip

* feat(frontend): Fix styling

* fix(frontend): fix loading state

* fix(frontend): correclty handle the case wihout any resouces
2023-11-30 15:43:03 +01:00
dependabot[bot]
2d72facbdf chore(deps): bump aws-sdk-s3 from 0.36.0 to 0.39.1 in /backend (#2672)
* chore(deps): bump aws-sdk-s3 from 0.36.0 to 0.39.1 in /backend

Bumps [aws-sdk-s3](https://github.com/awslabs/aws-sdk-rust) from 0.36.0 to 0.39.1.
- [Release notes](https://github.com/awslabs/aws-sdk-rust/releases)
- [Changelog](https://github.com/awslabs/aws-sdk-rust/blob/main/CHANGELOG.md)
- [Commits](https://github.com/awslabs/aws-sdk-rust/commits)

---
updated-dependencies:
- dependency-name: aws-sdk-s3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix compile breaks

* Add behavior version

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: gbouv <guillaume@windmill.dev>
2023-11-30 14:01:20 +01:00
Ruben Fiszel
06411c8a63 build short sha for reports as well 2023-11-30 14:00:37 +01:00
Guillaume Bouvignies
0bf3685cbd fix: Pull patched version of gosyn (#2734) 2023-11-30 13:34:49 +01:00
Faton Ramadani
56c0d5a755 Fix app export (#2735)
* fix(frontend): fix app export

* fix(frontend): fix app export
2023-11-30 13:30:18 +01:00
Faton Ramadani
06656924ae feat(frontend): add menu component (#2721)
* feat(frontend): add menu component

* wip

* feat(frontend): menu component done

* feat(frontend): Fix styling + handle menu items everywhere

* feat(frontend): Fix styling + handle menu items everywhere
2023-11-30 12:39:18 +01:00
Guillaume Bouvignies
70c504edfa fix: Workspace error handler creation was not adding the slack resource (#2733)
Co-authored-by: admin <admin@windmill.dev>
2023-11-30 12:20:32 +01:00
Ruben Fiszel
0d93986b18 add reports image 2023-11-30 10:29:04 +01:00
Ruben Fiszel
651f74b00e improve python client 2023-11-29 21:41:44 +01:00
HugoCasa
16d10aedf7 fix: s3 resource openapi spec (#2730) 2023-11-29 21:18:17 +01:00
Ruben Fiszel
72bb15f6a0 chore(main): release 1.217.0 (#2705)
* chore(main): release 1.217.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2023-11-29 18:28:51 +01:00
Ruben Fiszel
772c04c9b3 update python-client 2023-11-29 18:19:47 +01:00
Ruben Fiszel
03d3bd33d0 restart dedicated worker regardless of config being present 2023-11-29 17:49:01 +01:00
HugoCasa
3789b34dae feat: scheduled app reports (#2714)
* feat: app preview discord

* feat: add custom, slack, email + worker groups

* fix: npm build

* fix: add assignable chromium tag

* feat: use workspace linked slack resource + hub scripts

* fix: nit
2023-11-29 16:35:42 +01:00
Guillaume Bouvignies
3d6fb15a90 fix: minor fixes to private NPM and python registries to get everything working (#2728) 2023-11-29 16:30:45 +01:00
Ruben Fiszel
e6d67f4e59 fix: make dedicated workers able to redeploy automatically 2023-11-29 12:28:06 +01:00
Faton Ramadani
1a1d1db96f fix(frontend): Fix table initial ordering (#2727)
* fix(frontend): Fix initial column order

* fix(frontend): Fix initial column order
2023-11-29 12:07:51 +01:00
Guillaume Bouvignies
15ebf46abc chore: Add examples on how to deploy private registries (#2719)
* chore: Add examples on how to deploy private registries

* chore: example for private pypiserver with https

* Allow DENO_CERT certificate with native workers

* Add BUN_TLS_REJECT_UNAUTHORIZED
2023-11-29 11:38:23 +01:00
Ruben Fiszel
e9a7f0cf17 fix benchmark 2023-11-29 11:08:23 +01:00
Ruben Fiszel
4eb25216f3 fix benchmarks for dedicated 2023-11-29 10:55:18 +01:00
Ruben Fiszel
b7fe5ddf15 fix benchmarks 2023-11-29 10:13:32 +01:00
Ruben Fiszel
f7d93c56df fix backend tests 2023-11-29 10:04:15 +01:00
Ruben Fiszel
f78f14e268 fix backend tests 2023-11-29 09:58:34 +01:00
Ruben Fiszel
5a66806c76 fix backend tests 2023-11-29 09:44:37 +01:00
Ruben Fiszel
c8046af9d0 fix: only list session and permanent token in user settings 2023-11-29 00:34:04 +01:00
HugoCasa
15b2c9f171 fix: better error for moved openai resource (#2724)
* fix: better error for moved openai resource

* fix: nit
2023-11-29 00:24:56 +01:00
Faton Ramadani
89abb68f63 feat(frontend): Stat card improvement (#2709)
* feat(frontend): add class+ style support + added support for custom images

* feat(frontend): add class+ style support + added support for custom images

* feat(frontend): fix styling

* feat(frontend): add quick style buttons

* feat(frontend): fix quick tailwind class

* feat(frontend): add space only if needed

* feat(frontend): fix build

* feat(frontend): fix line heights
2023-11-28 22:37:11 +01:00
Faton Ramadani
9525ab7bba fix(frontend): fix separator z-index (#2720) 2023-11-28 22:34:47 +01:00
Guillaume Bouvignies
bd31979a62 fix: generate cargo lock file (#2722) 2023-11-28 20:07:49 +01:00
HugoCasa
76a387f4a1 fix: ai fix popup placement + update edit/fix prompt to return complete code (#2715) 2023-11-28 18:51:30 +01:00
Faton Ramadani
46e0f91387 feat(frontend): add "hide schedules" filter (#2710)
* feat(frontend): add hide schedules filter

* feat(frontend): rework filters

* feat(frontend): fix filters

* feat(frontend): fix filter layout
2023-11-28 16:57:10 +01:00
Ruben Fiszel
55e49fbd4b migrate tests on ubicloud 2023-11-28 15:27:18 +01:00
Faton Ramadani
c990f856aa fix(frontend): Improve error message + fix overflow when file name is too long (#2691) 2023-11-28 11:31:33 +01:00
Faton Ramadani
2d3ce8a49c feat(frontend): Added tailwind classes auto-complete (#2712)
* feat(frontend): Added tailwind classes auto-complete

* feat(frontend): handle tailwindcss as a language
2023-11-28 11:13:49 +01:00
Guillaume Bouvignies
726866b410 fix: Deno can talk to private NPM registries behind HTTPS (#2713) 2023-11-27 21:46:19 +01:00
Ruben Fiszel
c3eaf0bf4a fix: improve autocomplete reactivity 2023-11-27 18:55:41 +01:00
dependabot[bot]
2bbd0b34b5 chore(deps): bump aws-config from 0.57.2 to 1.0.0 in /backend (#2671)
Bumps [aws-config](https://github.com/smithy-lang/smithy-rs) from 0.57.2 to 1.0.0.
- [Release notes](https://github.com/smithy-lang/smithy-rs/releases)
- [Changelog](https://github.com/smithy-lang/smithy-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/smithy-lang/smithy-rs/commits)

---
updated-dependencies:
- dependency-name: aws-config
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Guillaume Bouvignies <guillaume@windmill.dev>
2023-11-27 12:17:26 +01:00
Guillaume Bouvignies
36e46e2e47 fix: Error handler now supports flows (#2707)
* fix: Error handler now supports flows

* Update README

* remove unused import
2023-11-27 10:15:46 +00:00
Ikko Eltociear Ashimine
eec7d83d98 Update README.md (#2706)
chartss -> charts
2023-11-27 01:09:32 +01:00
Ruben Fiszel
d2b3026032 fix: trim .bun.ts for local imports 2023-11-27 00:59:14 +01:00
Ruben Fiszel
8275602372 feat: add support for raw query args 2023-11-26 16:47:32 +01:00
348 changed files with 24283 additions and 4022 deletions

View File

@@ -41,7 +41,7 @@ RUN apt-get update \
&& rm -rf /var/lib/apt/lists/*
RUN wget https://golang.org/dl/go1.19.1.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.19.1.linux-amd64.tar.gz
RUN wget https://golang.org/dl/go1.21.5.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz
ENV PATH="${PATH}:/usr/local/go/bin"
ENV GO_PATH=/usr/local/go/bin/go

View File

@@ -15,7 +15,7 @@ on:
jobs:
cargo_test:
runs-on: [self-hosted, new]
runs-on: ubicloud-standard-8
container:
image: ghcr.io/windmill-labs/backend-tests
services:
@@ -31,15 +31,16 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions-rust-lang/setup-rust-toolchain@v1
- uses: Swatinem/rust-cache@v2
with:
workspaces: |
backend
backend -> target
# - uses: Swatinem/rust-cache@v2
# with:
# workspaces: |
# backend
# backend -> target
- name: cargo test
timeout-minutes: 10
timeout-minutes: 15
run:
mkdir frontend/build && cd backend && touch
windmill-api/openapi-deref.yaml &&
DATABASE_URL=postgres://postgres:changeme@postgres:5432/windmill DISABLE_EMBEDDING=true cargo
test --features enterprise --all -- --nocapture
DATABASE_URL=postgres://postgres:changeme@postgres:5432/windmill
DISABLE_EMBEDDING=true RUST_LOG=info cargo test --features enterprise --all --
--nocapture

View File

@@ -7,14 +7,13 @@ on:
jobs:
benchmark:
runs-on: [self-hosted, new]
runs-on: ubicloud-standard-8
services:
postgres:
image: postgres
env:
POSTGRES_DB: windmill
POSTGRES_PASSWORD: changeme
options: >-
--health-cmd pg_isready --health-interval 10s --health-timeout 5s
--health-retries 5
@@ -35,12 +34,12 @@ jobs:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
DISABLE_SERVER: true
MODE: worker
WORKER_GROUP: dedicated
DEDICATED_WORKER: "admins:f/benchmarks/dedicated"
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
options: >-
--pull always
--pull always --restart unless-stopped
steps:
- uses: denoland/setup-deno@v1
with:
@@ -50,9 +49,11 @@ jobs:
ref: benchmarks
- name: benchmark
timeout-minutes: 20
run: deno run --unstable -A -r
run:
deno run --unstable -A -r
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts
-c https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json
-c
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json
- name: Push changes
run: |
pwd

View File

@@ -6,7 +6,7 @@ on:
- "version.txt"
jobs:
change_version:
runs-on: ubuntu-latest
runs-on: ubicloud
container: node:18
steps:
- uses: actions/checkout@v3

View File

@@ -1,13 +0,0 @@
name: Clean docker
on:
schedule:
# * is a special character in YAML so you have to quote this string
- cron: "0 0 */2 * *"
jobs:
build:
runs-on: [self-hosted, new]
steps:
- name: clean docker
run: |
sudo docker system prune -f

View File

@@ -8,7 +8,7 @@ env:
jobs:
build_deno_and_push_to_repo:
runs-on: ubuntu-latest
runs-on: ubicloud
steps:
- uses: actions/checkout@v3
- name: generate_deno
@@ -31,7 +31,7 @@ jobs:
tag_repo:
needs: [build_deno_and_push_to_repo]
runs-on: ubuntu-latest
runs-on: ubicloud
steps:
- uses: actions/checkout@v3
with:

View File

@@ -16,7 +16,7 @@ permissions:
jobs:
build_ee:
runs-on: ubuntu-22.04
runs-on: ubicloud
steps:
- uses: actions/checkout@v3
with:

View File

@@ -20,7 +20,7 @@ permissions:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ubicloud
steps:
- uses: actions/checkout@v3
with:
@@ -63,7 +63,7 @@ jobs:
org.opencontainers.image.licenses=AGPLv3
build_ee:
runs-on: ubuntu-latest
runs-on: ubicloud
steps:
- uses: actions/checkout@v3
with:
@@ -99,7 +99,7 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
build-args: |
features=enterprise
features=enterprise,saml
nsjail=true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:latest
@@ -108,6 +108,52 @@ jobs:
${{ steps.meta-ee-public.outputs.labels }}
org.opencontainers.image.licenses=Windmill-Enterprise-License
build_ee_reports_privately:
needs: [build_ee]
runs-on: ubicloud
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: Docker meta
id: meta-ee-public
uses: docker/metadata-action@v4
with:
images: |
${{ env.ECR_REGISTRY }}/${{ env.IMAGE_NAME }}-ee-reports
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,enable=true,priority=100,prefix=,suffix=,format=short
- name: Login to ECR
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
registry: ${{ env.ECR_REGISTRY }}
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Build and push publicly ee
uses: depot/build-push-action@v1
with:
context: .
platforms: linux/amd64
push: true
file: "./docker/DockerfileReports"
tags: |
${{ steps.meta-ee-public.outputs.tags }}
labels: |
${{ steps.meta-ee-public.outputs.labels }}
org.opencontainers.image.licenses=Windmill-Enterprise-License
# disabled until we make it 100% reliable and add more meaningful tests
# playwright:
# runs-on: [self-hosted, new]
@@ -143,7 +189,7 @@ jobs:
deploy_s3:
needs: [build_ee]
runs-on: ubuntu-latest
runs-on: ubicloud
if: github.event_name != 'pull_request'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
@@ -165,10 +211,9 @@ jobs:
bucket: windmill-frontend
bucket-region: us-east-1
attach_amd64_binary_to_release:
needs: [build, build_ee]
runs-on: ubuntu-latest
runs-on: ubicloud
if: ${{ startsWith(github.ref, 'refs/tags/') }}
env:
ARCH: amd64
@@ -203,7 +248,6 @@ jobs:
mv "${{ steps.extract.outputs.destination }}/windmill" "${{ steps.extract.outputs.destination }}/windmill-${ARCH}"
mv "${{ steps.extract-ee.outputs.destination }}/windmill" "${{ steps.extract-ee.outputs.destination }}/windmill-ee-${ARCH}"
- name: Attach binary to release
uses: softprops/action-gh-release@v1
with:
@@ -213,7 +257,7 @@ jobs:
attach_arm64_binary_to_release:
needs: [build, build_ee]
runs-on: ubuntu-latest
runs-on: ubicoud
if: ${{ startsWith(github.ref, 'refs/tags/') }}
env:
ARCH: arm64
@@ -248,7 +292,6 @@ jobs:
mv "${{ steps.extract.outputs.destination }}/windmill" "${{ steps.extract.outputs.destination }}/windmill-${ARCH}"
mv "${{ steps.extract-ee.outputs.destination }}/windmill" "${{ steps.extract-ee.outputs.destination }}/windmill-ee-${ARCH}"
- name: Attach binary to release
uses: softprops/action-gh-release@v1
with:
@@ -258,7 +301,7 @@ jobs:
publish_ecr:
needs: [build_ee]
runs-on: ubuntu-latest
runs-on: ubicloud
if: github.event_name != 'pull_request'
steps:
- uses: actions/checkout@v3

View File

@@ -5,8 +5,6 @@ env:
name: Build windmill:mssql
on:
workflow_dispatch:
schedule:
- cron: "0 0 */4 * *"
concurrency:
group: ${{ github.ref }}-mssql

View File

@@ -1,14 +1,14 @@
name: check frontend build
on:
pull_request:
types: [opened,synchronize,reopened,closed]
types: [opened, synchronize, reopened, closed]
paths:
- "frontend/**"
merge_group:
jobs:
npm_check:
runs-on: ubuntu-latest
runs-on: ubicloud-standard-8
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
@@ -16,4 +16,6 @@ jobs:
node-version: 18
- name: "npm check"
timeout-minutes: 2
run: cd frontend && npm ci && npm run generate-backend-client && npm run check
run:
cd frontend && npm ci && npm run generate-backend-client && npm run
check

View File

@@ -10,7 +10,7 @@ env:
jobs:
build_go_and_push_to_repo:
runs-on: ubuntu-latest
runs-on: ubicloud-standard-8
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4

View File

@@ -6,7 +6,7 @@ on:
jobs:
build_npm:
runs-on: ubuntu-latest
runs-on: ubicloud-standard-8
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3

View File

@@ -16,14 +16,17 @@ permissions:
packages: write
jobs:
publish_lsp:
runs-on: ubuntu-latest
sleep:
runs-on: ubicloud
steps:
- name: Sleep for 900 seconds waiting for pypi to update index
if: startsWith(github.ref, 'refs/tags/v')
run: sleep 900
shell: bash
publish_lsp:
needs: [sleep]
runs-on: ubicloud
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
@@ -62,7 +65,7 @@ jobs:
publish_lsp_private:
needs: [publish_lsp]
runs-on: ubuntu-latest
runs-on: ubicloud-standard-8
steps:
- uses: actions/checkout@v3
with:

View File

@@ -7,7 +7,7 @@ on:
jobs:
publish_pypi:
runs-on: ubuntu-latest
runs-on: ubicloud-standard-8
if: startsWith(github.ref, 'refs/tags/v')
container:
image: ghcr.io/windmill-labs/python-client-builder

View File

@@ -6,7 +6,7 @@ name: release-please
jobs:
release-please:
name: "Release please"
runs-on: ubuntu-latest
runs-on: ubicloud-standard-8
steps:
- uses: GoogleCloudPlatform/release-please-action@v3
with:

View File

@@ -1,6 +1,192 @@
# Changelog
## [1.224.0](https://github.com/windmill-labs/windmill/compare/v1.223.1...v1.224.0) (2023-12-10)
### Features
* add authelia sso support ([#2824](https://github.com/windmill-labs/windmill/issues/2824)) ([6fefdb5](https://github.com/windmill-labs/windmill/commit/6fefdb5438d0fd7546759d93b5f4465731a4cac5))
* embedded server only get killed after last job of worker ([4f4ca26](https://github.com/windmill-labs/windmill/commit/4f4ca264aef3e1d3ee4748593f0ebcb425d333ef))
### Bug Fixes
* make cleanup_module backcompatible ([58c4c0b](https://github.com/windmill-labs/windmill/commit/58c4c0b5c2b009ff56e3eb6bd19e766dbff4cf54))
## [1.223.1](https://github.com/windmill-labs/windmill/compare/v1.223.0...v1.223.1) (2023-12-08)
### Bug Fixes
* improve aggrid selectedRow output + make first selectedRow configurable ([651d998](https://github.com/windmill-labs/windmill/commit/651d99824d1932a48fdb9ffd5dff944fa2c2c8aa))
## [1.223.0](https://github.com/windmill-labs/windmill/compare/v1.222.0...v1.223.0) (2023-12-08)
### Features
* Add a git sync test button ([#2817](https://github.com/windmill-labs/windmill/issues/2817)) ([535202c](https://github.com/windmill-labs/windmill/commit/535202c1fe1e3a90b1aff2dad6632f615ca8aef8))
* add mysql datetime ([#2808](https://github.com/windmill-labs/windmill/issues/2808)) ([8896a8c](https://github.com/windmill-labs/windmill/commit/8896a8cacffb5da5575203a5fc9aa6863b303fde))
* Add possibility to delete flow step results when the flow is complete ([#2806](https://github.com/windmill-labs/windmill/issues/2806)) ([b65657d](https://github.com/windmill-labs/windmill/commit/b65657d0f8db21f87a90bdcd6ce1122a8bb209ce))
* no worker with tag warning ([#2803](https://github.com/windmill-labs/windmill/issues/2803)) ([e7141c1](https://github.com/windmill-labs/windmill/commit/e7141c147d9ff4728399ae0703ca636c776cb264))
* sql types autocomplete ([#2810](https://github.com/windmill-labs/windmill/issues/2810)) ([a9b5d6b](https://github.com/windmill-labs/windmill/commit/a9b5d6bebb00c96cc329b4178816c73db5cbe3f0))
### Bug Fixes
* conditional component also for toggles ([d26730a](https://github.com/windmill-labs/windmill/commit/d26730ad3419df5c5d75328e7f441c701b2c0c68))
* db schema autocomplete ([#2809](https://github.com/windmill-labs/windmill/issues/2809)) ([e250897](https://github.com/windmill-labs/windmill/commit/e2508972378f455b7af7984f2c33894dadd4f7bc))
* deno chromium support when nsjail enabled ([#2815](https://github.com/windmill-labs/windmill/issues/2815)) ([2964569](https://github.com/windmill-labs/windmill/commit/296456996293d861c7cab2d10e01e3b6443e421c))
* Fix test_complex_flow_restart ([#2813](https://github.com/windmill-labs/windmill/issues/2813)) ([b089449](https://github.com/windmill-labs/windmill/commit/b0894492f1e3c06880c0e1875c8c8886e31a165f))
* improve sql default arg parsing + auto invite ([033000f](https://github.com/windmill-labs/windmill/commit/033000fed36d6172cf3b9a83ff449f5ba051fa08))
* support interval in pg ([009a83d](https://github.com/windmill-labs/windmill/commit/009a83dd9bbc115b94978d2cb3113cbe10acd05d))
## [1.222.0](https://github.com/windmill-labs/windmill/compare/v1.221.0...v1.222.0) (2023-12-07)
### Features
* conditional fields on forms ([b10df30](https://github.com/windmill-labs/windmill/commit/b10df302455202940348cd20c2e733734fb7d027))
* mysql named params ([#2805](https://github.com/windmill-labs/windmill/issues/2805)) ([c4a1054](https://github.com/windmill-labs/windmill/commit/c4a1054a5a4ab3fd700de842a1f46d5886632b11))
### Bug Fixes
* add saml feature flag ([#2802](https://github.com/windmill-labs/windmill/issues/2802)) ([da15a56](https://github.com/windmill-labs/windmill/commit/da15a56a07288d4164c88289dd559389735db719))
* app reports screenshot refresh ([#2800](https://github.com/windmill-labs/windmill/issues/2800)) ([f7f7130](https://github.com/windmill-labs/windmill/commit/f7f71302f7d0fcac7468d506ebaceb55332ec1ed))
* fix extraOptions for agGrid ([f7d7746](https://github.com/windmill-labs/windmill/commit/f7d7746327411da06cede4e0ff06222d318d01bc))
* fix extraOptions for agGrid ([2f7fb9b](https://github.com/windmill-labs/windmill/commit/2f7fb9bfd490932a2f0bc01f02df678668487f8c))
* **frontend:** Fix chartjs options merge ([#2804](https://github.com/windmill-labs/windmill/issues/2804)) ([e11b257](https://github.com/windmill-labs/windmill/commit/e11b257b2d761b2b26160d61f8e113dbe47e9efe))
## [1.221.0](https://github.com/windmill-labs/windmill/compare/v1.220.0...v1.221.0) (2023-12-06)
### Features
* limit pro plans ([#2794](https://github.com/windmill-labs/windmill/issues/2794)) ([c58190e](https://github.com/windmill-labs/windmill/commit/c58190ef788feaae9bac9848e8cfa59d10f51cab))
### Bug Fixes
* Allow run git for Deno ([#2789](https://github.com/windmill-labs/windmill/issues/2789)) ([8ad8d20](https://github.com/windmill-labs/windmill/commit/8ad8d20136f3151ca28784ba3513af562fcb5c9b))
* Allow run git for Deno ([#2790](https://github.com/windmill-labs/windmill/issues/2790)) ([d822beb](https://github.com/windmill-labs/windmill/commit/d822beba63f08e4e1e1dac4aff56353276558f02))
* app reports nits ([#2788](https://github.com/windmill-labs/windmill/issues/2788)) ([0b13f2d](https://github.com/windmill-labs/windmill/commit/0b13f2d782d5626d64b5efe39232ec8da268cd27))
* app reports ts ([#2798](https://github.com/windmill-labs/windmill/issues/2798)) ([4f6a116](https://github.com/windmill-labs/windmill/commit/4f6a116e462d198cb81260749fdd8d57bc0ef03a))
* Deno allow run git ([#2791](https://github.com/windmill-labs/windmill/issues/2791)) ([256ee64](https://github.com/windmill-labs/windmill/commit/256ee646b49bca091ee6e7a0fea37f7d98c9e378))
* **frontend:** fix storage key typo ([#2796](https://github.com/windmill-labs/windmill/issues/2796)) ([e4da819](https://github.com/windmill-labs/windmill/commit/e4da819cda12e0e1f27cabc594fe5af5e2d9966c))
* **frontend:** use correct id for selected for loop iteration ([#2787](https://github.com/windmill-labs/windmill/issues/2787)) ([90668fb](https://github.com/windmill-labs/windmill/commit/90668fb0e5b4ac9f09a0b1e57fcb10b4b4208d2e))
* improve conditionalwrapper selection ([b5f5355](https://github.com/windmill-labs/windmill/commit/b5f53556a9afb3becbaaab034eeb8fdf4b1178a1))
* linked variable already being a variable ([9c4f1e0](https://github.com/windmill-labs/windmill/commit/9c4f1e0333743b35c237b4d8fe49757c8ae122e7))
* smtp doesn't require username/password ([abb50fa](https://github.com/windmill-labs/windmill/commit/abb50fac93a615d0ee18e9776c1fb84829fa2464))
## [1.220.0](https://github.com/windmill-labs/windmill/compare/v1.219.1...v1.220.0) (2023-12-05)
### Features
* Add workspace settings to sync scripts/flows/apps to git repo on deployment ([#2740](https://github.com/windmill-labs/windmill/issues/2740)) ([194ee83](https://github.com/windmill-labs/windmill/commit/194ee830cc8d8cc0ea69c877b428966f3f20bdb5))
* app reports v2 ([#2778](https://github.com/windmill-labs/windmill/issues/2778)) ([f36a510](https://github.com/windmill-labs/windmill/commit/f36a510025d87fc20e15f66e041390cc701c65c8))
* CLI now accepts message arg for sync push ([#2784](https://github.com/windmill-labs/windmill/issues/2784)) ([d9458e9](https://github.com/windmill-labs/windmill/commit/d9458e96ebc26160e9d5f909574bd1cf1d288d73))
* Custom content type for script and flow results ([#2767](https://github.com/windmill-labs/windmill/issues/2767)) ([6c0f921](https://github.com/windmill-labs/windmill/commit/6c0f921b3ab3acac8890ceb1fa0565bf082de0d4))
* **frontend:** add before and after icons for text input components ([#2746](https://github.com/windmill-labs/windmill/issues/2746)) ([a3f1111](https://github.com/windmill-labs/windmill/commit/a3f1111ca7715ffaa3a615786adee20e00870b30))
* History commit message can be updated from the UI ([#2777](https://github.com/windmill-labs/windmill/issues/2777)) ([874b7a5](https://github.com/windmill-labs/windmill/commit/874b7a50f72a6505490a02c82786ea5f5c8261e0))
* perpetual scripts ([bea0da3](https://github.com/windmill-labs/windmill/commit/bea0da3d6ff81ace956d6a1e7fb643e449927b05))
* result preview in app editor ([#2761](https://github.com/windmill-labs/windmill/issues/2761)) ([51d648d](https://github.com/windmill-labs/windmill/commit/51d648d450c578580f6d9a9630ff91920060cc27))
* save inline scripts of apps to workspace + search by path ([f50cef0](https://github.com/windmill-labs/windmill/commit/f50cef03670e50b8d7a6bb4719ae636f2714f22f))
* Various minor improvements for S3 DX ([#2760](https://github.com/windmill-labs/windmill/issues/2760)) ([5cb3c34](https://github.com/windmill-labs/windmill/commit/5cb3c34e440be878687217fdd95d735f053c7839))
### Bug Fixes
* add access-control-allow-origin to static assets ([60cd14f](https://github.com/windmill-labs/windmill/commit/60cd14ff59a80f671838a5f9bfa21effc309cb54))
* add run_flow_sync to python client + few nits ([042504f](https://github.com/windmill-labs/windmill/commit/042504f9052963c882ac346d2026c518c6d94f0f))
* Array of S3 files now shows links to the S3 explorer ([#2783](https://github.com/windmill-labs/windmill/issues/2783)) ([20b0500](https://github.com/windmill-labs/windmill/commit/20b0500a2824be1e715d4301e05c6081a5575dbe))
* deployment_metadata table app_version is a nullable BIGINT ([#2769](https://github.com/windmill-labs/windmill/issues/2769)) ([df86538](https://github.com/windmill-labs/windmill/commit/df865380bceb8663defb322a88f58f7c3f1a263c))
* do not prorate for team plan checkouts mornings of firsts ([#2751](https://github.com/windmill-labs/windmill/issues/2751)) ([8fc0afc](https://github.com/windmill-labs/windmill/commit/8fc0afce714774647f86e1aca8eeec4b7d4c13fc))
* fix early return sync on existing flows ([60c4860](https://github.com/windmill-labs/windmill/commit/60c486023310ef85d1cb171ebca792be8d7917e8))
* **frontend:** Fix dark mode observer, removed code duplication ([#2770](https://github.com/windmill-labs/windmill/issues/2770)) ([a6d1e7c](https://github.com/windmill-labs/windmill/commit/a6d1e7c79bb16d2c02d53f9ede56451e064956e0))
* **frontend:** fix fileinput drag and drop check ([#2781](https://github.com/windmill-labs/windmill/issues/2781)) ([0c3d4a6](https://github.com/windmill-labs/windmill/commit/0c3d4a64c4a8ad823646f9ce27de7e58ccc002e8))
* **frontend:** fix resource search ([#2748](https://github.com/windmill-labs/windmill/issues/2748)) ([6eaec47](https://github.com/windmill-labs/windmill/commit/6eaec471629d6c60e6c3123372fa3569bc5471c7))
* **frontend:** fix sign in with Google button ([#2756](https://github.com/windmill-labs/windmill/issues/2756)) ([8ca04e4](https://github.com/windmill-labs/windmill/commit/8ca04e49b061675f820e4aeaad6196403eabe135))
* **frontend:** fix tutorials contols ([#2745](https://github.com/windmill-labs/windmill/issues/2745)) ([85805bd](https://github.com/windmill-labs/windmill/commit/85805bdf8265f4fe14a583b43b786257ffccc4dc))
* **frontend:** infer args if the schema is not an object ([#2782](https://github.com/windmill-labs/windmill/issues/2782)) ([b67d005](https://github.com/windmill-labs/windmill/commit/b67d00540b7adfac3550299d50fc9a620b5a6e2e))
* improve azure openai readme ([#2754](https://github.com/windmill-labs/windmill/issues/2754)) ([d47d4cc](https://github.com/windmill-labs/windmill/commit/d47d4ccf85747a14e0498be871ead08371392ff5))
* show bg runnables currently running ([a122b6c](https://github.com/windmill-labs/windmill/commit/a122b6cab664e0b8b3d7223b9fd70569a2fe0991))
* string or enum ([#2752](https://github.com/windmill-labs/windmill/issues/2752)) ([583dae6](https://github.com/windmill-labs/windmill/commit/583dae6a72ee6be70dee5a356e1b4172db9a7e2d))
* string results with custom content types are not quoted ([#2768](https://github.com/windmill-labs/windmill/issues/2768)) ([1cad51b](https://github.com/windmill-labs/windmill/commit/1cad51b5941a3bd1892c935581373bb7375da45a))
* strip prefix of s3 resource ([#2780](https://github.com/windmill-labs/windmill/issues/2780)) ([f3a0c81](https://github.com/windmill-labs/windmill/commit/f3a0c81639662a7da65af49e4032dfc573dd93eb))
## [1.219.1](https://github.com/windmill-labs/windmill/compare/v1.219.0...v1.219.1) (2023-12-01)
### Bug Fixes
* fix editorContext in preview ([cb42f10](https://github.com/windmill-labs/windmill/commit/cb42f10dc605f00ba70fadc61069aba98af8ec88))
* maps mapRegion update on move ([62de305](https://github.com/windmill-labs/windmill/commit/62de305908ba7e6ac75aa5e8ffba822522b08345))
## [1.219.0](https://github.com/windmill-labs/windmill/compare/v1.218.0...v1.219.0) (2023-12-01)
### Features
* eval preview for apps ([d4374a0](https://github.com/windmill-labs/windmill/commit/d4374a0103d2244b31b5cec7649dbfb96b2af1b4))
### Bug Fixes
* **frontend:** rename menu component ([#2738](https://github.com/windmill-labs/windmill/issues/2738)) ([f816ad0](https://github.com/windmill-labs/windmill/commit/f816ad01d3e39917714abadcf3833a4d8619e4f3))
* improve quickstyle to be in static ccomponents only ([4774e03](https://github.com/windmill-labs/windmill/commit/4774e03be5663017bccbe95f026cc39d4c43f536))
* improve stat card ([fc5f054](https://github.com/windmill-labs/windmill/commit/fc5f054b94fa58ee889d10d06e486b6e8c4f885e))
* limit log pull from queued jobs ([592d783](https://github.com/windmill-labs/windmill/commit/592d7839d183843b425521a319b802fb8cac3f21))
* support INET in pg ([007d5be](https://github.com/windmill-labs/windmill/commit/007d5be23e038fbf82907c3b335b3d747791295d))
## [1.218.0](https://github.com/windmill-labs/windmill/compare/v1.217.0...v1.218.0) (2023-11-30)
### Features
* **frontend:** add menu component ([#2721](https://github.com/windmill-labs/windmill/issues/2721)) ([0665692](https://github.com/windmill-labs/windmill/commit/06656924ae8173e906a243aab2af658a1689af01))
* **frontend:** resource rework ([#2725](https://github.com/windmill-labs/windmill/issues/2725)) ([ec25856](https://github.com/windmill-labs/windmill/commit/ec25856b7c8a7d33a50d1beb5f3e99c7b912e1ca))
### Bug Fixes
* make REST language support URLSearchParams, headers, FormData ([830dec0](https://github.com/windmill-labs/windmill/commit/830dec0f90e0189b4dea4de8c44a03e437acf440))
* Pull patched version of gosyn ([#2734](https://github.com/windmill-labs/windmill/issues/2734)) ([0bf3685](https://github.com/windmill-labs/windmill/commit/0bf3685cbdecae0a8e7a24a5198ae2fed98fe340))
* s3 resource openapi spec ([#2730](https://github.com/windmill-labs/windmill/issues/2730)) ([16d10ae](https://github.com/windmill-labs/windmill/commit/16d10aedf79cddf7cdc15d55c42c72f4948b3ee2))
* update deployed even when draft only ([#2694](https://github.com/windmill-labs/windmill/issues/2694)) ([1f99fcd](https://github.com/windmill-labs/windmill/commit/1f99fcd3af21208676aa90015559359740b0534a))
* Using latest gosyn ([#2737](https://github.com/windmill-labs/windmill/issues/2737)) ([920cc9a](https://github.com/windmill-labs/windmill/commit/920cc9a576db0112ffd9572480c3d0a8aa08055b))
* Workspace error handler creation was not adding the slack resource ([#2733](https://github.com/windmill-labs/windmill/issues/2733)) ([70c504e](https://github.com/windmill-labs/windmill/commit/70c504edfaf7c114c91af2b3ca39dfee073f04f2))
## [1.217.0](https://github.com/windmill-labs/windmill/compare/v1.216.0...v1.217.0) (2023-11-29)
### Features
* add support for raw query args ([8275602](https://github.com/windmill-labs/windmill/commit/82756023728fe392fee93a7bba0567ce582aad8c))
* **frontend:** add "hide schedules" filter ([#2710](https://github.com/windmill-labs/windmill/issues/2710)) ([46e0f91](https://github.com/windmill-labs/windmill/commit/46e0f913878c9f688594cfe1c9184438e4facf49))
* **frontend:** Added tailwind classes auto-complete ([#2712](https://github.com/windmill-labs/windmill/issues/2712)) ([2d3ce8a](https://github.com/windmill-labs/windmill/commit/2d3ce8a49c952accb4f1ceb0e6ea608df539f179))
* **frontend:** Stat card improvement ([#2709](https://github.com/windmill-labs/windmill/issues/2709)) ([89abb68](https://github.com/windmill-labs/windmill/commit/89abb68f635d062778485c15d88fd6272ebd40a2))
* scheduled app reports ([#2714](https://github.com/windmill-labs/windmill/issues/2714)) ([3789b34](https://github.com/windmill-labs/windmill/commit/3789b34dae72ff57bb9e0bb7e93439446c78095d))
### Bug Fixes
* ai fix popup placement + update edit/fix prompt to return complete code ([#2715](https://github.com/windmill-labs/windmill/issues/2715)) ([76a387f](https://github.com/windmill-labs/windmill/commit/76a387f4a181d21afad0138c07f947aa292978cc))
* better error for moved openai resource ([#2724](https://github.com/windmill-labs/windmill/issues/2724)) ([15b2c9f](https://github.com/windmill-labs/windmill/commit/15b2c9f171122ab074f8430a785f8ed577921fcc))
* Deno can talk to private NPM registries behind HTTPS ([#2713](https://github.com/windmill-labs/windmill/issues/2713)) ([726866b](https://github.com/windmill-labs/windmill/commit/726866b410863ca9583a8145ebd9f4c4557cef08))
* Error handler now supports flows ([#2707](https://github.com/windmill-labs/windmill/issues/2707)) ([36e46e2](https://github.com/windmill-labs/windmill/commit/36e46e2e47e1e949e1e235fa4ec0365b46d80de1))
* **frontend:** fix separator z-index ([#2720](https://github.com/windmill-labs/windmill/issues/2720)) ([9525ab7](https://github.com/windmill-labs/windmill/commit/9525ab7bbab9f820d1f6fee44f9a2e9ad19d1b54))
* **frontend:** Fix table initial ordering ([#2727](https://github.com/windmill-labs/windmill/issues/2727)) ([1a1d1db](https://github.com/windmill-labs/windmill/commit/1a1d1db96fd069434291f4b5f37ae1d0e21c5e44))
* **frontend:** Improve error message + fix overflow when file name is too long ([#2691](https://github.com/windmill-labs/windmill/issues/2691)) ([c990f85](https://github.com/windmill-labs/windmill/commit/c990f856aaf821899cc54db20a653af396ceae7f))
* generate cargo lock file ([#2722](https://github.com/windmill-labs/windmill/issues/2722)) ([bd31979](https://github.com/windmill-labs/windmill/commit/bd31979a62a30071e68385b5eb5dc28f37ea8fb5))
* improve autocomplete reactivity ([c3eaf0b](https://github.com/windmill-labs/windmill/commit/c3eaf0bf4aab531b52a1b3ac276c6840b0925786))
* make dedicated workers able to redeploy automatically ([e6d67f4](https://github.com/windmill-labs/windmill/commit/e6d67f4e5994360e7ae83c1303b31514b6062d1d))
* minor fixes to private NPM and python registries to get everything working ([#2728](https://github.com/windmill-labs/windmill/issues/2728)) ([3d6fb15](https://github.com/windmill-labs/windmill/commit/3d6fb15a90f4d5aa18a6d6158760a380295e3d9e))
* only list session and permanent token in user settings ([c8046af](https://github.com/windmill-labs/windmill/commit/c8046af9d01ade6891ea97e56974bb742bbf3e6e))
* trim .bun.ts for local imports ([d2b3026](https://github.com/windmill-labs/windmill/commit/d2b3026032d288e1e5de1f37979d92d02094b3e4))
## [1.216.0](https://github.com/windmill-labs/windmill/compare/v1.215.0...v1.216.0) (2023-11-26)

View File

@@ -1,4 +1,8 @@
FROM debian:bookworm-slim as nsjail
ARG DEBIAN_IMAGE=debian:bookworm-slim
ARG RUST_IMAGE=rust:slim-bookworm
ARG PYTHON_IMAGE=python:3.11.4-slim-bookworm
FROM ${DEBIAN_IMAGE} as nsjail
WORKDIR /nsjail
@@ -22,7 +26,7 @@ RUN if [ "$nsjail" = "true" ]; then git clone -b master --single-branch https://
&& git checkout dccf911fd2659e7b08ce9507c25b2b38ec2c5800; fi
RUN if [ "$nsjail" = "true" ]; then make; else touch nsjail; fi
FROM rust:slim-bookworm AS rust_base
FROM ${RUST_IMAGE} AS rust_base
RUN apt-get update && apt-get install -y git libssl-dev pkg-config npm
@@ -87,7 +91,7 @@ COPY .git/ .git/
RUN CARGO_NET_GIT_FETCH_WITH_CLI=true cargo build --release --features "$features"
FROM debian:bookworm-slim as downloader
FROM ${DEBIAN_IMAGE} as downloader
ARG TARGETPLATFORM
@@ -101,29 +105,44 @@ RUN [ "$TARGETPLATFORM" == "linux/amd64" ] && curl -Lsf https://github.com/denol
RUN unzip deno.zip && rm deno.zip
FROM python:3.11.4-slim-bookworm
FROM ${PYTHON_IMAGE}
ARG TARGETPLATFORM
ARG POWERSHELL_VERSION=7.3.5
ARG POWERSHELL_DEB_VERSION=7.3.5-1
ARG RCLONE_VERSION=1.60.1
ARG KUBECTL_VERSION=1.27.2
ARG HELM_VERSION=3.12.0
ARG APP=/usr/src/app
ARG WITH_POWERSHELL=true
ARG WITH_RCLONE=true
ARG WITH_KUBECTL=true
ARG WITH_HELM=true
RUN apt-get update \
&& apt-get install -y ca-certificates wget curl git jq libprotobuf-dev libnl-route-3-dev unzip build-essential unixodbc xmlsec1 \
&& rm -rf /var/lib/apt/lists/*
RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then apt-get update -y && apt install libicu-dev -y && wget -O 'pwsh.deb' 'https://github.com/PowerShell/PowerShell/releases/download/v7.3.5/powershell_7.3.5-1.deb_amd64.deb' && \
dpkg --install 'pwsh.deb' && \
rm 'pwsh.deb'; else echo 'pwshell not on amd64'; fi
RUN if [ "$WITH_POWERSHELL" = "true" ]; then \
if [ "$TARGETPLATFORM" = "linux/amd64" ]; then apt-get update -y && apt install libicu-dev -y && wget -O 'pwsh.deb' "https://github.com/PowerShell/PowerShell/releases/download/v${POWERSHELL_VERSION}/powershell_${POWERSHELL_DEB_VERSION}.deb_amd64.deb" && \
dpkg --install 'pwsh.deb' && \
rm 'pwsh.deb'; else echo 'pwshell not on amd64'; fi; \
else echo 'Building the image without powershell'; fi
RUN arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
wget https://get.helm.sh/helm-v3.12.0-linux-$arch.tar.gz && \
tar -zxvf helm-v3.12.0-linux-$arch.tar.gz && \
mv linux-$arch/helm /usr/local/bin/helm &&\
chmod +x /usr/local/bin/helm
RUN if [ "$WITH_HELM" = "true" ]; then \
arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
wget "https://get.helm.sh/helm-v${HELM_VERSION}-linux-$arch.tar.gz" && \
tar -zxvf "helm-v${HELM_VERSION}-linux-$arch.tar.gz" && \
mv linux-$arch/helm /usr/local/bin/helm &&\
chmod +x /usr/local/bin/helm; \
else echo 'Building the image without helm'; fi
RUN arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
curl -LO "https://dl.k8s.io/release/v1.27.2/bin/linux/$arch/kubectl" && \
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
RUN if [ "$WITH_KUBECTL" = "true" ]; then \
arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
curl -LO "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/$arch/kubectl" && \
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl; \
else echo 'Building the image without kubectl'; fi
RUN set -eux; \
arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
@@ -140,22 +159,25 @@ RUN set -eux; \
unzip awscliv2.zip && \
./aws/install && rm awscliv2.zip
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 if [ "$WITH_RCLONE" = "true" ]; then \
arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
curl -o rclone.zip "https://downloads.rclone.org/v${RCLONE_VERSION}/rclone-v${RCLONE_VERSION}-linux-$arch.zip"; \
unzip -p rclone.zip rclone-v${RCLONE_VERSION}-linux-$arch/rclone > /usr/bin/rclone; rm rclone.zip; \
chown root:root /usr/bin/rclone; chmod 755 /usr/bin/rclone; \
else echo 'Building the image without rclone'; fi
RUN set -eux; \
arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \
case "$arch" in \
'amd64') \
targz='go1.21.0.linux-amd64.tar.gz'; \
targz='go1.21.5.linux-amd64.tar.gz'; \
;; \
'arm64') \
targz='go1.21.0.linux-arm64.tar.gz'; \
targz='go1.21.5.linux-arm64.tar.gz'; \
;; \
'armhf') \
targz='go1.21.0.linux-armv6l.tar.gz'; \
targz='go1.21.5.linux-armv6l.tar.gz'; \
;; \
*) echo >&2 "error: unsupported architecture '$arch' (likely packaging update needed)"; exit 1 ;; \
esac; \

View File

@@ -56,7 +56,7 @@ https://github.com/windmill-labs/windmill/assets/122811744/0b132cd1-ee67-4505-82
- [Architecture](#architecture)
- [How to self-host](#how-to-self-host)
- [Docker compose](#docker-compose)
- [Kubernetes (k8s) and Helm chartss](#kubernetes-k8s-and-helm-chartss)
- [Kubernetes (k8s) and Helm charts](#kubernetes-k8s-and-helm-charts)
- [Run from binaries](#run-from-binaries)
- [OAuth, SSO \& SMTP](#oauth-sso--smtp)
- [Commercial license](#commercial-license)
@@ -240,7 +240,7 @@ From there, you can follow the setup app and create other users.
More details in [Self-Host Documention](https://www.windmill.dev/docs/advanced/self_host#docker).
### Kubernetes (k8s) and Helm chartss
### Kubernetes (k8s) and Helm charts
We publish helm charts at:
<https://github.com/windmill-labs/windmill-helm-charts>.
@@ -363,11 +363,11 @@ it being synced automatically everyday.
| SMTP_PASSWORD | None | password for the smtp server to send invite emails | Server |
| SMTP_TLS_IMPLICIT | false | https://docs.rs/mail-send/latest/mail_send/struct.SmtpClientBuilder.html#method.implicit_tlsemails | Server |
| CREATE_WORKSPACE_REQUIRE_SUPERADMIN | true | If true, only superadmin can create workspaces | Server |
| GLOBAL_ERROR_HANDLER_PATH_IN_ADMINS_WORKSPACE | None | Path to a script to run when a root job fails. The script will be run in and from the admins workspace | Server |
| GLOBAL_ERROR_HANDLER_PATH_IN_ADMINS_WORKSPACE | None | Path to a script or flow to run when a root job fails. The path needs to be prefixed with either `script/` or `flow/` to indicate the kind of error handler being used (assuming `script/` by default). The error handler will be run in and from the admins workspace | Server |
| WHITELIST_ENVS | None | List of envs variables, separated by a ',' that are whitelisted as being safe to passthrough the workers | Worker |
| SAML_METADATA | None | SAML Metadata URL to enable SAML SSO (EE only) | Server |
| SECRET_SALT | None | Secret Salt used for encryption and decryption of secrets. If defined, the secrets will not be decryptable unless the right salt is passed in, which is the case for the workers and the server | Server + Worker |
| OPENAI_AZURE_BASE_PATH | None | Azure OpenAI API base path (no trailing slash) | Server |
| OPENAI_AZURE_BASE_PATH | None | Azure OpenAI path to be used instead of the OpenAI path. All Windmill AI features will run on the specified deployed model. Format: `https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}` | Server |
| DISABLE_EMBEDDING | false | Disable local embedding search of hub scripts | Server |
| DISABLE_NSJAIL | true | Disable Nsjail Sandboxing | Worker |
| DISABLE_SERVER | false | Disable the external API, operate as a worker only instance | Worker |
@@ -416,4 +416,4 @@ running options.
## Copyright
Windmill Labs, Inc 2023
Windmill Labs, Inc 2023

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "select tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority from script where hash = $1 AND workspace_id = $2",
"query": "select tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority, delete_after_use from script where hash = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
@@ -37,7 +37,6 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",
@@ -59,6 +58,11 @@
"ordinal": 6,
"name": "priority",
"type_info": "Int2"
},
{
"ordinal": 7,
"name": "delete_after_use",
"type_info": "Bool"
}
],
"parameters": {
@@ -74,8 +78,9 @@
true,
false,
true,
true,
true
]
},
"hash": "d48070ef889723f408ed6c1a97b5ab9f0e98340cd33c6e29824f6030f0089071"
"hash": "0234079d04372b82ec8f2990916433bd8771c21d22a1db02eb1e388a8bfb28ff"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['failure_module'], $1), ARRAY['step'], $2)\n WHERE id = $3",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Jsonb",
"Jsonb",
"Uuid"
]
},
"nullable": []
},
"hash": "0aff8b0f2cddc0dd696759f48368c7315ff39060be5430c59582e1e05a4b3d90"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['failure_module'], $1)\n WHERE id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Jsonb",
"Uuid"
]
},
"nullable": []
},
"hash": "0c0b5d5d1e6ab2fed7532f94b50be3210e3845b61551691bbef81c2b6fb01121"
}

View File

@@ -87,6 +87,11 @@
"ordinal": 16,
"name": "large_file_storage",
"type_info": "Jsonb"
},
{
"ordinal": 17,
"name": "git_sync",
"type_info": "Jsonb"
}
],
"parameters": {
@@ -111,6 +116,7 @@
false,
true,
false,
true,
true
]
},

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "SELECT restart_unless_cancelled FROM script WHERE hash = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "restart_unless_cancelled",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Int8",
"Text"
]
},
"nullable": [
true
]
},
"hash": "1d27895aa42ccbb542479b19baefd62790205b529ab0d8af36f18c470e8bb838"
}

View File

@@ -45,7 +45,8 @@
"graphql",
"postgresql",
"noop",
"appdependencies"
"appdependencies",
"deploymentcallback"
]
}
}
@@ -67,7 +68,6 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['step'], $1)\n WHERE id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Jsonb",
"Uuid"
]
},
"nullable": []
},
"hash": "26106be4d94c159cc8d9eb37a3b94927c5d3d43c18cdb5e98e979e761ed6ed0e"
}

View File

@@ -1,16 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE queue\n SET leaf_jobs = JSONB_SET(coalesce(leaf_jobs, '{}'::jsonb), ARRAY[$1::TEXT], $2)\n WHERE COALESCE((SELECT root_job FROM queue WHERE id = $3), $3) = id\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Jsonb",
"Uuid"
]
},
"nullable": []
},
"hash": "2a3ebe1b0eae5b2164894321e138cc4dc0293788aeb98d05d95d18dfc708d6a6"
}

View File

@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "SELECT flow_status->'cleanup_module' as cleanup_module\n FROM queue\n WHERE id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "cleanup_module",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
null
]
},
"hash": "322038aae939d738f47b5bddaec2a6342e25e815dcd8b9fd620ddb81063c5d76"
}

View File

@@ -0,0 +1,29 @@
{
"db_name": "PostgreSQL",
"query": "SELECT s.hash as hash, dm.deployment_msg as deployment_msg \n FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash\n WHERE s.workspace_id = $1 AND s.path = $2\n ORDER by created_at DESC",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "hash",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "deployment_msg",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false,
true
]
},
"hash": "362419eb262c83d6a98a0200b116e831ada60399fe5f55a56d930cc69aff2675"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE queue\n SET suspend = $1, suspend_until = now() + interval '14 day', running = true\n WHERE id = $2\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int4",
"Uuid"
]
},
"nullable": []
},
"hash": "3c83e6adf21b7a8572529f9175cf6d2d30ea8c921ae2bf751b6825a3e453a012"
}

View File

@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE completed_job\n SET logs = '##DELETED##', args = '{}'::jsonb, result = '{}'::jsonb\n WHERE id = ANY($1)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"UuidArray"
]
},
"nullable": []
},
"hash": "42543450a5c1b988b258fb1e0c00cd0a53a0395bc60e35817ff149c4f27a19fb"
}

View File

@@ -1,16 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2)\n WHERE id = $3\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Jsonb",
"Uuid"
]
},
"nullable": []
},
"hash": "47a74719faba8a6377ba157d25929a6f1e74afdffd642d5a1c7db57e745e0c01"
}

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['step'], $1)\n WHERE id = $2\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Jsonb",
"Uuid"
]
},
"nullable": []
},
"hash": "4de3ba6930b91c639f9f3939dab7b3ab35aa792484e4ccb8084c336a2ec60f45"
}

View File

@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE workspace_settings SET git_sync = NULL WHERE workspace_id = $1",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text"
]
},
"nullable": []
},
"hash": "51d253d3cba9e2a36c291f6f9cf7023834484e89d91f0267d9dcaaafe406c64c"
}

View File

@@ -1,17 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2),\n ARRAY['step'], $3)\n WHERE id = $4\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Jsonb",
"Jsonb",
"Uuid"
]
},
"nullable": []
},
"hash": "53ec897ac019abf3fc4499f593adadf85af465d66c1a23b0549d176edbabad79"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue\n SET suspend = $1, suspend_until = now() + interval '14 day', running = true\n WHERE id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int4",
"Uuid"
]
},
"nullable": []
},
"hash": "53ff0e14c35a3e84585a699e55093546db5c5f0ad0c5f92f34aaf2cdd125d130"
}

View File

@@ -87,6 +87,11 @@
"ordinal": 16,
"name": "large_file_storage",
"type_info": "Jsonb"
},
{
"ordinal": 17,
"name": "git_sync",
"type_info": "Jsonb"
}
],
"parameters": {
@@ -111,6 +116,7 @@
false,
true,
false,
true,
true
]
},

View File

@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "SELECT EXISTS(SELECT 1 FROM worker_ping WHERE custom_tags @> $1 AND ping_at > now() - interval '1 minute')",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "exists",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"TextArray"
]
},
"nullable": [
null
]
},
"hash": "5d1ec728380ea8baf64df54743e73008ee9a58e6f39a5bf31a2ed099727f5c04"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2)\n WHERE id = $3",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Jsonb",
"Uuid"
]
},
"nullable": []
},
"hash": "61656548991bf6d44c839373cb3e29d3ca1170a2ac0d3dce0b5df0e8677a4874"
}

View File

@@ -40,7 +40,8 @@
"graphql",
"postgresql",
"noop",
"appdependencies"
"appdependencies",
"deploymentcallback"
]
}
}
@@ -60,7 +61,6 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",

View File

@@ -0,0 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO deployment_metadata (workspace_id, path, app_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, app_version) WHERE app_version IS NOT NULL DO UPDATE SET deployment_msg = $4",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Int8",
"Text"
]
},
"nullable": []
},
"hash": "652835b2b7f801532a591988ac76d385188991c6654d529f6d65f6f03794844a"
}

View File

@@ -0,0 +1,18 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO deployment_metadata (workspace_id, path, script_hash, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Int8",
"UuidArray",
"Text"
]
},
"nullable": []
},
"hash": "6bb4fab9e74c421fb30cfd856a1a9b44c76f1cf1485ceba50aeede9daec94c01"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)",
"query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)",
"describe": {
"columns": [],
"parameters": {
@@ -28,7 +28,6 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",
@@ -62,10 +61,12 @@
"Int4",
"Bool",
"Bool",
"Int2"
"Int2",
"Bool",
"Bool"
]
},
"nullable": []
},
"hash": "52ad1773a9f5a1b6be1bb16af408f051f27d3f21b615c468b85aa75b64d943db"
"hash": "7db59e88a10150ab6a3e81f164343e6bb2be6149fb690e63b8a0c376f39e9924"
}

View File

@@ -0,0 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO deployment_metadata (workspace_id, path, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path) WHERE script_hash IS NULL AND app_version IS NULL DO UPDATE SET callback_job_ids = $3, deployment_msg = $4",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"UuidArray",
"Text"
]
},
"nullable": []
},
"hash": "7f9f1ce221835fc3ff6c864c362519fde3d08e496119e51d8ed4fe65792a85c1"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue\n SET leaf_jobs = JSONB_SET(coalesce(leaf_jobs, '{}'::jsonb), ARRAY[$1::TEXT], $2)\n WHERE COALESCE((SELECT root_job FROM queue WHERE id = $3), $3) = id",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Jsonb",
"Uuid"
]
},
"nullable": []
},
"hash": "8929150cb9262623eb12c908cb96c4d7e8cac594900ab0956c098e1cfa75e2f0"
}

View File

@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE completed_job\n SET logs = '##DELETED##', args = '{}'::jsonb, result = '{}'::jsonb\n WHERE id = $1",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": []
},
"hash": "8a44ca0cfe1e154138cbbf5ebddebbba2e1149d2e329d1d89eecc1c008c93a31"
}

View File

@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'branchall', 'branch'], ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb)\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "int4",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Int4",
"Uuid"
]
},
"nullable": [
null
]
},
"hash": "9681c348429f5e647ab55a4a54933faa64018f8539a143641f10d14ddd3a09c4"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes FROM token WHERE email = $1\n ORDER BY created_at DESC",
"query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes FROM token WHERE email = $1\n ORDER BY created_at DESC",
"describe": {
"columns": [
{
@@ -48,5 +48,5 @@
true
]
},
"hash": "773c145013623e4eb29a8df70e46d805dc5b15942a36a0d988521abb0cb34e41"
"hash": "9ce6eecfa10c2f71cc536957ead498ad829ea8023ce449eab27225ec66738525"
}

View File

@@ -24,7 +24,8 @@
"graphql",
"postgresql",
"noop",
"appdependencies"
"appdependencies",
"deploymentcallback"
]
}
}
@@ -40,7 +41,6 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",

View File

@@ -1,15 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['failure_module'], $1)\n WHERE id = $2\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Jsonb",
"Uuid"
]
},
"nullable": []
},
"hash": "a6145b0482c9e5da245059a80b1563cad20318fd2dd8aef33f9ca97de1826b8b"
}

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'branchall', 'branch'], ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb)\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "int4",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Int4",
"Uuid"
]
},
"nullable": [
null
]
},
"hash": "a94dbd1f7aab20682548471c5cc06c7a496edac36e3de537b00b76ad93c7556c"
}

View File

@@ -30,7 +30,8 @@
"graphql",
"postgresql",
"noop",
"appdependencies"
"appdependencies",
"deploymentcallback"
]
}
}
@@ -46,7 +47,6 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",

View File

@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "SELECT path FROM app WHERE id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "path",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false
]
},
"hash": "abb56dec024600d0d7a63212f63c3296196898003707f2cb3a890690173a51a5"
}

View File

@@ -0,0 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2), ARRAY['step'], $3)\n WHERE id = $4",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Jsonb",
"Jsonb",
"Uuid"
]
},
"nullable": []
},
"hash": "b64c7f3b483c74c51aecda65664c36d9686112182dbf35651e494da739b922f9"
}

View File

@@ -0,0 +1,18 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO deployment_metadata (workspace_id, path, app_version, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Int8",
"UuidArray",
"Text"
]
},
"nullable": []
},
"hash": "b83e58972d8f8285eb604de94c3d363102f5f6705d6debb442a85126ed59d863"
}

View File

@@ -0,0 +1,52 @@
{
"db_name": "PostgreSQL",
"query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes FROM token WHERE email = $1 AND label != 'ephemeral-script'\n ORDER BY created_at DESC",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "label",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "token_prefix",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "expiration",
"type_info": "Timestamptz"
},
{
"ordinal": 3,
"name": "created_at",
"type_info": "Timestamptz"
},
{
"ordinal": 4,
"name": "last_used_at",
"type_info": "Timestamptz"
},
{
"ordinal": 5,
"name": "scopes",
"type_info": "TextArray"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
true,
null,
true,
false,
false,
true
]
},
"hash": "bc1f2f169b4960dae02f62e37bb4ae081146e598109860ee1f42cd3778c5ebaf"
}

View File

@@ -1,16 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE queue\n SET flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['failure_module'], $1),\n ARRAY['step'], $2)\n WHERE id = $3\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Jsonb",
"Jsonb",
"Uuid"
]
},
"nullable": []
},
"hash": "c461bdf083d7aaacafb6c981556b218342626524ee0c92f083dbff2930b7de9e"
}

View File

@@ -42,7 +42,6 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",

View File

@@ -0,0 +1,35 @@
{
"db_name": "PostgreSQL",
"query": "SELECT a.id as app_id, av.id as version_id, dm.deployment_msg as deployment_msg\n FROM app a LEFT JOIN app_version av ON a.id = av.app_id LEFT JOIN deployment_metadata dm ON av.id = dm.app_version\n WHERE a.workspace_id = $1 AND a.path = $2\n ORDER BY created_at DESC",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "app_id",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "version_id",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "deployment_msg",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false,
false,
true
]
},
"hash": "d02c1cfefc7d5f87ca4555a092f5fbc777a91271b80d0e488a8cae79afd56d8f"
}

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'iterator', 'index'], ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb)\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "int4",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Int4",
"Uuid"
]
},
"nullable": [
null
]
},
"hash": "d6c25421bb6513908697ebe74c159da0ef78b5252b6ac669a3c9e545d05c0d43"
}

View File

@@ -0,0 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO deployment_metadata (workspace_id, path, script_hash, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL DO UPDATE SET deployment_msg = $4",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Int8",
"Text"
]
},
"nullable": []
},
"hash": "db558b5ecdc4c3b1af0def511f1bcd91a548f00376f644c8ba38f73812b462d0"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "select path, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority from script where hash = $1 AND workspace_id = $2",
"query": "select path, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority, delete_after_use from script where hash = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
@@ -42,7 +42,6 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",
@@ -64,6 +63,11 @@
"ordinal": 7,
"name": "priority",
"type_info": "Int2"
},
{
"ordinal": 8,
"name": "delete_after_use",
"type_info": "Bool"
}
],
"parameters": {
@@ -80,8 +84,9 @@
true,
false,
true,
true,
true
]
},
"hash": "11ae5e1aade2bc9a78af2d956ece73766ae3330319619a68383b508a76f2d19c"
"hash": "e541faa52c530c787d45c5ee70930687587c139606056bdc06cd860abeb0805b"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT success\n FROM completed_job\n WHERE id = ANY($1)\n ",
"query": "SELECT success FROM completed_job WHERE id = ANY($1)",
"describe": {
"columns": [
{
@@ -18,5 +18,5 @@
false
]
},
"hash": "ed400dbf4a1fa597bc984240cfe9069369a8a265b127116187d78c0ba93ae076"
"hash": "e58cf2e3deb9aa1e9f37313a33e5d44fcfdb40a4764e1dc34896bd85b3d007f9"
}

View File

@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'iterator', 'index'], ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb)\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "int4",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Int4",
"Uuid"
]
},
"nullable": [
null
]
},
"hash": "e5a130478c8a581412f786b430c2c166c489faccbd0d9b448f8e7ad57559e605"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE workspace_settings SET git_sync = $1 WHERE workspace_id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Jsonb",
"Text"
]
},
"nullable": []
},
"hash": "ec4f70d9e6392ade016245de73e7a2831d430838307b086c96511f91e76a214a"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "select hash, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority from script where path = $1 AND workspace_id = $2 AND\n created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2 AND\n deleted = false AND lock IS not NULL AND lock_error_logs IS NULL)",
"query": "select hash, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority, delete_after_use from script where path = $1 AND workspace_id = $2 AND\n created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2 AND\n deleted = false AND lock IS not NULL AND lock_error_logs IS NULL)",
"describe": {
"columns": [
{
@@ -42,7 +42,6 @@
"bash",
"postgresql",
"nativets",
"Nativets",
"bun",
"mysql",
"bigquery",
@@ -64,6 +63,11 @@
"ordinal": 7,
"name": "priority",
"type_info": "Int2"
},
{
"ordinal": 8,
"name": "delete_after_use",
"type_info": "Bool"
}
],
"parameters": {
@@ -80,8 +84,9 @@
true,
false,
true,
true,
true
]
},
"hash": "b6074eb9369de071f79165bf506fbc44f1688035e1928b4c3649d875187b1e1d"
"hash": "f760667ffd6727443f023480627f62728bf9b6284d92e5fa9b97dc4e0b1c3a93"
}

View File

@@ -39,7 +39,8 @@
"graphql",
"postgresql",
"noop",
"appdependencies"
"appdependencies",
"deploymentcallback"
]
}
}
@@ -61,7 +62,8 @@
"graphql",
"postgresql",
"noop",
"appdependencies"
"appdependencies",
"deploymentcallback"
]
}
}

619
backend/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.216.0"
version = "1.224.0"
authors.workspace = true
edition.workspace = true
@@ -21,7 +21,7 @@ members = [
]
[workspace.package]
version = "1.216.0"
version = "1.224.0"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
@@ -35,6 +35,7 @@ incremental = true
[features]
enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise"]
saml = ["windmill-api/saml"]
benchmark = ["windmill-api/benchmark", "windmill-worker/benchmark", "windmill-queue/benchmark"]
flamegraph = ["windmill-common/flamegraph", "windmill-worker/flamegraph"]
loki = ["windmill-common/loki"]
@@ -98,7 +99,7 @@ hyper = { version = "^0", features = ["full"] }
tokio = { version = "^1", features = ["full", "tracing"] }
tower = "^0"
tower-http = { version = "^0.4", features = ["trace", "cors"] }
tower-cookies = "^0"
tower-cookies = "0.9.0"
serde = "^1"
serde_json = { version = "^1", features = ["preserve_order", "raw_value"] }
uuid = { version = "^1", features = ["serde", "v4"] }
@@ -137,6 +138,7 @@ json-pointer = "^0"
itertools = "^0"
regex = "^1"
deno_fetch = "0.139.0"
deno_tls = "0.102.0"
deno_console = "0.115.0"
deno_url = "0.115.0"
deno_webidl = "0.115.0"
@@ -180,7 +182,7 @@ async-stripe = { version = "0.25.2", features = [
async_zip = { version = "0.0.11", features = ["full"] }
once_cell = "1.17.1"
rsmq_async = { version = "5.1.5" }
gosyn = "0.2.2"
gosyn = "0.2.6"
bytes = "1.4.0"
gethostname = "0.4.3"
wasm-bindgen = "0.2"
@@ -192,7 +194,7 @@ tokio-postgres = {version = "^0.7", features = ["array-impls", "with-serde_json-
mysql_async = { version = "*", default-features = false, features = ["minimal", "default", "native-tls-tls"]}
postgres-native-tls = "^0"
native-tls = "^0"
samael = { version = "0.0.12", features = ["xmlsec"] }
samael = { version = "0.0.14", features = ["xmlsec"] }
gcp_auth = "0.9.0"
rust_decimal = { version = "^1", features = ["db-postgres"]}
jsonwebtoken = "8.3.0"

View File

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

View File

@@ -0,0 +1,3 @@
-- Add up migration script here
INSERT INTO config (name, config) VALUES
('worker__reports', '{"init_bash": "apt-get update\napt-get install -y chromium", "worker_tags": ["deno", "python3", "go", "bash", "powershell", "dependency", "flow", "hub", "other", "bun", "chromium"]}') ON CONFLICT DO NOTHING;

View File

@@ -0,0 +1,4 @@
-- Add down migration script here
ALTER TABLE workspace_settings DROP COLUMN git_sync;
DROP TABLE IF EXISTS deployment_metadata;

View File

@@ -0,0 +1,16 @@
-- Add up migration script here
ALTER TYPE JOB_KIND ADD VALUE IF NOT EXISTS 'deploymentcallback';
ALTER TABLE workspace_settings ADD COLUMN IF NOT EXISTS git_sync JSONB;
CREATE TABLE deployment_metadata(
workspace_id VARCHAR(50) NOT NULL REFERENCES workspace(id),
path VARCHAR(255) NOT NULL,
script_hash BIGINT,
app_version BIGSERIAL,
callback_job_ids UUID[],
deployment_msg TEXT
);
CREATE UNIQUE INDEX IF NOT EXISTS deployment_metadata_script ON deployment_metadata (workspace_id, script_hash) WHERE script_hash IS NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS deployment_metadata_flow ON deployment_metadata (workspace_id, path) WHERE script_hash IS NULL AND app_version IS NULL;
CREATE UNIQUE INDEX IF NOT EXISTS deployment_metadata_app ON deployment_metadata (workspace_id, path, app_version) WHERE app_version IS NOT NULL;

View File

@@ -0,0 +1,5 @@
-- Add down migration script here
ALTER TABLE script DROP COLUMN timeout;
ALTER TABLE script DROP COLUMN delete_after_use;
ALTER TABLE script DROP COLUMN restart_unless_cancelled;
ALTER TABLE flow DROP COLUMN timeout;

View File

@@ -0,0 +1,5 @@
-- Add up migration script here
ALTER TABLE script ADD COLUMN timeout INTEGER;
ALTER TABLE flow ADD COLUMN timeout INTEGER;
ALTER TABLE script ADD COLUMN delete_after_use BOOLEAN;
ALTER TABLE script ADD COLUMN restart_unless_cancelled BOOLEAN;

View File

@@ -0,0 +1,15 @@
-- Add down migration script here
DROP TABLE deployment_metadata;
CREATE TABLE deployment_metadata(
workspace_id VARCHAR(50) NOT NULL REFERENCES workspace(id),
path VARCHAR(255) NOT NULL,
script_hash BIGINT,
app_version BIGINT,
callback_job_ids UUID[],
deployment_msg TEXT
);
CREATE UNIQUE INDEX IF NOT EXISTS deployment_metadata_script ON deployment_metadata (workspace_id, script_hash) WHERE script_hash IS NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS deployment_metadata_flow ON deployment_metadata (workspace_id, path) WHERE script_hash IS NULL AND app_version IS NULL;
CREATE UNIQUE INDEX IF NOT EXISTS deployment_metadata_app ON deployment_metadata (workspace_id, path, app_version) WHERE app_version IS NOT NULL;

View File

@@ -0,0 +1,15 @@
-- Add up migration script here
DROP TABLE deployment_metadata;
CREATE TABLE deployment_metadata(
workspace_id VARCHAR(50) NOT NULL REFERENCES workspace(id),
path VARCHAR(255) NOT NULL,
script_hash BIGINT,
app_version BIGINT,
callback_job_ids UUID[],
deployment_msg TEXT
);
CREATE UNIQUE INDEX IF NOT EXISTS deployment_metadata_script ON deployment_metadata (workspace_id, script_hash) WHERE script_hash IS NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS deployment_metadata_flow ON deployment_metadata (workspace_id, path) WHERE script_hash IS NULL AND app_version IS NULL;
CREATE UNIQUE INDEX IF NOT EXISTS deployment_metadata_app ON deployment_metadata (workspace_id, path, app_version) WHERE app_version IS NOT NULL;

View File

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

View File

@@ -0,0 +1,6 @@
-- Add up migration script here
INSERT INTO global_settings VALUES ('custom_tags', '["chromium"]')
ON CONFLICT (name) DO UPDATE SET value = jsonb_insert(global_settings.value, '{0}', '"chromium"', true)
WHERE NOT global_settings.value @> '["chromium"]';

View File

@@ -61,24 +61,39 @@ lazy_static::lazy_static! {
static ref RE_CODE_PGSQL: Regex = Regex::new(r#"(?m)\$(\d+)(?:::(\w+(?:\[\])?))?"#).unwrap();
// -- $1 name (type) = default
static ref RE_ARG_MYSQL: Regex = Regex::new(r#"(?m)^-- \? (\w+) \((\w+)\)(?: ?\= ?(.+))? *[\r\n$]"#).unwrap();
static ref RE_ARG_MYSQL: Regex = Regex::new(r#"(?m)^-- \? (\w+) \((\w+)\)(?: ?\= ?(.+))? *(?:\r|\n|$)"#).unwrap();
pub static ref RE_ARG_MYSQL_NAMED: Regex = Regex::new(r#"(?m)^-- :([a-z_][a-z0-9_]*) \((\w+)\)(?: ?\= ?(.+))? *(?:\r|\n|$)"#).unwrap();
static ref RE_ARG_PGSQL: Regex = Regex::new(r#"(?m)^-- \$(\d+) (\w+)(?: ?\= ?(.+))? *[\r\n$]"#).unwrap();
static ref RE_ARG_PGSQL: Regex = Regex::new(r#"(?m)^-- \$(\d+) (\w+)(?: ?\= ?(.+))? *(?:\r|\n|$)"#).unwrap();
// -- @name (type) = default
static ref RE_ARG_BIGQUERY: Regex = Regex::new(r#"(?m)^-- @(\w+) \((\w+(?:\[\])?)\)(?: ?\= ?(.+))? *[\r\n$]"#).unwrap();
static ref RE_ARG_BIGQUERY: Regex = Regex::new(r#"(?m)^-- @(\w+) \((\w+(?:\[\])?)\)(?: ?\= ?(.+))? *(?:\r|\n|$)"#).unwrap();
static ref RE_ARG_SNOWFLAKE: Regex = Regex::new(r#"(?m)^-- \? (\w+) \((\w+)\)(?: ?\= ?(.+))? *[\r\n$]"#).unwrap();
static ref RE_ARG_SNOWFLAKE: Regex = Regex::new(r#"(?m)^-- \? (\w+) \((\w+)\)(?: ?\= ?(.+))? *(?:\r|\n|$)"#).unwrap();
static ref RE_ARG_MSSQL: Regex = Regex::new(r#"(?m)^-- @(?:P|p)\d+ (\w+) \((\w+)\)(?: ?\= ?(.+))? *[\r\n$]"#).unwrap();
static ref RE_ARG_MSSQL: Regex = Regex::new(r#"(?m)^-- @(?:P|p)\d+ (\w+) \((\w+)\)(?: ?\= ?(.+))? *(?:\r|\n|$)"#).unwrap();
}
fn parsed_default(parsed_typ: &Typ, default: String) -> Option<serde_json::Value> {
match parsed_typ {
_ if default.to_lowercase() == "null" => None,
Typ::Int => default.parse::<i64>().ok().map(|x| json!(x)),
Typ::Float => default.parse::<f64>().ok().map(|x| json!(x)),
Typ::Bool => default.parse::<bool>().ok().map(|x| json!(x)),
Typ::Str(_) if default.len() >= 2 && default.starts_with("'") && default.ends_with("'") => {
Some(json!(&default[1..default.len() - 1]))
}
_ => Some(json!(default)),
}
}
fn parse_mysql_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let mut args: Vec<Arg> = vec![];
for cap in RE_ARG_MYSQL.captures_iter(code) {
let mut using_named_args = false;
for cap in RE_ARG_MYSQL_NAMED.captures_iter(code) {
using_named_args = true;
let name = cap.get(1).map(|x| x.as_str().to_string()).unwrap();
let typ = cap
.get(2)
@@ -88,11 +103,7 @@ fn parse_mysql_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let has_default = default.is_some();
let parsed_typ = parse_mysql_typ(typ.as_str());
let parsed_default = default.and_then(|x| match parsed_typ {
Typ::Int => x.parse::<i64>().ok().map(|x| json!(x)),
Typ::Float => x.parse::<f64>().ok().map(|x| json!(x)),
_ => Some(json!(x)),
});
let parsed_default = default.and_then(|x| parsed_default(&parsed_typ, x));
args.push(Arg {
name,
typ: parsed_typ,
@@ -102,6 +113,30 @@ fn parse_mysql_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
});
}
if !using_named_args {
// backwards compatibility
for cap in RE_ARG_MYSQL.captures_iter(code) {
let name = cap.get(1).map(|x| x.as_str().to_string()).unwrap();
let typ = cap
.get(2)
.map(|x| x.as_str().to_string().to_lowercase())
.unwrap();
let default = cap.get(3).map(|x| x.as_str().to_string());
let has_default = default.is_some();
let parsed_typ = parse_mysql_typ(typ.as_str());
let parsed_default = default.and_then(|x| parsed_default(&parsed_typ, x));
args.push(Arg {
name,
typ: parsed_typ,
default: parsed_default,
otyp: Some(typ),
has_default,
});
}
}
Ok(Some(args))
}
@@ -141,11 +176,8 @@ fn parse_pg_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let default = cap.get(3).map(|x| x.as_str().to_string());
let has_default = default.is_some();
let oarg = args[(i.unwrap() - 1) as usize].clone();
let parsed_default = default.and_then(|x| match oarg.typ {
Typ::Int => x.parse::<i64>().ok().map(|x| json!(x)),
Typ::Float => x.parse::<f64>().ok().map(|x| json!(x)),
_ => Some(json!(x)),
});
let parsed_default = default.and_then(|x| parsed_default(&oarg.typ, x));
args[(i.unwrap() - 1) as usize] =
Arg { name, typ: oarg.typ, default: parsed_default, otyp: oarg.otyp, has_default };
}
@@ -166,11 +198,8 @@ fn parse_bigquery_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let has_default = default.is_some();
let parsed_typ = parse_bigquery_typ(typ.as_str());
let parsed_default = default.and_then(|x| match parsed_typ {
Typ::Int => x.parse::<i64>().ok().map(|x| json!(x)),
Typ::Float => x.parse::<f64>().ok().map(|x| json!(x)),
_ => Some(json!(x)),
});
let parsed_default = default.and_then(|x| parsed_default(&parsed_typ, x));
args.push(Arg {
name,
typ: parsed_typ,
@@ -196,11 +225,8 @@ fn parse_snowflake_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let has_default = default.is_some();
let parsed_typ = parse_snowflake_typ(typ.as_str());
let parsed_default = default.and_then(|x| match parsed_typ {
Typ::Int => x.parse::<i64>().ok().map(|x| json!(x)),
Typ::Float => x.parse::<f64>().ok().map(|x| json!(x)),
_ => Some(json!(x)),
});
let parsed_default = default.and_then(|x| parsed_default(&parsed_typ, x));
args.push(Arg {
name,
typ: parsed_typ,
@@ -226,11 +252,8 @@ fn parse_mssql_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let has_default = default.is_some();
let parsed_typ = parse_mssql_typ(typ.as_str());
let parsed_default = default.and_then(|x| match parsed_typ {
Typ::Int => x.parse::<i64>().ok().map(|x| json!(x)),
Typ::Float => x.parse::<f64>().ok().map(|x| json!(x)),
_ => Some(json!(x)),
});
let parsed_default = default.and_then(|x| parsed_default(&parsed_typ, x));
args.push(Arg {
name,
typ: parsed_typ,
@@ -251,6 +274,7 @@ pub fn parse_mysql_typ(typ: &str) -> Typ {
"int" | "uint" | "integer" => Typ::Int,
"bool" | "bit" => Typ::Bool,
"double precision" | "float" | "real" | "dec" | "fixed" => Typ::Float,
"date" | "datetime" | "timestamp" | "time" => Typ::Datetime,
_ => Typ::Str(None),
}
}

View File

@@ -311,6 +311,12 @@ fn tstype_to_typ(ts_type: &TsType) -> (Typ, bool) {
} else {
let literals = types
.into_iter()
.filter(|x| match ***x {
TsType::TsKeywordType(TsKeywordType { kind, .. }) => {
kind != TsKeywordTypeKind::TsStringKeyword
}
_ => true,
})
.map(|x| match &**x {
TsType::TsLitType(TsLitType {
lit: TsLit::Str(Str { value, .. }), ..

View File

@@ -3,7 +3,7 @@
"collaborators": [
"Ruben Fiszel <ruben@windmill.dev>"
],
"version": "1.202.1",
"version": "1.222.0",
"files": [
"windmill_parser_wasm_bg.wasm",
"windmill_parser_wasm.js",

View File

@@ -97,15 +97,6 @@ function getInt32Memory0() {
return cachedInt32Memory0;
}
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}
function addHeapObject(obj) {
if (heap_next === heap.length) heap.push(heap.length + 1);
const idx = heap_next;
@@ -124,6 +115,15 @@ function getFloat64Memory0() {
return cachedFloat64Memory0;
}
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}
let cachedBigInt64Memory0 = null;
function getBigInt64Memory0() {
@@ -518,6 +518,10 @@ function __wbg_get_imports() {
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
takeObject(arg0);
};
imports.wbg.__wbg_eval_1081105c41705556 = function(arg0, arg1) {
const ret = eval(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
};
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
const obj = getObject(arg1);
const ret = typeof(obj) === 'string' ? obj : undefined;
@@ -526,14 +530,6 @@ function __wbg_get_imports() {
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
};
imports.wbg.__wbg_eval_f742bccbdcf34b02 = function(arg0, arg1) {
const ret = eval(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
};
imports.wbg.__wbindgen_error_new = function(arg0, arg1) {
const ret = new Error(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
};
imports.wbg.__wbindgen_boolean_get = function(arg0) {
const v = getObject(arg0);
const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
@@ -551,6 +547,10 @@ function __wbg_get_imports() {
const ret = getObject(arg0) === getObject(arg1);
return ret;
};
imports.wbg.__wbindgen_bigint_from_u64 = function(arg0) {
const ret = BigInt.asUintN(64, arg0);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_number_get = function(arg0, arg1) {
const obj = getObject(arg1);
const ret = typeof(obj) === 'number' ? obj : undefined;
@@ -566,19 +566,19 @@ function __wbg_get_imports() {
const ret = getObject(arg0) in getObject(arg1);
return ret;
};
imports.wbg.__wbindgen_bigint_from_u64 = function(arg0) {
const ret = BigInt.asUintN(64, arg0);
imports.wbg.__wbindgen_error_new = function(arg0, arg1) {
const ret = new Error(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
};
imports.wbg.__wbindgen_jsval_loose_eq = function(arg0, arg1) {
const ret = getObject(arg0) == getObject(arg1);
return ret;
};
imports.wbg.__wbg_get_4a9aa5157afeb382 = function(arg0, arg1) {
imports.wbg.__wbg_get_f01601b5a68d10e3 = function(arg0, arg1) {
const ret = getObject(arg0)[arg1 >>> 0];
return addHeapObject(ret);
};
imports.wbg.__wbg_length_cace2e0b3ddc0502 = function(arg0) {
imports.wbg.__wbg_length_1009b1af0c481d7b = function(arg0) {
const ret = getObject(arg0).length;
return ret;
};
@@ -586,39 +586,39 @@ function __wbg_get_imports() {
const ret = typeof(getObject(arg0)) === 'function';
return ret;
};
imports.wbg.__wbg_next_15da6a3df9290720 = function(arg0) {
imports.wbg.__wbg_next_9b877f231f476d01 = function(arg0) {
const ret = getObject(arg0).next;
return addHeapObject(ret);
};
imports.wbg.__wbg_next_1989a20442400aaa = function() { return handleError(function (arg0) {
imports.wbg.__wbg_next_6529ee0cca8d57ed = function() { return handleError(function (arg0) {
const ret = getObject(arg0).next();
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_done_bc26bf4ada718266 = function(arg0) {
imports.wbg.__wbg_done_5fe336b092d60cf2 = function(arg0) {
const ret = getObject(arg0).done;
return ret;
};
imports.wbg.__wbg_value_0570714ff7d75f35 = function(arg0) {
imports.wbg.__wbg_value_0c248a78fdc8e19f = function(arg0) {
const ret = getObject(arg0).value;
return addHeapObject(ret);
};
imports.wbg.__wbg_iterator_7ee1a391d310f8e4 = function() {
imports.wbg.__wbg_iterator_db7ca081358d4fb2 = function() {
const ret = Symbol.iterator;
return addHeapObject(ret);
};
imports.wbg.__wbg_get_2aff440840bb6202 = function() { return handleError(function (arg0, arg1) {
imports.wbg.__wbg_get_7b48513de5dc5ea4 = function() { return handleError(function (arg0, arg1) {
const ret = Reflect.get(getObject(arg0), getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_call_669127b9d730c650 = function() { return handleError(function (arg0, arg1) {
imports.wbg.__wbg_call_90c26b09837aba1c = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg0).call(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_isArray_38525be7442aa21e = function(arg0) {
imports.wbg.__wbg_isArray_74fb723e24f76012 = function(arg0) {
const ret = Array.isArray(getObject(arg0));
return ret;
};
imports.wbg.__wbg_instanceof_ArrayBuffer_c7cc317e5c29cc0d = function(arg0) {
imports.wbg.__wbg_instanceof_ArrayBuffer_e7d53d51371448e2 = function(arg0) {
let result;
try {
result = getObject(arg0) instanceof ArrayBuffer;
@@ -628,30 +628,30 @@ function __wbg_get_imports() {
const ret = result;
return ret;
};
imports.wbg.__wbg_isSafeInteger_c38b0a16d0c7cef7 = function(arg0) {
imports.wbg.__wbg_isSafeInteger_f93fde0dca9820f8 = function(arg0) {
const ret = Number.isSafeInteger(getObject(arg0));
return ret;
};
imports.wbg.__wbg_entries_6d727b73ee02b7ce = function(arg0) {
imports.wbg.__wbg_entries_9e2e2aa45aa5094a = function(arg0) {
const ret = Object.entries(getObject(arg0));
return addHeapObject(ret);
};
imports.wbg.__wbg_buffer_344d9b41efe96da7 = function(arg0) {
imports.wbg.__wbg_buffer_a448f833075b71ba = function(arg0) {
const ret = getObject(arg0).buffer;
return addHeapObject(ret);
};
imports.wbg.__wbg_new_d8a000788389a31e = function(arg0) {
imports.wbg.__wbg_new_8f67e318f15d7254 = function(arg0) {
const ret = new Uint8Array(getObject(arg0));
return addHeapObject(ret);
};
imports.wbg.__wbg_set_dcfd613a3420f908 = function(arg0, arg1, arg2) {
imports.wbg.__wbg_set_2357bf09366ee480 = function(arg0, arg1, arg2) {
getObject(arg0).set(getObject(arg1), arg2 >>> 0);
};
imports.wbg.__wbg_length_a5587d6cd79ab197 = function(arg0) {
imports.wbg.__wbg_length_1d25fa9e4ac21ce7 = function(arg0) {
const ret = getObject(arg0).length;
return ret;
};
imports.wbg.__wbg_instanceof_Uint8Array_19e6f142a5e7e1e1 = function(arg0) {
imports.wbg.__wbg_instanceof_Uint8Array_bced6f43aed8c1aa = function(arg0) {
let result;
try {
result = getObject(arg0) instanceof Uint8Array;

View File

@@ -218,7 +218,7 @@ async fn main() -> anyhow::Result<()> {
// migration code to avoid break
windmill_api::migrate_db(&db).await?;
let last_mig_version = sqlx::query_scalar!(
let new_last_mig_version = sqlx::query_scalar!(
"select version from _sqlx_migrations order by version desc limit 1;"
)
.fetch_optional(&db)
@@ -226,12 +226,19 @@ async fn main() -> anyhow::Result<()> {
.ok()
.flatten();
tracing::info!(
"Completed potential migration of the db. Last migration version: {last_mig_version:?}",
);
if last_mig_version != new_last_mig_version {
tracing::info!(
"Completed migration of the db. New migration version: {}",
new_last_mig_version.unwrap_or(-1)
);
} else {
tracing::info!("No migration, db was up-to-date");
}
}
let (tx, rx) = tokio::sync::broadcast::channel::<()>(3);
let shutdown_signal = windmill_common::shutdown_signal(tx.clone(), rx.resubscribe());
let (killpill_tx, killpill_rx) = tokio::sync::broadcast::channel::<()>(2);
let (killpill_phase2_tx, killpill_phase2_rx) = tokio::sync::broadcast::channel::<()>(2);
let shutdown_signal =
windmill_common::shutdown_signal(killpill_tx.clone(), killpill_rx.resubscribe());
#[cfg(feature = "enterprise")]
tracing::info!(
@@ -275,7 +282,7 @@ Windmill Community Edition {GIT_VERSION}
default_base_internal_url.clone()
};
initial_load(&db, tx.clone(), worker_mode, server_mode).await;
initial_load(&db, killpill_tx.clone(), worker_mode, server_mode).await;
monitor_db(&db, &base_internal_url, rsmq.clone(), server_mode).await;
@@ -298,7 +305,7 @@ Windmill Community Edition {GIT_VERSION}
db.clone(),
rsmq2,
addr,
rx.resubscribe(),
killpill_phase2_rx.resubscribe(),
base_internal_tx,
server_mode,
)
@@ -318,25 +325,29 @@ Windmill Community Edition {GIT_VERSION}
if worker_mode {
run_workers(
db.clone(),
rx.resubscribe(),
tx.clone(),
killpill_rx.resubscribe(),
killpill_tx.clone(),
num_workers,
base_internal_url.clone(),
rsmq.clone(),
)
.await?;
tracing::info!("All workers exited.");
tx.send(())?; // signal server to shutdown
killpill_tx.send(())?;
} else {
killpill_rx.resubscribe().recv().await?;
}
tracing::info!("Starting phase 2 of shutdown");
killpill_phase2_tx.send(())?;
Ok(()) as anyhow::Result<()>
};
let monitor_f = async {
let db = db.clone();
let tx = tx.clone();
let tx = killpill_tx.clone();
let rsmq = rsmq.clone();
let mut rx = rx.resubscribe();
let mut rx = killpill_rx.resubscribe();
let base_internal_url = base_internal_url.to_string();
let h = tokio::spawn(async move {
let mut listener = retry_listen_pg(&db).await;
@@ -472,8 +483,12 @@ Windmill Community Edition {GIT_VERSION}
tracing::error!("Metrics are only available in the EE, ignoring...");
#[cfg(feature = "enterprise")]
windmill_common::serve_metrics(*METRICS_ADDR, rx.resubscribe(), num_workers > 0)
.await;
windmill_common::serve_metrics(
*METRICS_ADDR,
killpill_phase2_rx.resubscribe(),
num_workers > 0,
)
.await;
}
Ok(()) as anyhow::Result<()>
};
@@ -481,7 +496,7 @@ Windmill Community Edition {GIT_VERSION}
let instance_name = rd_string(8);
schedule_stats(instance_name, mode, &db, &HTTP_CLIENT).await;
futures::try_join!(shutdown_signal, server_f, metrics_f, workers_f, monitor_f)?;
futures::try_join!(shutdown_signal, workers_f, monitor_f, server_f, metrics_f)?;
} else {
tracing::info!("Nothing to do, exiting.");
}

View File

@@ -719,7 +719,7 @@ async fn handle_zombie_jobs<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
&db,
&job.workspace_id,
&job.permissioned_as,
"ephemeral-zombie-jobs",
"ephemeral-script",
*SCRIPT_TOKEN_EXPIRY,
&job.email,
)

View File

@@ -1084,6 +1084,7 @@ async fn test_deno_flow(db: Pool<Postgres>) {
mock: None,
timeout: None,
priority: None,
delete_after_use: None
},
FlowModule {
id: "b".to_string(),
@@ -1119,6 +1120,7 @@ async fn test_deno_flow(db: Pool<Postgres>) {
mock: None,
timeout: None,
priority: None,
delete_after_use: None,
}],
},
stop_after_if: Default::default(),
@@ -1130,6 +1132,7 @@ async fn test_deno_flow(db: Pool<Postgres>) {
mock: None,
timeout: None,
priority: None,
delete_after_use: None,
},
],
same_worker: false,
@@ -1230,6 +1233,7 @@ async fn test_deno_flow_same_worker(db: Pool<Postgres>) {
mock: None,
timeout: None,
priority: None,
delete_after_use: None,
},
FlowModule {
id: "b".to_string(),
@@ -1276,6 +1280,7 @@ async fn test_deno_flow_same_worker(db: Pool<Postgres>) {
mock: None,
timeout: None,
priority: None,
delete_after_use: None,
},
FlowModule {
id: "e".to_string(),
@@ -1308,6 +1313,7 @@ async fn test_deno_flow_same_worker(db: Pool<Postgres>) {
mock: None,
timeout: None,
priority: None,
delete_after_use: None
},
],
},
@@ -1320,26 +1326,26 @@ async fn test_deno_flow_same_worker(db: Pool<Postgres>) {
mock: None,
timeout: None,
priority: None,
delete_after_use: None
},
FlowModule {
id: "c".to_string(),
value: FlowModuleValue::RawScript {
input_transforms: [
(
"loops".to_string(),
InputTransform::Javascript { expr: "results.b".to_string() },
),
(
"path".to_string(),
InputTransform::Static { value: json!("outer.txt") },
),
(
"path2".to_string(),
InputTransform::Static { value: json!("inner.txt") },
),
]
.into(),
(
"loops".to_string(),
InputTransform::Javascript { expr: "results.b".to_string() },
),
(
"path".to_string(),
InputTransform::Static { value: json!("outer.txt") },
),
(
"path2".to_string(),
InputTransform::Static { value: json!("inner.txt") },
),
]
.into(),
language: ScriptLang::Deno,
content: r#"export async function main(path: string, loops: string[], path2: string) {
return await Deno.readTextFile(`./shared/${path}`) + "," + loops + "," + await Deno.readTextFile(`./shared/${path2}`);
@@ -1360,6 +1366,7 @@ async fn test_deno_flow_same_worker(db: Pool<Postgres>) {
mock: None,
timeout: None,
priority: None,
delete_after_use: None,
},
],
same_worker: true,
@@ -2579,8 +2586,10 @@ async fn test_flow_lock_all(db: Pool<Postgres>) {
ws_error_handler_muted: None,
priority: None,
dedicated_worker: None,
timeout: None,
},
draft_only: None,
deployment_message: None,
},
)
.await
@@ -2802,10 +2811,9 @@ async fn test_complex_flow_restart(db: Pool<Postgres>) {
}),
}).run_until_complete(&db, port).await;
assert_eq!(
first_run_result.json_result().unwrap(),
restarted_flow_result.json_result().unwrap()
);
let first_run_result_int = serde_json::from_value::<i32>(first_run_result.json_result().unwrap()).expect("first_run_result was not an int");
let restarted_flow_result_int = serde_json::from_value::<i32>(restarted_flow_result.json_result().unwrap()).expect("restarted_flow_result was not an int");
assert_eq!(first_run_result_int, restarted_flow_result_int);
}
#[sqlx::test(fixtures("base"))]
@@ -3129,6 +3137,10 @@ async fn run_deployed_relative_imports(db: &Pool<Postgres>, script_content: Stri
schema: std::collections::HashMap::new(),
ws_error_handler_muted: Some(false),
priority: None,
delete_after_use: None,
timeout: None,
restart_unless_cancelled: None,
deployment_message: None,
},
).await.unwrap();

View File

@@ -9,7 +9,8 @@ name = "windmill_api"
path = "src/lib.rs"
[features]
enterprise = ["windmill-queue/enterprise", "async-stripe", "samael", "windmill-audit/enterprise"]
enterprise = ["windmill-queue/enterprise", "async-stripe", "windmill-audit/enterprise"]
saml = ["samael"]
benchmark = []
[dependencies]
@@ -82,8 +83,8 @@ tokenizers.workspace = true
candle-core.workspace = true
candle-transformers.workspace = true
candle-nn.workspace = true
aws-sdk-s3 = "0.36.0"
aws-config = "0.57.2"
aws-sdk-s3 = "0.39.1"
aws-config = "1.0.0"
polars = { version = "0.35.2", features = ["lazy", "parquet", "aws", "csv", "dtype-full"] }
polars-io = { version = "0.35.2", features = ["csv"] }
object_store = { version = "0.8.0", features = ["aws"] }

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.216.0
version: 1.224.0
title: Windmill API
contact:
@@ -1192,6 +1192,8 @@ paths:
type: boolean
large_file_storage:
$ref: "#/components/schemas/LargeFileStorage"
git_sync:
$ref: "#/components/schemas/WorkspaceGitSync"
required:
- code_completion_enabled
@@ -1513,6 +1515,32 @@ paths:
application/json:
schema: {}
/w/{workspace}/workspaces/edit_git_sync_config:
post:
summary: edit workspace git sync settings
operationId: editWorkspaceGitSyncConfig
tags:
- workspace
parameters:
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: Workspace Git sync settings
required: true
content:
application/json:
schema:
type: object
properties:
git_sync_settings:
$ref: "#/components/schemas/WorkspaceGitSync"
responses:
"200":
description: status
content:
application/json:
schema: {}
/w/{workspace}/workspaces/get_large_file_storage_config:
get:
summary: get large file storage config
@@ -1634,6 +1662,11 @@ paths:
operationId: listTokens
tags:
- user
parameters:
- name: exclude_ephemeral
in: query
schema:
type: boolean
responses:
"200":
description: truncated token
@@ -3192,6 +3225,53 @@ paths:
schema:
$ref: "#/components/schemas/NewScriptWithDraft"
/w/{workspace}/scripts/history/p/{path}:
get:
summary: get history of a script by path
operationId: getScriptHistoryByPath
tags:
- script
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
responses:
"200":
description: script history
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ScriptHistory"
/w/{workspace}/scripts/history_update/h/{hash}/p/{path}:
post:
summary: update history of a script
operationId: updateScriptHistory
tags:
- script
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptHash"
- $ref: "#/components/parameters/ScriptPath"
requestBody:
description: Script deployment message
required: true
content:
application/json:
schema:
type: object
properties:
deployment_msg:
type: string
responses:
"200":
description: success
content:
text/plain:
schema:
type: string
/w/{workspace}/scripts/raw/p/{path}:
get:
summary: raw script by path
@@ -3707,6 +3787,8 @@ paths:
properties:
draft_only:
type: boolean
deployment_message:
type: string
responses:
"201":
description: flow created
@@ -3730,7 +3812,12 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/OpenFlowWPath"
allOf:
- $ref: "#/components/schemas/OpenFlowWPath"
- type: object
properties:
deployment_message:
type: string
responses:
"200":
@@ -3968,6 +4055,8 @@ paths:
$ref: "#/components/schemas/Policy"
draft_only:
type: boolean
deployment_message:
type: string
required:
- path
- value
@@ -4032,6 +4121,53 @@ paths:
schema:
$ref: "#/components/schemas/AppWithLastVersionWDraft"
/w/{workspace}/apps/history/p/{path}:
get:
summary: get app history by path
operationId: getAppHistoryByPath
tags:
- app
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
responses:
"200":
description: app history
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/AppHistory"
/w/{workspace}/apps/history_update/a/{id}/v/{version}:
post:
summary: update app history
operationId: updateAppHistory
tags:
- app
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/PathId"
- $ref: "#/components/parameters/PathVersion"
requestBody:
description: App deployment message
required: true
content:
application/json:
schema:
type: object
properties:
deployment_msg:
type: string
responses:
"200":
description: success
content:
text/plain:
schema:
type: string
/w/{workspace}/apps_u/public_app/{path}:
get:
summary: get public app by secret
@@ -4222,6 +4358,8 @@ paths:
value: {}
policy:
$ref: "#/components/schemas/Policy"
deployment_message:
type: string
responses:
"200":
description: app updated
@@ -5890,6 +6028,26 @@ paths:
items:
$ref: "#/components/schemas/WorkerPing"
/workers/exists_worker_with_tag:
get:
summary: exists worker with tag
operationId: existsWorkerWithTag
tags:
- worker
parameters:
- name: tag
in: query
required: true
schema:
type: string
responses:
"200":
description: whether a worker with the tag exists
content:
application/json:
schema:
type: boolean
/configs/list_worker_groups:
get:
summary: list worker groups
@@ -6308,8 +6466,10 @@ paths:
content:
application/json:
schema:
s3_resource:
$ref: "#/components/schemas/S3Resource"
type: object
properties:
s3_resource:
$ref: "#/components/schemas/S3Resource"
responses:
"200":
description: Connection settings
@@ -6320,6 +6480,39 @@ paths:
properties:
connection_settings_str:
type: string
/w/{workspace}/job_helpers/v2/duckdb_connection_settings:
post:
summary:
Converts an S3 resource to the set of instructions necessary to connect
DuckDB to an S3 bucket
operationId: duckdbConnectionSettingsV2
tags:
- helpers
parameters:
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: S3 resource path to use to generate the connection settings. If empty, the S3 resource defined in the workspace settings will be used
required: true
content:
application/json:
schema:
type: object
properties:
s3_resource_path:
type: string
responses:
"200":
description: Connection settings
content:
application/json:
schema:
type: object
properties:
connection_settings_str:
type: string
required:
- connection_settings_str
/w/{workspace}/job_helpers/polars_connection_settings:
post:
summary:
@@ -6336,8 +6529,10 @@ paths:
content:
application/json:
schema:
s3_resource:
$ref: "#/components/schemas/S3Resource"
type: object
properties:
s3_resource:
$ref: "#/components/schemas/S3Resource"
responses:
"200":
description: Connection settings
@@ -6363,6 +6558,74 @@ paths:
- use_ssl
- cache_regions
- client_kwargs
/w/{workspace}/job_helpers/v2/polars_connection_settings:
post:
summary:
Converts an S3 resource to the set of arguments necessary to connect
Polars to an S3 bucket
operationId: polarsConnectionSettingsV2
tags:
- helpers
parameters:
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: S3 resource path to use to generate the connection settings. If empty, the S3 resource defined in the workspace settings will be used
required: true
content:
application/json:
schema:
type: object
properties:
s3_resource_path:
type: string
responses:
"200":
description: Connection settings
content:
application/json:
schema:
type: object
properties:
s3fs_args:
type: object
properties:
endpoint_url:
type: string
key:
type: string
secret:
type: string
use_ssl:
type: boolean
cache_regions:
type: boolean
client_kwargs:
$ref: "#/components/schemas/PolarsClientKwargs"
required:
- endpoint_url
- use_ssl
- cache_regions
- client_kwargs
polars_cloud_options:
type: object
properties:
aws_endpoint_url:
type: string
aws_access_key_id:
type: string
aws_secret_access_key:
type: string
aws_region:
type: string
aws_allow_http:
type: boolean
required:
- aws_endpoint_url
- aws_region
- aws_allow_http
required:
- s3fs_args
- cloud_options
/w/{workspace}/job_helpers/test_connection:
get:
@@ -6459,6 +6722,10 @@ paths:
in: query
schema:
type: string
- name: csv_has_header
in: query
schema:
type: boolean
- name: read_bytes_from
in: query
schema:
@@ -6553,6 +6820,12 @@ components:
required: true
schema:
type: integer
PathVersion:
name: version
in: path
required: true
schema:
type: integer
Name:
name: name
in: path
@@ -6882,6 +7155,12 @@ components:
type: boolean
priority:
type: integer
restart_unless_cancelled:
type: boolean
timeout:
type: integer
delete_after_use:
type: boolean
required:
- hash
- path
@@ -6960,6 +7239,14 @@ components:
type: boolean
priority:
type: integer
restart_unless_cancelled:
type: boolean
timeout:
type: integer
delete_after_use:
type: boolean
deployment_message:
type: string
required:
- path
- summary
@@ -6979,6 +7266,16 @@ components:
required:
- hash
ScriptHistory:
type: object
properties:
script_hash:
type: string
deployment_msg:
type: string
required:
- script_hash
ScriptArgs:
type: object
additionalProperties: {}
@@ -7095,6 +7392,7 @@ components:
"flowpreview",
"script_hub",
"identity",
"deploymentcallback",
]
schedule_path:
type: string
@@ -7203,6 +7501,7 @@ components:
"flowpreview",
"script_hub",
"identity",
"deploymentcallback",
]
schedule_path:
type: string
@@ -8227,6 +8526,8 @@ components:
type: integer
dedicated_worker:
type: boolean
timeout:
type: number
required:
- path
- edited_by
@@ -8249,6 +8550,8 @@ components:
type: integer
dedicated_worker:
type: boolean
timeout:
type: number
required:
- path
@@ -8412,6 +8715,16 @@ components:
type: boolean
draft: {}
AppHistory:
type: object
properties:
version:
type: integer
deployment_msg:
type: string
required:
- version
SlackToken:
type: object
properties:
@@ -8532,3 +8845,14 @@ components:
- endPoint
- useSSL
- pathStyle
WorkspaceGitSync:
type: object
properties:
script_path:
type: string
git_repo_resource_path:
type: string
required:
- script_path
- git_repo_resource_path

View File

@@ -9,6 +9,7 @@ use std::collections::HashMap;
*/
use crate::{
db::{ApiAuthed, DB},
deployment_metadata_helpers,
users::{require_owner_of_path, OptAuthed},
variables::build_crypt,
webhook_util::{WebhookMessage, WebhookShared},
@@ -54,6 +55,8 @@ pub fn workspaced_service() -> Router {
.route("/update/*path", post(update_app))
.route("/delete/*path", delete(delete_app))
.route("/create", post(create_app))
.route("/history/p/*path", get(get_app_history))
.route("/history_update/a/:id/v/:version", post(update_app_history))
}
pub fn unauthed_service() -> Router {
@@ -124,6 +127,19 @@ pub struct AppWithLastVersionAndDraft {
pub draft_only: Option<bool>,
}
#[derive(Serialize)]
pub struct AppHistory {
pub app_id: i64,
pub version: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub deployment_msg: Option<String>,
}
#[derive(Deserialize)]
pub struct AppHistoryUpdate {
pub deployment_msg: Option<String>,
}
pub type StaticFields = HashMap<String, Box<RawValue>>;
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
@@ -153,6 +169,7 @@ pub struct CreateApp {
pub value: serde_json::Value,
pub policy: Policy,
pub draft_only: Option<bool>,
pub deployment_message: Option<String>,
}
#[derive(Deserialize)]
@@ -161,6 +178,7 @@ pub struct EditApp {
pub summary: Option<String>,
pub value: Option<serde_json::Value>,
pub policy: Option<Policy>,
pub deployment_message: Option<String>,
}
#[derive(Serialize, FromRow)]
@@ -312,6 +330,64 @@ async fn get_app_w_draft(
Ok(Json(app))
}
async fn get_app_history(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<Vec<AppHistory>> {
let mut tx = user_db.begin(&authed).await?;
let query_result = sqlx::query!(
"SELECT a.id as app_id, av.id as version_id, dm.deployment_msg as deployment_msg
FROM app a LEFT JOIN app_version av ON a.id = av.app_id LEFT JOIN deployment_metadata dm ON av.id = dm.app_version
WHERE a.workspace_id = $1 AND a.path = $2
ORDER BY created_at DESC",
w_id,
path.to_path(),
).fetch_all(&mut *tx).await?;
tx.commit().await?;
let result: Vec<AppHistory> = query_result
.into_iter()
.map(|row| AppHistory {
app_id: row.app_id,
version: row.version_id,
deployment_msg: row.deployment_msg,
})
.collect();
return Ok(Json(result));
}
async fn update_app_history(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, app_id, app_version)): Path<(String, i64, i64)>,
Json(app_history_update): Json<AppHistoryUpdate>,
) -> Result<()> {
let mut tx = user_db.begin(&authed).await?;
let app_path = sqlx::query_scalar!("SELECT path FROM app WHERE id = $1", app_id)
.fetch_optional(&mut *tx)
.await?;
if app_path.is_none() {
tx.commit().await?;
return Err(Error::NotFound(
format!("App with ID {app_id} not found").to_string(),
));
}
sqlx::query!(
"INSERT INTO deployment_metadata (workspace_id, path, app_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, app_version) WHERE app_version IS NOT NULL DO UPDATE SET deployment_msg = $4",
w_id,
app_path.unwrap(),
app_version,
app_history_update.deployment_msg,
)
.fetch_optional(&mut *tx)
.await?;
tx.commit().await?;
return Ok(());
}
async fn get_app_by_id(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
@@ -512,8 +588,8 @@ async fn create_app(
)
.await?;
let tx = PushIsolationLevel::Transaction(tx);
let (dependency_job_uuid, tx) = push(
let mut tx = PushIsolationLevel::Transaction(tx);
let (dependency_job_uuid, new_tx) = push(
&db,
tx,
&w_id,
@@ -538,7 +614,27 @@ async fn create_app(
)
.await?;
tracing::info!("Pushed app dependency job {}", dependency_job_uuid);
tx.commit().await?;
tx = PushIsolationLevel::Transaction(new_tx);
tx = deployment_metadata_helpers::handle_deployment_metadata(
tx,
&authed,
&db,
&w_id,
deployment_metadata_helpers::DeployedObject::App { path: app.path.clone(), version: v_id },
app.deployment_message,
)
.await?;
match tx {
PushIsolationLevel::Transaction(tx) => tx.commit().await?,
_ => {
return Err(Error::InternalErr(
"Expected a transaction here".to_string(),
));
}
}
webhook.send_message(
w_id.clone(),
WebhookMessage::CreateApp { workspace: w_id, path: app.path.clone() },
@@ -753,11 +849,10 @@ async fn update_app(
)
.await?;
let mut tx: PushIsolationLevel<'_, rsmq_async::MultiplexedRsmq> =
PushIsolationLevel::Transaction(tx);
if let Some(v_id) = v_id {
let tx: PushIsolationLevel<'_, rsmq_async::MultiplexedRsmq> =
PushIsolationLevel::Transaction(tx);
let (dependency_job_uuid, tx) = push(
let (dependency_job_uuid, new_tx) = push(
&db,
tx,
&w_id,
@@ -782,10 +877,28 @@ async fn update_app(
)
.await?;
tracing::info!("Pushed app dependency job {}", dependency_job_uuid);
tx.commit().await?;
} else {
tx.commit().await?;
tx = PushIsolationLevel::Transaction(new_tx);
tx = deployment_metadata_helpers::handle_deployment_metadata(
tx,
&authed,
&db,
&w_id,
deployment_metadata_helpers::DeployedObject::App { path: npath.clone(), version: v_id },
ns.deployment_message,
)
.await?;
}
match tx {
PushIsolationLevel::Transaction(tx) => tx.commit().await?,
_ => {
return Err(Error::InternalErr(
"Expected a transaction here".to_string(),
));
}
}
webhook.send_message(
w_id.clone(),
WebhookMessage::UpdateApp {

View File

@@ -0,0 +1,176 @@
/*
* Author: Ruben Fiszel
* Copyright: Windmill Labs, Inc 2022
* This file and its contents are licensed under the AGPLv3 License.
* Please see the included NOTICE for copyright information and
* LICENSE-AGPL for a copy of the license.
*/
use std::collections::HashMap;
use crate::db::ApiAuthed;
use crate::db::DB;
use crate::workspaces::{WorkspaceGitRepo, WorkspaceSettings};
use serde_json::json;
use windmill_common::error::{Error, Result};
use windmill_common::jobs::JobPayload;
use windmill_common::scripts::ScriptHash;
use windmill_common::users::username_to_permissioned_as;
use windmill_queue::PushIsolationLevel;
#[derive(Clone)]
pub enum DeployedObject {
Script { hash: ScriptHash, path: String },
Flow { path: String },
App { path: String, version: i64 },
}
impl DeployedObject {
pub fn get_path(&self) -> &str {
match self {
DeployedObject::Script { path, .. } => path,
DeployedObject::Flow { path } => path,
DeployedObject::App { path, .. } => path,
}
}
}
pub async fn handle_deployment_metadata<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
mut tx: PushIsolationLevel<'c, R>,
authed: &ApiAuthed,
db: &DB,
w_id: &str,
obj: DeployedObject,
deployment_message: Option<String>,
) -> Result<PushIsolationLevel<'c, R>> {
let skip_git_sync = if obj.get_path().starts_with("u/") {
tracing::debug!(
"Ignoring {} from git sync as it's in a private user folder",
obj.get_path()
);
true
} else {
false
};
let workspace_git_repo_setting = sqlx::query_as::<_, WorkspaceSettings>(
"SELECT * FROM workspace_settings WHERE workspace_id = $1",
)
.bind(&w_id)
.fetch_optional(db)
.await?;
if workspace_git_repo_setting.is_none() {
return Err(Error::InternalErr(
"No workspace settings found for workspace ID".to_string(),
));
}
let workspace_git_repo = workspace_git_repo_setting
.unwrap()
.git_sync
.map(|conf| serde_json::from_value::<WorkspaceGitRepo>(conf).ok())
.flatten();
let (git_sync_job_uuid, mut new_tx) = if !skip_git_sync && workspace_git_repo.is_some() {
let workspace_git_repo = workspace_git_repo.unwrap();
let mut args: HashMap<String, serde_json::Value> = HashMap::new();
args.insert(
"repo_url_resource_path".to_string(),
json!(workspace_git_repo
.git_repo_resource_path
.strip_prefix("$res:")),
);
let default_commit_msg: String;
match obj.clone() {
DeployedObject::Script { path, .. } => {
args.insert("path".to_string(), json!(path.to_string()));
default_commit_msg = format!("Script '{}' deployed", path);
}
DeployedObject::Flow { path } => {
args.insert("path".to_string(), json!(path.to_string()));
default_commit_msg = format!("Flow '{}' deployed", path);
}
DeployedObject::App { path, .. } => {
args.insert("path".to_string(), json!(path.to_string()));
default_commit_msg = format!("App '{}' deployed", path);
}
}
args.insert(
"commit_msg".to_string(),
json!(deployment_message.clone().unwrap_or(default_commit_msg)),
);
let (job_uuid, new_tx) = windmill_queue::push(
&db,
tx,
&w_id,
JobPayload::DeploymentCallback { path: workspace_git_repo.script_path.clone() },
args,
&authed.username,
&authed.email,
username_to_permissioned_as(&authed.username),
None,
None,
None,
None,
None,
false,
false,
None,
true,
None,
None,
None,
None,
)
.await?;
(Some(job_uuid), new_tx)
} else {
let new_tx = match tx {
PushIsolationLevel::Isolated(user_db, authed, rsmq) => {
(rsmq, user_db.begin(&authed).await?).into()
}
PushIsolationLevel::IsolatedRoot(db, rsmq) => (rsmq, db.begin().await?).into(),
PushIsolationLevel::Transaction(tx) => tx,
};
(None, new_tx)
};
// We're not persisting the default commit msg as it's pretty useless. We will persist the ones manually set by users
let job_uuids = if git_sync_job_uuid.is_some() {
vec![git_sync_job_uuid.unwrap()]
} else {
vec![]
};
if deployment_message.is_some() || job_uuids.len() > 0 {
// if the git sync job hasn't been triggered, and there is not custom deployment message, there's not point adding an entry to the table
match obj.clone() {
DeployedObject::Script { path, hash, .. } => {
sqlx::query!(
"INSERT INTO deployment_metadata (workspace_id, path, script_hash, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)",
w_id, path, hash.0, &job_uuids, deployment_message,
)
},
DeployedObject::Flow { path } => {
sqlx::query!(
"INSERT INTO deployment_metadata (workspace_id, path, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path) WHERE script_hash IS NULL AND app_version IS NULL DO UPDATE SET callback_job_ids = $3, deployment_msg = $4",
w_id, path, &job_uuids, deployment_message,
)
}
DeployedObject::App { path, version } => {
sqlx::query!(
"INSERT INTO deployment_metadata (workspace_id, path, app_version, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)",
w_id, path, version, &job_uuids, deployment_message,
)
}
}.execute(&mut new_tx)
.await?;
}
tx = PushIsolationLevel::Transaction(new_tx);
return Ok(tx);
}

View File

@@ -7,6 +7,7 @@
*/
use crate::db::ApiAuthed;
use crate::deployment_metadata_helpers;
use crate::{
db::DB,
schedule::clear_schedule,
@@ -354,8 +355,8 @@ async fn create_flow(
)
.await?;
let tx = PushIsolationLevel::Transaction(tx);
let (dependency_job_uuid, mut tx) = push(
let mut tx = PushIsolationLevel::Transaction(tx);
let (dependency_job_uuid, mut new_tx) = push(
&db,
tx,
&w_id,
@@ -389,9 +390,28 @@ async fn create_flow(
nf.path,
w_id
)
.execute(&mut tx)
.execute(&mut new_tx)
.await?;
tx.commit().await?;
tx = PushIsolationLevel::Transaction(new_tx);
tx = deployment_metadata_helpers::handle_deployment_metadata(
tx,
&authed,
&db,
&w_id,
deployment_metadata_helpers::DeployedObject::Flow { path: nf.path.clone() },
nf.deployment_message,
)
.await?;
match tx {
PushIsolationLevel::Transaction(tx) => tx.commit().await?,
_ => {
return Err(Error::InternalErr(
"Expected a transaction here".to_string(),
));
}
}
webhook.send_message(
w_id.clone(),
@@ -565,9 +585,9 @@ async fn update_flow(
},
);
let tx = PushIsolationLevel::Transaction(tx);
let mut tx = PushIsolationLevel::Transaction(tx);
let (dependency_job_uuid, mut tx) = push(
let (dependency_job_uuid, mut new_tx) = push(
&db,
tx,
&w_id,
@@ -600,17 +620,37 @@ async fn update_flow(
nf.path,
w_id
)
.execute(&mut tx)
.execute(&mut new_tx)
.await?;
if let Some(old_dep_job) = old_dep_job {
sqlx::query!(
"UPDATE queue SET canceled = true WHERE id = $1",
old_dep_job
)
.execute(&mut tx)
.execute(&mut new_tx)
.await?;
}
tx.commit().await?;
tx = PushIsolationLevel::Transaction(new_tx);
tx = deployment_metadata_helpers::handle_deployment_metadata(
tx,
&authed,
&db,
&w_id,
deployment_metadata_helpers::DeployedObject::Flow { path: nf.path.clone() },
nf.deployment_message,
)
.await?;
match tx {
PushIsolationLevel::Transaction(tx) => tx.commit().await?,
_ => {
return Err(Error::InternalErr(
"Expected a transaction here".to_string(),
));
}
}
Ok(nf.path.to_string())
}
@@ -823,6 +863,7 @@ mod tests {
mock: None,
timeout: None,
priority: None,
delete_after_use: None,
},
FlowModule {
id: "b".to_string(),
@@ -848,6 +889,7 @@ mod tests {
mock: None,
timeout: None,
priority: None,
delete_after_use: None,
},
FlowModule {
id: "c".to_string(),
@@ -870,6 +912,7 @@ mod tests {
mock: None,
timeout: None,
priority: None,
delete_after_use: None,
},
],
failure_module: Some(FlowModule {
@@ -891,6 +934,7 @@ mod tests {
mock: None,
timeout: None,
priority: None,
delete_after_use: None,
}),
same_worker: false,
concurrent_limit: None,

View File

@@ -1,9 +1,10 @@
use std::cmp;
use crate::{
db::DB, resources::transform_json_value, users::Tokened, workspaces::LargeFileStorage,
db::DB, resources::get_resource_value_interpolated_internal, users::Tokened,
workspaces::LargeFileStorage,
};
use aws_sdk_s3::config::{Credentials, Region};
use aws_sdk_s3::config::{BehaviorVersion, Credentials, Region};
use axum::{
extract::{Path, Query},
routing::{get, post},
@@ -39,10 +40,22 @@ pub fn workspaced_service() -> Router {
"/duckdb_connection_settings",
post(duckdb_connection_settings).layer(cors.clone()),
)
.route(
"/v2/duckdb_connection_settings",
post(duckdb_connection_settings_v2).layer(cors.clone()),
)
.route(
"/polars_connection_settings",
post(polars_connection_settings).layer(cors.clone()),
)
.route(
"/v2/polars_connection_settings",
post(polars_connection_settings_v2).layer(cors.clone()),
)
.route(
"/blah",
post(polars_connection_settings_v2).layer(cors.clone()),
)
.route("/test_connection", get(test_connection).layer(cors.clone()))
.route(
"/list_stored_files",
@@ -113,13 +126,83 @@ async fn duckdb_connection_settings(
return Ok(Json(response));
}
#[derive(Deserialize)]
struct DuckdbConnectionSettingsQueryV2 {
s3_resource_path: Option<String>,
}
async fn duckdb_connection_settings_v2(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Extension(db): Extension<DB>,
Tokened { token }: Tokened,
Path(w_id): Path<String>,
Json(query): Json<DuckdbConnectionSettingsQueryV2>,
) -> error::JsonResult<DuckdbConnectionSettingsResponse> {
let s3_resource_opt = match query.s3_resource_path {
Some(s3_resource_path) => {
get_s3_resource(
&authed,
&user_db,
&db,
&token,
&w_id,
s3_resource_path.as_str(),
)
.await?
}
None => get_workspace_s3_resource(&authed, &user_db, &db, &token, &w_id).await?,
};
let s3_resource = s3_resource_opt.ok_or(error::Error::NotFound(
"No datasets storage resource defined at the workspace level".to_string(),
))?;
return duckdb_connection_settings(
Path(w_id),
Json(DuckdbConnectionSettingsQuery { s3_resource }),
)
.await;
}
#[derive(Deserialize)]
struct PolarsConnectionSettingsQuery {
s3_resource: S3Resource,
}
#[derive(Serialize)]
struct PolarsConnectionSettings {
pub region_name: String,
}
async fn polars_connection_settings(
Path(_w_id): Path<String>,
Json(query): Json<PolarsConnectionSettingsQuery>,
) -> error::JsonResult<S3fsArgs> {
let s3_resource = query.s3_resource;
let response = S3fsArgs {
endpoint_url: render_endpoint(&s3_resource),
key: s3_resource.access_key,
secret: s3_resource.secret_key,
use_ssl: s3_resource.use_ssl,
cache_regions: false,
client_kwargs: PolarsConnectionSettings { region_name: s3_resource.region },
};
return Ok(Json(response));
}
#[derive(Deserialize)]
struct PolarsConnectionSettingsQueryV2 {
s3_resource_path: Option<String>,
}
#[derive(Serialize)]
struct PolarsConnectionSettingsResponse {
s3fs_args: S3fsArgs,
polars_cloud_options: PolarsCloudOptions,
}
#[derive(Serialize)]
struct S3fsArgs {
endpoint_url: String,
#[serde(skip_serializing_if = "Option::is_none")]
key: Option<String>,
@@ -131,25 +214,57 @@ struct PolarsConnectionSettingsResponse {
}
#[derive(Serialize)]
struct PolarsConnectionSettings {
pub region_name: String,
struct PolarsCloudOptions {
aws_endpoint_url: String,
#[serde(skip_serializing_if = "Option::is_none")]
aws_access_key_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
aws_secret_access_key: Option<String>,
aws_region: String,
aws_allow_http: bool,
}
async fn polars_connection_settings(
Path(_w_id): Path<String>,
Json(query): Json<PolarsConnectionSettingsQuery>,
async fn polars_connection_settings_v2(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Extension(db): Extension<DB>,
Tokened { token }: Tokened,
Path(w_id): Path<String>,
Json(query): Json<PolarsConnectionSettingsQueryV2>,
) -> error::JsonResult<PolarsConnectionSettingsResponse> {
let s3_resource = query.s3_resource;
let response = PolarsConnectionSettingsResponse {
endpoint_url: render_endpoint(&s3_resource),
key: s3_resource.access_key,
secret: s3_resource.secret_key,
use_ssl: s3_resource.use_ssl,
cache_regions: false,
client_kwargs: PolarsConnectionSettings { region_name: s3_resource.region },
let s3_resource_opt = match query.s3_resource_path {
Some(s3_resource_path) => {
get_s3_resource(
&authed,
&user_db,
&db,
&token,
&w_id,
s3_resource_path.as_str(),
)
.await?
}
None => get_workspace_s3_resource(&authed, &user_db, &db, &token, &w_id).await?,
};
let s3_resource = s3_resource_opt.ok_or(error::Error::NotFound(
"No datasets storage resource defined at the workspace level".to_string(),
))?;
let s3fs = polars_connection_settings(
Path(w_id),
Json(PolarsConnectionSettingsQuery { s3_resource: s3_resource.clone() }),
)
.await?
.0;
let response = PolarsConnectionSettingsResponse {
s3fs_args: s3fs,
polars_cloud_options: PolarsCloudOptions {
aws_endpoint_url: render_endpoint(&s3_resource),
aws_access_key_id: s3_resource.access_key,
aws_secret_access_key: s3_resource.secret_key,
aws_region: s3_resource.region,
aws_allow_http: !s3_resource.use_ssl,
},
};
return Ok(Json(response));
}
@@ -244,7 +359,7 @@ async fn list_stored_files(
));
}
let next_marker = if bucket_objects.is_truncated() {
let next_marker = if bucket_objects.is_truncated().unwrap_or(false) {
if bucket_objects.next_marker().is_some() {
// some S3 providers returns the next marker for us. If that's the case just re-use it
bucket_objects.next_marker().map(|v| v.to_owned())
@@ -284,8 +399,9 @@ struct LoadFilePreviewQuery {
pub file_size_in_bytes: Option<i64>,
pub file_mime_type: Option<String>,
// For CSV files, the separator needs to be specify
pub csv_separator: Option<String>,
// For CSV files, the separator needs to be specified
pub csv_separator: Option<String>, // defaults to ','
pub csv_has_header: Option<bool>, // defaults to true
// Specify the content length to be read. Both will be taken into account when reading files, except for:
// - CSVs: only the length will be taken into account, a CSV file larger than this will be truncated.
@@ -337,7 +453,7 @@ async fn load_file_metadata(
let response = LoadFileMetadataResponse {
mime_type: s3_object_metadata.content_type().map(&str::to_string),
size_in_bytes: Some(s3_object_metadata.content_length()),
size_in_bytes: s3_object_metadata.content_length(),
last_modified: s3_object_metadata
.last_modified()
.map(|dt| chrono::DateTime::from_timestamp(dt.secs(), dt.subsec_nanos()))
@@ -378,7 +494,9 @@ async fn load_file_preview(
// if content length is provided in the request, use it, otherwise get it from s3
let (s3_object_mime_type, s3_object_content_length) =
if query.file_size_in_bytes.is_none() || query.file_mime_type.is_none() {
if query.file_size_in_bytes.is_some() || query.file_mime_type.is_some() {
(query.file_mime_type.clone(), query.file_size_in_bytes)
} else {
let s3_object_metadata = s3_client
.head_object()
.bucket(&s3_bucket)
@@ -390,28 +508,27 @@ async fn load_file_preview(
s3_object_metadata.content_type().map(|v| v.to_owned()),
s3_object_metadata.content_length(),
)
} else {
(
query.file_mime_type.clone(),
query.file_size_in_bytes.unwrap(),
)
};
let file_chunk_length = cmp::min(
query.read_bytes_length,
s3_object_content_length - query.read_bytes_from,
);
let file_chunk_length = if s3_object_content_length.is_some() {
cmp::min(
query.read_bytes_length,
s3_object_content_length.unwrap() - query.read_bytes_from,
)
} else {
query.read_bytes_length
};
let content_type: WindmillContentType;
let content_preview = match s3_object_mime_type.as_deref() {
Some("text/csv") => {
content_type = WindmillContentType::Csv;
read_s3_csv_object_head(
csv_file_preview_with_fallback(
&s3_client,
&s3_bucket,
&file_key,
file_chunk_length,
query.csv_separator,
query.csv_has_header,
)
.await
}
@@ -431,18 +548,36 @@ async fn load_file_preview(
.await
}
mt_opt => {
let lowercased_file_key = file_key.to_lowercase();
// sometimes S3 doesn't infer the content type on upload. Guess it from the file extension
if file_key.to_lowercase().ends_with(".parquet") {
if lowercased_file_key.ends_with(".parquet") {
content_type = WindmillContentType::Parquet;
read_s3_parquet_object_head(&s3_resource, &file_key).await
} else if file_key.to_lowercase().ends_with(".csv") {
} else if lowercased_file_key.ends_with(".csv") {
content_type = WindmillContentType::Csv;
read_s3_csv_object_head(
csv_file_preview_with_fallback(
&s3_client,
&s3_bucket,
&file_key,
file_chunk_length,
query.csv_separator,
query.csv_has_header,
)
.await
} else if lowercased_file_key.ends_with(".json")
|| lowercased_file_key.ends_with(".yaml")
|| lowercased_file_key.ends_with(".yml")
|| lowercased_file_key.ends_with(".xml")
|| lowercased_file_key.ends_with(".txt")
|| lowercased_file_key.ends_with(".log")
{
content_type = WindmillContentType::RawText;
read_s3_text_object_head(
&s3_client,
&s3_bucket,
&file_key,
query.read_bytes_from,
file_chunk_length,
)
.await
} else {
@@ -513,20 +648,37 @@ async fn get_workspace_s3_resource<'c>(
LargeFileStorage::S3Storage(s3_lfs) => s3_lfs,
};
let resource_path_json_value = serde_json::to_value(s3_lfs.s3_resource_path)
.map_err(|err| error::Error::InternalErr(err.to_string()))?;
let interpolated_value = transform_json_value(
let stripped_resource_path = match s3_lfs.s3_resource_path.strip_prefix("$res:") {
Some(stripped) => stripped,
None => s3_lfs.s3_resource_path.as_str(),
};
return get_s3_resource(authed, user_db, db, token, w_id, stripped_resource_path).await;
}
async fn get_s3_resource<'c>(
authed: &ApiAuthed,
user_db: &UserDB,
db: &DB,
token: &str,
w_id: &str,
s3_resource_path: &str,
) -> error::Result<Option<S3Resource>> {
let s3_resource_value_raw = get_resource_value_interpolated_internal(
authed,
user_db,
db,
&w_id,
resource_path_json_value,
&Option::None,
w_id,
s3_resource_path,
None,
token,
)
.await?;
let s3_resource = serde_json::from_value::<S3Resource>(interpolated_value)
if s3_resource_value_raw.is_none() {
return Err(error::Error::NotFound("Resource not found".to_string()));
}
let s3_resource = serde_json::from_value::<S3Resource>(s3_resource_value_raw.unwrap())
.map_err(|err| error::Error::InternalErr(err.to_string()))?;
return Ok(Some(s3_resource));
}
@@ -537,6 +689,7 @@ fn build_s3_client(s3_resource_ref: &S3Resource) -> aws_sdk_s3::Client {
let endpoint = render_endpoint(&s3_resource);
let mut s3_config_builder = aws_sdk_s3::Config::builder()
.endpoint_url(endpoint)
.behavior_version(BehaviorVersion::latest())
.region(Region::new(s3_resource.region));
if s3_resource.access_key.is_some() {
s3_config_builder = s3_config_builder.credentials_provider(Credentials::new(
@@ -680,23 +833,45 @@ async fn read_s3_parquet_object_head(
return polars_df_result;
}
async fn csv_file_preview_with_fallback(
s3_client: &aws_sdk_s3::Client,
s3_bucket: &str,
file_key: &str,
length: i64,
separator: Option<String>,
has_header: Option<bool>,
) -> error::Result<String> {
match read_s3_csv_object_head(
&s3_client, &s3_bucket, &file_key, length, separator, has_header,
)
.await
{
Ok(csv_preview) => Ok(csv_preview),
Err(_) => {
// fallback to default text file preview is the CSV could not be parsed. It's a text file after all
let raw_text =
read_s3_text_object_head(&s3_client, &s3_bucket, &file_key, 0, length).await?;
return Ok(raw_text);
}
}
}
async fn read_s3_csv_object_head(
s3_client: &aws_sdk_s3::Client,
s3_bucket: &str,
file_key: &str,
length: i64,
separator: Option<String>,
has_header: Option<bool>,
) -> error::Result<String> {
let separator_final = if let Some(separator_char) = separator {
if separator_char.len() != 1 {
return Err(error::Error::BadRequest(
"Separator must be a single character".to_string(),
));
}
separator_char.as_bytes()[0]
} else {
",".as_bytes()[0] // polars uses the comma as default, doing the same here
};
let separator_final = match separator {
Some(separator_char) if separator_char == "\\t" => Ok("\t".as_bytes()[0]),
Some(separator_char) if separator_char.len() != 1 => Err(error::Error::BadRequest(
"Separator must be a single character".to_string(),
)),
Some(separator_char) => Ok(separator_char.as_bytes()[0]),
None => Ok(",".as_bytes()[0]), // polars uses the comma as default, doing the same here
}?;
let s3_object = s3_client
.get_object()
@@ -721,6 +896,7 @@ async fn read_s3_csv_object_head(
let csv_df = CsvReader::new(cursor)
.with_n_rows(Some(10)) // for now read only first 10 lines
.with_separator(separator_final)
.has_header(has_header.unwrap_or(true))
.finish()
.map_err(|err| error::Error::InternalErr(err.to_string()))?;

View File

@@ -6,6 +6,7 @@
* LICENSE-AGPL for a copy of the license.
*/
use axum::http::HeaderValue;
use serde_json::value::RawValue;
use std::collections::HashMap;
use windmill_common::flow_status::RestartedFrom;
@@ -36,7 +37,7 @@ use sqlx::{query_scalar, types::Uuid, FromRow, Postgres, Transaction};
use tower_http::cors::{Any, CorsLayer};
use urlencoding::encode;
use windmill_audit::{audit_log, ActionKind};
use windmill_common::worker::{CUSTOM_TAGS_PER_WORKSPACE, SERVER_CONFIG};
use windmill_common::worker::{to_raw_value, CUSTOM_TAGS_PER_WORKSPACE, SERVER_CONFIG};
use windmill_common::{
db::UserDB,
error::{self, to_anyhow, Error},
@@ -50,8 +51,8 @@ use windmill_common::{
};
use windmill_common::{get_latest_deployed_hash_for_path, BASE_URL};
use windmill_queue::{
add_completed_job_error, empty_args, get_queued_job, get_result_by_id_from_running_flow,
job_is_complete, push, CanceledBy, PushArgs, PushIsolationLevel,
add_completed_job_error, get_queued_job, get_result_by_id_from_running_flow, job_is_complete,
push, CanceledBy, PushArgs, PushIsolationLevel,
};
pub fn workspaced_service() -> Router {
@@ -328,9 +329,10 @@ pub async fn get_path_tag_limits_cache_for_hash(
ScriptLang,
Option<bool>,
Option<i16>,
Option<bool>,
)> {
let script = sqlx::query!(
"select path, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority from script where hash = $1 AND workspace_id = $2",
"select path, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker, priority, delete_after_use from script where hash = $1 AND workspace_id = $2",
hash,
w_id
)
@@ -350,6 +352,7 @@ pub async fn get_path_tag_limits_cache_for_hash(
script.language,
script.dedicated_worker,
script.priority,
script.delete_after_use,
))
}
@@ -364,7 +367,7 @@ async fn get_job(
async fn get_job_internal(db: &DB, workspace_id: &str, job_id: Uuid) -> error::Result<Job> {
let cjob_maybe = sqlx::query_as::<_, CompletedJob>("SELECT
id, workspace_id, parent_job, created_by, created_at, duration_ms, success, script_hash, script_path,
CASE WHEN args is null or pg_column_size(args) < 2000000 THEN args ELSE '{\"reason\": \"WINDMILL_TOO_BIG\"}'::jsonb END as args, CASE WHEN result is null or pg_column_size(result) < 2000000 THEN result ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as result, logs, deleted, raw_code, canceled, canceled_by, canceled_reason, job_kind, env_id,
CASE WHEN args is null or pg_column_size(args) < 2000000 THEN args ELSE '{\"reason\": \"WINDMILL_TOO_BIG\"}'::jsonb END as args, CASE WHEN result is null or pg_column_size(result) < 2000000 THEN result ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as result, right(logs, 20000000) as logs, deleted, raw_code, canceled, canceled_by, canceled_reason, job_kind, env_id,
schedule_path, permissioned_as, flow_status, raw_flow, is_flow_step, language, started_at, is_skipped,
raw_lock, email, visible_to_owner, mem_peak, tag, priority
FROM completed_job WHERE id = $1 AND workspace_id = $2")
@@ -378,7 +381,7 @@ async fn get_job_internal(db: &DB, workspace_id: &str, job_id: Uuid) -> error::R
} else {
let job_o = sqlx::query_as::<_, QueuedJob>(
"SELECT id, workspace_id, parent_job, created_by, created_at, started_at, scheduled_for, running,
script_hash, script_path, CASE WHEN args is null or pg_column_size(args) < 2000000 THEN args ELSE '{\"reason\": \"WINDMILL_TOO_BIG\"}'::jsonb END as args, logs, raw_code, canceled, canceled_by, canceled_reason, last_ping,
script_hash, script_path, CASE WHEN args is null or pg_column_size(args) < 2000000 THEN args ELSE '{\"reason\": \"WINDMILL_TOO_BIG\"}'::jsonb END as args, right(logs, 20000000) as logs, raw_code, canceled, canceled_by, canceled_reason, last_ping,
job_kind, env_id, schedule_path, permissioned_as, flow_status, raw_flow, is_flow_step, language,
suspend, suspend_until, same_worker, raw_lock, pre_run_error, email, visible_to_owner, mem_peak,
root_job, leaf_jobs, tag, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id, cache_ttl, priority
@@ -1564,6 +1567,53 @@ fn decode_payload<D: DeserializeOwned>(t: String) -> anyhow::Result<D> {
serde_json::from_slice(vec.as_slice()).context("invalid json")
}
#[derive(Deserialize)]
pub struct DecodeQuery {
pub include_query: Option<String>,
}
#[derive(Deserialize)]
pub struct IncludeQuery {
pub include_query: Option<String>,
}
pub struct DecodeQueries(pub HashMap<String, Box<RawValue>>);
#[axum::async_trait]
impl<S> FromRequest<S, axum::body::Body> for DecodeQueries
where
S: Send + Sync,
{
type Rejection = Response;
async fn from_request(
req: Request<axum::body::Body>,
_state: &S,
) -> Result<Self, Self::Rejection> {
let query = req.uri().query().unwrap_or("");
let include_query = serde_urlencoded::from_str::<IncludeQuery>(query)
.map(|x| x.include_query)
.ok()
.flatten()
.unwrap_or_default();
let parse_query_args = include_query
.split(",")
.map(|s| s.to_string())
.collect::<Vec<_>>();
let mut args = HashMap::new();
if !parse_query_args.is_empty() {
let queries =
serde_urlencoded::from_str::<HashMap<String, String>>(query).unwrap_or_default();
parse_query_args.iter().for_each(|h| {
if let Some(v) = queries.get(h) {
args.insert(h.to_string(), to_raw_value(v));
}
});
}
Ok(DecodeQueries(args))
}
}
pub fn add_raw_string(
raw_string: Option<String>,
mut args: serde_json::Map<String, serde_json::Value>,
@@ -1764,7 +1814,8 @@ pub async fn run_job_by_path(
check_scopes(&authed, || format!("run:script/{script_path}"))?;
let (job_payload, tag) = script_path_to_payload(script_path, &db, &w_id).await?;
let (job_payload, tag, _delete_after_use) =
script_path_to_payload(script_path, &db, &w_id).await?;
let scheduled_for = run_query.get_scheduled_for(&db).await?;
check_tag_available_for_workspace(&w_id, &tag).await?;
@@ -1827,15 +1878,16 @@ impl Drop for Guard {
}
#[derive(Deserialize)]
pub struct WindmillStatusCode {
pub struct WindmillCompositeResult {
windmill_status_code: Option<u16>,
windmill_content_type: Option<String>,
result: Option<Box<RawValue>>,
}
async fn run_wait_result<T>(
db: &DB,
uuid: Uuid,
Path((w_id, _)): Path<(String, T)>,
node_id: Option<String>,
node_id_for_empty_return: Option<String>,
) -> error::Result<Response> {
let mut result;
let timeout = SERVER_CONFIG.read().await.timeout_wait_result.clone();
@@ -1851,10 +1903,16 @@ async fn run_wait_result<T>(
let mut accumulated_delay = 0 as u64;
loop {
if let Some(node_id) = node_id.as_ref() {
result = get_result_by_id_from_running_flow(&db, &w_id, &uuid, node_id, None)
.await
.ok();
if let Some(node_id_for_empty_return) = node_id_for_empty_return.as_ref() {
result = get_result_by_id_from_running_flow(
&db,
&w_id,
&uuid,
node_id_for_empty_return,
None,
)
.await
.ok();
} else {
let row =
sqlx::query("SELECT result FROM completed_job WHERE id = $1 AND workspace_id = $2")
@@ -1887,13 +1945,53 @@ async fn run_wait_result<T>(
if let Some(result) = result {
g.done = true;
let status_code = serde_json::from_str::<WindmillStatusCode>(result.get());
match status_code {
Ok(WindmillStatusCode { windmill_status_code: Some(status_code), result }) => {
Err(Error::CustomStatusCode(
StatusCode::from_u16(status_code).unwrap_or_else(|_| StatusCode::IM_A_TEAPOT),
result,
))
let composite_result = serde_json::from_str::<WindmillCompositeResult>(result.get());
match composite_result {
Ok(WindmillCompositeResult {
windmill_status_code,
windmill_content_type,
result: result_value,
}) => {
if windmill_content_type.is_none() && windmill_status_code.is_none() {
return Ok(Json(result).into_response());
}
let status_code_or_default = windmill_status_code
.map(|val| match StatusCode::from_u16(val) {
Ok(sc) => Ok(sc),
Err(_) => Err(Error::ExecutionErr("Invalid status code".to_string())),
})
.unwrap_or(if result_value.is_some() {
Ok(StatusCode::OK)
} else {
Ok(StatusCode::NO_CONTENT)
})?;
if windmill_content_type.is_some() {
let serialized_json_result = result_value
.map(|val| val.get().to_owned())
.unwrap_or_else(String::new);
// if the `result` was just a single string, the below removes the surrounding quotes by parsing it as a string.
// it falls back to the original serialized JSON if it doesn't work.
let serialized_result =
serde_json::from_str::<String>(serialized_json_result.as_str())
.ok()
.unwrap_or(serialized_json_result);
return Ok((
status_code_or_default,
[(
http::header::CONTENT_TYPE,
HeaderValue::from_str(windmill_content_type.unwrap().as_str()).unwrap(),
)],
serialized_result,
)
.into_response());
}
return Ok((
status_code_or_default,
Json(result_value), // default to JSON result if no content type is provided
)
.into_response());
}
_ => Ok(Json(result).into_response()),
}
@@ -1902,6 +2000,18 @@ async fn run_wait_result<T>(
}
}
async fn delete_job_metadata_after_use(db: &DB, job_uuid: Uuid) -> Result<(), Error> {
sqlx::query!(
"UPDATE completed_job
SET logs = '##DELETED##', args = '{}'::jsonb, result = '{}'::jsonb
WHERE id = $1",
job_uuid,
)
.execute(db)
.await?;
Ok(())
}
pub async fn check_queue_too_long(db: &DB, queue_limit: Option<i64>) -> error::Result<()> {
if let Some(limit) = queue_limit {
let count = sqlx::query_scalar!(
@@ -1949,6 +2059,7 @@ pub async fn run_wait_result_job_by_path_get(
Extension(db): Extension<DB>,
Path((w_id, script_path)): Path<(String, StripPath)>,
Query(run_query): Query<RunJobQuery>,
DecodeQueries(queries): DecodeQueries,
) -> error::Result<Response> {
#[cfg(feature = "enterprise")]
check_license_key_valid().await?;
@@ -1961,19 +2072,24 @@ pub async fn run_wait_result_job_by_path_get(
.map(decode_payload)
.map(|x| x.map_err(|e| Error::InternalErr(e.to_string())));
let payload_args = if let Some(payload) = payload_r {
let mut payload_args = if let Some(payload) = payload_r {
payload?
} else {
HashMap::new()
};
queries.iter().for_each(|(k, v)| {
payload_args.insert(k.to_string(), v.clone());
});
let args = PushArgs { extra: payload_args, args: sqlx::types::Json(empty_args()) };
let inner_args: HashMap<String, Box<RawValue>> = HashMap::new();
let args = PushArgs { extra: payload_args, args: sqlx::types::Json(inner_args) };
check_queue_too_long(&db, QUEUE_LIMIT_WAIT_RESULT.or(run_query.queue_limit)).await?;
let script_path = script_path.to_path();
check_scopes(&authed, || format!("run:script/{script_path}"))?;
let (job_payload, tag) = script_path_to_payload(script_path, &db, &w_id).await?;
let (job_payload, tag, delete_after_use) =
script_path_to_payload(script_path, &db, &w_id).await?;
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into(), rsmq);
@@ -2003,7 +2119,11 @@ pub async fn run_wait_result_job_by_path_get(
.await?;
tx.commit().await?;
run_wait_result(&db, uuid, Path((w_id, script_path)), None).await
let wait_result = run_wait_result(&db, uuid, Path((w_id, script_path)), None).await;
if delete_after_use.unwrap_or(false) {
delete_job_metadata_after_use(&db, uuid).await?;
}
return wait_result;
}
pub async fn run_wait_result_flow_by_path_get(
@@ -2013,8 +2133,8 @@ pub async fn run_wait_result_flow_by_path_get(
Extension(user_db): Extension<UserDB>,
Extension(db): Extension<DB>,
Path((w_id, flow_path)): Path<(String, StripPath)>,
Query(run_query): Query<RunJobQuery>,
DecodeQueries(queries): DecodeQueries,
) -> error::Result<Response> {
#[cfg(feature = "enterprise")]
check_license_key_valid().await?;
@@ -2028,12 +2148,16 @@ pub async fn run_wait_result_flow_by_path_get(
.map(decode_payload)
.map(|x| x.map_err(|e| Error::InternalErr(e.to_string())));
let payload_args = if let Some(payload) = payload_r {
let mut payload_args = if let Some(payload) = payload_r {
payload?
} else {
HashMap::new()
};
queries.iter().for_each(|(k, v)| {
payload_args.insert(k.to_string(), v.clone());
});
let args = PushArgs { extra: payload_args, args: sqlx::types::Json(HashMap::new()) };
run_wait_result_flow_by_path_internal(
@@ -2081,7 +2205,8 @@ async fn run_wait_result_script_by_path_internal(
let script_path = script_path.to_path();
check_scopes(&authed, || format!("run:script/{script_path}"))?;
let (job_payload, tag) = script_path_to_payload(script_path, &db, &w_id).await?;
let (job_payload, tag, delete_after_use) =
script_path_to_payload(script_path, &db, &w_id).await?;
check_tag_available_for_workspace(&w_id, &tag).await?;
let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into(), rsmq);
@@ -2112,7 +2237,11 @@ async fn run_wait_result_script_by_path_internal(
.await?;
tx.commit().await?;
run_wait_result(&db, uuid, Path((w_id, script_path)), None).await
let wait_result = run_wait_result(&db, uuid, Path((w_id, script_path)), None).await;
if delete_after_use.unwrap_or(false) {
delete_job_metadata_after_use(&db, uuid).await?;
}
return wait_result;
}
pub async fn run_wait_result_script_by_hash(
@@ -2139,6 +2268,7 @@ pub async fn run_wait_result_script_by_hash(
language,
dedicated_worker,
priority,
delete_after_use,
) = get_path_tag_limits_cache_for_hash(&db, &w_id, hash).await?;
check_scopes(&authed, || format!("run:script/{path}"))?;
@@ -2180,7 +2310,11 @@ pub async fn run_wait_result_script_by_hash(
.await?;
tx.commit().await?;
run_wait_result(&db, uuid, Path((w_id, script_hash)), None).await
let wait_result = run_wait_result(&db, uuid, Path((w_id, script_hash)), None).await;
if delete_after_use.unwrap_or(false) {
delete_job_metadata_after_use(&db, uuid).await?;
}
return wait_result;
}
pub async fn run_wait_result_flow_by_path(
@@ -2361,6 +2495,7 @@ async fn add_batch_jobs(
language,
dedicated_worker,
_priority,
_delete_after_use,
) = get_latest_deployed_hash_for_path(&db, &w_id, &path).await?;
(
Some(script_hash),
@@ -2552,6 +2687,7 @@ pub async fn run_job_by_hash(
language,
dedicated_worker,
priority,
_delete_after_use, // not taken into account in async endpoints
) = get_path_tag_limits_cache_for_hash(&db, &w_id, hash).await?;
check_scopes(&authed, || format!("run:script/{path}"))?;
@@ -2822,7 +2958,7 @@ async fn get_completed_job<'a>(
Path((w_id, id)): Path<(String, Uuid)>,
) -> error::Result<Response> {
let job_o = sqlx::query("SELECT id, workspace_id, parent_job, created_by, created_at, duration_ms, success, script_hash, script_path,
CASE WHEN args is null or pg_column_size(args) < 2000000 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args, CASE WHEN result is null or pg_column_size(result) < 2000000 THEN result ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as result, logs, deleted, raw_code, canceled, canceled_by, canceled_reason, job_kind, env_id,
CASE WHEN args is null or pg_column_size(args) < 2000000 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args, CASE WHEN result is null or pg_column_size(result) < 2000000 THEN result ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as result, right(logs, 20000000) as logs, deleted, raw_code, canceled, canceled_by, canceled_reason, job_kind, env_id,
schedule_path, permissioned_as, flow_status, raw_flow, is_flow_step, language, started_at, is_skipped,
raw_lock, email, visible_to_owner, mem_peak, tag, priority FROM completed_job WHERE id = $1 AND workspace_id = $2")
.bind(id)

View File

@@ -36,6 +36,8 @@ use tower_http::{
trace::TraceLayer,
};
use windmill_common::db::UserDB;
#[cfg(feature = "saml")]
use windmill_common::ee::{get_license_plan, LicensePlan};
use windmill_common::utils::rd_string;
use windmill_common::worker::ALL_TAGS;
use windmill_common::BASE_URL;
@@ -47,6 +49,7 @@ mod audit;
mod capture;
mod configs;
mod db;
mod deployment_metadata_helpers;
mod drafts;
pub mod ee;
pub mod embeddings;
@@ -157,10 +160,13 @@ pub async fn run_server(
.allow_headers([http::header::CONTENT_TYPE, http::header::AUTHORIZATION])
.allow_origin(Any);
#[cfg(feature = "enterprise")]
let sp_extension: (ServiceProviderExt, SamlSsoLogin) = saml::build_sp_extension().await?;
#[cfg(feature = "saml")]
let sp_extension: (ServiceProviderExt, SamlSsoLogin) = match get_license_plan().await {
LicensePlan::Enterprise => saml::build_sp_extension().await?,
LicensePlan::Pro => (ServiceProviderExt(None), SamlSsoLogin(None)),
};
#[cfg(not(feature = "enterprise"))]
#[cfg(not(feature = "saml"))]
let sp_extension = (ServiceProviderExt(), SamlSsoLogin(None));
let embeddings_db = if server_mode {

View File

@@ -873,6 +873,7 @@ async fn slack_command(
language,
dedicated_worker,
priority,
_delete_after_use,
) = windmill_common::get_latest_deployed_hash_for_path(
&db,
&settings.workspace_id,

View File

@@ -102,9 +102,13 @@ async fn proxy(
&openai_resource_path,
&w_id
)
.fetch_one(&mut *tx)
.await?;
tx.commit().await?;
.fetch_optional(&mut *tx)
.await?
.ok_or_else(|| {
create_openai_json_error(format!(
"Could not find the OpenAI resource at path {openai_resource_path}, update the resource path in the workspace settings"
))
})?;
if resource.is_none() {
return Err(create_openai_json_error(

View File

@@ -399,42 +399,54 @@ async fn get_resource_value_interpolated(
Path((w_id, path)): Path<(String, StripPath)>,
Query(job_info): Query<JobInfo>,
) -> JsonResult<Option<serde_json::Value>> {
let path = path.to_path();
let mut tx = user_db.clone().begin(&authed).await?;
return get_resource_value_interpolated_internal(
&authed,
&user_db,
&db,
w_id.as_str(),
path.to_path(),
job_info.job_id,
token.as_str(),
)
.await
.map(|success| Json(success));
}
use async_recursion::async_recursion;
pub async fn get_resource_value_interpolated_internal(
authed: &ApiAuthed,
user_db: &UserDB,
db: &DB,
workspace: &str,
path: &str,
job_id: Option<Uuid>,
token: &str,
) -> Result<Option<serde_json::Value>> {
let mut tx = user_db.clone().begin(authed).await?;
let value_o = sqlx::query_scalar!(
"SELECT value from resource WHERE path = $1 AND workspace_id = $2",
path.to_owned(),
&w_id
path,
workspace
)
.fetch_optional(&mut *tx)
.await?;
tx.commit().await?;
if value_o.is_none() {
explain_resource_perm_error(&path, &w_id, &db).await?;
explain_resource_perm_error(path, workspace, db).await?;
}
let value = not_found_if_none(value_o, "Resource", path)?;
if let Some(value) = value {
Ok(Json(Some(
transform_json_value(
&authed,
&user_db,
&db,
&w_id,
value,
&job_info.job_id,
&token,
)
.await?,
)))
Ok(Some(
transform_json_value(authed, user_db, db, workspace, value, &job_id, token).await?,
))
} else {
Ok(Json(None))
Ok(None)
}
}
use async_recursion::async_recursion;
#[async_recursion]
pub async fn transform_json_value<'c>(
authed: &ApiAuthed,

View File

@@ -7,42 +7,45 @@
*/
#![allow(non_snake_case)]
#[cfg(feature = "enterprise")]
#[cfg(feature = "saml")]
use axum::response::Redirect;
use axum::{routing::post, Router};
#[cfg(feature = "enterprise")]
#[cfg(feature = "saml")]
use axum::{Extension, Form};
#[cfg(feature = "enterprise")]
#[cfg(feature = "saml")]
use std::sync::Arc;
#[cfg(feature = "enterprise")]
#[cfg(feature = "saml")]
use samael::metadata::{ContactPerson, ContactType, EntityDescriptor};
#[cfg(feature = "enterprise")]
#[cfg(feature = "saml")]
use samael::service_provider::{ServiceProvider, ServiceProviderBuilder};
use serde::Deserialize;
#[cfg(feature = "enterprise")]
#[cfg(feature = "saml")]
use tower_cookies::Cookies;
#[cfg(feature = "enterprise")]
#[cfg(feature = "saml")]
use windmill_common::error::{Error, Result};
#[cfg(feature = "enterprise")]
#[cfg(feature = "saml")]
use crate::db::DB;
#[cfg(feature = "enterprise")]
#[cfg(feature = "saml")]
use crate::users::login_externally;
#[cfg(feature = "enterprise")]
#[cfg(feature = "saml")]
use crate::BASE_URL;
#[cfg(feature = "enterprise")]
#[cfg(feature = "saml")]
#[derive(Clone)]
pub struct ServiceProviderExt(pub Option<ServiceProvider>);
#[cfg(not(feature = "enterprise"))]
#[cfg(not(feature = "saml"))]
pub struct ServiceProviderExt();
#[cfg(feature = "saml")]
use windmill_common::ee::{get_license_plan, LicensePlan};
pub struct SamlSsoLogin(pub Option<String>);
#[cfg(feature = "enterprise")]
#[cfg(feature = "saml")]
pub async fn build_sp_extension() -> anyhow::Result<(ServiceProviderExt, SamlSsoLogin)> {
if let Some(url_metadata) = std::env::var("SAML_METADATA").ok() {
//todo restrict for non ee
@@ -90,13 +93,18 @@ pub struct SamlForm {
pub SAMLResponse: Option<String>,
}
#[cfg(feature = "enterprise")]
#[cfg(feature = "saml")]
pub async fn acs(
Extension(db): Extension<DB>,
cookies: Cookies,
Extension(se): Extension<Arc<ServiceProviderExt>>,
Form(s): Form<SamlForm>,
) -> Result<Redirect> {
if matches!(get_license_plan().await, LicensePlan::Pro) {
return Err(Error::BadRequest(
"SAML not available in the pro plan".to_string(),
));
};
if let Some(sp_m) = &se.0 {
let sp = sp_m.clone();
if let Some(encoded_resp) = s.SAMLResponse {
@@ -121,7 +129,7 @@ pub async fn acs(
}
}
#[cfg(not(feature = "enterprise"))]
#[cfg(not(feature = "saml"))]
pub async fn acs() -> String {
"SAML available only in enterprise version".to_string()
}

View File

@@ -8,6 +8,7 @@
use crate::{
db::{ApiAuthed, DB},
deployment_metadata_helpers,
schedule::clear_schedule,
users::{maybe_refresh_folders, require_owner_of_path, AuthCache},
webhook_util::{WebhookMessage, WebhookShared},
@@ -39,7 +40,7 @@ use windmill_common::{
schedule::Schedule,
scripts::{
to_i64, HubScript, ListScriptQuery, ListableScript, NewScript, Schema, Script, ScriptHash,
ScriptKind, ScriptLang,
ScriptHistory, ScriptHistoryUpdate, ScriptKind, ScriptLang,
},
users::username_to_permissioned_as,
utils::{
@@ -81,6 +82,10 @@ pub struct ScriptWDraft {
pub ws_error_handler_muted: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub priority: Option<i16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub restart_unless_cancelled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delete_after_use: Option<bool>,
}
pub fn global_service() -> Router {
@@ -120,6 +125,11 @@ pub fn workspaced_service() -> Router {
"/toggle_workspace_error_handler/p/*path",
post(toggle_workspace_error_handler),
)
.route("/history/p/*path", get(get_script_history))
.route(
"/history_update/h/:hash/p/*path",
post(update_script_history),
)
}
#[derive(Serialize, FromRow)]
@@ -310,6 +320,7 @@ async fn create_script(
));
}
let script_path = ns.path.clone();
let hash = ScriptHash(hash_script(&ns));
let authed = maybe_refresh_folders(&ns.path, &w_id, authed, &db).await;
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
@@ -463,8 +474,8 @@ async fn create_script(
"INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, \
content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, \
draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, \
dedicated_worker, ws_error_handler_muted, priority) \
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)",
dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use) \
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)",
&w_id,
&hash.0,
ns.path,
@@ -488,6 +499,8 @@ async fn create_script(
ns.dedicated_worker,
ns.ws_error_handler_muted.unwrap_or(false),
ns.priority,
ns.restart_unless_cancelled,
ns.delete_after_use,
)
.execute(&mut tx)
.await?;
@@ -636,6 +649,16 @@ async fn create_script(
tx = PushIsolationLevel::Transaction(new_tx);
}
tx = deployment_metadata_helpers::handle_deployment_metadata(
tx,
&authed,
&db,
&w_id,
deployment_metadata_helpers::DeployedObject::Script { hash: hash, path: script_path },
ns.deployment_message,
)
.await?;
match tx {
PushIsolationLevel::Transaction(tx) => tx.commit().await?,
_ => {
@@ -696,7 +719,7 @@ async fn get_script_by_path_w_draft(
let mut tx = user_db.begin(&authed).await?;
let script_o = sqlx::query_as::<_, ScriptWDraft>(
"SELECT hash, script.path, summary, description, content, language, kind, tag, schema, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, ws_error_handler_muted, draft.value as draft, dedicated_worker, priority FROM script LEFT JOIN draft ON
"SELECT hash, script.path, summary, description, content, language, kind, tag, schema, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, ws_error_handler_muted, draft.value as draft, dedicated_worker, priority, restart_unless_cancelled, delete_after_use FROM script LEFT JOIN draft ON
script.path = draft.path AND script.workspace_id = draft.workspace_id AND draft.typ = 'script'
WHERE script.path = $1 AND script.workspace_id = $2 \
AND script.created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND \
@@ -712,6 +735,54 @@ async fn get_script_by_path_w_draft(
Ok(Json(script))
}
async fn get_script_history(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<Vec<ScriptHistory>> {
let mut tx = user_db.begin(&authed).await?;
let query_result = sqlx::query!(
"SELECT s.hash as hash, dm.deployment_msg as deployment_msg
FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash
WHERE s.workspace_id = $1 AND s.path = $2
ORDER by created_at DESC",
w_id,
path.to_path(),
)
.fetch_all(&mut *tx)
.await?;
tx.commit().await?;
let result: Vec<ScriptHistory> = query_result
.into_iter()
.map(|row| ScriptHistory {
script_hash: ScriptHash(row.hash),
deployment_msg: row.deployment_msg,
})
.collect();
return Ok(Json(result));
}
async fn update_script_history(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, script_hash, script_path)): Path<(String, ScriptHash, StripPath)>,
Json(script_history_update): Json<ScriptHistoryUpdate>,
) -> Result<()> {
let mut tx = user_db.begin(&authed).await?;
sqlx::query!(
"INSERT INTO deployment_metadata (workspace_id, path, script_hash, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL DO UPDATE SET deployment_msg = $4",
w_id,
script_path.to_path(),
script_hash.0,
script_history_update.deployment_msg,
)
.fetch_optional(&mut *tx)
.await?;
tx.commit().await?;
return Ok(());
}
async fn list_paths(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
@@ -814,6 +885,8 @@ async fn raw_script_by_path(
}
let path = path
.trim_end_matches(".py")
.trim_end_matches(".bun.ts")
.trim_end_matches(".deno.ts")
.trim_end_matches(".ts")
.trim_end_matches(".go")
.trim_end_matches(".sh");
@@ -1087,6 +1160,14 @@ async fn delete_script_by_path(
.await
.map_err(|e| Error::InternalErr(format!("deleting script by path {w_id}: {e}")))?;
sqlx::query!(
"DELETE FROM draft WHERE path = $1 AND workspace_id = $2 AND typ = 'script'",
path,
w_id
)
.execute(&db)
.await?;
audit_log(
&mut *tx,
&authed.username,

Some files were not shown because too many files have changed in this diff Show More