From a14623feaab4a36c01d558b775a42e587a74cdc9 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 11 Jan 2023 00:51:04 +0100 Subject: [PATCH] feat: add 'add user to workspace' --- backend/windmill-api/openapi.yaml | 37 ++++++++++ backend/windmill-api/src/workspaces.rs | 40 +++++++++++ frontend/src/lib/components/AddUser.svelte | 72 +++++++++++++++++++ frontend/src/lib/components/InviteUser.svelte | 1 - .../apps/components/DisplayComponent.svelte | 2 +- .../apps/components/buttons/AppButton.svelte | 1 + .../components/dataDisplay/AppBarChart.svelte | 2 +- .../components/dataDisplay/AppHtml.svelte | 2 +- .../components/dataDisplay/AppPieChart.svelte | 2 +- .../dataDisplay/AppScatterChart.svelte | 2 +- .../components/dataDisplay/AppText.svelte | 2 +- .../dataDisplay/AppTimeseries.svelte | 2 +- .../helpers/RunnableComponent.svelte | 4 +- .../components/helpers/RunnableWrapper.svelte | 2 + .../apps/components/table/AppTable.svelte | 2 +- .../src/lib/components/home/ItemsList.svelte | 16 +++-- .../(logged)/workspace_settings/+page.svelte | 21 ++++-- 17 files changed, 190 insertions(+), 20 deletions(-) create mode 100644 frontend/src/lib/components/AddUser.svelte diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 6a3080f6c6..ac3a52a111 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -670,6 +670,43 @@ paths: schema: type: string + /w/{workspace}/workspaces/add_user: + post: + summary: add user to workspace + operationId: addUser + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: WorkspaceInvite + required: true + content: + application/json: + schema: + type: object + properties: + email: + type: string + is_admin: + type: boolean + username: + type: string + operator: + type: boolean + required: + - email + - is_admin + - operator + - username + responses: + "200": + description: status + content: + text/plain: + schema: + type: string + /w/{workspace}/workspaces/delete_invite: post: summary: delete user invite diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index 22d4a9b470..04aa11e569 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -43,6 +43,7 @@ pub fn workspaced_service() -> Router { .route("/update", post(edit_workspace)) .route("/archive", post(archive_workspace)) .route("/invite_user", post(invite_user)) + .route("/add_user", post(add_user)) .route("/delete_invite", post(delete_invite)) .route("/get_settings", get(get_settings)) .route("/edit_slack_command", post(edit_slack_command)) @@ -152,6 +153,14 @@ pub struct NewWorkspaceInvite { pub operator: bool, } +#[derive(Deserialize)] +pub struct NewWorkspaceUser { + pub email: String, + pub username: String, + pub is_admin: bool, + pub operator: bool, +} + async fn list_pending_invites( authed: Authed, Extension(user_db): Extension, @@ -744,6 +753,37 @@ async fn invite_user( )) } +async fn add_user( + Authed { username, is_admin, .. }: Authed, + Extension(db): Extension, + Path(w_id): Path, + Json(nu): Json, +) -> Result<(StatusCode, String)> { + require_admin(is_admin, &username)?; + + let mut tx = db.begin().await?; + + sqlx::query!( + "INSERT INTO usr + (workspace_id, email, username, is_admin, operator) + VALUES ($1, $2, $3, $4, $5)", + &w_id, + nu.email, + nu.username, + nu.is_admin, + nu.operator + ) + .execute(&mut tx) + .await?; + + tx.commit().await?; + + Ok(( + StatusCode::CREATED, + format!("user with email {} added", nu.email), + )) +} + async fn delete_invite( Authed { username, is_admin, .. }: Authed, Extension(db): Extension, diff --git a/frontend/src/lib/components/AddUser.svelte b/frontend/src/lib/components/AddUser.svelte new file mode 100644 index 0000000000..e1dbf442f3 --- /dev/null +++ b/frontend/src/lib/components/AddUser.svelte @@ -0,0 +1,72 @@ + + +
+ + + + Operator An operator can only execute and view scripts/flows/apps from your workspace, and only + those that he has visibility on + Author An Author can execute and view scripts/flows/apps, but he can also create new ones + Admin + + +
diff --git a/frontend/src/lib/components/InviteUser.svelte b/frontend/src/lib/components/InviteUser.svelte index 64aefcae52..fa2fb67be4 100644 --- a/frontend/src/lib/components/InviteUser.svelte +++ b/frontend/src/lib/components/InviteUser.svelte @@ -9,7 +9,6 @@ const dispatch = createEventDispatcher() let email: string - let is_admin = false function handleKeyUp(event: KeyboardEvent) { const key = event.key diff --git a/frontend/src/lib/components/apps/components/DisplayComponent.svelte b/frontend/src/lib/components/apps/components/DisplayComponent.svelte index 85fc34120f..35757b9bf5 100644 --- a/frontend/src/lib/components/apps/components/DisplayComponent.svelte +++ b/frontend/src/lib/components/apps/components/DisplayComponent.svelte @@ -11,7 +11,7 @@ export const staticOutputs: string[] = ['result', 'loading'] - +
Results
diff --git a/frontend/src/lib/components/apps/components/buttons/AppButton.svelte b/frontend/src/lib/components/apps/components/buttons/AppButton.svelte index dd3acf8165..155446e0e5 100644 --- a/frontend/src/lib/components/apps/components/buttons/AppButton.svelte +++ b/frontend/src/lib/components/apps/components/buttons/AppButton.svelte @@ -57,6 +57,7 @@ - + {#if result} {#if lineChart} diff --git a/frontend/src/lib/components/apps/components/dataDisplay/AppHtml.svelte b/frontend/src/lib/components/apps/components/dataDisplay/AppHtml.svelte index 5c9390df03..2637b2e388 100644 --- a/frontend/src/lib/components/apps/components/dataDisplay/AppHtml.svelte +++ b/frontend/src/lib/components/apps/components/dataDisplay/AppHtml.svelte @@ -14,7 +14,7 @@ let result: string | undefined = undefined - +
{ diff --git a/frontend/src/lib/components/apps/components/dataDisplay/AppPieChart.svelte b/frontend/src/lib/components/apps/components/dataDisplay/AppPieChart.svelte index 56d916a4fb..28087f2ca8 100644 --- a/frontend/src/lib/components/apps/components/dataDisplay/AppPieChart.svelte +++ b/frontend/src/lib/components/apps/components/dataDisplay/AppPieChart.svelte @@ -65,7 +65,7 @@ - + {#if result} {#if doughnut} diff --git a/frontend/src/lib/components/apps/components/dataDisplay/AppScatterChart.svelte b/frontend/src/lib/components/apps/components/dataDisplay/AppScatterChart.svelte index d21784a0ee..728eb889c5 100644 --- a/frontend/src/lib/components/apps/components/dataDisplay/AppScatterChart.svelte +++ b/frontend/src/lib/components/apps/components/dataDisplay/AppScatterChart.svelte @@ -70,7 +70,7 @@ - + {#if result} {/if} diff --git a/frontend/src/lib/components/apps/components/dataDisplay/AppText.svelte b/frontend/src/lib/components/apps/components/dataDisplay/AppText.svelte index 925d5f9615..e1e1efc6c7 100644 --- a/frontend/src/lib/components/apps/components/dataDisplay/AppText.svelte +++ b/frontend/src/lib/components/apps/components/dataDisplay/AppText.svelte @@ -54,7 +54,7 @@ - + {#if !result || result === ''}
diff --git a/frontend/src/lib/components/apps/components/dataDisplay/AppTimeseries.svelte b/frontend/src/lib/components/apps/components/dataDisplay/AppTimeseries.svelte index 39a8db5a48..3450bc53fa 100644 --- a/frontend/src/lib/components/apps/components/dataDisplay/AppTimeseries.svelte +++ b/frontend/src/lib/components/apps/components/dataDisplay/AppTimeseries.svelte @@ -85,7 +85,7 @@ - + {#if result} {/if} diff --git a/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte b/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte index 6db6abc44b..b61a0b1fce 100644 --- a/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte +++ b/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte @@ -23,6 +23,7 @@ export let forceSchemaDisplay: boolean = false export let noMinH = false export let defaultUserInput = false + export let flexWrap = false const { worldStore, runnableComponents, workspace, appPath, isEditor, jobs, noBackend } = getContext('AppEditorContext') @@ -279,8 +280,7 @@ {#if schemaStripped && Object.keys(schemaStripped?.properties ?? {}).length > 0 && (autoRefresh || forceSchemaDisplay)}
('AppEditorContext') @@ -35,6 +36,7 @@ {:else if componentInput.type === 'runnable' && isRunnableDefined()} - + {#if Array.isArray(result) && result.every(isObject)}
{#if search !== 'Disabled'} diff --git a/frontend/src/lib/components/home/ItemsList.svelte b/frontend/src/lib/components/home/ItemsList.svelte index 67300180bc..d94e44312e 100644 --- a/frontend/src/lib/components/home/ItemsList.svelte +++ b/frontend/src/lib/components/home/ItemsList.svelte @@ -46,6 +46,8 @@ let loading = true + let nbDisplayed = 30 + async function loadScripts(): Promise { const loadedScripts = await ScriptService.listScripts({ workspace: $workspaceStore!, @@ -294,9 +296,9 @@ {:else if filteredItems.length === 0} {:else} -
+
- {#each items ?? [] as item, i (item.type + '/' + item.path + (item.summary ?? ''))} + {#each (items ?? []).slice(0, nbDisplayed) as item, i (item.type + '/' + item.path + (item.summary ?? ''))} {#if item.type == 'script'} -->
- + {#if items && items?.length > 30} + {nbDisplayed} items out of {items.length} + + {/if} +
+ --> {/if}
diff --git a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte index 90d42ae5e4..68ab1cc33b 100644 --- a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte @@ -23,6 +23,7 @@ import Toggle from '$lib/components/Toggle.svelte' import ToggleButtonGroup from '$lib/components/common/toggleButton/ToggleButtonGroup.svelte' import ToggleButton from '$lib/components/common/toggleButton/ToggleButton.svelte' + import AddUser from '$lib/components/AddUser.svelte' let users: User[] | undefined = undefined let invites: WorkspaceInvite[] = [] @@ -35,6 +36,7 @@ let itemKind: 'flow' | 'script' = 'flow' let operatorOnly: boolean | undefined = undefined let premium_info: { premium: boolean; usage?: number } | undefined = undefined + let nbDisplayed = 30 // function getDropDownItems(username: string): DropdownItem[] { // return [ @@ -138,12 +140,14 @@ {#if $userStore?.is_admin} - + -
+ + +
-
+
email @@ -160,7 +164,7 @@ {#if filteredUsers} - {#each filteredUsers as { email, username, is_admin, operator, usage, disabled } (email)} + {#each filteredUsers.slice(0, nbDisplayed) as { email, username, is_admin, operator, usage, disabled } (email)} {email} {username} @@ -238,6 +242,13 @@ {/each} + {#if filteredUsers?.length > 50} + {nbDisplayed} items out of {filteredUsers.length} + + {/if} {:else} {#each new Array(6) as _} @@ -252,7 +263,7 @@
- +