mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-25 08:00:59 +00:00
feat: PostgreSQL parametrized statement handled as typescript template
This commit is contained in:
+1
-1
@@ -50,6 +50,6 @@ export function pgSql(
|
||||
query: TemplateStringsArray,
|
||||
...args: unknown[]
|
||||
) {
|
||||
return await pgClient(db).queryObject(query, args)
|
||||
return await pgClient(db).queryObject(query, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="max-w-screen-lg flex grow w-full h-full xl:-ml-20">
|
||||
<div class="max-w-screen-lg flex grow w-full h-full xl:-ml-20 mr-2 lg:mr-0">
|
||||
<div class="grow w-full h-full mt-4">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -345,6 +345,7 @@
|
||||
|
||||
websocketInterval && clearInterval(websocketInterval)
|
||||
websocketInterval = setInterval(() => {
|
||||
console.log('websocketInterval')
|
||||
if (document.visibilityState == 'visible') {
|
||||
if (
|
||||
!lastWsAttempt ||
|
||||
@@ -436,6 +437,16 @@
|
||||
|
||||
editor.onDidFocusEditorText(() => {
|
||||
dispatch('focus')
|
||||
if (deno || lang == 'typescript') {
|
||||
if (
|
||||
!websocketAlive.black &&
|
||||
!websocketAlive.deno &&
|
||||
!websocketAlive.pyright &&
|
||||
!websocketInterval
|
||||
) {
|
||||
reloadWebsocket()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
editor.onDidBlurEditorText(() => {
|
||||
|
||||
@@ -220,17 +220,15 @@
|
||||
}}
|
||||
>
|
||||
Reload assistants
|
||||
{#if lang == 'deno'}
|
||||
<span
|
||||
>(<span class={websocketAlive.deno ? 'text-green-600' : 'text-red-700'}>Deno</span>)</span
|
||||
>
|
||||
{:else if lang == 'python3'}
|
||||
<span
|
||||
>(<span class={websocketAlive.pyright ? 'text-green-600' : 'text-red-700'}>Pyright</span>
|
||||
<span class="ml-1">
|
||||
{#if lang == 'deno'}
|
||||
(<span class={websocketAlive.deno ? 'text-green-600' : 'text-red-700'}>Deno</span>)
|
||||
{:else if lang == 'python3'}
|
||||
(<span class={websocketAlive.pyright ? 'text-green-600' : 'text-red-700'}>Pyright</span>
|
||||
|
||||
<span class={websocketAlive.black ? 'text-green-600' : 'text-red-700'}>Black</span>)</span
|
||||
>
|
||||
{/if}
|
||||
<span class={websocketAlive.black ? 'text-green-600' : 'text-red-700'}>Black</span>)
|
||||
{/if}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
|
||||
@@ -192,6 +192,12 @@
|
||||
</button>
|
||||
{:else}
|
||||
<button class="default-button px-6 self-end" on:click={saveFlow}>Save</button>
|
||||
<button
|
||||
class="default-button-secondary px-6 max-h-8 mr-2"
|
||||
on:click={async () => {
|
||||
changeStep(1)
|
||||
}}>Back</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -62,7 +62,10 @@
|
||||
$: opened = $stepOpened === String(indexes.join('-'))
|
||||
</script>
|
||||
|
||||
<FlowBox bind:opened>
|
||||
<FlowBox
|
||||
headerClickable={true}
|
||||
on:clickheader={() => ($stepOpened = !opened ? String(indexes.join('-')) : undefined)}
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<FlowModuleHeader
|
||||
bind:mod
|
||||
@@ -90,7 +93,7 @@
|
||||
on:new={(e) =>
|
||||
apply(createInlineScriptModule, {
|
||||
language: e.detail.language,
|
||||
isTrigger: e.detail.isTrigger
|
||||
type: e.detail.type
|
||||
})}
|
||||
on:loop={() => applyCreateLoop()}
|
||||
/>
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
import { goto } from '$app/navigation'
|
||||
import { page } from '$app/stores'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import { DENO_INIT_CODE, DENO_INIT_CODE_TRIGGER, initialCode } from '$lib/script_helpers'
|
||||
import {
|
||||
DENO_INIT_CODE,
|
||||
DENO_INIT_CODE_TRIGGER,
|
||||
initialCode,
|
||||
POSTGRES_INIT_CODE
|
||||
} from '$lib/script_helpers'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { emptySchema, encodeState, sendUserToast, setQueryWithoutLoad } from '$lib/utils'
|
||||
import { Breadcrumb, BreadcrumbItem } from 'flowbite-svelte'
|
||||
@@ -22,6 +27,7 @@
|
||||
|
||||
export let script: Script
|
||||
export let initialPath: string = ''
|
||||
export let template: 'pgsql' | undefined = undefined
|
||||
|
||||
let pathError = ''
|
||||
|
||||
@@ -34,11 +40,14 @@
|
||||
}
|
||||
|
||||
if (script.content == '') {
|
||||
initContent(script.language, script.is_trigger)
|
||||
initContent(script.language, template)
|
||||
}
|
||||
|
||||
function initContent(lang: 'deno' | 'python3', is_trigger: boolean) {
|
||||
script.content = initialCode(lang, is_trigger, false)
|
||||
function initContent(language: 'deno' | 'python3', template: 'pgsql' | undefined) {
|
||||
script.content = initialCode(
|
||||
language,
|
||||
template == 'pgsql' ? 'pgsql' : script.is_trigger ? 'trigger' : undefined
|
||||
)
|
||||
}
|
||||
|
||||
async function editScript(): Promise<void> {
|
||||
@@ -136,17 +145,25 @@
|
||||
changeStep(step + 1)
|
||||
}}>Next</button
|
||||
>
|
||||
{#if step == 2}
|
||||
<button
|
||||
class="default-button-secondary px-6 max-h-8 mr-2"
|
||||
on:click={async () => {
|
||||
await inferSchema()
|
||||
editScript()
|
||||
}}>Save (commit)</button
|
||||
>
|
||||
{/if}
|
||||
{:else}
|
||||
<button class="default-button px-6 self-end" on:click={editScript}>Save (commit)</button>
|
||||
<button class="default-button px-6 self-end" on:click={editScript}>Save</button>
|
||||
{/if}
|
||||
{#if step > 1}
|
||||
<button
|
||||
class="default-button-secondary px-6 max-h-8 mr-2"
|
||||
on:click={async () => {
|
||||
changeStep(step - 1)
|
||||
}}>Back</button
|
||||
>
|
||||
{/if}
|
||||
{#if step == 2}
|
||||
<button
|
||||
class="default-button-secondary px-6 max-h-8 mr-2"
|
||||
on:click={async () => {
|
||||
await inferSchema()
|
||||
editScript()
|
||||
}}>Save (commit)</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -183,7 +200,7 @@
|
||||
<a href="https://docs.windmill.dev/docs/reference/namespaces">docs</a>
|
||||
</div>
|
||||
</Path>
|
||||
<h3 class="text-gray-700 pb-1 border-b">Language</h3>
|
||||
<h3 class="text-gray-700 border-b">Language</h3>
|
||||
<div class="max-w-md">
|
||||
<RadioButton
|
||||
label="Language"
|
||||
@@ -191,10 +208,26 @@
|
||||
['Typescript (Deno)', 'deno'],
|
||||
['Python 3.10', 'python3']
|
||||
]}
|
||||
on:change={(e) => initContent(e.detail, script.is_trigger)}
|
||||
on:change={(e) => initContent(e.detail, template)}
|
||||
bind:value={script.language}
|
||||
/>
|
||||
</div>
|
||||
{#if script.language == 'deno'}
|
||||
<h4 class="text-gray-700 border-b">Template</h4>
|
||||
|
||||
<div class="max-w-md">
|
||||
<RadioButton
|
||||
label="Template"
|
||||
options={[
|
||||
['None', undefined],
|
||||
['PostgreSQL', 'pgsql']
|
||||
]}
|
||||
on:change={(e) => initContent(script.language, e.detail)}
|
||||
bind:value={template}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<h3 class="text-gray-700 pb-1 border-b">Metadata</h3>
|
||||
|
||||
<label class="block ">
|
||||
@@ -247,8 +280,12 @@
|
||||
type="checkbox"
|
||||
bind:checked={script.is_trigger}
|
||||
on:change={() => {
|
||||
if (script.content == DENO_INIT_CODE || script.content == DENO_INIT_CODE_TRIGGER) {
|
||||
initContent(script.language, script.is_trigger)
|
||||
if (
|
||||
script.content == DENO_INIT_CODE ||
|
||||
script.content == DENO_INIT_CODE_TRIGGER ||
|
||||
script.content == POSTGRES_INIT_CODE
|
||||
) {
|
||||
initContent(script.language, template)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import FlowBoxHeader from './FlowBoxHeader.svelte'
|
||||
export let title: string | undefined = undefined
|
||||
export let headerClickable = false
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let slots = $$props.$$slots
|
||||
</script>
|
||||
|
||||
<div class="bg-white border border-gray-300 rounded-md shadow-md">
|
||||
<FlowBoxHeader {title}>
|
||||
<FlowBoxHeader clickable={headerClickable} on:click={() => dispatch('clickheader')} {title}>
|
||||
<slot name="header" />
|
||||
</FlowBoxHeader>
|
||||
{#if slots.content}
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let title: string | undefined = undefined
|
||||
export let clickable: boolean = false
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<div class="flex items-center justify-between flex-wra px-6 py-2 ">
|
||||
<div
|
||||
on:click={() => dispatch('click')}
|
||||
class="flex items-center justify-between flex-wrap px-6 py-2"
|
||||
class:cursor-pointer={clickable}
|
||||
>
|
||||
{#if title}
|
||||
<h3 class="text-sm font-bold text-gray-900">{title}</h3>
|
||||
{/if}
|
||||
|
||||
@@ -30,27 +30,24 @@
|
||||
/>
|
||||
|
||||
<FlowScriptPicker
|
||||
label={`New Postgres SQL query`}
|
||||
label={`New PostgreSQL query`}
|
||||
icon={faCode}
|
||||
iconColor="text-blue-800"
|
||||
on:click={() =>
|
||||
dispatch('pick', {
|
||||
path: 'hub/173/postgresql/execute_query_and_return_results_postgresql'
|
||||
})}
|
||||
on:click={() => dispatch('new', { language: RawScript.language.DENO, type: 'pgsql' })}
|
||||
/>
|
||||
|
||||
<FlowScriptPicker
|
||||
label="New Python script (3.10)"
|
||||
icon={faCode}
|
||||
iconColor="text-green-500"
|
||||
on:click={() => dispatch('new', { language: RawScript.language.PYTHON3 })}
|
||||
on:click={() => dispatch('new', { language: RawScript.language.PYTHON3, type: 'flow' })}
|
||||
/>
|
||||
|
||||
<FlowScriptPicker
|
||||
label="New Typescript script (Deno)"
|
||||
icon={faCode}
|
||||
iconColor="text-blue-800"
|
||||
on:click={() => dispatch('new', { language: RawScript.language.DENO, isTrigger: false })}
|
||||
on:click={() => dispatch('new', { language: RawScript.language.DENO, type: 'flow' })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -64,7 +61,7 @@
|
||||
label="New Typescript script (Deno)"
|
||||
icon={faCode}
|
||||
iconColor="text-blue-800"
|
||||
on:click={() => dispatch('new', { language: RawScript.language.DENO, isTrigger: true })}
|
||||
on:click={() => dispatch('new', { language: RawScript.language.DENO, type: 'trigger' })}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
$: opened = $stepOpened === String(indexes.join('-'))
|
||||
</script>
|
||||
|
||||
<div on:click={() => stepOpened.update(() => String(indexes.join('-')))}>
|
||||
<div>
|
||||
<h3 class="text-sm font-bold text-gray-900">
|
||||
{#if 'path' in mod.value && mod.value.path}
|
||||
<IconedPath path={mod.value.path} />
|
||||
@@ -57,7 +57,7 @@
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row space-x-2">
|
||||
<div class="flex flex-row space-x-2" on:click|stopPropagation={() => undefined}>
|
||||
{#if mod.value.type === 'script' && !shouldPick}
|
||||
<Button size="xs" color="alternative" on:click={() => dispatch('fork')}>
|
||||
<Icon data={faCodeBranch} class="mr-2" />
|
||||
|
||||
@@ -46,12 +46,12 @@ export async function pickScript(path: string): Promise<FlowModuleSchema> {
|
||||
|
||||
export async function createInlineScriptModule({
|
||||
language,
|
||||
isTrigger = false
|
||||
type
|
||||
}: {
|
||||
language: RawScript.language
|
||||
isTrigger: boolean
|
||||
type: 'trigger' | 'flow' | 'pgsql'
|
||||
}): Promise<FlowModuleSchema> {
|
||||
const code = initialCode(language, isTrigger, true)
|
||||
const code = initialCode(language, type)
|
||||
|
||||
const flowModule: FlowModule = {
|
||||
value: { type: 'rawscript', content: code, language },
|
||||
|
||||
@@ -33,7 +33,7 @@ export const DENO_INIT_CODE = `// reload the smart assistant on the top right if
|
||||
// (Ctrl+space to cache dependencies on imports hover).
|
||||
// to import most npm packages without deno.land, use esm:
|
||||
// import { toWords } from "https://esm.sh/number-to-words"
|
||||
// import * as wmill from 'https://deno.land/x/windmill@v${__pkg__.version}/mod.ts'
|
||||
// import * as wmill from "https://deno.land/x/windmill@v${__pkg__.version}/mod.ts"
|
||||
|
||||
export async function main(
|
||||
a: number,
|
||||
@@ -47,7 +47,7 @@ export async function main(
|
||||
}
|
||||
`
|
||||
|
||||
export const DENO_INIT_CODE_CLEAR = `// import * as wmill from 'https://deno.land/x/windmill@v${__pkg__.version}/mod.ts'
|
||||
export const DENO_INIT_CODE_CLEAR = `// import * as wmill from "https://deno.land/x/windmill@v${__pkg__.version}/mod.ts"
|
||||
|
||||
export async function main() {
|
||||
return
|
||||
@@ -60,7 +60,24 @@ def main():
|
||||
return
|
||||
`
|
||||
|
||||
export const DENO_INIT_CODE_TRIGGER = `import * as wmill from 'https://deno.land/x/windmill@v${__pkg__.version}/mod.ts'
|
||||
export const POSTGRES_INIT_CODE = `import {
|
||||
pgSql,
|
||||
type Resource,
|
||||
} from "https://deno.land/x/windmill@v${__pkg__.version}/mod.ts";
|
||||
|
||||
//PG parametrized statement. No SQL injection is possible.
|
||||
export async function main(
|
||||
db: Resource<"postgresql"> = "$res:g/all/demodb",
|
||||
key: string,
|
||||
value: string,
|
||||
) {
|
||||
const query = await pgSql(
|
||||
db,
|
||||
)\`INSERT INTO demo VALUES (\${key}, \${value}) RETURNING *\`;
|
||||
return query.rows;
|
||||
}`
|
||||
|
||||
export const DENO_INIT_CODE_TRIGGER = `import * as wmill from "https://deno.land/x/windmill@v${__pkg__.version}/mod.ts"
|
||||
|
||||
export async function main() {
|
||||
|
||||
@@ -81,10 +98,22 @@ export async function main() {
|
||||
}
|
||||
`
|
||||
|
||||
export function initialCode(language: 'deno' | 'python3', is_trigger: boolean, is_flow: boolean): string {
|
||||
return language === 'deno'
|
||||
? is_trigger
|
||||
? DENO_INIT_CODE_TRIGGER
|
||||
: is_flow ? DENO_INIT_CODE_CLEAR : DENO_INIT_CODE
|
||||
: is_flow ? PYTHON_INIT_CODE_CLEAR : PYTHON_INIT_CODE
|
||||
export function initialCode(language: 'deno' | 'python3', type: 'trigger' | 'flow' | 'pgsql' | undefined): string {
|
||||
if (language === 'deno') {
|
||||
if (type === 'trigger') {
|
||||
return DENO_INIT_CODE_TRIGGER
|
||||
} else if (type === 'flow') {
|
||||
return DENO_INIT_CODE_CLEAR
|
||||
} else if (type === 'pgsql') {
|
||||
return POSTGRES_INIT_CODE
|
||||
} else {
|
||||
return DENO_INIT_CODE
|
||||
}
|
||||
} else {
|
||||
if (type === 'flow') {
|
||||
return PYTHON_INIT_CODE_CLEAR
|
||||
} else {
|
||||
return PYTHON_INIT_CODE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user