From abf450145f7c7fdb655092aa59d5261ec909117e Mon Sep 17 00:00:00 2001 From: Steve Kinney Date: Wed, 14 Jul 2021 14:42:45 -0400 Subject: [PATCH 1/5] Get basic list of workflows up and running --- src/routes/__layout.svelte | 1 - src/routes/api/workflows.json.ts | 86 ++++++++-- src/routes/workflows/[workflow].svelte | 7 - src/routes/workflows/[workflow]/[run].svelte | 14 ++ src/routes/workflows/__layout.svelte | 160 +++++++++++++++++-- static/css/global.css | 2 +- 6 files changed, 239 insertions(+), 31 deletions(-) delete mode 100644 src/routes/workflows/[workflow].svelte create mode 100644 src/routes/workflows/[workflow]/[run].svelte diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte index f854fcca47..c72b1002e6 100644 --- a/src/routes/__layout.svelte +++ b/src/routes/__layout.svelte @@ -33,7 +33,6 @@ diff --git a/src/routes/workflows/__layout.svelte b/src/routes/workflows/__layout.svelte index ea9ac5940f..633e40189e 100644 --- a/src/routes/workflows/__layout.svelte +++ b/src/routes/workflows/__layout.svelte @@ -25,20 +25,156 @@ } - -

Workflows

+
+ + + + + + + + + + + {#await workflows} +

Loading…

+ {:then workflows} + {#each workflows.executions as workflow} + + + + + + + {/each} + {:catch} +

There was an error.

+ {/await} + +
Workflow/Run IDStatusStartedEnded
+ +

+ {workflow.type.name} +

+

+ {workflow.execution.runId} +

+
+
+ +

{workflow.status}

+
+
+ +

{formatDate(workflow.startTime)}

+
+
+ +

{formatDate(workflow.closeTime)}

+
+
+ +
-{#await workflows} -

Loading…

-{:then workflows} - {#each workflows.executions as workflow} -

{workflow.execution.workflowId}

- {/each} -{:catch} -

There was an error.

-{/await} + diff --git a/static/css/global.css b/static/css/global.css index 9de9851443..d230a06339 100644 --- a/static/css/global.css +++ b/static/css/global.css @@ -5,5 +5,5 @@ } body { - font-family: Inter; + font-family: Inter, sans-serif; } From 55d9f1d6c293c2bb30c8c9856e086edf319fc20e Mon Sep 17 00:00:00 2001 From: Steve Kinney Date: Wed, 14 Jul 2021 17:25:03 -0400 Subject: [PATCH 2/5] Refactor file structure --- src/routes/api/workflows.json.ts | 110 ------------------ .../api/workflows/[workflow]/[run]/index.ts | 17 +++ src/routes/api/workflows/index.ts | 44 +++++++ src/routes/workflows/[workflow]/[run].svelte | 44 ++++++- 4 files changed, 104 insertions(+), 111 deletions(-) delete mode 100644 src/routes/api/workflows.json.ts create mode 100644 src/routes/api/workflows/[workflow]/[run]/index.ts create mode 100644 src/routes/api/workflows/index.ts diff --git a/src/routes/api/workflows.json.ts b/src/routes/api/workflows.json.ts deleted file mode 100644 index 9b943977c0..0000000000 --- a/src/routes/api/workflows.json.ts +++ /dev/null @@ -1,110 +0,0 @@ -const apiHost = process.env.TEMPORAL_API_HOST || 'http://localhost:8088'; - -/* - * TODO: Add better error handling. - * - If one request has a 400- 500-level error, they both should. - * - The current implementation is naive. It only deals with - * an error state if the promises outright reject, which will - * only happen in the event that there is no error handling. - */ - -const fetchWorkflows: ( - type: string, - query: URLSearchParams, -) => Promise = async (type, query) => { - const response = await fetch( - `${apiHost}/api/namespaces/default/workflows/${type}?${query.toString()}`, - ); - return await response.json(); -}; - -export async function get({ query }: { query: URLSearchParams }) { - const openWorkflows = fetchWorkflows('open', query); - const closedWorkflows = fetchWorkflows('closed', query); - - // const workflows = await Promise.all([openWorkflows, closedWorkflows]).then( - // ([open, closed]) => { - // return { - // executions: [...open.executions, ...closed.executions], - // nextPageTokens: { - // open: open.nextPageToken, - // closed: closed.nextPageToken, - // }, - // }; - // }, - // ); - - // DELETE ME - const workflows = { - executions: [ - { - execution: { - workflowId: 'expense_1ff36d8c-a592-4413-8c3e-60f2b2987218', - runId: 'a81f12a5-58f7-4e07-a0b0-e303d9fa4cff', - }, - type: { name: 'SampleExpenseWorkflow' }, - startTime: '2021-07-09T17:24:21.000Z', - closeTime: null, - status: 'Running', - historyLength: '0', - parentNamespaceId: '', - parentExecution: null, - executionTime: '2021-07-09T17:24:21.000Z', - memo: [], - searchAttributes: null, - autoResetPoints: null, - taskQueue: 'expense', - stateTransitionCount: '0', - }, - { - execution: { - workflowId: 'greetings_31ea8053-a90e-40ae-bd3b-669df4e8b984', - runId: '19e777fa-1c76-47af-8d9b-f8a042f62e18', - }, - type: { name: 'GreetingSample' }, - startTime: '2021-07-13T16:32:24.000Z', - closeTime: '2021-07-13T16:32:24.000Z', - status: 'Completed', - historyLength: '23', - parentNamespaceId: '', - parentExecution: null, - executionTime: '2021-07-13T16:32:24.000Z', - memo: [], - searchAttributes: null, - autoResetPoints: null, - taskQueue: 'greetings', - stateTransitionCount: '0', - }, - { - execution: { - workflowId: 'pick-first_156a0e9a-de9c-4c99-9409-511c2f99ae90', - runId: 'e6a1d020-39b8-4217-bcaa-95cff29ab49a', - }, - type: { name: 'SamplePickFirstWorkflow' }, - startTime: '2021-07-13T16:31:21.000Z', - closeTime: '2021-07-13T16:31:23.000Z', - status: 'Completed', - historyLength: '18', - parentNamespaceId: '', - parentExecution: null, - executionTime: '2021-07-13T16:31:21.000Z', - memo: [], - searchAttributes: null, - autoResetPoints: null, - taskQueue: 'pick-first', - stateTransitionCount: '0', - }, - ], - nextPageTokens: { open: '', closed: '' }, - }; - - // END DELETE - - if (workflows) { - return { - body: { - workflows, - }, - }; - } -} diff --git a/src/routes/api/workflows/[workflow]/[run]/index.ts b/src/routes/api/workflows/[workflow]/[run]/index.ts new file mode 100644 index 0000000000..171c02353d --- /dev/null +++ b/src/routes/api/workflows/[workflow]/[run]/index.ts @@ -0,0 +1,17 @@ +const apiHost = process.env.TEMPORAL_API_HOST || 'http://localhost:8088'; + +export async function get({ params }) { + const { workflow: workflowId, run: runId } = params; + + const response = await fetch( + `${apiHost}/api/namespaces/default/workflows/${workflowId}/${runId}`, + ); + + const workflow = await response.json(); + + return { + body: { + ...workflow, + }, + }; +} diff --git a/src/routes/api/workflows/index.ts b/src/routes/api/workflows/index.ts new file mode 100644 index 0000000000..b7c40f0edb --- /dev/null +++ b/src/routes/api/workflows/index.ts @@ -0,0 +1,44 @@ +const apiHost = process.env.TEMPORAL_API_HOST || 'http://localhost:8088'; + +/* + * TODO: Add better error handling. + * - If one request has a 400- 500-level error, they both should. + * - The current implementation is naive. It only deals with + * an error state if the promises outright reject, which will + * only happen in the event that there is no error handling. + */ + +const fetchWorkflows: ( + type: string, + query: URLSearchParams, +) => Promise = async (type, query) => { + const response = await fetch( + `${apiHost}/api/namespaces/default/workflows/${type}?${query.toString()}`, + ); + return await response.json(); +}; + +export async function get({ query }: { query: URLSearchParams }) { + const openWorkflows = fetchWorkflows('open', query); + const closedWorkflows = fetchWorkflows('closed', query); + + const workflows = await Promise.all([openWorkflows, closedWorkflows]).then( + ([open, closed]) => { + return { + executions: [...open.executions, ...closed.executions], + nextPageTokens: { + open: open.nextPageToken, + closed: closed.nextPageToken, + }, + }; + }, + ); + + if (workflows) { + return { + body: { + workflows, + }, + }; + } +} diff --git a/src/routes/workflows/[workflow]/[run].svelte b/src/routes/workflows/[workflow]/[run].svelte index 6103c098bd..08806b8c66 100644 --- a/src/routes/workflows/[workflow]/[run].svelte +++ b/src/routes/workflows/[workflow]/[run].svelte @@ -1,5 +1,47 @@ + + + +
-

Hello World

+

{name}

+

{runId}

Close

From b0e7d16de4e1a2d7db78c702221d53be098acfd8 Mon Sep 17 00:00:00 2001 From: Steve Kinney Date: Wed, 14 Jul 2021 17:25:20 -0400 Subject: [PATCH 3/5] Add history endpoint --- .../api/workflows/[workflow]/[run]/history.ts | 17 +++++++++++++++++ src/routes/workflows/__layout.svelte | 3 +-- 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 src/routes/api/workflows/[workflow]/[run]/history.ts diff --git a/src/routes/api/workflows/[workflow]/[run]/history.ts b/src/routes/api/workflows/[workflow]/[run]/history.ts new file mode 100644 index 0000000000..89fbf65425 --- /dev/null +++ b/src/routes/api/workflows/[workflow]/[run]/history.ts @@ -0,0 +1,17 @@ +const apiHost = process.env.TEMPORAL_API_HOST || 'http://localhost:8088'; + +export async function get({ params, query }) { + const { workflow: workflowId, run: runId } = params; + + const response = await fetch( + `${apiHost}/api/namespaces/default/workflows/${workflowId}/${runId}/history?{query}`, + ); + + const history = await response.json(); + + return { + body: { + ...history, + }, + }; +} diff --git a/src/routes/workflows/__layout.svelte b/src/routes/workflows/__layout.svelte index 633e40189e..b9d6ba1b48 100644 --- a/src/routes/workflows/__layout.svelte +++ b/src/routes/workflows/__layout.svelte @@ -6,8 +6,7 @@ const endTime = addDays(new Date(), 30).toISOString(); const query = new URLSearchParams({ startTime, endTime }); - - const response = await fetch(`/api/workflows.json?${query.toString()}`); + const response = await fetch(`/api/workflows?${query}`); if (!response.ok) { const message = `An error has occured: ${response.status}`; From 94786eb27ea96ffb251a85f6dcf3a7f28bac7b31 Mon Sep 17 00:00:00 2001 From: Steve Kinney Date: Thu, 15 Jul 2021 14:54:27 -0400 Subject: [PATCH 4/5] Get a rough and ugly version of the UI implemented --- src/lib/utilities/format-date.ts | 6 ++ src/routes/workflows/[workflow]/[run].svelte | 98 ++++++++++++++++++-- src/routes/workflows/__layout.svelte | 1 + 3 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 src/lib/utilities/format-date.ts diff --git a/src/lib/utilities/format-date.ts b/src/lib/utilities/format-date.ts new file mode 100644 index 0000000000..7a8d9f3a99 --- /dev/null +++ b/src/lib/utilities/format-date.ts @@ -0,0 +1,6 @@ +import { format } from 'date-fns'; + +export function formatDate(date: string | null): string { + if (!date) return ''; + return format(new Date(date), 'MMMM dd, yyyy — h:mm a'); +} diff --git a/src/routes/workflows/[workflow]/[run].svelte b/src/routes/workflows/[workflow]/[run].svelte index 08806b8c66..b8af3bc8b1 100644 --- a/src/routes/workflows/[workflow]/[run].svelte +++ b/src/routes/workflows/[workflow]/[run].svelte @@ -22,35 +22,115 @@ const name = workflow.workflowExecutionInfo.type.name; const workflowId = workflow.workflowExecutionInfo.execution.workflowId; const runId = workflow.workflowExecutionInfo.execution.runId; + const events = history.events; + + const lastEvent = events[events.length - 1]; + const input = lastEvent.details.input && lastEvent.details.input.payloads; + const result = + lastEvent.details.result && lastEvent.details.result.payloads; return { props: { + workflow, name, workflowId, runId, + events, + input, + result, }, }; }
-

{name}

-

{runId}

-

- Close -

+
+

{name}

+

{runId}

+
+
+

Start Time

+

{formatDate(workflow.workflowExecutionInfo.startTime)}

+

End Time

+ {#if workflow.workflowExecutionInfo.closeTime} +

{formatDate(workflow.workflowExecutionInfo.closeTime)}

+ {:else} +

Still running…

+ {/if} +

Task Queue

+

{workflow.workflowExecutionInfo.taskQueue || '(None)'}

+

History Events

+

{events.length}

+ {#if input} +

Input

+ +
+          {JSON.stringify(input)}
+        
+
+ {/if} + {#if result} +

Result

+ +
+          {JSON.stringify(result)}
+        
+
+ {/if} +
diff --git a/src/routes/workflows/__layout.svelte b/src/routes/workflows/__layout.svelte index b9d6ba1b48..553ebea9f0 100644 --- a/src/routes/workflows/__layout.svelte +++ b/src/routes/workflows/__layout.svelte @@ -101,6 +101,7 @@