From fed2b2105da4c6dfeca855df526c7778754f5504 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Thu, 1 Dec 2022 19:28:00 +0100 Subject: [PATCH] feat: unify resources under a single connect API --- backend/windmill-api/openapi.yaml | 2 +- backend/windmill-api/src/apps.rs | 10 +- .../src/lib/components/ApiConnectForm.svelte | 24 +++ frontend/src/lib/components/AppConnect.svelte | 116 ++++++----- frontend/src/lib/components/ArgInput.svelte | 42 ++-- frontend/src/lib/components/EditorBar.svelte | 9 - frontend/src/lib/components/Password.svelte | 2 - frontend/src/lib/components/Path.svelte | 5 +- .../src/lib/components/ResourceEditor.svelte | 149 +++++-------- .../src/lib/components/ResourcePicker.svelte | 12 +- frontend/src/lib/components/SchemaForm.svelte | 2 + .../src/lib/components/VariableEditor.svelte | 20 +- .../flows/pickers/PickHubScript.svelte | 4 +- .../components/sidebar/SidebarContent.svelte | 7 +- frontend/src/routes/resources.svelte | 196 +++++++++--------- 15 files changed, 287 insertions(+), 313 deletions(-) create mode 100644 frontend/src/lib/components/ApiConnectForm.svelte diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 57ec973fd4..c93f92af66 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1331,7 +1331,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/ListableResource" + $ref: "#/components/schemas/Resource" /w/{workspace}/resources/get_value/{path}: get: diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index f66789095f..a773cc5849 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -126,14 +126,14 @@ async fn list_apps( let sqlb = SqlBuilder::select_from("app") .fields(&[ - "id", + "app.id", "app.workspace_id", "app.path", - "summary", - "versions[array_upper(versions, 1)] as version", - "policy->>'execution_mode' as execution_mode", + "app.summary", + "app.versions[array_upper(app.versions, 1)] as version", + "app.policy->>'execution_mode' as execution_mode", "app_version.created_at as edited_at", - "extra_perms", + "app.extra_perms", "favorite.path IS NOT NULL as starred", ]) .left() diff --git a/frontend/src/lib/components/ApiConnectForm.svelte b/frontend/src/lib/components/ApiConnectForm.svelte new file mode 100644 index 0000000000..6aa76f71f6 --- /dev/null +++ b/frontend/src/lib/components/ApiConnectForm.svelte @@ -0,0 +1,24 @@ + + + diff --git a/frontend/src/lib/components/AppConnect.svelte b/frontend/src/lib/components/AppConnect.svelte index 596dc5771f..da7bea1c30 100644 --- a/frontend/src/lib/components/AppConnect.svelte +++ b/frontend/src/lib/components/AppConnect.svelte @@ -2,19 +2,11 @@ const apiTokenApps: Record = { airtable: { img: 'airtable_connect.png', - instructions: [ - 'Click on the top-right avatar', - 'Click on Account', - 'Find "Api"' - ] + instructions: ['Click on the top-right avatar', 'Click on Account', 'Find "Api"'] }, discord_webhook: { img: 'discord_webhook.png', - instructions: [ - 'Click on Server Settings', - 'Click on Integration', - 'Find "Webhooks"' - ], + instructions: ['Click on Server Settings', 'Click on Integration', 'Find "Webhooks"'], key: 'webhook_url' }, toggl: { @@ -30,7 +22,7 @@ 'Go to https://admin.mailchimp.com/account/api', 'Find "Your API Keys"' ] - }, + } } @@ -43,10 +35,10 @@ import { sendUserToast, truncateRev } from '$lib/utils' import { createEventDispatcher } from 'svelte' import Icon from 'svelte-awesome' - import Password from './Password.svelte' import Path from './Path.svelte' import { Alert, Button, Drawer } from './common' import DrawerContent from './common/drawer/DrawerContent.svelte' + import ApiConnectForm from './ApiConnectForm.svelte' let manual = false let value: string = '' @@ -54,6 +46,7 @@ let connects: Record }> = {} let connectsManual: [string, { img?: string; instructions: string[]; key?: string }][] = [] let key: string = 'token' + let args = {} $: key = apiTokenApps[resource_type]?.key ?? 'token' @@ -83,7 +76,7 @@ scopes = connect.scopes extra_params = Object.entries(connect.extra_params ?? {}) } - drawer.openDrawer() + drawer.openDrawer?.() } export function openFromOauth(rt: string) { @@ -94,7 +87,7 @@ manual = false step = 3 no_back = true - drawer.openDrawer() + drawer.openDrawer?.() } async function loadConnects() { @@ -105,7 +98,16 @@ const availableRts = await ResourceService.listResourceTypeNames({ workspace: $workspaceStore! }) - connectsManual = Object.entries(apiTokenApps).filter(([key, _]) => availableRts.includes(key)) + connectsManual = availableRts + .filter((x) => !Object.keys(connects).includes(x)) + .map((x) => [ + x, + apiTokenApps[x] ?? { + instructions: '', + img: undefined, + key: undefined + } + ]) } async function next() { @@ -150,18 +152,19 @@ ) } const description = `${manual ? 'Token' : 'OAuth token'} for ${resource_type}` + await VariableService.createVariable({ workspace: $workspaceStore!, requestBody: { path, - value, + value: manual ? args[key] : value, is_secret: true, description, is_oauth: !manual, account: account } }) - const resourceValue = {} + const resourceValue = args resourceValue[key] = `$var:${path}` await ResourceService.createResource({ workspace: $workspaceStore!, @@ -174,7 +177,7 @@ }) dispatch('refresh') sendUserToast(`App token set at resource and variable path: ${path}`) - drawer.closeDrawer() + drawer.closeDrawer?.() } } @@ -195,7 +198,11 @@ resource_type == 'gsheets') $: disabled = (step == 1 && resource_type == '') || - (step == 2 && value == '') || + (step == 2 && + value == '' && + args['token'] == '' && + args['password'] == '' && + args['api_key'] == '') || (step == 3 && pathError != '') @@ -307,23 +314,26 @@ {:else if step == 2} {#if manual} -
Instructions
-
-
    - {#each apiTokenApps[resource_type].instructions as step} -
  1. - {@html step} -
  2. - {/each} -
-
- {#if apiTokenApps[resource_type].img} -
- connect + {#if apiTokenApps[resource_type]} +
Instructions
+
+
    + {#each apiTokenApps[resource_type].instructions as step} +
  1. + {@html step} +
  2. + {/each} +
+ {#if apiTokenApps[resource_type].img} +
+ connect +
+ {/if} {/if} +
- +
{/if} {:else} @@ -333,27 +343,25 @@ initialPath={`u/${$userStore?.username ?? ''}/my_${resource_type}`} kind="resource" /> -
    -
  • - 1. A secret variable containing the token {truncateRev(value, 5, '*****')} - will be stored at - {path}. You can refer to this variable anywhere this token - is required. -
  • -
  • - 2. A resource with a unique token field will be stored at {path} - and refer to the secret variable {path} as its token (using - variable templating - `$var:${path}`). You can refer to this resource anywhere - this token is required. A script can use the resource type - {resource_type} as a type parameter to restrict the kind of - tokens it accepts to this api. -
  • -
+ {#if apiTokenApps[resource_type] || !manual} + {manual} + {apiTokenApps[resource_type]} +
    +
  • + 1. A secret variable containing the {apiTokenApps[resource_type]?.key ?? 'token'}{truncateRev(value, 5, '*****')} + will be stored a + {path}. +
  • +
  • + 2. The resource containing that token will be stored at the same path{path}. The Variable and Resource will be "linked together", they will be deleted and renamed + together. +
+ {/if} {/if}
{#if step > 1 && !no_back} diff --git a/frontend/src/lib/components/ArgInput.svelte b/frontend/src/lib/components/ArgInput.svelte index 9b20f2e6e2..65556ae98b 100644 --- a/frontend/src/lib/components/ArgInput.svelte +++ b/frontend/src/lib/components/ArgInput.svelte @@ -15,6 +15,7 @@ import SimpleEditor from './SimpleEditor.svelte' import autosize from 'svelte-autosize' import Toggle from './Toggle.svelte' + import Password from './Password.svelte' export let label: string = '' export let value: any @@ -39,6 +40,7 @@ export let properties: { [name: string]: SchemaProperty } | undefined = undefined export let autofocus = false export let compact = false + export let password = false let seeEditable: boolean = enum_ != undefined || pattern != undefined const dispatch = createEventDispatcher() @@ -336,24 +338,28 @@ : undefined} /> {:else if inputCat == 'string'} -