fix(frontend): filter script by is_trigger and jobs by is_skipped + path fix

This commit is contained in:
Ruben Fiszel
2022-07-19 20:23:58 +02:00
parent 08ab4d171a
commit 97292d18fb
27 changed files with 228 additions and 110 deletions
@@ -0,0 +1 @@
-- Add down migration script here
@@ -0,0 +1,4 @@
-- Add up migration script here
ALTER TABLE script DROP COLUMN trigger_reco_interval;
ALTER TABLE script ADD COLUMN is_trigger BOOLEAN NOT NULL DEFAULT false;
+24 -6
View File
@@ -1609,6 +1609,15 @@ paths:
in: query
schema:
type: boolean
- name: is_trigger
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
in: query
schema:
type: boolean
responses:
"200":
description: All available scripts
@@ -1656,8 +1665,8 @@ paths:
language:
type: string
enum: [python3, deno]
trigger_reco_interval:
type: integer
is_trigger:
type: boolean
required:
- path
- summary
@@ -2266,7 +2275,11 @@ paths:
- $ref: "#/components/parameters/CreatedAfter"
- $ref: "#/components/parameters/Success"
- $ref: "#/components/parameters/JobKinds"
- name: is_skipped
description: is the job skipped
in: query
schema:
type: boolean
responses:
"200":
description: All available completed jobs
@@ -2293,7 +2306,11 @@ paths:
- $ref: "#/components/parameters/CreatedBefore"
- $ref: "#/components/parameters/CreatedAfter"
- $ref: "#/components/parameters/JobKinds"
- name: is_skipped
description: is the job skipped
in: query
schema:
type: boolean
- name: success
description: filter on successful jobs
in: query
@@ -3115,8 +3132,8 @@ components:
language:
type: string
enum: [python3, deno]
trigger_reco_interval:
type: integer
is_trigger:
type: boolean
required:
- hash
- path
@@ -3129,6 +3146,7 @@ components:
- is_template
- extra_perms
- language
- is_trigger
ScriptArgs:
type: object
+4
View File
@@ -557,6 +557,7 @@ pub struct ListCompletedQuery {
pub parent_job: Option<String>,
pub order_desc: Option<bool>,
pub job_kinds: Option<String>,
pub is_skipped: Option<bool>,
}
fn list_completed_jobs_query(
w_id: &str,
@@ -597,6 +598,9 @@ fn list_completed_jobs_query(
if let Some(dt) = &lq.created_after {
sqlb.and_where_gt("created_at", format!("to_timestamp({})", dt.timestamp()));
}
if let Some(sk) = &lq.is_skipped {
sqlb.and_where_eq("is_skipped", sk);
}
if let Some(jk) = &lq.job_kinds {
sqlb.and_where_in(
"job_kind",
+9 -5
View File
@@ -129,7 +129,7 @@ pub struct Script {
pub lock: Option<String>,
pub lock_error_logs: Option<String>,
pub language: ScriptLang,
pub trigger_reco_interval: Option<i32>,
pub is_trigger: bool,
}
#[derive(Serialize, Deserialize, sqlx::Type, Debug)]
@@ -156,7 +156,7 @@ pub struct NewScript {
pub is_template: Option<bool>,
pub lock: Option<Vec<String>>,
pub language: ScriptLang,
pub trigger_reco_interval: Option<i32>,
pub is_trigger: Option<bool>,
}
#[derive(Deserialize)]
@@ -171,6 +171,7 @@ pub struct ListScriptQuery {
pub order_by: Option<String>,
pub order_desc: Option<bool>,
pub is_template: Option<bool>,
pub is_trigger: Option<bool>,
}
async fn list_scripts(
@@ -201,7 +202,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",
"trigger_reco_interval",
"is_trigger",
])
.order_by("created_at", lq.order_desc.unwrap_or(true))
.and_where("workspace_id = ? OR workspace_id = 'starter'".bind(&w_id))
@@ -239,6 +240,9 @@ 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);
}
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
let mut tx = user_db.begin(&authed).await?;
@@ -411,7 +415,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, trigger_reco_interval) VALUES \
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)",
&w_id,
&hash.0,
@@ -426,7 +430,7 @@ async fn create_script(
extra_perms,
lock,
ns.language: ScriptLang,
ns.trigger_reco_interval,
ns.is_trigger.unwrap_or(false),
)
.execute(&mut tx)
.await?;
+1 -1
View File
@@ -20,7 +20,7 @@
export let contentEncoding = ''
export let type: string | undefined = undefined
export let required = false
export let pattern: undefined | string
export let pattern: undefined | string = undefined
export let valid = required ? false : true
export let minRows = 1
export let maxRows = 10
@@ -3,11 +3,11 @@
import { page } from '$app/stores'
import { FlowService, ScriptService, type Flow } from '$lib/gen'
import { clearPreviewResults, hubScripts, workspaceStore } from '$lib/stores'
import { pathIsEmpty, sendUserToast } from '$lib/utils'
import { sendUserToast, setQueryWithoutLoad } from '$lib/utils'
import { onMount } from 'svelte'
import SvelteMarkdown from 'svelte-markdown'
import FlowEditor from './FlowEditor.svelte'
import { flowStore, initFlow, type FlowMode } from './flows/flowStore'
import { flowStore, type FlowMode } from './flows/flowStore'
import { flowToMode } from './flows/utils'
import Path from './Path.svelte'
import Required from './Required.svelte'
@@ -68,12 +68,9 @@
}
flowStore.subscribe((flow: Flow) => {
$page.url.searchParams.set('state', btoa(JSON.stringify(flowToMode(flow, mode))))
history.replaceState({}, '', $page.url)
setQueryWithoutLoad($page.url, 'state', btoa(JSON.stringify(flowToMode(flow, mode))))
})
$: flow && initFlow(flow)
onMount(() => {
loadSearchData()
clearPreviewResults()
+21 -19
View File
@@ -66,31 +66,33 @@
let validateTimeout: NodeJS.Timeout | undefined = undefined
async function validatePath(path: string, kind: PathKind): Promise<void> {
if (initialPath == '' && initialPath != path) {
if (initialPath != '' && initialPath != path) {
if (validateTimeout) {
clearTimeout(validateTimeout)
}
validateTimeout = setTimeout(async () => {
if (
(kind == 'flow' &&
initialPath != '' &&
initialPath != path &&
((kind == 'flow' &&
(await FlowService.existsFlowByPath({ workspace: $workspaceStore!, path: path }))) ||
(kind == 'script' &&
(await ScriptService.existsScriptByPath({
workspace: $workspaceStore!,
path: path
}))) ||
(kind == 'resource' &&
(await ResourceService.existsResource({
workspace: $workspaceStore!,
path: path
}))) ||
(kind == 'variable' &&
(await VariableService.existsVariable({
workspace: $workspaceStore!,
path: path
}))) ||
(kind == 'schedule' &&
(await ScheduleService.existsSchedule({ workspace: $workspaceStore!, path: path })))
(kind == 'script' &&
(await ScriptService.existsScriptByPath({
workspace: $workspaceStore!,
path: path
}))) ||
(kind == 'resource' &&
(await ResourceService.existsResource({
workspace: $workspaceStore!,
path: path
}))) ||
(kind == 'variable' &&
(await VariableService.existsVariable({
workspace: $workspaceStore!,
path: path
}))) ||
(kind == 'schedule' &&
(await ScheduleService.existsSchedule({ workspace: $workspaceStore!, path: path }))))
) {
error = 'path already used'
} else if (validateName(meta)) {
@@ -61,7 +61,7 @@
export let property: ModalSchemaProperty = DEFAULT_PROPERTY
export let error = ''
export let editing = false
export let oldArgName: string | undefined
export let oldArgName: string | undefined = undefined
const dispatch = createEventDispatcher()
let modal: Modal
@@ -1,7 +1,7 @@
<script lang="ts">
import { ScriptService, type Script } from '$lib/gen'
import { emptySchema, pathIsEmpty, sendUserToast } from '$lib/utils'
import { emptySchema, sendUserToast, setQueryWithoutLoad } from '$lib/utils'
import { onDestroy } from 'svelte'
import ScriptEditor from './ScriptEditor.svelte'
import { page } from '$app/stores'
@@ -13,7 +13,7 @@
import { inferArgs } from '$lib/infer'
import Required from './Required.svelte'
import RadioButton from './RadioButton.svelte'
import { DENO_INIT_CODE, PYTHON_INIT_CODE } from '$lib/script_helpers'
import { DENO_INIT_CODE, DENO_INIT_CODE_TRIGGER, initialCode } from '$lib/script_helpers'
let editor: ScriptEditor
let scriptSchema: ScriptSchema
@@ -25,15 +25,21 @@
let pathError = ''
$: {
$page.url.searchParams.set('state', btoa(JSON.stringify(script)))
history.replaceState({}, '', $page.url)
setQueryWithoutLoad($page.url, 'state', btoa(JSON.stringify(script)))
}
$: {
if (script.language == 'python3') {
script.is_trigger = false
}
}
if (script.content == '') {
initContent(script.language)
initContent(script.language, script.is_trigger)
}
function initContent(lang: string) {
script.content = lang == 'deno' ? DENO_INIT_CODE : PYTHON_INIT_CODE
function initContent(lang: 'deno' | 'python3', is_trigger: boolean) {
script.content = initialCode(lang, is_trigger)
}
async function editScript(): Promise<void> {
@@ -48,7 +54,8 @@
parent_hash: script.hash != '' ? script.hash : undefined,
schema: script.schema,
is_template: script.is_template,
language: script.language
language: script.language,
is_trigger: script.is_trigger
}
})
sendUserToast(`Success! New script version created with hash ${newHash}`)
@@ -176,10 +183,10 @@
label="Language"
small={true}
options={[
['Python 3.10', 'python3'],
['Typescript (Deno)', 'deno']
['Typescript (Deno)', 'deno'],
['Python 3.10', 'python3']
]}
on:change={(e) => initContent(e.detail)}
on:change={(e) => initContent(e.detail, script.is_trigger)}
bind:value={script.language}
/>
</div>
@@ -221,11 +228,26 @@
/>
</span></label
>
<label class="block">
<span class="text-gray-700 mr-2">Save as template</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) {
initContent(script.language, script.is_trigger)
}
}}
/>
</label>
<div>
<h3 class="text-gray-700 ">Description rendered</h3>
<div
@@ -1,5 +1,4 @@
<script lang="ts">
import { sendUserToast } from '$lib/utils'
import { ScriptService, FlowService, Script } from '$lib/gen'
import Icon from 'svelte-awesome'
@@ -18,6 +17,7 @@
export let allowFlow = false
export let allowHub = false
export let itemKind: 'hub' | 'script' | 'flow' = allowHub ? 'hub' : 'script'
export let isTrigger: boolean | undefined = undefined
let items: { summary: String; path: String; version?: String }[] = []
let itemPicker: ItemPicker
@@ -48,7 +48,7 @@
if (itemKind == 'flow') {
items = await FlowService.listFlows({ workspace: $workspaceStore! })
} else if (itemKind == 'script') {
items = await ScriptService.listScripts({ workspace: $workspaceStore! })
items = await ScriptService.listScripts({ workspace: $workspaceStore!, isTrigger })
} else {
items = $hubScripts ?? []
}
@@ -22,6 +22,7 @@
on:click={() => dispatch('new', { language: FlowModuleValue.language.DENO })}
/>
<FlowScriptPicker
disabled={isTrigger}
label="New Python {isTrigger ? 'trigger ' : ''}script (3.10)"
icon={faCode}
iconColor="text-yellow-500"
+12 -11
View File
@@ -1,6 +1,6 @@
import type { Schema } from '$lib/common'
import { FlowModuleValue, ScriptService, type Flow, type FlowModule } from '$lib/gen'
import { DENO_INIT_CODE, DENO_INIT_CODE_TRIGGER, PYTHON_INIT_CODE, PYTHON_INIT_CODE_TRIGGER } from '$lib/script_helpers'
import { initialCode } from '$lib/script_helpers'
import { userStore, workspaceStore } from '$lib/stores'
import { derived, get, writable } from 'svelte/store'
import { createInlineScriptModuleFromPath, getFirstStepSchema, loadSchemaFromModule } from './utils'
@@ -18,12 +18,16 @@ export function initFlow(flow: Flow) {
})
}
export const isCopyFirstStepSchemaDisabled = derived(flowStore, (flow: Flow) => {
const modules = flow.value.modules
const [firstModule] = modules
return (
modules.length === 0 || (firstModule.value.path === '' && firstModule.value.type === 'script')
)
export const isCopyFirstStepSchemaDisabled = derived(flowStore, (flow: Flow | undefined) => {
if (flow) {
const modules = flow.value.modules
const [firstModule] = modules
return (
modules.length === 0 || (firstModule.value.path === '' && firstModule.value.type === 'script')
)
} else {
return true
}
})
export function addModule() {
@@ -51,10 +55,7 @@ export async function pickScript(path: string, step: number) {
}
export async function createInlineScriptModule(language: FlowModuleValue.language, step: number, mode: FlowMode) {
const code = language === FlowModuleValue.language.DENO ? (
(mode === 'pull' && step == 0) ? DENO_INIT_CODE_TRIGGER : DENO_INIT_CODE) : (
(mode === 'pull' && step == 0) ? PYTHON_INIT_CODE_TRIGGER : PYTHON_INIT_CODE
)
const code = initialCode(language, (mode === 'pull' && step == 0))
flowStore.update((flow: Flow) => {
flow.value.modules[step].value = {
type: FlowModuleValue.type.RAWSCRIPT,
@@ -2,12 +2,13 @@
import type { IconDefinition } from '@fortawesome/free-brands-svg-icons'
import Icon from 'svelte-awesome'
export let disabled: boolean = false
export let icon: IconDefinition
export let label: string
export let iconColor: string
</script>
<button type="button" on:click class="default-secondary-button-v2 mb-2 w-full">
<button {disabled} type="button" on:click class="default-secondary-button-v2 mb-2 w-full">
<Icon data={icon} class={`w-4 h-4 mr-2 -ml-2 ${iconColor}`} />
{label}
</button>
@@ -17,7 +17,7 @@
const dispatch = createEventDispatcher()
async function loadItems(): Promise<void> {
items = await ScriptService.listScripts({ workspace: $workspaceStore! })
items = await ScriptService.listScripts({ workspace: $workspaceStore!, isTrigger })
}
$: {
@@ -8,7 +8,6 @@ import {
} from '$lib/gen'
import { inferArgs } from '$lib/infer'
import { loadSchema } from '$lib/scripts'
import { DENO_INIT_CODE, PYTHON_INIT_CODE } from '$lib/script_helpers'
import { workspaceStore } from '$lib/stores'
import { emptySchema } from '$lib/utils'
import { get } from 'svelte/store'
@@ -121,16 +120,6 @@ export async function getFirstStepSchema(flow: Flow): Promise<Schema> {
return emptySchema()
}
export function createInlineScriptModule(language: FlowModuleValue.language): FlowModuleValue {
const code = language === FlowModuleValue.language.DENO ? DENO_INIT_CODE : PYTHON_INIT_CODE
return {
type: FlowModuleValue.type.RAWSCRIPT,
content: code,
language
}
}
export async function getScriptByPath(path: string): Promise<{
content: string
language: FlowModuleValue.language
+6 -5
View File
@@ -33,11 +33,6 @@ def main(name: str = "Nicolas Bourbaki",
return {"version": version, "splitted": name.split(), "user": user}
`
export const PYTHON_INIT_CODE_TRIGGER = `import wmill
def main():
return [1,2,3]
`
export const DENO_INIT_CODE = `// only do the following import if you require your script to interact with the windmill
// for instance to get a variable or resource
// import * as wmill from 'https://deno.land/x/windmill@v${__pkg__.version}/mod.ts'
@@ -68,3 +63,9 @@ export async function main() {
return [1,2,3]
}
`
export function initialCode(language: 'deno' | 'python3', is_trigger: boolean): string {
return language === 'deno' ?
(is_trigger ? DENO_INIT_CODE_TRIGGER : DENO_INIT_CODE) : PYTHON_INIT_CODE
}
+12
View File
@@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { goto } from '$app/navigation'
import type { User } from '$lib/gen'
import { toast } from '@zerodevx/svelte-toast'
import type { Schema } from './common'
@@ -194,6 +195,17 @@ export function pathIsEmpty(path: string): boolean {
return path == undefined || path.split('/')[2] == ''
}
export async function setQuery(url: URL, key: string, value: string): Promise<void> {
url.searchParams.set(key, value)
await goto(`?${url.searchParams.toString()}`)
}
export async function setQueryWithoutLoad(url: URL, key: string, value: string): Promise<void> {
const nurl = new URL(url.toString())
nurl.searchParams.set(key, value)
history.replaceState(null, '', nurl.toString())
}
export function groupBy<T>(
scripts: T[],
toGroup: (t: T) => string,
+3
View File
@@ -2,6 +2,7 @@
import { page } from '$app/stores'
import FlowBuilder from '$lib/components/FlowBuilder.svelte'
import { initFlow } from '$lib/components/flows/flowStore'
import type { Flow } from '$lib/gen'
import { emptySchema } from '$lib/utils'
@@ -20,6 +21,8 @@
extra_perms: {},
schema: emptySchema()
}
initFlow(flow)
</script>
<FlowBuilder {flow} />
@@ -6,6 +6,7 @@
import { workspaceStore } from '$lib/stores'
import { emptySchema } from '$lib/utils'
import { flattenForloopFlows } from '$lib/components/flows/utils'
import { initFlow } from '$lib/components/flows/flowStore'
const initialState = $page.url.searchParams.get('state')
let flowLoadedFromUrl = initialState != undefined ? JSON.parse(atob(initialState)) : undefined
@@ -33,6 +34,7 @@
})
flow = flattenForloopFlows(flow)
initialPath = flow.path
initFlow(flow)
}
$: {
+72 -25
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import { onDestroy } from 'svelte'
import { JobService, Job, CompletedJob } from '$lib/gen'
import { displayDate, displayDaysAgo, forLater, truncateHash } from '$lib/utils'
import { displayDate, displayDaysAgo, forLater, setQuery, truncateHash } from '$lib/utils'
import Icon from 'svelte-awesome'
import { check } from 'svelte-awesome/icons'
import {
@@ -28,45 +28,76 @@
let intervalId: NodeJS.Timer | undefined
let path: string = $page.params.path
let createdBefore: string | undefined = $page.url.searchParams.get('createdBefore') ?? undefined
let success: boolean | undefined =
$page.url.searchParams.get('success') != undefined
? $page.url.searchParams.get('success') == 'true'
: undefined
let isSkipped: boolean | undefined =
$page.url.searchParams.get('is_skipped') != undefined
? $page.url.searchParams.get('is_skipped') == 'true'
: false
let showOlderJobs = true
// The API returns 30 jobs per page. We use it to display a next page button or not.
const jobsPerPage = 30
let jobKinds: string
let jobKindsCat: string | undefined = $page.url.searchParams.get('job_kinds') ?? 'runs'
$: jobKinds =
$page.url.searchParams.get('jobKinds') ??
`${CompletedJob.job_kind.SCRIPT},${CompletedJob.job_kind.FLOW}`
$: jobKinds = computeJobKinds(jobKindsCat)
$: jobKinds && $workspaceStore && loadJobs()
function computeJobKinds(jobKindsCat: string | undefined): string {
console.log('jobKindsCat', jobKindsCat)
if (jobKindsCat == 'all') {
return `${CompletedJob.job_kind.SCRIPT},${CompletedJob.job_kind.FLOW},${CompletedJob.job_kind.DEPENDENCIES},${CompletedJob.job_kind.PREVIEW},${CompletedJob.job_kind.FLOWPREVIEW},${CompletedJob.job_kind.SCRIPT_HUB}`
} else if (jobKindsCat == 'dependencies') {
return CompletedJob.job_kind.DEPENDENCIES
} else if (jobKindsCat == 'previews') {
return `${CompletedJob.job_kind.PREVIEW},${CompletedJob.job_kind.FLOWPREVIEW}`
} else {
return `${CompletedJob.job_kind.SCRIPT},${CompletedJob.job_kind.FLOW},${CompletedJob.job_kind.SCRIPT_HUB}`
}
}
$: $workspaceStore && loadJobs(createdBefore, success, isSkipped, jobKinds)
const SMALL_ICON_SCALE = 0.7
async function fetchJobs(
createdBefore: string | undefined,
createdAfter?: string
createdAfter: string | undefined
): Promise<Job[]> {
return JobService.listJobs({
workspace: $workspaceStore!,
createdBefore,
createdAfter,
scriptPathExact: path === '' ? undefined : path,
jobKinds
jobKinds,
success,
isSkipped
})
}
async function fetchCompletedJobs(createdBefore: string): Promise<CompletedJob[]> {
async function fetchCompletedJobs(createdBefore: string | undefined): Promise<CompletedJob[]> {
return JobService.listCompletedJobs({
workspace: $workspaceStore!,
createdBefore,
scriptPathExact: path === '' ? undefined : path,
jobKinds: jobKinds
jobKinds: jobKinds,
success,
isSkipped
})
}
async function loadJobs(): Promise<void> {
async function loadJobs(
createdBefore: string | undefined,
success: boolean | undefined,
isSkipped: boolean | undefined,
jobKinds: string | undefined
): Promise<void> {
try {
const newJobs = await fetchJobs(createdBefore)
console.log(jobKinds, success, isSkipped)
const newJobs = await fetchJobs(createdBefore, undefined)
showOlderJobs = newJobs.length === jobsPerPage
jobs = newJobs
} catch (err) {
@@ -102,7 +133,10 @@
$: {
if ($workspaceStore) {
loadJobs()
loadJobs(createdBefore, success, isSkipped, jobKinds)
if (intervalId) {
clearInterval(intervalId)
}
intervalId = setInterval(syncer, 5000)
}
}
@@ -128,23 +162,36 @@ the bearer token they use has less privilege."
/>
<div class="max-w-7x">
<div class="flex flex-row space-x-4">
<select
bind:value={success}
on:change={async () => await setQuery($page.url, 'success', String(success))}
>
<option value={undefined}>Successful and not jobs</option>
<option value={true}>Only successful jobs</option>
<option value={false}>Only non successful jobs</option>
</select>
<select
bind:value={isSkipped}
on:change={async () => await setQuery($page.url, 'is_skipped', String(isSkipped))}
>
<option value={false}>Ignore skipped flow jobs</option>
<option value={true}>If a flow jobs, show only if it was not skipped</option>
<option value={undefined}>Show flow jobs regardless of being skipped</option>
</select>
</div>
<div class="xl:max-w-screen-lg">
<Tabs
tabs={[
[
`${CompletedJob.job_kind.SCRIPT},${CompletedJob.job_kind.FLOW},${CompletedJob.job_kind.DEPENDENCIES},${CompletedJob.job_kind.PREVIEW},${CompletedJob.job_kind.FLOWPREVIEW},${CompletedJob.job_kind.SCRIPT_HUB}`,
'all'
],
[
`${CompletedJob.job_kind.SCRIPT},${CompletedJob.job_kind.FLOW},${CompletedJob.job_kind.SCRIPT_HUB}`,
'runs'
],
[`${CompletedJob.job_kind.PREVIEW},${CompletedJob.job_kind.FLOWPREVIEW}`, 'previews'],
[CompletedJob.job_kind.DEPENDENCIES, 'dependencies']
['all', 'all'],
['runs', 'runs'],
['previews', 'previews'],
['dependencies', 'dependencies']
]}
dflt={1}
on:update={(tab) => {
goto(`?jobKinds=${tab.detail}`)
bind:tab={jobKindsCat}
on:update={async (tab) => {
await setQuery($page.url, 'job_kinds', tab.detail)
}}
/>
{#if jobs}
+1 -1
View File
@@ -165,7 +165,7 @@
<p class="text-xs text-gray-600">
Pick a script or flow to be triggered by the schedule<Required required={true} />
</p>
<ScriptPicker allowFlow={true} bind:itemKind bind:scriptPath={script_path} />
<ScriptPicker isTrigger={false} allowFlow={true} bind:itemKind bind:scriptPath={script_path} />
<div class="max-w-5xl {edit ? '' : 'mt-2 md:mt-6'}">
<h2>Arguments</h2>
{#if runnable}
+1 -1
View File
@@ -63,7 +63,7 @@
<th>Schedule</th>
<th>Script or Flow</th>
<th>schedule</th>
<th>on/off</th>
<th>off/on</th>
<th>timezone</th>
<th>last edit</th>
<th />
+4 -1
View File
@@ -207,7 +207,7 @@
</p>
{:else}
<div class="grid md:grid-cols-2 gap-4 sm:grid-cols-1 2xl:grid-cols-3">
{#each scripts as { summary, path, hash, language, extra_perms, canWrite, lock_error_logs }}
{#each scripts as { summary, path, hash, language, extra_perms, canWrite, lock_error_logs, is_trigger }}
<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"
>
@@ -227,6 +227,9 @@
<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}
{#if lock_error_logs}<Badge
twBgColor="bg-red-200"
tooltip="The script was not deployed due to an error during deployment. See more details about the error on the script page."
+2 -1
View File
@@ -29,7 +29,8 @@
deleted: false,
is_template: false,
extra_perms: {},
language: 'python3'
language: 'deno',
is_trigger: false
}
async function loadTemplate(): Promise<void> {
@@ -120,6 +120,11 @@
>Template</span
>
{/if}
{#if script?.is_trigger}
<span class="mx-2 bg-blue-500 rounded-md bg-opacity-25 text-sm font-normal px-1 py-px"
>Trigger</span
>
{/if}
<SharedBadge canWrite={can_write} extraPerms={script?.extra_perms ?? {}} />
{#if deploymentInProgress}
<span class="bg-yellow-200 text-gray-700 text-xs rounded px-1 mx-3">
@@ -205,7 +205,7 @@
</button>
{/if}
<h3 class="mt-5 text-gray-700">Select a script to run on /windmill command:</h3>
<ScriptPicker bind:scriptPath on:select={editSlackCommand} />
<ScriptPicker isTrigger={false} 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