Files
windmill/frontend/src/lib/components/TestConnection.svelte
2024-10-09 21:30:28 +02:00

209 lines
4.7 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 * as wmill from "windmill-client"
type S3 = object
export async function main(s3: S3) {
return 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({
type: "S3",
region: s3.region,
bucket: s3.bucket,
endpoint: s3.endPoint,
port: s3.port,
allow_http: !s3.useSSL,
access_key: s3.accessKey,
secret_key: s3.secretKey,
path_style: s3.pathStyle,
}),
}).then(async (res) => {
if (!res.ok) {
throw new Error(await res.text())
}
return res.text()
})
}
`,
lang: 'bun',
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}