Files
windmill/frontend/src/lib/components/TestConnection.svelte

190 lines
4.3 KiB
Svelte

<script lang="ts">
import { type CompletedJob, JobService, type Preview } from '$lib/gen'
import { Database, Loader2 } from 'lucide-svelte'
import Button from './common/button/Button.svelte'
import { sendUserToast } from '$lib/toast'
import { workspaceStore } from '$lib/stores'
import { tryEvery } from '$lib/utils'
export let workspaceOverride: string | undefined = undefined
export let resourceType: string | undefined
export let args: Record<string, any> | any = {}
export let buttonTextOverride: string | undefined = undefined
const scripts: {
[key: string]: {
code: string
lang: string
argName: string
additionalCheck?: (testResult: CompletedJob) => CompletedJob
}
} = {
postgresql: {
code: `SELECT 1`,
lang: 'postgresql',
argName: 'database'
},
mysql: {
code: `SELECT 1`,
lang: 'mysql',
argName: 'database'
},
bigquery: {
code: `select 1`,
lang: 'bigquery',
argName: 'database'
},
snowflake: {
code: `select 1`,
lang: 'snowflake',
argName: 'database'
},
ms_sql_server: {
code: `SELECT 1`,
lang: 'mssql',
argName: 'database'
},
s3: {
code: `
import { S3Client } from "https://deno.land/x/s3_lite_client@0.6.1/mod.ts";
type S3 = object
export async function main(s3: S3) {
const s3client = new S3Client(s3);
for await (const obj of s3client.listObjects({ prefix: "/" })) {
console.log(obj);
}
}
`,
lang: 'deno',
argName: 's3'
},
graphql: {
code: '{ __typename }',
lang: 'graphql',
argName: 'api',
additionalCheck: (testResult: CompletedJob) => {
if (
testResult.success &&
(typeof testResult.result !== 'object' || !('__typename' in (testResult.result ?? {})))
) {
return {
...testResult,
result: {
error: {
message: 'Invalid GraphQL API response'
}
},
success: false
}
} else {
return testResult
}
}
},
s3_bucket: {
code: `
const process = require('process');
export async function main(bucket: any) {
const req = await fetch(process.env.BASE_URL + '/api/settings/test_object_storage_config', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + process.env.WM_TOKEN,
},
body: JSON.stringify(bucket),
});
if (!req.ok) {
throw new Error(await req.text());
}
return await req.text();
}
`,
lang: 'bun',
argName: 'bucket'
}
}
let loading = false
async function testConnection() {
if (!resourceType) return
loading = true
const resourceScript = scripts[resourceType]
const job = await JobService.runScriptPreview({
workspace: workspaceOverride ?? $workspaceStore!,
requestBody: {
path: `testConnection: ${resourceType}`,
language: resourceScript.lang as Preview['language'],
content: resourceScript.code,
args: {
[resourceScript.argName]: args
}
}
})
tryEvery({
tryCode: async () => {
let testResult = await JobService.getCompletedJob({
workspace: workspaceOverride ?? $workspaceStore!,
id: job
})
if (resourceScript.additionalCheck) {
testResult = resourceScript.additionalCheck(testResult)
}
loading = false
sendUserToast(
testResult.success
? 'Connection successful'
: 'Connection error: ' + testResult.result?.['error']?.['message'],
!testResult.success
)
},
timeoutCode: async () => {
loading = false
sendUserToast(
'Connection did not resolve after 5s or job did not start. Do you have native workers or a worker group listening to the proper tag available?',
true
)
try {
await JobService.cancelQueuedJob({
workspace: workspaceOverride ?? $workspaceStore!,
id: job,
requestBody: {
reason:
'Connection did not resolve after 5s. Do you have native workers or a worker group listening to the proper tag available?'
}
})
} catch (err) {
console.error(err)
}
},
interval: 500,
timeout: 5000
})
}
</script>
{#if Object.keys(scripts).includes(resourceType || '')}
<Button
spacingSize="sm"
size="xs"
btnClasses="h-8"
color="light"
variant="border"
on:click={testConnection}
>
{#if loading}
<Loader2 class="animate-spin mr-2 !h-4 !w-4" />
{:else}
<Database class="mr-2 !h-4 !w-4" />
{/if}
{buttonTextOverride ?? 'Test connection'}
</Button>
{/if}