diff --git a/src/lib/components/event/event-details-row.svelte b/src/lib/components/event/event-details-row.svelte index b3041cecf..8e7b5bb95 100644 --- a/src/lib/components/event/event-details-row.svelte +++ b/src/lib/components/event/event-details-row.svelte @@ -32,7 +32,7 @@ > {#if typeof value === 'object'}

{format(key)} diff --git a/src/lib/components/workflow-actions.svelte b/src/lib/components/workflow-actions.svelte index f4f822a7b..e1a08c439 100644 --- a/src/lib/components/workflow-actions.svelte +++ b/src/lib/components/workflow-actions.svelte @@ -21,6 +21,11 @@ } from '$lib/services/workflow-service'; import { authUser } from '$lib/stores/auth-user'; import { coreUserStore } from '$lib/stores/core-user'; + import { + codecEndpoint, + includeCredentials, + passAccessToken, + } from '$lib/stores/data-encoder-config'; import { resetEvents } from '$lib/stores/events'; import { resetWorkflows } from '$lib/stores/reset-workflows'; import { settings } from '$lib/stores/settings'; @@ -40,8 +45,11 @@ export let cancelInProgress: boolean; export let isRunning: boolean; + const getDefaultSignalInput = () => + $codecEndpoint ? '{"metadata": {"encoding": ""}, "data": ""}' : ''; + let reason = ''; - let signalInput = ''; + let signalInput = getDefaultSignalInput(); let signalName = ''; let cancelConfirmationModalOpen = false; let terminateConfirmationModalOpen = false; @@ -53,6 +61,7 @@ let resetReason: string; let loading = false; let resetTooltipText: string; + let signalInputCodeBlock: CodeBlock; $: cancelEnabled = workflowCancelEnabled($page.data.settings); $: signalEnabled = workflowSignalEnabled($page.data.settings); @@ -64,8 +73,9 @@ }; const hideSignalModal = () => { - signalInput = ''; + signalInput = getDefaultSignalInput(); signalName = ''; + signalInputCodeBlock?.resetView(signalInput); }; const hideResetModal = () => { @@ -139,6 +149,15 @@ runId: workflow.runId, signalInput, signalName, + settings: { + ...$page.data.settings, + codec: { + endpoint: $codecEndpoint, + includeCredentials: $includeCredentials, + passAccessToken: $passAccessToken, + }, + }, + accessToken: $authUser.accessToken, }); signalConfirmationModalOpen = false; $refresh = Date.now(); @@ -146,11 +165,10 @@ message: translate('workflows', 'signal-success'), id: 'workflow-signal-success-toast', }); + hideSignalModal(); } catch (err) { error = err?.message ?? translate('unknown-error'); } - - hideSignalModal(); }; const reset = async () => { @@ -383,6 +401,7 @@ on:change={handleSignalInputChange} editable copyable={false} + bind:this={signalInputCodeBlock} />

diff --git a/src/lib/holocene/code-block.svelte b/src/lib/holocene/code-block.svelte index 9e14d3f7a..e070c65ba 100644 --- a/src/lib/holocene/code-block.svelte +++ b/src/lib/holocene/code-block.svelte @@ -77,7 +77,10 @@ return stringifyWithBigInt(parsedData, undefined, inline ? 0 : 2); }; - $: value = language === 'json' ? formatJSON(content) : content; + const formatValue = ({ value, language }) => + language === 'json' ? formatJSON(value) : value; + + $: value = formatValue({ value: content, language }); const createEditorView = (): EditorView => { return new EditorView({ @@ -127,10 +130,15 @@ view = createEditorView(); }); + export const resetView = (value = '', format = true) => { + const formattedValue = format ? formatValue({ value, language }) : value; + const newState = createEditorState(formattedValue); + view.setState(newState); + }; + const setView = () => { if (view && !editable) { - const newState = createEditorState(value); - view.setState(newState); + resetView(value, false); } }; diff --git a/src/lib/i18n/locales/en/common.ts b/src/lib/i18n/locales/en/common.ts index 68dc8d951..e630d57d3 100644 --- a/src/lib/i18n/locales/en/common.ts +++ b/src/lib/i18n/locales/en/common.ts @@ -137,6 +137,8 @@ export const Strings = { timeline: 'Timeline', 'equal-to': 'Equal to', 'not-equal-to': 'Not equal to', + 'encode-failed': 'Data encoding failed', + 'decode-failed': 'Data decoding failed', 'job-id': 'Job ID', 'auto-refresh': 'Auto refresh', 'auto-refresh-tooltip': '{{ interval }} second page refresh', diff --git a/src/lib/services/data-encoder.ts b/src/lib/services/data-encoder.ts index cf34f73aa..00023fa13 100644 --- a/src/lib/services/data-encoder.ts +++ b/src/lib/services/data-encoder.ts @@ -1,8 +1,10 @@ +import { translate } from '$lib/i18n/translate'; import { setLastDataEncoderFailure, setLastDataEncoderSuccess, } from '$lib/stores/data-encoder-config'; -import type { Settings } from '$lib/types/global'; +import type { NetworkError, Settings } from '$lib/types/global'; +import { has } from '$lib/utilities/has'; import { validateHttps } from '$lib/utilities/is-http'; import { stringifyWithBigInt } from '$lib/utilities/parse-with-big-int'; @@ -13,11 +15,13 @@ export async function convertPayloadsWithCodec({ namespace, settings, accessToken, + encode = false, }: { payloads: PotentialPayloads; namespace: string; settings: Settings; accessToken: string; + encode?: boolean; }): Promise { const endpoint = settings?.codec?.endpoint; const passAccessToken = settings?.codec?.passAccessToken; @@ -51,17 +55,30 @@ export async function convertPayloadsWithCodec({ }; const encoderResponse: Promise = fetch( - endpoint + '/decode', + endpoint + (encode ? '/encode' : '/decode'), requestOptions, ) - .then((r) => r.json()) + .then((response) => { + if (has(response, 'ok') && !response.ok) { + throw { + statusCode: response.status, + statusText: response.statusText, + response, + message: encode + ? translate('encode-failed') + : translate('decode-failed'), + } as NetworkError; + } else { + return response.json(); + } + }) .then((response) => { setLastDataEncoderSuccess(); return response; }) - .catch(() => { - setLastDataEncoderFailure(); + .catch((err: unknown) => { + setLastDataEncoderFailure(err); return payloads; }); diff --git a/src/lib/services/workflow-service.ts b/src/lib/services/workflow-service.ts index e0972153d..a05785f4f 100644 --- a/src/lib/services/workflow-service.ts +++ b/src/lib/services/workflow-service.ts @@ -1,18 +1,29 @@ import { noop } from 'svelte/internal'; +import { get } from 'svelte/store'; import { v4 } from 'uuid'; +import { translate } from '$lib/i18n/translate'; import type { ResetReapplyType } from '$lib/models/workflow-actions'; import { toWorkflowExecution, toWorkflowExecutions, } from '$lib/models/workflow-execution'; +import { convertPayloadsWithCodec } from '$lib/services/data-encoder'; +import { + lastDataEncoderError, + lastDataEncoderStatus, +} from '$lib/stores/data-encoder-config'; import type { ResetWorkflowRequest } from '$lib/types'; import type { ValidWorkflowEndpoints, ValidWorkflowParameters, } from '$lib/types/api'; -import type { NamespaceScopedRequest, Replace } from '$lib/types/global'; +import type { + NamespaceScopedRequest, + Replace, + Settings, +} from '$lib/types/global'; import type { ArchiveFilterParameters, ListWorkflowExecutionsResponse, @@ -26,10 +37,8 @@ import { } from '$lib/utilities/handle-error'; import { stringifyWithBigInt } from '$lib/utilities/parse-with-big-int'; import { toListWorkflowQuery } from '$lib/utilities/query/list-workflow-query'; -import { - type ErrorCallback, - requestFromAPI, -} from '$lib/utilities/request-from-api'; +import type { ErrorCallback } from '$lib/utilities/request-from-api'; +import { requestFromAPI } from '$lib/utilities/request-from-api'; import { base, pathForApi, routeForApi } from '$lib/utilities/route-for-api'; export type GetWorkflowExecutionRequest = NamespaceScopedRequest & { @@ -55,6 +64,8 @@ type SignalWorkflowOptions = { runId: string; signalName: string; signalInput: string; + settings: Settings; + accessToken: string; }; type TerminateWorkflowOptions = { @@ -293,6 +304,8 @@ export async function signalWorkflow({ runId, signalName, signalInput, + settings, + accessToken, }: SignalWorkflowOptions) { const route = routeForApi('workflow.signal', { namespace, @@ -300,6 +313,35 @@ export async function signalWorkflow({ runId, }); + let payloads = null; + + if (signalInput) { + if (settings?.codec?.endpoint) { + const awaitData = await convertPayloadsWithCodec({ + payloads: { payloads: [JSON.parse(signalInput)] }, + namespace, + settings, + accessToken, + encode: true, + }); + if (get(lastDataEncoderStatus) === 'error') { + throw new Error( + get(lastDataEncoderError) || translate('encode-failed'), + ); + } + payloads = awaitData?.payloads ?? null; + } else { + payloads = [ + { + metadata: { + encoding: btoa('json/plain'), + }, + data: btoa(signalInput), + }, + ]; + } + } + return requestFromAPI(route, { notifyOnError: false, options: { @@ -307,16 +349,7 @@ export async function signalWorkflow({ body: stringifyWithBigInt({ signalName, input: { - payloads: signalInput - ? [ - { - metadata: { - encoding: btoa('json/plain'), - }, - data: btoa(signalInput), - }, - ] - : null, + payloads, }, }), }, diff --git a/src/lib/stores/data-encoder-config.ts b/src/lib/stores/data-encoder-config.ts index 7825f5e2e..f3981ecee 100644 --- a/src/lib/stores/data-encoder-config.ts +++ b/src/lib/stores/data-encoder-config.ts @@ -2,6 +2,7 @@ import { writable } from 'svelte/store'; import { persistStore } from '$lib/stores/persist-store'; import type { DataEncoderStatus } from '$lib/types/global'; +import { has } from '$lib/utilities/has'; export const codecEndpoint = persistStore('endpoint', null, true); @@ -26,8 +27,16 @@ export const overrideRemoteCodecConfiguration = persistStore( export const lastDataEncoderStatus = writable('notRequested'); -export function setLastDataEncoderFailure(): void { +export const lastDataEncoderError = writable(''); + +export function setLastDataEncoderFailure(error?: unknown): void { lastDataEncoderStatus.set('error'); + + if (error && has(error, 'message') && typeof error.message === 'string') { + lastDataEncoderError.set(error.message); + } else { + lastDataEncoderError.set(''); + } } export function setLastDataEncoderSuccess(): void {