Skip to content

Commit ac1396a

Browse files
bergundylorensr
andauthored
feat: Improve activity registration and proxy types (#742)
Co-authored-by: Loren ☺️ <251288+lorensr@users.noreply.github.com>
1 parent fa852b8 commit ac1396a

File tree

8 files changed

+71
-33
lines changed

8 files changed

+71
-33
lines changed

packages/activity/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ import { msToNumber } from '@temporalio/internal-workflow-common';
3030
import { AbortSignal } from 'abort-controller';
3131
import { AsyncLocalStorage } from 'async_hooks';
3232
export { CancelledFailure } from '@temporalio/common';
33-
export { ActivityFunction, ActivityInterface } from '@temporalio/internal-workflow-common';
33+
export {
34+
ActivityFunction,
35+
ActivityInterface, // eslint-disable-line deprecation/deprecation
36+
UntypedActivities,
37+
} from '@temporalio/internal-workflow-common';
3438
export * from '@temporalio/internal-workflow-common/lib/errors';
3539

3640
/**

packages/internal-workflow-common/src/activity-options.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,3 @@ export interface LocalActivityOptions {
144144
*/
145145
cancellationType?: coresdk.workflow_commands.ActivityCancellationType;
146146
}
147-
148-
export interface ActivityFunction<P extends any[], R> {
149-
(...args: P): Promise<R>;
150-
}
151-
152-
/**
153-
* Mapping of Activity name to function
154-
*/
155-
export type ActivityInterface = Record<string, ActivityFunction<any[], any>>;

packages/internal-workflow-common/src/interfaces.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,18 @@ export type WorkflowResultType<W extends Workflow> = ReturnType<W> extends Promi
4747
*/
4848
export type SearchAttributes = Record<string, SearchAttributeValue | undefined>;
4949
export type SearchAttributeValue = string[] | number[] | boolean[] | Date[];
50+
51+
export interface ActivityFunction<P extends any[] = any[], R = any> {
52+
(...args: P): Promise<R>;
53+
}
54+
55+
/**
56+
* Mapping of Activity name to function
57+
* @deprecated not required anymore, for untyped activities use {@link UntypedActivities}
58+
*/
59+
export type ActivityInterface = Record<string, ActivityFunction>;
60+
61+
/**
62+
* Mapping of Activity name to function
63+
*/
64+
export type UntypedActivities = Record<string, ActivityFunction>;

packages/test/src/activities/async-completer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Observer } from 'rxjs';
2-
import { ActivityInterface, CompleteAsyncError, Context, Info } from '@temporalio/activity';
2+
import { CompleteAsyncError, Context, Info } from '@temporalio/activity';
33

4-
export interface Activities extends ActivityInterface {
4+
export interface Activities {
55
completeAsync(): Promise<string>;
66
}
77

packages/worker/src/worker-options.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DataConverter, LoadedDataConverter } from '@temporalio/common';
22
import { loadDataConverter } from '@temporalio/internal-non-workflow-common';
3-
import { ActivityInterface, msToNumber } from '@temporalio/internal-workflow-common';
3+
import { msToNumber } from '@temporalio/internal-workflow-common';
44
import os from 'os';
55
import { NativeConnection } from './connection';
66
import { WorkerInterceptors } from './interceptors';
@@ -63,9 +63,9 @@ export interface WorkerOptions {
6363
taskQueue: string;
6464

6565
/**
66-
* Mapping of activity name to implementation.
66+
* Mapping of activity name to implementation
6767
*/
68-
activities?: ActivityInterface;
68+
activities?: object;
6969

7070
/**
7171
* Path to look up workflows in, any function exported in this path will be registered as a Workflows in this Worker.

packages/worker/src/worker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,8 @@ export class Worker {
757757
);
758758

759759
const { activityType } = info;
760-
const fn = this.options.activities?.[activityType];
760+
// activities is of type "object" which does not support string indexes
761+
const fn = (this.options.activities as any)?.[activityType];
761762
if (typeof fn !== 'function') {
762763
output = {
763764
type: 'result',

packages/workflow/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@ export {
6161
export {
6262
ActivityCancellationType,
6363
ActivityFunction,
64-
ActivityInterface,
64+
ActivityInterface, // eslint-disable-line deprecation/deprecation
6565
ActivityOptions,
6666
RetryPolicy,
67+
UntypedActivities,
6768
} from '@temporalio/internal-workflow-common';
6869
export * from '@temporalio/internal-workflow-common/lib/errors';
6970
export * from '@temporalio/internal-workflow-common/lib/interfaces';

packages/workflow/src/workflow.ts

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { mapToPayloads, searchAttributePayloadConverter, toPayloads } from '@temporalio/common';
22
import {
33
ActivityFunction,
4-
ActivityInterface,
54
ActivityOptions,
65
compileRetryPolicy,
76
composeInterceptors,
@@ -14,6 +13,7 @@ import {
1413
SearchAttributes,
1514
SignalDefinition,
1615
tsToMs,
16+
UntypedActivities,
1717
WithWorkflowArgs,
1818
Workflow,
1919
WorkflowResultType,
@@ -117,13 +117,6 @@ export function sleep(ms: number | string): Promise<void> {
117117
});
118118
}
119119

120-
export interface ActivityInfo {
121-
name: string;
122-
type: string;
123-
}
124-
125-
export type InternalActivityFunction<P extends any[], R> = ActivityFunction<P, R> & ActivityInfo;
126-
127120
function validateActivityOptions(options: ActivityOptions): void {
128121
if (options.scheduleToCloseTimeout === undefined && options.startToCloseTimeout === undefined) {
129122
throw new TypeError('Required either scheduleToCloseTimeout or startToCloseTimeout');
@@ -444,6 +437,43 @@ function signalWorkflowNextHandler({ seq, signalName, args, target, headers }: S
444437
});
445438
}
446439

440+
/**
441+
* Symbol used in the return type of proxy methods to mark that an attribute on the source type is not a method.
442+
*
443+
* @see {@link ActivityInterfaceFor}
444+
* @see {@link proxyActivities}
445+
* @see {@link proxyLocalActivities}
446+
*/
447+
export const NotAnActivityMethod = Symbol.for('__TEMPORAL_NOT_AN_ACTIVITY_METHOD');
448+
449+
/**
450+
* Type helper that takes a type `T` and transforms attributes that are not {@link ActivityFunction} to
451+
* {@link NotAnActivityMethod}.
452+
*
453+
* @example
454+
*
455+
* Used by {@link proxyActivities} to get this compile-time error:
456+
*
457+
* ```ts
458+
* interface MyActivities {
459+
* valid(input: number): Promise<number>;
460+
* invalid(input: number): number;
461+
* }
462+
*
463+
* const act = proxyActivities<MyActivities>({ startToCloseTimeout: '5m' });
464+
*
465+
* await act.valid(true);
466+
* await act.invalid();
467+
* // ^ TS complains with:
468+
* // (property) invalidDefinition: typeof NotAnActivityMethod
469+
* // This expression is not callable.
470+
* // Type 'Symbol' has no call signatures.(2349)
471+
* ```
472+
*/
473+
export type ActivityInterfaceFor<T> = {
474+
[K in keyof T]: T[K] extends ActivityFunction ? T[K] : typeof NotAnActivityMethod;
475+
};
476+
447477
/**
448478
* Configure Activity functions with given {@link ActivityOptions}.
449479
*
@@ -452,11 +482,9 @@ function signalWorkflowNextHandler({ seq, signalName, args, target, headers }: S
452482
* @return a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
453483
* for which each attribute is a callable Activity function
454484
*
455-
* @typeparam A An {@link ActivityInterface} - mapping of name to function
456-
*
457485
* @example
458486
* ```ts
459-
* import { proxyActivities, ActivityInterface } from '@temporalio/workflow';
487+
* import { proxyActivities } from '@temporalio/workflow';
460488
* import * as activities from '../activities';
461489
*
462490
* // Setup Activities from module exports
@@ -465,7 +493,7 @@ function signalWorkflowNextHandler({ seq, signalName, args, target, headers }: S
465493
* });
466494
*
467495
* // Setup Activities from an explicit interface (e.g. when defined by another SDK)
468-
* interface JavaActivities extends ActivityInterface {
496+
* interface JavaActivities {
469497
* httpGetFromJava(url: string): Promise<string>
470498
* someOtherJavaActivity(arg1: number, arg2: string): Promise<string>;
471499
* }
@@ -484,7 +512,7 @@ function signalWorkflowNextHandler({ seq, signalName, args, target, headers }: S
484512
* }
485513
* ```
486514
*/
487-
export function proxyActivities<A extends ActivityInterface>(options: ActivityOptions): A {
515+
export function proxyActivities<A = UntypedActivities>(options: ActivityOptions): ActivityInterfaceFor<A> {
488516
if (options === undefined) {
489517
throw new TypeError('options must be defined');
490518
}
@@ -513,13 +541,11 @@ export function proxyActivities<A extends ActivityInterface>(options: ActivityOp
513541
* @return a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
514542
* for which each attribute is a callable Activity function
515543
*
516-
* @typeparam A An {@link ActivityInterface} - mapping of name to function
517-
*
518544
* @experimental
519545
*
520546
* See {@link proxyActivities} for examples
521547
*/
522-
export function proxyLocalActivities<A extends ActivityInterface>(options: LocalActivityOptions): A {
548+
export function proxyLocalActivities<A = UntypedActivities>(options: LocalActivityOptions): ActivityInterfaceFor<A> {
523549
if (options === undefined) {
524550
throw new TypeError('options must be defined');
525551
}

0 commit comments

Comments
 (0)