mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-06 08:01:35 +00:00
feat: snowflake schema explorer + refactoring (#2260)
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { JobService, Preview } from '$lib/gen'
|
||||
import { dbSchemas, workspaceStore, type DBSchema, type GraphqlSchema } from '$lib/stores'
|
||||
import {
|
||||
dbSchemas,
|
||||
workspaceStore,
|
||||
type DBSchema,
|
||||
type GraphqlSchema,
|
||||
type SQLSchema
|
||||
} from '$lib/stores'
|
||||
import Button from './common/button/Button.svelte'
|
||||
import Drawer from './common/drawer/Drawer.svelte'
|
||||
import DrawerContent from './common/drawer/DrawerContent.svelte'
|
||||
@@ -180,6 +186,51 @@ GROUP BY
|
||||
cols.append(col)
|
||||
schema[dataset.dataset_id][row[0]] = cols
|
||||
return schema
|
||||
`,
|
||||
lang: 'python3'
|
||||
},
|
||||
snowflake: {
|
||||
code: `# requirements:
|
||||
# snowflake-connector-python==3.2.0
|
||||
from typing import Any
|
||||
import snowflake.connector as sf
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
def main(args):
|
||||
if not args["database"]:
|
||||
raise Exception("a selected database is required for the schema explorer")
|
||||
p_key = serialization.load_pem_private_key(
|
||||
args["private_key"].encode(), password=None, backend=default_backend()
|
||||
)
|
||||
pkb = p_key.private_bytes(
|
||||
encoding=serialization.Encoding.DER,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption(),
|
||||
)
|
||||
ctx = sf.connect(
|
||||
user=args["username"],
|
||||
account=args["account_identifier"],
|
||||
private_key=pkb,
|
||||
warehouse=args["warehouse"],
|
||||
database=args["database"],
|
||||
schema=args["schema"],
|
||||
role=args["role"],
|
||||
)
|
||||
cs = ctx.cursor()
|
||||
rows = cs.execute("select TABLE_SCHEMA, TABLE_NAME, DATA_TYPE, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE from information_schema.columns where table_schema != 'INFORMATION_SCHEMA'")
|
||||
schema = dict()
|
||||
for row in rows:
|
||||
if row[0] not in schema:
|
||||
schema[row[0]] = dict()
|
||||
if row[1] not in schema[row[0]]:
|
||||
schema[row[0]][row[1]] = dict()
|
||||
schema[row[0]][row[1]][row[3]] = {
|
||||
"type": row[2],
|
||||
"required": row[5] == "YES",
|
||||
}
|
||||
if row[4] is not None:
|
||||
schema[row[0]][row[1]][row[3]]["default"] = row[4]
|
||||
return schema
|
||||
`,
|
||||
lang: 'python3'
|
||||
}
|
||||
@@ -210,19 +261,18 @@ GROUP BY
|
||||
if (!testResult.success) {
|
||||
console.error(testResult.result?.['error']?.['message'])
|
||||
} else {
|
||||
if (resourceType === 'postgresql') {
|
||||
$dbSchemas[resourcePath] = {
|
||||
lang: 'postgresql',
|
||||
schema: testResult.result,
|
||||
publicOnly: true
|
||||
}
|
||||
} else if (
|
||||
resourceType !== undefined &&
|
||||
['mysql', 'graphql', 'bigquery'].includes(resourceType)
|
||||
) {
|
||||
$dbSchemas[resourcePath] = {
|
||||
lang: resourceType as 'mysql' | 'graphql' | 'bigquery',
|
||||
schema: testResult.result
|
||||
if (resourceType !== undefined) {
|
||||
if (resourceType !== 'graphql') {
|
||||
$dbSchemas[resourcePath] = {
|
||||
lang: resourceType as SQLSchema['lang'],
|
||||
schema: testResult.result,
|
||||
publicOnly: !!testResult.result.public || !!testResult.result.PUBLIC
|
||||
}
|
||||
} else {
|
||||
$dbSchemas[resourcePath] = {
|
||||
lang: 'graphql',
|
||||
schema: testResult.result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,8 +303,8 @@ GROUP BY
|
||||
}
|
||||
|
||||
function formatSchema(dbSchema: DBSchema) {
|
||||
if (dbSchema.lang === 'postgresql' && dbSchema.publicOnly) {
|
||||
return dbSchema.schema.public || dbSchema
|
||||
if (dbSchema.lang !== 'graphql' && dbSchema.publicOnly) {
|
||||
return dbSchema.schema.public || dbSchema.schema.PUBLIC || dbSchema
|
||||
} else if (dbSchema.lang === 'mysql' && Object.keys(dbSchema.schema).length === 1) {
|
||||
return dbSchema.schema[Object.keys(dbSchema.schema)[0]]
|
||||
} else {
|
||||
@@ -299,7 +349,7 @@ GROUP BY
|
||||
>Refresh
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
{#if dbSchema.lang === 'postgresql'}
|
||||
{#if dbSchema.lang !== 'graphql' && (dbSchema.schema?.public || dbSchema.schema?.PUBLIC)}
|
||||
<ToggleButtonGroup class="mb-4" bind:selected={dbSchema.publicOnly}>
|
||||
<ToggleButton value={true} label="Public" />
|
||||
<ToggleButton value={false} label="All" />
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
export let yContent: Text | undefined = undefined
|
||||
export let awareness: any | undefined = undefined
|
||||
export let folding = false
|
||||
export let args: Record<string, any> | undefined = undefined
|
||||
|
||||
languages.typescript.typescriptDefaults.setModeConfiguration({
|
||||
completionItems: false,
|
||||
@@ -224,7 +225,8 @@
|
||||
let command: Disposable | undefined = undefined
|
||||
|
||||
let sqlSchemaCompletor: Disposable | undefined = undefined
|
||||
$: dbSchema = $dbSchemas[Object.keys($dbSchemas)[0]]
|
||||
$: args &&
|
||||
(dbSchema = $dbSchemas[(lang === 'graphql' ? args.api : args.database)?.replace('$res:', '')])
|
||||
$: dbSchema && ['sql', 'graphql'].includes(lang) && addDBSchemaCompletions()
|
||||
$: (!dbSchema || lang !== 'sql') && sqlSchemaCompletor && sqlSchemaCompletor.dispose()
|
||||
$: (!dbSchema || lang !== 'graphql') && graphqlService && graphqlService.setSchemaConfig([])
|
||||
@@ -242,7 +244,7 @@
|
||||
introspectionJSON: schema
|
||||
}
|
||||
])
|
||||
} else if (schemaLang === 'mysql' || schemaLang === 'postgresql') {
|
||||
} else {
|
||||
if (sqlSchemaCompletor) {
|
||||
sqlSchemaCompletor.dispose()
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
export let collabUsers: { name: string }[] = []
|
||||
export let scriptPath: string | undefined = undefined
|
||||
export let diffEditor: DiffEditor | undefined = undefined
|
||||
export let args: Record<string, any>
|
||||
|
||||
let contextualVariablePicker: ItemPicker
|
||||
let variablePicker: ItemPicker
|
||||
@@ -530,7 +531,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<ScriptGen {editor} {diffEditor} {lang} {iconOnly} />
|
||||
<ScriptGen {editor} {diffEditor} {lang} {iconOnly} {args} />
|
||||
|
||||
<!-- <Popover
|
||||
notClickable
|
||||
|
||||
@@ -135,12 +135,13 @@
|
||||
jobId={testJob?.id}
|
||||
result={testJob.result}>
|
||||
<svelte:fragment slot="copilot-fix">
|
||||
{#if lang && editor && diffEditor && testJob?.result?.error}
|
||||
{#if lang && editor && diffEditor && stepArgs && testJob?.result?.error}
|
||||
<ScriptFix
|
||||
error={JSON.stringify(testJob.result.error)}
|
||||
{lang}
|
||||
{editor}
|
||||
{diffEditor}
|
||||
args={stepArgs}
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -249,6 +249,7 @@
|
||||
kind={asKind(kind)}
|
||||
{template}
|
||||
{diffEditor}
|
||||
{args}
|
||||
/>
|
||||
{#if !noSyncFromGithub}
|
||||
<div class="py-1">
|
||||
@@ -302,6 +303,7 @@
|
||||
deno={lang == 'deno'}
|
||||
automaticLayout={true}
|
||||
{fixedOverflowWidgets}
|
||||
{args}
|
||||
/>
|
||||
<DiffEditor
|
||||
bind:this={diffEditor}
|
||||
@@ -364,6 +366,7 @@
|
||||
previewIsLoading={testIsLoading}
|
||||
{editor}
|
||||
{diffEditor}
|
||||
{args}
|
||||
/>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
|
||||
@@ -195,6 +195,10 @@
|
||||
editor={inlineScript?.language === 'frontend' ? simpleEditor : editor}
|
||||
{diffEditor}
|
||||
inlineScript
|
||||
args={Object.entries(fields).reduce((acc, [key, obj]) => {
|
||||
acc[key] = obj.type === 'static' ? obj.value : undefined
|
||||
return acc
|
||||
}, {})}
|
||||
/>
|
||||
|
||||
<Button
|
||||
@@ -281,6 +285,10 @@
|
||||
}
|
||||
$app = $app
|
||||
}}
|
||||
args={Object.entries(fields).reduce((acc, [key, obj]) => {
|
||||
acc[key] = obj.type === 'static' ? obj.value : undefined
|
||||
return acc
|
||||
}, {})}
|
||||
/>
|
||||
{:else}
|
||||
<SimpleEditor
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<ManualPopover bind:this={copilotPopover}>
|
||||
<Button
|
||||
size="xs"
|
||||
btnClasses="mr-2 z-[901]"
|
||||
btnClasses={'mr-2 ' + ($currentStepStore !== undefined ? 'z-[901]' : '')}
|
||||
on:click={() => {
|
||||
if (copilotLoading || ($currentStepStore !== undefined && $currentStepStore !== 'Input')) {
|
||||
abortController?.abort()
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
export let editor: Editor | undefined
|
||||
export let diffEditor: DiffEditor | undefined
|
||||
export let error: string
|
||||
export let args: Record<string, any>
|
||||
|
||||
// state
|
||||
let genLoading: boolean = false
|
||||
@@ -103,7 +104,7 @@
|
||||
|
||||
$: !$generatedCode && hideDiff()
|
||||
|
||||
$: dbSchema = $dbSchemas[Object.keys($dbSchemas)[0]]
|
||||
$: dbSchema = $dbSchemas[(lang === 'graphql' ? args.api : args.database)?.replace('$res:', '')]
|
||||
</script>
|
||||
|
||||
{#if SUPPORTED_LANGUAGES.has(lang)}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
export let editor: Editor | SimpleEditor | undefined
|
||||
export let diffEditor: DiffEditor | undefined
|
||||
export let inlineScript = false
|
||||
export let args: Record<string, any>
|
||||
|
||||
// state
|
||||
let funcDesc: string = ''
|
||||
@@ -143,7 +144,8 @@
|
||||
$: !$generatedCode && hideDiff()
|
||||
$: editor && setSelectionHandler()
|
||||
$: selection && (isEdit = !selection.isEmpty())
|
||||
$: dbSchema = $dbSchemas[Object.keys($dbSchemas)[0]]
|
||||
|
||||
$: dbSchema = $dbSchemas[(lang === 'graphql' ? args.api : args.database)?.replace('$res:', '')]
|
||||
</script>
|
||||
|
||||
{#if $generatedCode.length > 0 && !genLoading}
|
||||
@@ -297,7 +299,7 @@
|
||||
In order to better generate the script, we pass the selected DB schema to GPT-4.
|
||||
</Tooltip>
|
||||
</p>
|
||||
{#if dbSchema.lang === 'postgresql'}
|
||||
{#if dbSchema.lang !== 'graphql' && (dbSchema.schema?.public || dbSchema.schema?.PUBLIC)}
|
||||
<ToggleButtonGroup class="w-auto shrink-0" bind:selected={dbSchema.publicOnly}>
|
||||
<ToggleButton value={true} label="Public schema" />
|
||||
<ToggleButton value={false} label="All schemas" />
|
||||
|
||||
@@ -210,6 +210,10 @@
|
||||
iconOnly={width < 850}
|
||||
kind={scriptKind}
|
||||
template={scriptTemplate}
|
||||
args={Object.entries(flowModule.value.input_transforms).reduce((acc, [key, obj]) => {
|
||||
acc[key] = obj.type === 'static' ? obj.value : undefined
|
||||
return acc
|
||||
}, {})}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -254,6 +258,13 @@
|
||||
saveDraft()
|
||||
}}
|
||||
fixedOverflowWidgets={true}
|
||||
args={Object.entries(flowModule.value.input_transforms).reduce(
|
||||
(acc, [key, obj]) => {
|
||||
acc[key] = obj.type === 'static' ? obj.value : undefined
|
||||
return acc
|
||||
},
|
||||
{}
|
||||
)}
|
||||
/>
|
||||
<DiffEditor
|
||||
bind:this={diffEditor}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
export let pastPreviews: CompletedJob[] = []
|
||||
export let editor: Editor | undefined = undefined
|
||||
export let diffEditor: DiffEditor | undefined = undefined
|
||||
export let args: Record<string, any> | undefined = undefined
|
||||
|
||||
type DrawerContent = {
|
||||
mode: 'json' | Preview.language | 'plain'
|
||||
@@ -99,12 +100,13 @@
|
||||
result={previewJob.result}
|
||||
>
|
||||
<svelte:fragment slot="copilot-fix">
|
||||
{#if lang && editor && diffEditor && previewJob?.result?.error}
|
||||
{#if lang && editor && diffEditor && args && previewJob?.result?.error}
|
||||
<ScriptFix
|
||||
error={JSON.stringify(previewJob.result.error)}
|
||||
{lang}
|
||||
{editor}
|
||||
{diffEditor}
|
||||
{args}
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
@@ -137,7 +139,9 @@
|
||||
{#each pastPreviews as { id, created_at, success, result }}
|
||||
<tr class="">
|
||||
<td class="text-xs">
|
||||
<a class="pr-3" href="/run/{id}?workspace={$workspaceStore}" target="_blank">{id.substring(30)}</a>
|
||||
<a class="pr-3" href="/run/{id}?workspace={$workspaceStore}" target="_blank"
|
||||
>{id.substring(30)}</a
|
||||
>
|
||||
</td>
|
||||
<td class="text-xs">{displayDate(created_at)}</td>
|
||||
<td class="text-xs">
|
||||
|
||||
@@ -82,14 +82,9 @@ type SQLBaseSchema = {
|
||||
}
|
||||
|
||||
export interface SQLSchema {
|
||||
lang: 'mysql' | 'bigquery'
|
||||
lang: 'mysql' | 'bigquery' | 'postgresql' | 'snowflake'
|
||||
schema: SQLBaseSchema
|
||||
}
|
||||
|
||||
export interface PostgresqlSchema {
|
||||
lang: 'postgresql'
|
||||
schema: SQLBaseSchema
|
||||
publicOnly: boolean
|
||||
publicOnly: boolean | undefined
|
||||
}
|
||||
|
||||
export interface GraphqlSchema {
|
||||
@@ -97,7 +92,7 @@ export interface GraphqlSchema {
|
||||
schema: IntrospectionQuery
|
||||
}
|
||||
|
||||
export type DBSchema = SQLSchema | PostgresqlSchema | GraphqlSchema
|
||||
export type DBSchema = SQLSchema | GraphqlSchema
|
||||
|
||||
interface DBSchemas {
|
||||
[resourcePath: string]: DBSchema
|
||||
|
||||
Reference in New Issue
Block a user