Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(client)!: Return a friendly type from handle.describe() #532

Merged
merged 3 commits into from
Mar 12, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,19 @@ export type DescribeWorkflowExecutionResponse = temporal.api.workflowservice.v1.
export type TerminateWorkflowExecutionResponse = temporal.api.workflowservice.v1.ITerminateWorkflowExecutionResponse;
export type RequestCancelWorkflowExecutionResponse =
temporal.api.workflowservice.v1.IRequestCancelWorkflowExecutionResponse;

export interface WorkflowExecutionDescription {
type: string;
workflowId: string;
runId?: string;
lorensr marked this conversation as resolved.
Show resolved Hide resolved
taskQueue: string;
status: temporal.api.enums.v1.WorkflowExecutionStatus;
historyLength: Long;
startTime: Date;
executionTime?: Date;
closeTime?: Date;
memo?: temporal.api.common.v1.IMemo;
lorensr marked this conversation as resolved.
Show resolved Hide resolved
searchAttributes?: temporal.api.common.v1.ISearchAttributes;
lorensr marked this conversation as resolved.
Show resolved Hide resolved
parentExecution?: temporal.api.common.v1.IWorkflowExecution;
lorensr marked this conversation as resolved.
Show resolved Hide resolved
raw: DescribeWorkflowExecutionResponse;
}
23 changes: 21 additions & 2 deletions packages/client/src/workflow-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import {
BaseWorkflowHandle,
compileRetryPolicy,
composeInterceptors,
optionalTsToDate,
QueryDefinition,
SignalDefinition,
tsToDate,
WithWorkflowArgs,
Workflow,
WorkflowNotFoundError,
Expand Down Expand Up @@ -57,6 +59,7 @@ import {
StartWorkflowExecutionRequest,
TerminateWorkflowExecutionResponse,
WorkflowExecution,
WorkflowExecutionDescription,
} from './types';
import { compileWorkflowOptions, WorkflowOptions, WorkflowSignalWithStartOptions } from './workflow-options';

Expand Down Expand Up @@ -113,7 +116,7 @@ export interface WorkflowHandle<T extends Workflow = Workflow> extends BaseWorkf
/**
* Describe the current workflow execution
*/
describe(): Promise<DescribeWorkflowExecutionResponse>;
describe(): Promise<WorkflowExecutionDescription>;

/**
* Readonly accessor to the underlying WorkflowClient
Expand Down Expand Up @@ -765,9 +768,25 @@ export class WorkflowClient {
async describe() {
const next = this.client._describeWorkflowHandler.bind(this.client);
const fn = interceptors.length ? composeInterceptors(interceptors, 'describe', next) : next;
return await fn({
const raw = await fn({
workflowExecution: { workflowId, runId },
});
return {
/* eslint-disable @typescript-eslint/no-non-null-assertion */
type: raw.workflowExecutionInfo!.type!.name!,
workflowId: raw.workflowExecutionInfo!.execution!.workflowId!,
runId: raw.workflowExecutionInfo!.execution!.runId!,
taskQueue: raw.workflowExecutionInfo!.taskQueue!,
status: raw.workflowExecutionInfo!.status!,
historyLength: raw.workflowExecutionInfo!.historyLength!,
startTime: tsToDate(raw.workflowExecutionInfo!.startTime!),
executionTime: optionalTsToDate(raw.workflowExecutionInfo!.executionTime),
closeTime: optionalTsToDate(raw.workflowExecutionInfo!.closeTime),
memo: raw.workflowExecutionInfo!.memo ?? undefined,
searchAttributes: raw.workflowExecutionInfo!.searchAttributes ?? undefined,
parentExecution: raw.workflowExecutionInfo!.parentExecution ?? undefined,
raw,
};
},
async signal<Args extends any[]>(def: SignalDefinition<Args> | string, ...args: Args): Promise<void> {
const next = this.client._signalWorkflowHandler.bind(this.client);
Expand Down
11 changes: 11 additions & 0 deletions packages/internal-workflow-common/src/time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,14 @@ export function msToNumber(val: string | number): number {
}
return ms(val);
}

export function tsToDate(ts: Timestamp): Date {
return new Date(tsToMs(ts));
}

export function optionalTsToDate(ts: Timestamp | null | undefined): Date | undefined {
if (ts === undefined || ts === null) {
return undefined;
}
return new Date(tsToMs(ts));
}
63 changes: 30 additions & 33 deletions packages/test/src/integration-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,22 +547,25 @@ export function runIntegrationTests(codec?: PayloadCodec): void {
});
await workflow.result();
const execution = await workflow.describe();
t.deepEqual(
execution.workflowExecutionInfo?.type,
new iface.temporal.api.common.v1.WorkflowType({ name: 'argsAndReturn' })
);
t.deepEqual(execution.workflowExecutionInfo?.memo, new iface.temporal.api.common.v1.Memo({ fields: {} }));
t.deepEqual(Object.keys(execution.workflowExecutionInfo!.searchAttributes!.indexedFields!), ['BinaryChecksums']);
t.deepEqual(execution.type, 'argsAndReturn');
t.deepEqual(execution.memo, new iface.temporal.api.common.v1.Memo({ fields: {} }));
t.true(execution.startTime instanceof Date);
t.deepEqual(Object.keys(execution.raw.workflowExecutionInfo!.searchAttributes!.indexedFields!), [
'BinaryChecksums',
]);

const checksums = defaultPayloadConverter.fromPayload(
execution.workflowExecutionInfo!.searchAttributes!.indexedFields!.BinaryChecksums!
execution.raw.workflowExecutionInfo!.searchAttributes!.indexedFields!.BinaryChecksums!
);
t.true(checksums instanceof Array && checksums.length === 1);
t.regex((checksums as string[])[0], /@temporalio\/worker@\d+\.\d+\.\d+/);
t.is(execution.executionConfig?.taskQueue?.name, 'test');
t.is(execution.executionConfig?.taskQueue?.kind, iface.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL);
t.is(execution.executionConfig?.workflowRunTimeout, null);
t.is(execution.executionConfig?.workflowExecutionTimeout, null);
t.is(execution.raw.executionConfig?.taskQueue?.name, 'test');
t.is(
execution.raw.executionConfig?.taskQueue?.kind,
iface.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL
);
t.is(execution.raw.executionConfig?.workflowRunTimeout, null);
t.is(execution.raw.executionConfig?.workflowExecutionTimeout, null);
});

test('WorkflowOptions are passed correctly', async (t) => {
Expand All @@ -584,22 +587,23 @@ export function runIntegrationTests(codec?: PayloadCodec): void {
});
const execution = await workflow.describe();
t.deepEqual(
execution.workflowExecutionInfo?.type,
execution.raw.workflowExecutionInfo?.type,
new iface.temporal.api.common.v1.WorkflowType({ name: 'sleeper' })
);
t.deepEqual(await fromPayload(execution.workflowExecutionInfo!.memo!.fields!.a!), 'b');
t.deepEqual(await fromPayload(execution.raw.workflowExecutionInfo!.memo!.fields!.a!), 'b');
t.deepEqual(
await defaultPayloadConverter.fromPayload(
execution.workflowExecutionInfo!.searchAttributes!.indexedFields!.CustomIntField!
),
await fromPayload(execution.raw.workflowExecutionInfo!.searchAttributes!.indexedFields!.CustomIntField!),
3
);
t.is(execution.executionConfig?.taskQueue?.name, 'test2');
t.is(execution.executionConfig?.taskQueue?.kind, iface.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL);
t.is(execution.raw.executionConfig?.taskQueue?.name, 'test2');
t.is(
execution.raw.executionConfig?.taskQueue?.kind,
iface.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL
);

t.is(tsToMs(execution.executionConfig!.workflowRunTimeout!), ms(options.workflowRunTimeout));
t.is(tsToMs(execution.executionConfig!.workflowExecutionTimeout!), ms(options.workflowExecutionTimeout));
t.is(tsToMs(execution.executionConfig!.defaultWorkflowTaskTimeout!), ms(options.workflowTaskTimeout));
t.is(tsToMs(execution.raw.executionConfig!.workflowRunTimeout!), ms(options.workflowRunTimeout));
t.is(tsToMs(execution.raw.executionConfig!.workflowExecutionTimeout!), ms(options.workflowExecutionTimeout));
t.is(tsToMs(execution.raw.executionConfig!.defaultWorkflowTaskTimeout!), ms(options.workflowTaskTimeout));
});

test('WorkflowHandle.result() throws if terminated', async (t) => {
Expand Down Expand Up @@ -667,7 +671,7 @@ export function runIntegrationTests(codec?: PayloadCodec): void {
});
await workflow.result();
const info = await workflow.describe();
t.is(info.workflowExecutionInfo?.type?.name, 'sleeper');
t.is(info.raw.workflowExecutionInfo?.type?.name, 'sleeper');
const { history } = await client.service.getWorkflowExecutionHistory({
namespace,
execution: { workflowId: workflow.workflowId, runId: err.newExecutionRunId },
Expand Down Expand Up @@ -753,14 +757,7 @@ export function runIntegrationTests(codec?: PayloadCodec): void {
return;
}
t.is(failure.message, 'unhandled rejection');
t.true(
failure.stackTrace?.includes(
dedent`
Error: unhandled rejection
at eval (webpack-internal:///./lib/workflows/unhandled-rejection.js
`
)
);
t.true(failure.stackTrace?.includes(`Error: unhandled rejection`));
t.is(failure.cause?.message, 'root failure');
},
{ minTimeout: 300, factor: 1, retries: 100 }
Expand Down Expand Up @@ -815,8 +812,8 @@ export function runIntegrationTests(codec?: PayloadCodec): void {
});
await t.throwsAsync(handle.result());
const handleForSecondAttempt = client.getHandle(workflowId);
const { workflowExecutionInfo } = await handleForSecondAttempt.describe();
t.not(workflowExecutionInfo?.execution?.runId, handle.originalRunId);
const { raw } = await handleForSecondAttempt.describe();
t.not(raw.workflowExecutionInfo?.execution?.runId, handle.originalRunId);
});

test('Workflow RetryPolicy ignored with nonRetryable failure', async (t) => {
Expand All @@ -835,7 +832,7 @@ export function runIntegrationTests(codec?: PayloadCodec): void {
await t.throwsAsync(handle.result());
const res = await handle.describe();
t.is(
res.workflowExecutionInfo?.status,
res.raw.workflowExecutionInfo?.status,
iface.temporal.api.enums.v1.WorkflowExecutionStatus.WORKFLOW_EXECUTION_STATUS_FAILED
);
});
Expand Down