Skip to content

Commit 2146b14

Browse files
authored
feat: Pass a PayloadConverter to FailureConverter methods (#936)
1 parent 6fd2539 commit 2146b14

File tree

5 files changed

+62
-68
lines changed

5 files changed

+62
-68
lines changed

packages/common/src/converter/failure-converter.ts

Lines changed: 42 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,7 @@ import {
1313
TimeoutType,
1414
} from '../failure';
1515
import { hasOwnProperties, isRecord } from '../type-helpers';
16-
import {
17-
arrayFromPayloads,
18-
defaultPayloadConverter,
19-
fromPayloadsAtIndex,
20-
PayloadConverter,
21-
toPayloads,
22-
} from './payload-converter';
16+
import { arrayFromPayloads, fromPayloadsAtIndex, PayloadConverter, toPayloads } from './payload-converter';
2317

2418
/**
2519
* Stack traces will be cutoff when on of these patterns is matched
@@ -60,11 +54,11 @@ export interface FailureConverter {
6054
/**
6155
* Converts a caught error to a Failure proto message.
6256
*/
63-
errorToFailure(err: unknown): ProtoFailure;
57+
errorToFailure(err: unknown, payloadConverter: PayloadConverter): ProtoFailure;
6458
/**
6559
* Converts a Failure proto message to a JS Error object.
6660
*/
67-
failureToError(err: ProtoFailure): Error;
61+
failureToError(err: ProtoFailure, payloadConverter: PayloadConverter): Error;
6862
}
6963

7064
/**
@@ -80,10 +74,6 @@ export interface DefaultEncodedFailureAttributes {
8074
* Options for the {@link DefaultFailureConverter} constructor.
8175
*/
8276
export interface DefaultFailureConverterOptions {
83-
/**
84-
* The {@link PayloadConverter} to use for converting failure attributes.
85-
*/
86-
payloadConverter: PayloadConverter;
8777
/**
8878
* Whether to encode error messages and stack traces (for encrypting these attributes use a {@link PayloadCodec}).
8979
*/
@@ -103,10 +93,9 @@ export class DefaultFailureConverter implements FailureConverter {
10393
public readonly options: DefaultFailureConverterOptions;
10494

10595
constructor(options?: Partial<DefaultFailureConverterOptions>) {
106-
const { encodeCommonAttributes, payloadConverter } = options ?? {};
96+
const { encodeCommonAttributes } = options ?? {};
10797
this.options = {
10898
encodeCommonAttributes: encodeCommonAttributes ?? false,
109-
payloadConverter: payloadConverter ?? defaultPayloadConverter,
11099
};
111100
}
112101

@@ -115,54 +104,50 @@ export class DefaultFailureConverter implements FailureConverter {
115104
*
116105
* Does not set common properties, that is done in {@link failureToError}.
117106
*/
118-
failureToErrorInner(failure: ProtoFailure): TemporalFailure {
107+
failureToErrorInner(failure: ProtoFailure, payloadConverter: PayloadConverter): TemporalFailure {
119108
if (failure.applicationFailureInfo) {
120109
return new ApplicationFailure(
121110
failure.message ?? undefined,
122111
failure.applicationFailureInfo.type,
123112
Boolean(failure.applicationFailureInfo.nonRetryable),
124-
arrayFromPayloads(this.options.payloadConverter, failure.applicationFailureInfo.details?.payloads),
125-
this.optionalFailureToOptionalError(failure.cause)
113+
arrayFromPayloads(payloadConverter, failure.applicationFailureInfo.details?.payloads),
114+
this.optionalFailureToOptionalError(failure.cause, payloadConverter)
126115
);
127116
}
128117
if (failure.serverFailureInfo) {
129118
return new ServerFailure(
130119
failure.message ?? undefined,
131120
Boolean(failure.serverFailureInfo.nonRetryable),
132-
this.optionalFailureToOptionalError(failure.cause)
121+
this.optionalFailureToOptionalError(failure.cause, payloadConverter)
133122
);
134123
}
135124
if (failure.timeoutFailureInfo) {
136125
return new TimeoutFailure(
137126
failure.message ?? undefined,
138-
fromPayloadsAtIndex(
139-
this.options.payloadConverter,
140-
0,
141-
failure.timeoutFailureInfo.lastHeartbeatDetails?.payloads
142-
),
127+
fromPayloadsAtIndex(payloadConverter, 0, failure.timeoutFailureInfo.lastHeartbeatDetails?.payloads),
143128
failure.timeoutFailureInfo.timeoutType ?? TimeoutType.TIMEOUT_TYPE_UNSPECIFIED
144129
);
145130
}
146131
if (failure.terminatedFailureInfo) {
147-
return new TerminatedFailure(failure.message ?? undefined, this.optionalFailureToOptionalError(failure.cause));
132+
return new TerminatedFailure(
133+
failure.message ?? undefined,
134+
this.optionalFailureToOptionalError(failure.cause, payloadConverter)
135+
);
148136
}
149137
if (failure.canceledFailureInfo) {
150138
return new CancelledFailure(
151139
failure.message ?? undefined,
152-
arrayFromPayloads(this.options.payloadConverter, failure.canceledFailureInfo.details?.payloads),
153-
this.optionalFailureToOptionalError(failure.cause)
140+
arrayFromPayloads(payloadConverter, failure.canceledFailureInfo.details?.payloads),
141+
this.optionalFailureToOptionalError(failure.cause, payloadConverter)
154142
);
155143
}
156144
if (failure.resetWorkflowFailureInfo) {
157145
return new ApplicationFailure(
158146
failure.message ?? undefined,
159147
'ResetWorkflow',
160148
false,
161-
arrayFromPayloads(
162-
this.options.payloadConverter,
163-
failure.resetWorkflowFailureInfo.lastHeartbeatDetails?.payloads
164-
),
165-
this.optionalFailureToOptionalError(failure.cause)
149+
arrayFromPayloads(payloadConverter, failure.resetWorkflowFailureInfo.lastHeartbeatDetails?.payloads),
150+
this.optionalFailureToOptionalError(failure.cause, payloadConverter)
166151
);
167152
}
168153
if (failure.childWorkflowExecutionFailureInfo) {
@@ -175,7 +160,7 @@ export class DefaultFailureConverter implements FailureConverter {
175160
workflowExecution,
176161
workflowType.name,
177162
retryState ?? RetryState.RETRY_STATE_UNSPECIFIED,
178-
this.optionalFailureToOptionalError(failure.cause)
163+
this.optionalFailureToOptionalError(failure.cause, payloadConverter)
179164
);
180165
}
181166
if (failure.activityFailureInfo) {
@@ -187,17 +172,18 @@ export class DefaultFailureConverter implements FailureConverter {
187172
failure.activityFailureInfo.activityId ?? undefined,
188173
failure.activityFailureInfo.retryState ?? RetryState.RETRY_STATE_UNSPECIFIED,
189174
failure.activityFailureInfo.identity ?? undefined,
190-
this.optionalFailureToOptionalError(failure.cause)
175+
this.optionalFailureToOptionalError(failure.cause, payloadConverter)
191176
);
192177
}
193-
return new TemporalFailure(failure.message ?? undefined, this.optionalFailureToOptionalError(failure.cause));
178+
return new TemporalFailure(
179+
failure.message ?? undefined,
180+
this.optionalFailureToOptionalError(failure.cause, payloadConverter)
181+
);
194182
}
195183

196-
failureToError(failure: ProtoFailure): Error {
184+
failureToError(failure: ProtoFailure, payloadConverter: PayloadConverter): Error {
197185
if (failure.encodedAttributes) {
198-
const attrs = this.options.payloadConverter.fromPayload<DefaultEncodedFailureAttributes>(
199-
failure.encodedAttributes
200-
);
186+
const attrs = payloadConverter.fromPayload<DefaultEncodedFailureAttributes>(failure.encodedAttributes);
201187
// Don't apply encodedAttributes unless they conform to an expected schema
202188
if (typeof attrs === 'object' && attrs !== null) {
203189
const { message, stack_trace } = attrs;
@@ -211,30 +197,30 @@ export class DefaultFailureConverter implements FailureConverter {
211197
}
212198
}
213199
}
214-
const err = this.failureToErrorInner(failure);
200+
const err = this.failureToErrorInner(failure, payloadConverter);
215201
err.stack = failure.stackTrace ?? '';
216202
err.failure = failure;
217203
return err;
218204
}
219205

220-
errorToFailure(err: unknown): ProtoFailure {
221-
const failure = this.errorToFailureInner(err);
206+
errorToFailure(err: unknown, payloadConverter: PayloadConverter): ProtoFailure {
207+
const failure = this.errorToFailureInner(err, payloadConverter);
222208
if (this.options.encodeCommonAttributes) {
223209
const { message, stackTrace } = failure;
224210
failure.message = 'Encoded failure';
225211
failure.stackTrace = '';
226-
failure.encodedAttributes = this.options.payloadConverter.toPayload({ message, stack_trace: stackTrace });
212+
failure.encodedAttributes = payloadConverter.toPayload({ message, stack_trace: stackTrace });
227213
}
228214
return failure;
229215
}
230216

231-
errorToFailureInner(err: unknown): ProtoFailure {
217+
errorToFailureInner(err: unknown, payloadConverter: PayloadConverter): ProtoFailure {
232218
if (err instanceof TemporalFailure) {
233219
if (err.failure) return err.failure;
234220
const base = {
235221
message: err.message,
236222
stackTrace: cutoffStackTrace(err.stack),
237-
cause: this.optionalErrorToOptionalFailure(err.cause),
223+
cause: this.optionalErrorToOptionalFailure(err.cause, payloadConverter),
238224
source: FAILURE_SOURCE,
239225
};
240226

@@ -265,7 +251,7 @@ export class DefaultFailureConverter implements FailureConverter {
265251
nonRetryable: err.nonRetryable,
266252
details:
267253
err.details && err.details.length
268-
? { payloads: toPayloads(this.options.payloadConverter, ...err.details) }
254+
? { payloads: toPayloads(payloadConverter, ...err.details) }
269255
: undefined,
270256
},
271257
};
@@ -276,7 +262,7 @@ export class DefaultFailureConverter implements FailureConverter {
276262
canceledFailureInfo: {
277263
details:
278264
err.details && err.details.length
279-
? { payloads: toPayloads(this.options.payloadConverter, ...err.details) }
265+
? { payloads: toPayloads(payloadConverter, ...err.details) }
280266
: undefined,
281267
},
282268
};
@@ -287,7 +273,7 @@ export class DefaultFailureConverter implements FailureConverter {
287273
timeoutFailureInfo: {
288274
timeoutType: err.timeoutType,
289275
lastHeartbeatDetails: err.lastHeartbeatDetails
290-
? { payloads: toPayloads(this.options.payloadConverter, err.lastHeartbeatDetails) }
276+
? { payloads: toPayloads(payloadConverter, err.lastHeartbeatDetails) }
291277
: undefined,
292278
},
293279
};
@@ -317,7 +303,7 @@ export class DefaultFailureConverter implements FailureConverter {
317303
...base,
318304
message: String(err.message) ?? '',
319305
stackTrace: cutoffStackTrace(String(err.stack)),
320-
cause: this.optionalErrorToOptionalFailure(err.cause),
306+
cause: this.optionalErrorToOptionalFailure(err.cause, payloadConverter),
321307
};
322308
}
323309

@@ -342,14 +328,17 @@ export class DefaultFailureConverter implements FailureConverter {
342328
/**
343329
* Converts a Failure proto message to a JS Error object if defined or returns undefined.
344330
*/
345-
optionalFailureToOptionalError(failure: ProtoFailure | undefined | null): Error | undefined {
346-
return failure ? this.failureToError(failure) : undefined;
331+
optionalFailureToOptionalError(
332+
failure: ProtoFailure | undefined | null,
333+
payloadConverter: PayloadConverter
334+
): Error | undefined {
335+
return failure ? this.failureToError(failure, payloadConverter) : undefined;
347336
}
348337

349338
/**
350339
* Converts an error to a Failure proto message if defined or returns undefined
351340
*/
352-
optionalErrorToOptionalFailure(err: unknown): ProtoFailure | undefined {
353-
return err ? this.errorToFailure(err) : undefined;
341+
optionalErrorToOptionalFailure(err: unknown, payloadConverter: PayloadConverter): ProtoFailure | undefined {
342+
return err ? this.errorToFailure(err, payloadConverter) : undefined;
354343
}
355344
}

packages/common/src/internal-non-workflow/codec-helpers.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,10 @@ export async function decodeOptionalFailureToOptionalError(
110110
converter: LoadedDataConverter,
111111
failure: ProtoFailure | undefined | null
112112
): Promise<TemporalFailure | undefined> {
113-
const { failureConverter, payloadCodecs } = converter;
114-
return failure ? failureConverter.failureToError(await decodeFailure(payloadCodecs, failure)) : undefined;
113+
const { failureConverter, payloadConverter, payloadCodecs } = converter;
114+
return failure
115+
? failureConverter.failureToError(await decodeFailure(payloadCodecs, failure), payloadConverter)
116+
: undefined;
115117
}
116118

117119
export async function decodeOptionalMap(
@@ -199,8 +201,8 @@ export async function encodeMapToPayloads<K extends string>(
199201
* Run {@link errorToFailure} on `error`, and then {@link encodeFailure}.
200202
*/
201203
export async function encodeErrorToFailure(dataConverter: LoadedDataConverter, error: unknown): Promise<ProtoFailure> {
202-
const { failureConverter, payloadCodecs } = dataConverter;
203-
return await encodeFailure(payloadCodecs, failureConverter.errorToFailure(error));
204+
const { failureConverter, payloadConverter, payloadCodecs } = dataConverter;
205+
return await encodeFailure(payloadCodecs, failureConverter.errorToFailure(error, payloadConverter));
204206
}
205207

206208
/**

packages/test/src/test-workflows.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1481,7 +1481,7 @@ test('resolve activity with failure - http', async (t) => {
14811481
t,
14821482
makeResolveActivity(1, {
14831483
failed: {
1484-
failure: defaultFailureConverter.errorToFailure(failure),
1484+
failure: defaultFailureConverter.errorToFailure(failure, defaultPayloadConverter),
14851485
},
14861486
})
14871487
);

packages/workflow/src/internals.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,11 @@ export class Activator implements ActivationHandler {
143143
resolve(result);
144144
} else if (activation.result.failed) {
145145
const { failure } = activation.result.failed;
146-
const err = failure ? state.failureConverter.failureToError(failure) : undefined;
146+
const err = failure ? state.failureConverter.failureToError(failure, state.payloadConverter) : undefined;
147147
reject(err);
148148
} else if (activation.result.cancelled) {
149149
const { failure } = activation.result.cancelled;
150-
const err = failure ? state.failureConverter.failureToError(failure) : undefined;
150+
const err = failure ? state.failureConverter.failureToError(failure, state.payloadConverter) : undefined;
151151
reject(err);
152152
} else if (activation.result.backoff) {
153153
reject(new LocalActivityDoBackoff(activation.result.backoff));
@@ -181,7 +181,7 @@ export class Activator implements ActivationHandler {
181181
if (!activation.cancelled.failure) {
182182
throw new TypeError('Got no failure in cancelled variant');
183183
}
184-
reject(state.failureConverter.failureToError(activation.cancelled.failure));
184+
reject(state.failureConverter.failureToError(activation.cancelled.failure, state.payloadConverter));
185185
} else {
186186
throw new TypeError('Got ResolveChildWorkflowExecutionStart with no status');
187187
}
@@ -201,13 +201,13 @@ export class Activator implements ActivationHandler {
201201
if (failure === undefined || failure === null) {
202202
throw new TypeError('Got failed result with no failure attribute');
203203
}
204-
reject(state.failureConverter.failureToError(failure));
204+
reject(state.failureConverter.failureToError(failure, state.payloadConverter));
205205
} else if (activation.result.cancelled) {
206206
const { failure } = activation.result.cancelled;
207207
if (failure === undefined || failure === null) {
208208
throw new TypeError('Got cancelled result with no failure attribute');
209209
}
210-
reject(state.failureConverter.failureToError(failure));
210+
reject(state.failureConverter.failureToError(failure, state.payloadConverter));
211211
}
212212
}
213213

@@ -301,7 +301,7 @@ export class Activator implements ActivationHandler {
301301
public resolveSignalExternalWorkflow(activation: coresdk.workflow_activation.IResolveSignalExternalWorkflow): void {
302302
const { resolve, reject } = consumeCompletion('signalWorkflow', getSeq(activation));
303303
if (activation.failure) {
304-
reject(state.failureConverter.failureToError(activation.failure));
304+
reject(state.failureConverter.failureToError(activation.failure, state.payloadConverter));
305305
} else {
306306
resolve(undefined);
307307
}
@@ -312,7 +312,7 @@ export class Activator implements ActivationHandler {
312312
): void {
313313
const { resolve, reject } = consumeCompletion('cancelWorkflow', getSeq(activation));
314314
if (activation.failure) {
315-
reject(state.failureConverter.failureToError(activation.failure));
315+
reject(state.failureConverter.failureToError(activation.failure, state.payloadConverter));
316316
} else {
317317
resolve(undefined);
318318
}
@@ -611,7 +611,7 @@ export async function handleWorkflowFailure(error: unknown): Promise<void> {
611611
state.pushCommand(
612612
{
613613
failWorkflowExecution: {
614-
failure: state.failureConverter.errorToFailure(error),
614+
failure: state.failureConverter.errorToFailure(error, state.payloadConverter),
615615
},
616616
},
617617
true
@@ -627,7 +627,10 @@ function completeQuery(queryId: string, result: unknown) {
627627

628628
async function failQuery(queryId: string, error: any) {
629629
state.pushCommand({
630-
respondToQuery: { queryId, failed: state.failureConverter.errorToFailure(ensureTemporalFailure(error)) },
630+
respondToQuery: {
631+
queryId,
632+
failed: state.failureConverter.errorToFailure(ensureTemporalFailure(error), state.payloadConverter),
633+
},
631634
});
632635
}
633636

packages/workflow/src/worker-interface.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,5 +298,5 @@ export async function dispose(): Promise<void> {
298298
}
299299

300300
export function errorToFailure(err: unknown): ProtoFailure {
301-
return state.failureConverter.errorToFailure(err);
301+
return state.failureConverter.errorToFailure(err, state.payloadConverter);
302302
}

0 commit comments

Comments
 (0)