feat: runs page have sharable args in hash of links

This commit is contained in:
Ruben Fiszel
2024-07-02 13:16:52 +02:00
parent 9e53c95bac
commit 792bbb3ff9
10 changed files with 115 additions and 86 deletions
+7 -24
View File
@@ -1,16 +1,17 @@
<script lang="ts">
import { defaultIfEmptyString, emptyString, truncateHash } from '$lib/utils'
import {
computeSharableHash as computeSharableHash,
defaultIfEmptyString,
emptyString,
truncateHash
} from '$lib/utils'
import type { Schema } from '$lib/common'
import CliHelpBox from './CliHelpBox.svelte'
import { Badge, Button } from './common'
import SchemaForm from './SchemaForm.svelte'
import SharedBadge from './SharedBadge.svelte'
import CollapseLink from './CollapseLink.svelte'
import { SCRIPT_VIEW_SHOW_RUN_FROM_CLI } from '$lib/consts'
import TimeAgo from './TimeAgo.svelte'
import ClipboardPanel from './details/ClipboardPanel.svelte'
import Popup from './common/popup/Popup.svelte'
import { autoPlacement } from '@floating-ui/core'
import { Calendar, CornerDownLeft } from 'lucide-svelte'
@@ -44,8 +45,6 @@
export let topButton = false
export let loading = false
export let noVariablePicker = false
export let viewCliRun = false
export let isFlow: boolean
export let viewKeybinding = false
export let scheduledForStr: string | undefined
@@ -68,9 +67,7 @@
export let isValid = true
$: cliCommand = `wmill ${isFlow ? 'flow' : 'script'} run ${runnable?.path} -d '${JSON.stringify(
args
)}'`
$: window.location.hash = computeSharableHash(args)
</script>
<div class="max-w-3xl">
@@ -213,18 +210,4 @@
{buttonText}
</Button>
{/if}
{#if viewCliRun}
<div>
<div class="mt-4" />
{#if SCRIPT_VIEW_SHOW_RUN_FROM_CLI}
<CollapseLink small text="Run it from CLI">
<div class="mt-2" />
<ClipboardPanel content={cliCommand} />
<CliHelpBox />
</CollapseLink>
{/if}
<div class="mb-20" />
</div>
{/if}
</div>
@@ -27,7 +27,6 @@
export let scheduledForStr: string | undefined
export let invisible_to_owner: boolean | undefined
export let overrideTag: string | undefined
loadWorkerGroups()
async function loadWorkerGroups() {
@@ -21,7 +21,14 @@
let userSettings: UserSettings
export let webhooks: {
export let token: string
export let args: any
export let scopes: string[] = []
export let isFlow: boolean = false
export let hash: string | undefined = undefined
export let path: string
let webhooks: {
async: {
hash?: string
path: string
@@ -32,10 +39,39 @@
get_path?: string
}
}
export let token: string
export let args: any
export let scopes: string[] = []
export let isFlow: boolean = false
$: webhooks = isFlow ? computeFlowWebhooks(path) : computeScriptWebhooks(hash, path)
function computeScriptWebhooks(hash: string | undefined, path: string) {
let base = `${$page.url.origin}/api/w/${$workspaceStore}/jobs/`
return {
async: {
hash: `${base}run/h/${hash}`,
path: `${base}run/p/${path}`
},
sync: {
hash: `${base}run_wait_result/h/${hash}`,
path: `${base}run_wait_result/p/${path}`,
get_path: `${base}run_wait_result/p/${path}`
}
}
}
function computeFlowWebhooks(path: string) {
let base = `${$page.url.origin}/api/w/${$workspaceStore}/jobs/`
let urlAsync = `${base}run/f/${path}`
let urlSync = `${base}run_wait_result/f/${path}`
return {
async: {
path: urlAsync
},
sync: {
path: urlSync,
get_path: urlSync
}
}
}
let webhookType: 'async' | 'sync' = DEFAULT_WEBHOOK_TYPE
let requestType: 'hash' | 'path' | 'get_path' = isFlow ? 'path' : 'path'
@@ -110,9 +110,8 @@
async function fetchJobs(
startedBefore: string | undefined,
startedAfter: string | undefined,
startedAfterCompletedJobs: string | undefined,
startedAfterCompletedJobs: string | undefined
): Promise<Job[]> {
console.log('fetching jobs', startedAfter, startedBefore)
return JobService.listJobs({
workspace: $workspaceStore!,
createdOrStartedBefore: startedBefore,
-1
View File
@@ -33,7 +33,6 @@ export const enterpriseLicense = writable<string | undefined>(undefined)
export const workerTags = writable<string[] | undefined>(undefined)
export const usageStore = writable<number>(0)
export const workspaceUsageStore = writable<number>(0)
export const runFormStore = writable<any>()
export const oauthStore = writable<TokenResponse | undefined>(undefined)
export const userStore = writable<UserExt | undefined>(undefined)
export const workspaceStore = writable<string | undefined>(
+18 -5
View File
@@ -49,14 +49,16 @@ export function displayDateOnly(dateString: string | Date | undefined): string {
}
}
export function subtractDaysFromDateString(dateString: string | undefined, days: number): string | undefined {
export function subtractDaysFromDateString(
dateString: string | undefined,
days: number
): string | undefined {
if (dateString == undefined) {
return undefined
}
let date = new Date(dateString)
date.setDate(date.getDate() - days)
return date.toISOString()
}
export function displayDate(
@@ -507,7 +509,7 @@ export function isObject(obj: any) {
export function debounce(func: (...args: any[]) => any, wait: number) {
let timeout: any
return function(...args: any[]) {
return function (...args: any[]) {
// @ts-ignore
const context = this
clearTimeout(timeout)
@@ -517,7 +519,7 @@ export function debounce(func: (...args: any[]) => any, wait: number) {
export function throttle<T>(func: (...args: any[]) => T, wait: number) {
let timeout: any
return function(...args: any[]) {
return function (...args: any[]) {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null
@@ -666,6 +668,17 @@ export function extractCustomProperties(styleStr: string): string {
return customStyleStr
}
export function computeSharableHash(args: any) {
let nargs = {}
Object.entries(args).forEach(([k, v]) => {
if (v !== undefined) {
// if
nargs[k] = JSON.stringify(v)
}
})
return new URLSearchParams(nargs).toString()
}
export function toCamel(s: string) {
return s.replace(/([-_][a-z])/gi, ($1) => {
return $1.toUpperCase().replace('-', '').replace('_', '')
@@ -711,7 +724,7 @@ export async function tryEvery({
try {
await tryCode()
break
} catch (err) { }
} catch (err) {}
i++
}
if (i >= times) {
@@ -9,7 +9,7 @@
import MoveDrawer from '$lib/components/MoveDrawer.svelte'
import RunForm from '$lib/components/RunForm.svelte'
import ShareModal from '$lib/components/ShareModal.svelte'
import { runFormStore, userStore, workspaceStore } from '$lib/stores'
import { userStore, workspaceStore } from '$lib/stores'
import { sendUserToast } from '$lib/toast'
import DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte'
import SavedInputs from '$lib/components/SavedInputs.svelte'
@@ -55,9 +55,13 @@
$: cliCommand = `wmill flow run ${flow?.path} -d '${JSON.stringify(args)}'`
let previousPath: string | undefined = undefined
$: {
if ($workspaceStore && $userStore && $page.params.path) {
loadFlow()
if (previousPath !== path) {
previousPath = path
loadFlow()
}
}
}
@@ -88,9 +92,6 @@
} catch {}
}
$: urlAsync = `${$page.url.origin}/api/w/${$workspaceStore}/jobs/run/f/${flow?.path}`
$: urlSync = `${$page.url.origin}/api/w/${$workspaceStore}/jobs/run_wait_result/f/${flow?.path}`
let isValid = true
let loading = false
@@ -113,11 +114,18 @@
await goto('/run/' + run + '?workspace=' + $workspaceStore)
}
let args = undefined
let args: Record<string, any> | undefined = undefined
if ($runFormStore) {
args = $runFormStore
$runFormStore = undefined
let hash = window.location.hash
if (hash.length > 1) {
console.log(hash)
try {
let searchParams = new URLSearchParams(hash.slice(1))
let params = [...searchParams.entries()].map(([k, v]) => [k, JSON.parse(v)])
args = Object.fromEntries(params)
} catch (e) {
console.error('Was not able to transform hash as args', e)
}
}
let moveDrawer: MoveDrawer
@@ -359,7 +367,6 @@
runnable={flow}
runAction={runFlow}
bind:args
isFlow
bind:this={runForm}
/>
<div class="py-10" />
@@ -418,15 +425,7 @@
<WebhooksPanel
bind:token
scopes={[`run:flow/${flow?.path}`]}
webhooks={{
async: {
path: urlAsync
},
sync: {
path: urlSync,
get_path: urlSync
}
}}
path={flow?.path}
isFlow={true}
{args}
/>
@@ -11,6 +11,7 @@
} from '$lib/gen'
import {
canWrite,
computeSharableHash,
copyToClipboard,
displayDate,
emptyString,
@@ -43,7 +44,6 @@
import DisplayResult from '$lib/components/DisplayResult.svelte'
import {
enterpriseLicense,
runFormStore,
superadmin,
userStore,
userWorkspaces,
@@ -564,8 +564,7 @@
{#if job?.job_kind === 'script' || job?.job_kind === 'flow'}
<Button
on:click|once={() => {
$runFormStore = job?.args
goto(viewHref)
goto(viewHref + `#${computeSharableHash(job?.args)}`)
}}
color="blue"
size="sm"
@@ -577,7 +576,6 @@
{#if canWrite(job?.script_path ?? '', {}, $userStore)}
<Button
on:click|once={() => {
$runFormStore = job?.args
goto(`${stem}/edit/${job?.script_path}${isScript ? `` : `?nodraft=true`}`)
}}
color="blue"
@@ -2,7 +2,7 @@
import { ScriptService, type NewScript, type NewScriptWithDraft, DraftService } from '$lib/gen'
import { page } from '$app/stores'
import { runFormStore, workspaceStore } from '$lib/stores'
import { workspaceStore } from '$lib/stores'
import ScriptBuilder from '$lib/components/ScriptBuilder.svelte'
import { decodeState, cleanValueProperties, orderedJsonStringify } from '$lib/utils'
import { goto } from '$app/navigation'
@@ -12,10 +12,6 @@
const initialState = $page.url.hash != '' ? $page.url.hash.slice(1) : undefined
let initialArgs = {}
if ($runFormStore) {
initialArgs = $runFormStore
$runFormStore = undefined
}
let topHash = $page.url.searchParams.get('topHash') ?? undefined
let hash = $page.url.searchParams.get('hash') ?? undefined
@@ -10,7 +10,7 @@
} from '$lib/utils'
import Tooltip from '$lib/components/Tooltip.svelte'
import ShareModal from '$lib/components/ShareModal.svelte'
import { hubBaseUrlStore, runFormStore, userStore, workspaceStore } from '$lib/stores'
import { hubBaseUrlStore, userStore, workspaceStore } from '$lib/stores'
import SchemaViewer from '$lib/components/SchemaViewer.svelte'
import { onDestroy } from 'svelte'
import HighlightCode from '$lib/components/HighlightCode.svelte'
@@ -83,18 +83,12 @@
$: cliCommand = `wmill script run ${script?.path} -d '${JSON.stringify(args)}'`
$: loading = !script
let previousHash: string | undefined = undefined
$: if ($workspaceStore) {
loadScript($page.params.hash)
}
$: webhooks = {
async: {
hash: `${$page.url.origin}/api/w/${$workspaceStore}/jobs/run/h/${script?.hash}`,
path: `${$page.url.origin}/api/w/${$workspaceStore}/jobs/run/p/${script?.path}`
},
sync: {
hash: `${$page.url.origin}/api/w/${$workspaceStore}/jobs/run_wait_result/h/${script?.hash}`,
path: `${$page.url.origin}/api/w/${$workspaceStore}/jobs/run_wait_result/p/${script?.path}`,
get_path: `${$page.url.origin}/api/w/${$workspaceStore}/jobs/run_wait_result/p/${script?.path}`
if (previousHash != $page.params.hash) {
previousHash = $page.params.hash
loadScript($page.params.hash)
}
}
@@ -208,13 +202,20 @@
}
}
let args = undefined
if ($runFormStore) {
args = $runFormStore
$runFormStore = undefined
let args: Record<string, any> | undefined = undefined
let hash = window.location.hash
if (hash.length > 1) {
console.log(hash)
try {
let searchParams = new URLSearchParams(hash.slice(1))
let params = [...searchParams.entries()].map(([k, v]) => [k, JSON.parse(v)])
args = Object.fromEntries(params)
} catch (e) {
console.error('Was not able to transform hash as args', e)
}
}
let moveDrawer: MoveDrawer
let deploymentDrawer: DeployWorkspaceDrawer
let persistentScriptDrawer: PersistentScriptDrawer
@@ -595,7 +596,6 @@
runAction={runScript}
bind:args
schedulable={true}
isFlow={false}
bind:this={runForm}
/>
@@ -607,7 +607,8 @@
{/if}
<div class="flex flex-row gap-x-2 flex-wrap items-center">
<span class="text-sm text-tertiary">
Edited <TimeAgo withDate date={script.created_at || ''} /> by {script.created_by || 'unknown'}
Edited <TimeAgo withDate date={script.created_at || ''} /> by {script.created_by ||
'unknown'}
</span>
<Badge small color="gray">
{truncateHash(script?.hash ?? '')}
@@ -641,7 +642,13 @@
{/if}
</svelte:fragment>
<svelte:fragment slot="webhooks">
<WebhooksPanel bind:token scopes={[`run:script/${script?.path}`]} {webhooks} {args} />
<WebhooksPanel
bind:token
scopes={[`run:script/${script?.path}`]}
hash={script.hash}
path={script.path}
{args}
/>
</svelte:fragment>
<svelte:fragment slot="schedule">
<RunPageSchedules isFlow={false} path={script.path ?? ''} {can_write} />