feat: is_trigger is just a type tag, soon to include failure and command (#523)
* script kind * all * init code flow * kind: trigger * kind: trigger
This commit is contained in:
1
backend/migrations/20220904123027_script_kind.down.sql
Normal file
1
backend/migrations/20220904123027_script_kind.down.sql
Normal file
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
5
backend/migrations/20220904123027_script_kind.up.sql
Normal file
5
backend/migrations/20220904123027_script_kind.up.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
-- Add up migration script here
|
||||
CREATE TYPE SCRIPT_KIND AS ENUM ('script', 'trigger', 'failure', 'command');
|
||||
|
||||
ALTER TABLE script ADD COLUMN kind SCRIPT_KIND NOT NULL DEFAULT 'script';
|
||||
ALTER TABLE script DROP COLUMN is_trigger;
|
||||
@@ -1532,8 +1532,9 @@ paths:
|
||||
type: string
|
||||
approved:
|
||||
type: boolean
|
||||
is_trigger:
|
||||
type: boolean
|
||||
kind:
|
||||
type: string
|
||||
enum: [script, failure, trigger, command]
|
||||
votes:
|
||||
type: number
|
||||
views:
|
||||
@@ -1544,7 +1545,7 @@ paths:
|
||||
- summary
|
||||
- app
|
||||
- approved
|
||||
- is_trigger
|
||||
- kind
|
||||
- views
|
||||
- votes
|
||||
|
||||
@@ -1688,15 +1689,13 @@ paths:
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: is_trigger
|
||||
- name: kind
|
||||
description: |
|
||||
(default regardless)
|
||||
if true show only the trigger scripts
|
||||
if false show only the non trigger scripts
|
||||
if not defined, show all regardless of if the script is a trigger script
|
||||
script kind
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: All available scripts
|
||||
@@ -1744,8 +1743,9 @@ paths:
|
||||
language:
|
||||
type: string
|
||||
enum: [python3, deno]
|
||||
is_trigger:
|
||||
type: boolean
|
||||
kind:
|
||||
type: string
|
||||
enum: [script, failure, trigger, command]
|
||||
required:
|
||||
- path
|
||||
- summary
|
||||
@@ -3241,8 +3241,9 @@ components:
|
||||
language:
|
||||
type: string
|
||||
enum: [python3, deno]
|
||||
is_trigger:
|
||||
type: boolean
|
||||
kind:
|
||||
type: string
|
||||
enum: [script, failure, trigger, command]
|
||||
required:
|
||||
- hash
|
||||
- path
|
||||
@@ -3255,7 +3256,7 @@ components:
|
||||
- is_template
|
||||
- extra_perms
|
||||
- language
|
||||
- is_trigger
|
||||
- kind
|
||||
|
||||
ScriptArgs:
|
||||
type: object
|
||||
|
||||
@@ -1215,6 +1215,52 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"5fee34ce48f01bb55707227548ea4d1e77ba75abfae412a902b1d8d6a1963725": {
|
||||
"query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Int8",
|
||||
"Varchar",
|
||||
"Int8Array",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Bool",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
{
|
||||
"Custom": {
|
||||
"name": "script_lang",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"python3",
|
||||
"deno"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Custom": {
|
||||
"name": "script_kind",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"script",
|
||||
"trigger",
|
||||
"failure"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"6199e8be5cb13db71108e555ea20f0b76dc38476670f9fc0667b057d2766d42e": {
|
||||
"query": "SELECT set_config('session.groups', $1, true)",
|
||||
"describe": {
|
||||
@@ -2368,41 +2414,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"b49868120aa48f9163ffb0262804c0528723c858d20d7e556750fe712e3d5123": {
|
||||
"query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, is_trigger) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Int8",
|
||||
"Varchar",
|
||||
"Int8Array",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Bool",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
{
|
||||
"Custom": {
|
||||
"name": "script_lang",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"python3",
|
||||
"deno"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"b7dd791cd69748ef51b7520f505c0c8bb1b4014a273476eddfecf1ab658a18b4": {
|
||||
"query": "select hash from script where path = $1 AND (workspace_id = $2 OR workspace_id = 'starter') AND\n created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND (workspace_id = $2 OR workspace_id = 'starter')) AND\n deleted = false",
|
||||
"describe": {
|
||||
|
||||
@@ -124,6 +124,15 @@ impl Serialize for ScriptHashes {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(sqlx::Type, Serialize, Deserialize, Debug, Hash)]
|
||||
#[sqlx(type_name = "SCRIPT_KIND", rename_all = "lowercase")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ScriptKind {
|
||||
Trigger,
|
||||
Failure,
|
||||
Script,
|
||||
}
|
||||
|
||||
#[derive(FromRow, Serialize)]
|
||||
pub struct Script {
|
||||
pub workspace_id: String,
|
||||
@@ -143,7 +152,7 @@ pub struct Script {
|
||||
pub lock: Option<String>,
|
||||
pub lock_error_logs: Option<String>,
|
||||
pub language: ScriptLang,
|
||||
pub is_trigger: bool,
|
||||
pub kind: ScriptKind,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, sqlx::Type, Debug)]
|
||||
@@ -170,7 +179,7 @@ pub struct NewScript {
|
||||
pub is_template: Option<bool>,
|
||||
pub lock: Option<Vec<String>>,
|
||||
pub language: ScriptLang,
|
||||
pub is_trigger: Option<bool>,
|
||||
pub kind: Option<ScriptKind>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -185,7 +194,7 @@ pub struct ListScriptQuery {
|
||||
pub order_by: Option<String>,
|
||||
pub order_desc: Option<bool>,
|
||||
pub is_template: Option<bool>,
|
||||
pub is_trigger: Option<bool>,
|
||||
pub kind: Option<String>,
|
||||
}
|
||||
|
||||
async fn list_scripts(
|
||||
@@ -216,7 +225,7 @@ async fn list_scripts(
|
||||
"null as lock",
|
||||
"CASE WHEN lock_error_logs IS NOT NULL THEN 'error' ELSE null END as lock_error_logs",
|
||||
"language",
|
||||
"is_trigger",
|
||||
"kind",
|
||||
])
|
||||
.order_by("created_at", lq.order_desc.unwrap_or(true))
|
||||
.and_where("workspace_id = ? OR workspace_id = 'starter'".bind(&w_id))
|
||||
@@ -254,8 +263,8 @@ async fn list_scripts(
|
||||
if let Some(it) = &lq.is_template {
|
||||
sqlb.and_where_eq("is_template", it);
|
||||
}
|
||||
if let Some(it) = &lq.is_trigger {
|
||||
sqlb.and_where_eq("is_trigger", it);
|
||||
if let Some(k) = &lq.kind {
|
||||
sqlb.and_where_eq("kind", "?".bind(&k.to_lowercase()));
|
||||
}
|
||||
|
||||
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
|
||||
@@ -407,7 +416,7 @@ async fn create_script(
|
||||
//::text::json is to ensure we use serde_json with preserve order
|
||||
sqlx::query!(
|
||||
"INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, \
|
||||
content, created_by, schema, is_template, extra_perms, lock, language, is_trigger) \
|
||||
content, created_by, schema, is_template, extra_perms, lock, language, kind) \
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14)",
|
||||
&w_id,
|
||||
&hash.0,
|
||||
@@ -422,7 +431,7 @@ async fn create_script(
|
||||
extra_perms,
|
||||
lock,
|
||||
ns.language: ScriptLang,
|
||||
ns.is_trigger.unwrap_or(false),
|
||||
ns.kind.unwrap_or(ScriptKind::Script): ScriptKind,
|
||||
)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</script>
|
||||
|
||||
<Modal bind:this={modal} z="z-30">
|
||||
<div slot="title">search a {itemName}</div>
|
||||
<div slot="title">Search a {itemName}</div>
|
||||
<div slot="content">
|
||||
<div class="w-12/12 pb-4">
|
||||
<input placeholder="Search {itemName}" bind:value={itemsFilter} class="search-item" />
|
||||
@@ -57,7 +57,7 @@
|
||||
<IconedResourceType silent={true} name={obj['app']} />
|
||||
<span class="mr-2" />
|
||||
{/if}
|
||||
<span class="mr-2">{obj[extraField]}</span><span class="font-normal whitespace-nowrap"
|
||||
<span class="mr-2">{obj[extraField]}</span><span class="font-normal break-all"
|
||||
>{obj['path'] ?? ''}</span
|
||||
>
|
||||
</p>
|
||||
|
||||
@@ -93,7 +93,8 @@
|
||||
on:new={(e) =>
|
||||
apply(createInlineScriptModule, {
|
||||
language: e.detail.language,
|
||||
type: e.detail.type
|
||||
kind: e.detail.kind,
|
||||
subkind: e.detail.subkind
|
||||
})}
|
||||
on:loop={() => applyCreateLoop()}
|
||||
/>
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { ScriptService, type Script } from '$lib/gen'
|
||||
import { Script, ScriptService } from '$lib/gen'
|
||||
|
||||
import { goto } from '$app/navigation'
|
||||
import { page } from '$app/stores'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import {
|
||||
DENO_INIT_CODE,
|
||||
DENO_INIT_CODE_TRIGGER,
|
||||
initialCode,
|
||||
POSTGRES_INIT_CODE
|
||||
} from '$lib/script_helpers'
|
||||
import { initialCode, isInitialCode } from '$lib/script_helpers'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { emptySchema, encodeState, sendUserToast, setQueryWithoutLoad } from '$lib/utils'
|
||||
import { Breadcrumb, BreadcrumbItem } from 'flowbite-svelte'
|
||||
@@ -21,33 +16,30 @@
|
||||
import ScriptEditor from './ScriptEditor.svelte'
|
||||
import ScriptSchema from './ScriptSchema.svelte'
|
||||
import CenteredPage from './CenteredPage.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
|
||||
let editor: ScriptEditor
|
||||
let scriptSchema: ScriptSchema
|
||||
|
||||
export let script: Script
|
||||
export let initialPath: string = ''
|
||||
export let template: 'pgsql' | undefined = undefined
|
||||
export let template: 'pgsql' | 'script' = 'script'
|
||||
|
||||
let pathError = ''
|
||||
|
||||
$: setQueryWithoutLoad($page.url, 'state', encodeState(script))
|
||||
$: step = Number($page.url.searchParams.get('step')) || 1
|
||||
$: {
|
||||
if (script.language == 'python3') {
|
||||
script.is_trigger = false
|
||||
}
|
||||
}
|
||||
|
||||
if (script.content == '') {
|
||||
initContent(script.language, template)
|
||||
initContent(script.language, script.kind, template)
|
||||
}
|
||||
|
||||
function initContent(language: 'deno' | 'python3', template: 'pgsql' | undefined) {
|
||||
script.content = initialCode(
|
||||
language,
|
||||
template == 'pgsql' ? 'pgsql' : script.is_trigger ? 'trigger' : undefined
|
||||
)
|
||||
function initContent(
|
||||
language: 'deno' | 'python3',
|
||||
kind: Script.kind,
|
||||
template: 'pgsql' | 'script'
|
||||
) {
|
||||
script.content = initialCode(language, kind, template)
|
||||
}
|
||||
|
||||
async function editScript(): Promise<void> {
|
||||
@@ -63,7 +55,7 @@
|
||||
schema: script.schema,
|
||||
is_template: script.is_template,
|
||||
language: script.language,
|
||||
is_trigger: script.is_trigger
|
||||
kind: script.kind
|
||||
}
|
||||
})
|
||||
sendUserToast(`Success! New script version created with hash ${newHash}`)
|
||||
@@ -200,28 +192,77 @@
|
||||
['Typescript (Deno)', 'deno'],
|
||||
['Python 3.10', 'python3']
|
||||
]}
|
||||
on:change={(e) => initContent(e.detail, template)}
|
||||
on:change={(e) => initContent(e.detail, script.kind, template)}
|
||||
bind:value={script.language}
|
||||
/>
|
||||
</div>
|
||||
<h4 class="text-gray-700 border-b">
|
||||
Script Kind <Tooltip
|
||||
>In most cases, you will want the General Script. <br />
|
||||
Trigger are meant to be used as the first module of flows to trigger them based on watching
|
||||
new events externally. <br />
|
||||
Failure scripts are used to handle unrecoverable errors of flows and for handling errors
|
||||
at the workspace level. <br />
|
||||
Command scripts are used when the workspace is associated with a slack workspace to be triggered
|
||||
on command.</Tooltip
|
||||
>
|
||||
</h4>
|
||||
|
||||
{#if script.language == 'deno'}
|
||||
<h4 class="text-gray-700 border-b">Template</h4>
|
||||
<div class="max-w-lg">
|
||||
<RadioButton
|
||||
label="Script Type"
|
||||
options={[
|
||||
['General Script', Script.kind.SCRIPT],
|
||||
['Trigger Script', Script.kind.TRIGGER]
|
||||
// ['Failure Handler', Script.kind.FAILURE],
|
||||
// ['Command Handler', Script.kind.COMMAND]
|
||||
]}
|
||||
on:change={(e) => {
|
||||
if (isInitialCode(script.content)) {
|
||||
template = 'script'
|
||||
initContent(script.language, e.detail, template)
|
||||
}
|
||||
}}
|
||||
bind:value={script.kind}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="max-w-lg">
|
||||
<RadioButton
|
||||
label="Script Type"
|
||||
options={[['General Script', Script.kind.SCRIPT]]}
|
||||
on:change={(e) => {
|
||||
if (isInitialCode(script.content)) {
|
||||
template = 'script'
|
||||
initContent(script.language, e.detail, template)
|
||||
}
|
||||
}}
|
||||
bind:value={script.kind}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if script.language == 'deno' && script.kind == Script.kind.SCRIPT}
|
||||
<h4 class="text-gray-700 border-b">
|
||||
Script Template <Tooltip
|
||||
>A template is a pre-filled script corresponding to a more specialized use-case</Tooltip
|
||||
>
|
||||
</h4>
|
||||
|
||||
<div class="max-w-md">
|
||||
<RadioButton
|
||||
label="Template"
|
||||
options={[
|
||||
['None', undefined],
|
||||
['PostgreSQL', 'pgsql']
|
||||
['Standard', 'script'],
|
||||
['PostgreSQL Prepared Statement', 'pgsql']
|
||||
]}
|
||||
on:change={(e) => initContent(script.language, e.detail)}
|
||||
on:change={(e) => initContent(script.language, script.kind, e.detail)}
|
||||
bind:value={template}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<h3 class="text-gray-700 pb-1 border-b">Metadata</h3>
|
||||
|
||||
<label class="block ">
|
||||
<span class="text-gray-700">Summary <Required required={false} /></span>
|
||||
<textarea
|
||||
@@ -261,28 +302,14 @@
|
||||
</label>
|
||||
|
||||
<label class="block">
|
||||
<span class="text-gray-700 mr-2">Save as template</span>
|
||||
<span class="text-gray-700 mr-2"
|
||||
>Save as workspace template <Tooltip
|
||||
>Enable your teammates to use this script as a template to write new scripts.</Tooltip
|
||||
>
|
||||
</span>
|
||||
<input type="checkbox" bind:checked={script.is_template} />
|
||||
</label>
|
||||
|
||||
<label class="block">
|
||||
<span class="text-gray-700 mr-2">Save as trigger script</span>
|
||||
<input
|
||||
disabled={script.language == 'python3'}
|
||||
type="checkbox"
|
||||
bind:checked={script.is_trigger}
|
||||
on:change={() => {
|
||||
if (
|
||||
script.content == DENO_INIT_CODE ||
|
||||
script.content == DENO_INIT_CODE_TRIGGER ||
|
||||
script.content == POSTGRES_INIT_CODE
|
||||
) {
|
||||
initContent(script.language, template)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<h3 class="text-gray-700 ">Description rendered</h3>
|
||||
<div
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { ScriptService, FlowService } from '$lib/gen'
|
||||
import { ScriptService, FlowService, Script } from '$lib/gen'
|
||||
|
||||
import Icon from 'svelte-awesome'
|
||||
import { faSearch } from '@fortawesome/free-solid-svg-icons'
|
||||
@@ -18,7 +18,7 @@
|
||||
export let allowFlow = false
|
||||
export let allowHub = false
|
||||
export let itemKind: 'hub' | 'script' | 'flow' = allowHub ? 'hub' : 'script'
|
||||
export let isTrigger: boolean | undefined = undefined
|
||||
export let kind: Script.kind = Script.kind.SCRIPT
|
||||
|
||||
let items: { summary: String; path: String; version?: String }[] = []
|
||||
let itemPicker: ItemPicker
|
||||
@@ -35,7 +35,7 @@
|
||||
if (itemKind == 'flow') {
|
||||
items = await FlowService.listFlows({ workspace: $workspaceStore! })
|
||||
} else if (itemKind == 'script') {
|
||||
items = await ScriptService.listScripts({ workspace: $workspaceStore!, isTrigger })
|
||||
items = await ScriptService.listScripts({ workspace: $workspaceStore!, kind })
|
||||
} else {
|
||||
items = $hubScripts ?? []
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
class="w-96 text-left"
|
||||
>
|
||||
<slot />
|
||||
<div id="arrow" data-popper-arrow />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { RawScript } from '$lib/gen'
|
||||
import { RawScript, Script } from '$lib/gen'
|
||||
|
||||
import { faCode, faRepeat } from '@fortawesome/free-solid-svg-icons'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -18,8 +18,8 @@
|
||||
{/if}
|
||||
|
||||
<div class="grid sm:grid-col-2 lg:grid-cols-3 gap-4">
|
||||
<PickScript on:pick />
|
||||
<PickHubScript on:pick />
|
||||
<PickScript kind={Script.kind.SCRIPT} on:pick />
|
||||
<PickHubScript kind={Script.kind.SCRIPT} on:pick />
|
||||
|
||||
<FlowScriptPicker
|
||||
label={`Create a for-loop here`}
|
||||
@@ -33,21 +33,24 @@
|
||||
label={`New PostgreSQL query`}
|
||||
icon={faCode}
|
||||
iconColor="text-blue-800"
|
||||
on:click={() => dispatch('new', { language: RawScript.language.DENO, type: 'pgsql' })}
|
||||
on:click={() =>
|
||||
dispatch('new', { language: RawScript.language.DENO, kind: 'script', subkind: 'pgsql' })}
|
||||
/>
|
||||
|
||||
<FlowScriptPicker
|
||||
label="New Python script (3.10)"
|
||||
icon={faCode}
|
||||
iconColor="text-green-500"
|
||||
on:click={() => dispatch('new', { language: RawScript.language.PYTHON3, type: 'flow' })}
|
||||
on:click={() =>
|
||||
dispatch('new', { language: RawScript.language.PYTHON3, kind: 'script', subkind: 'flow' })}
|
||||
/>
|
||||
|
||||
<FlowScriptPicker
|
||||
label="New Typescript script (Deno)"
|
||||
icon={faCode}
|
||||
iconColor="text-blue-800"
|
||||
on:click={() => dispatch('new', { language: RawScript.language.DENO, type: 'flow' })}
|
||||
on:click={() =>
|
||||
dispatch('new', { language: RawScript.language.DENO, kind: 'script', subkind: 'flow' })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -55,13 +58,13 @@
|
||||
<div class="text-sm font-bold">Trigger scripts</div>
|
||||
|
||||
<div class="grid sm:grid-col-1 md:grid-col-2 lg:grid-cols-3 gap-4">
|
||||
<PickScript isTrigger={true} on:pick />
|
||||
<PickHubScript isTrigger={true} on:pick />
|
||||
<PickScript kind={Script.kind.TRIGGER} on:pick />
|
||||
<PickHubScript kind={Script.kind.TRIGGER} on:pick />
|
||||
<FlowScriptPicker
|
||||
label="New Typescript script (Deno)"
|
||||
icon={faCode}
|
||||
iconColor="text-blue-800"
|
||||
on:click={() => dispatch('new', { language: RawScript.language.DENO, type: 'trigger' })}
|
||||
on:click={() => dispatch('new', { language: RawScript.language.DENO, kind: 'trigger' })}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Schema } from '$lib/common'
|
||||
import {
|
||||
CompletedJob,
|
||||
Job,
|
||||
Script,
|
||||
ScriptService,
|
||||
type Flow,
|
||||
type FlowModule,
|
||||
@@ -52,12 +53,14 @@ export async function pickScript(path: string): Promise<FlowModuleSchema> {
|
||||
|
||||
export async function createInlineScriptModule({
|
||||
language,
|
||||
type
|
||||
kind,
|
||||
subkind
|
||||
}: {
|
||||
language: RawScript.language
|
||||
type: 'trigger' | 'flow' | 'pgsql'
|
||||
kind: Script.kind,
|
||||
subkind: 'pgsql' | 'flow'
|
||||
}): Promise<FlowModuleSchema> {
|
||||
const code = initialCode(language, type)
|
||||
const code = initialCode(language, kind, subkind)
|
||||
|
||||
const flowModule: FlowModule = {
|
||||
value: { type: 'rawscript', content: code, language },
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
import ItemPicker from '$lib/components/ItemPicker.svelte'
|
||||
import { hubScripts } from '$lib/stores'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Script } from '$lib/gen'
|
||||
|
||||
export let isTrigger: boolean = false
|
||||
export let kind: Script.kind
|
||||
|
||||
type Item = { summary: String; path: String; version?: String }
|
||||
|
||||
let items: Item[]
|
||||
$: items = $hubScripts?.filter((x) => x.is_trigger == isTrigger) ?? []
|
||||
$: items = $hubScripts?.filter((x) => x.kind == kind) ?? []
|
||||
let itemPicker: ItemPicker
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@@ -30,7 +31,7 @@
|
||||
/>
|
||||
|
||||
<FlowScriptPicker
|
||||
label={`Pick a ${isTrigger ? 'trigger ' : ''} script from the Hub`}
|
||||
label={`Pick a ${kind == Script.kind.SCRIPT ? '' : kind} script from the Hub`}
|
||||
icon={faUserGroup}
|
||||
iconColor="text-blue-500"
|
||||
on:click={() => itemPicker.openModal()}
|
||||
|
||||
@@ -7,23 +7,16 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import FlowScriptPicker from './FlowScriptPicker.svelte'
|
||||
|
||||
export let isTrigger: boolean = false
|
||||
export let kind: string
|
||||
|
||||
type Item = { summary: String; path: String; version?: String }
|
||||
|
||||
let items: Item[] = []
|
||||
let itemPicker: ItemPicker
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function loadItems(): Promise<void> {
|
||||
items = await ScriptService.listScripts({ workspace: $workspaceStore!, isTrigger })
|
||||
}
|
||||
|
||||
$: {
|
||||
if ($workspaceStore) {
|
||||
loadItems()
|
||||
}
|
||||
async function loadItems(): Promise<Item[]> {
|
||||
return await ScriptService.listScripts({ workspace: $workspaceStore!, kind })
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -34,13 +27,11 @@
|
||||
}}
|
||||
itemName={'Script'}
|
||||
extraField="summary"
|
||||
loadItems={async () => {
|
||||
return items
|
||||
}}
|
||||
{loadItems}
|
||||
/>
|
||||
|
||||
<FlowScriptPicker
|
||||
label={`Pick a ${isTrigger ? 'trigger ' : ''}script from your workspace`}
|
||||
label={`Pick a ${kind == 'script' ? '' : kind} script from your workspace`}
|
||||
icon={faUserGroup}
|
||||
iconColor="text-blue-500"
|
||||
on:click={() => itemPicker.openModal()}
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
<span class="bg-blue-100 text-blue-800 text-xs font-semibold px-2.5 py-0.5 rounded ">
|
||||
{String(script.language).toUpperCase()}
|
||||
</span>
|
||||
{#if script.is_trigger}
|
||||
{#if script.kind != 'script'}
|
||||
<span class="bg-red-100 text-red-800 text-xs font-semibold px-2.5 py-0.5 rounded">
|
||||
TRIGGER
|
||||
{script.kind}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { Script } from "./gen"
|
||||
|
||||
export const PYTHON_INIT_CODE = `import os
|
||||
import wmill
|
||||
from datetime import datetime
|
||||
@@ -98,19 +100,34 @@ export async function main() {
|
||||
}
|
||||
`
|
||||
|
||||
export function initialCode(language: 'deno' | 'python3', type: 'trigger' | 'flow' | 'pgsql' | undefined): string {
|
||||
const ALL_INITIAL_CODE = [PYTHON_INIT_CODE, DENO_INIT_CODE, POSTGRES_INIT_CODE, DENO_INIT_CODE_TRIGGER, DENO_INIT_CODE_CLEAR, PYTHON_INIT_CODE_CLEAR]
|
||||
|
||||
export function isInitialCode(content: string): boolean {
|
||||
for (const code of ALL_INITIAL_CODE) {
|
||||
if (content === code) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function initialCode(language: 'deno' | 'python3', kind: Script.kind, subkind: 'pgsql' | 'flow' | 'script' | undefined): string {
|
||||
if (language === 'deno') {
|
||||
if (type === 'trigger') {
|
||||
if (kind === 'trigger') {
|
||||
return DENO_INIT_CODE_TRIGGER
|
||||
} else if (type === 'flow') {
|
||||
return DENO_INIT_CODE_CLEAR
|
||||
} else if (type === 'pgsql') {
|
||||
return POSTGRES_INIT_CODE
|
||||
} else if (kind === 'script') {
|
||||
if (subkind === 'flow') {
|
||||
return DENO_INIT_CODE_CLEAR
|
||||
} else if (subkind === 'pgsql') {
|
||||
return POSTGRES_INIT_CODE
|
||||
} else {
|
||||
return DENO_INIT_CODE
|
||||
}
|
||||
} else {
|
||||
return DENO_INIT_CODE
|
||||
}
|
||||
} else {
|
||||
if (type === 'flow') {
|
||||
if (subkind === 'flow') {
|
||||
return PYTHON_INIT_CODE_CLEAR
|
||||
} else {
|
||||
return PYTHON_INIT_CODE
|
||||
|
||||
@@ -27,7 +27,7 @@ export const hubScripts = writable<
|
||||
path: string
|
||||
summary: string
|
||||
approved: boolean
|
||||
is_trigger: boolean
|
||||
kind: string
|
||||
app: string
|
||||
}>
|
||||
| undefined
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { goto } from '$app/navigation'
|
||||
import { FlowService, ScriptService, type Flow, type FlowModule, type User } from '$lib/gen'
|
||||
import { FlowService, Script, ScriptService, type Flow, type FlowModule, type User } from '$lib/gen'
|
||||
import { toast } from '@zerodevx/svelte-toast'
|
||||
import { get } from 'svelte/store'
|
||||
import type { Schema } from './common'
|
||||
@@ -502,7 +502,7 @@ export async function loadHubScripts() {
|
||||
path: `hub/${x.id}/${x.app}/${x.summary.toLowerCase().replaceAll(/\s+/g, '_')}`,
|
||||
summary: `${x.summary} (${x.app}) ${x.views} uses`,
|
||||
approved: x.approved,
|
||||
is_trigger: x.is_trigger,
|
||||
kind: x.kind,
|
||||
app: x.app,
|
||||
views: x.views,
|
||||
votes: x.votes,
|
||||
@@ -545,14 +545,14 @@ export function scriptToHubUrl(
|
||||
content: string,
|
||||
summary: string,
|
||||
description: string,
|
||||
trigger: boolean
|
||||
kind: Script.kind
|
||||
): URL {
|
||||
const url = new URL('https://hub.windmill.dev/scripts/add')
|
||||
|
||||
url.searchParams.append('content', content)
|
||||
url.searchParams.append('summary', summary)
|
||||
url.searchParams.append('description', description)
|
||||
url.searchParams.append('trigger', trigger.toString())
|
||||
url.searchParams.append('kind', kind)
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores'
|
||||
import { sendUserToast, formatCron } from '$lib/utils'
|
||||
import { ScriptService, type Script, ScheduleService, type Flow, FlowService } from '$lib/gen'
|
||||
import { ScriptService, Script, ScheduleService, type Flow, FlowService } from '$lib/gen'
|
||||
|
||||
import PageHeader from '$lib/components/PageHeader.svelte'
|
||||
import Path from '$lib/components/Path.svelte'
|
||||
@@ -139,7 +139,12 @@
|
||||
<p class="text-xs mb-1 text-gray-600">
|
||||
Pick a script or flow to be triggered by the schedule<Required required={true} />
|
||||
</p>
|
||||
<ScriptPicker isTrigger={false} allowFlow={true} bind:itemKind bind:scriptPath={script_path} />
|
||||
<ScriptPicker
|
||||
kind={Script.kind.SCRIPT}
|
||||
allowFlow={true}
|
||||
bind:itemKind
|
||||
bind:scriptPath={script_path}
|
||||
/>
|
||||
<div class="max-w-5xl {edit ? '' : 'mt-2 md:mt-6'}">
|
||||
<h2>Arguments</h2>
|
||||
{#if runnable}
|
||||
|
||||
@@ -305,7 +305,7 @@
|
||||
<p class="text-xs text-gray-600 italic">No scripts yet</p>
|
||||
{:else}
|
||||
<div class="grid md:grid-cols-2 gap-4 sm:grid-cols-1 xl:grid-cols-3">
|
||||
{#each scripts as { summary, path, hash, language, extra_perms, canWrite, lock_error_logs, is_trigger }}
|
||||
{#each scripts as { summary, path, hash, language, extra_perms, canWrite, lock_error_logs, kind }}
|
||||
<div
|
||||
class="flex flex-col justify-between script max-w-lg overflow-visible shadow-sm shadow-blue-100 border border-gray-200 bg-gray-50 py-2"
|
||||
>
|
||||
@@ -325,8 +325,8 @@
|
||||
<div class="mr-3 w-full">
|
||||
<SharedBadge {canWrite} extraPerms={extra_perms} />
|
||||
<Badge twBgColor="bg-blue-200">{language}</Badge>
|
||||
{#if is_trigger}
|
||||
<Badge twBgColor="bg-blue-300">{is_trigger}</Badge>
|
||||
{#if kind != 'script'}
|
||||
<Badge twBgColor="bg-blue-300">{kind}</Badge>
|
||||
{/if}
|
||||
{#if lock_error_logs}<Badge
|
||||
twBgColor="bg-red-200"
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
import ScriptBuilder from '$lib/components/ScriptBuilder.svelte'
|
||||
import type { Schema } from '$lib/common'
|
||||
import { decodeState, emptySchema, getScriptByPath, sendUserToast } from '$lib/utils'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
|
||||
// Default
|
||||
let schema: Schema = emptySchema()
|
||||
@@ -40,7 +39,7 @@
|
||||
is_template: false,
|
||||
extra_perms: {},
|
||||
language: 'deno',
|
||||
is_trigger: false
|
||||
kind: Script.kind.SCRIPT
|
||||
}
|
||||
|
||||
async function loadTemplate(): Promise<void> {
|
||||
|
||||
@@ -134,9 +134,9 @@
|
||||
>Template</span
|
||||
>
|
||||
{/if}
|
||||
{#if script?.is_trigger}
|
||||
{#if script && script.kind != 'script'}
|
||||
<span class="mx-2 bg-blue-500 rounded-md bg-opacity-25 text-sm font-normal px-1 py-px"
|
||||
>Trigger</span
|
||||
>{script.kind}</span
|
||||
>
|
||||
{/if}
|
||||
<SharedBadge canWrite={can_write} extraPerms={script?.extra_perms ?? {}} />
|
||||
@@ -197,7 +197,7 @@
|
||||
script.content,
|
||||
script.summary,
|
||||
script.description ?? '',
|
||||
script.is_trigger
|
||||
script.kind
|
||||
).toString()}
|
||||
>
|
||||
<div class="inline-flex items-center justify-center">
|
||||
|
||||
@@ -8,7 +8,13 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Fuse from 'fuse.js'
|
||||
import { UserService, type WorkspaceInvite, WorkspaceService, OauthService } from '$lib/gen'
|
||||
import {
|
||||
UserService,
|
||||
type WorkspaceInvite,
|
||||
WorkspaceService,
|
||||
OauthService,
|
||||
Script
|
||||
} from '$lib/gen'
|
||||
import type { User } from '$lib/gen'
|
||||
import { sendUserToast, msToSec } from '$lib/utils'
|
||||
import PageHeader from '$lib/components/PageHeader.svelte'
|
||||
@@ -216,7 +222,7 @@
|
||||
</button>
|
||||
{/if}
|
||||
<h3 class="mt-5 text-gray-700">Select a script to run on /windmill command:</h3>
|
||||
<ScriptPicker isTrigger={false} bind:scriptPath on:select={editSlackCommand} />
|
||||
<ScriptPicker kind={Script.kind.SCRIPT} bind:scriptPath on:select={editSlackCommand} />
|
||||
|
||||
<p class="text-sm text-gray-700 italic mt-3">
|
||||
A script run from slack /windmill command is run as group 'slack' and hence every variables or
|
||||
|
||||
Reference in New Issue
Block a user