Skip to content

Commit b3272f3

Browse files
committed
Expose Client on Activity Context + impl all services over NativeConnection
1 parent 27e855c commit b3272f3

29 files changed

+703
-253
lines changed

packages/activity/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"author": "Temporal Technologies Inc. <[email protected]>",
1414
"license": "MIT",
1515
"dependencies": {
16+
"@temporalio/client": "file:../client",
1617
"@temporalio/common": "file:../common",
1718
"abort-controller": "^3.0.0"
1819
},

packages/activity/src/index.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,12 @@ import {
7878
MetricMeter,
7979
Priority,
8080
ActivityCancellationDetails,
81+
IllegalStateError,
8182
} from '@temporalio/common';
8283
import { msToNumber } from '@temporalio/common/lib/time';
8384
import { SymbolBasedInstanceOfError } from '@temporalio/common/lib/type-helpers';
8485
import { ActivityCancellationDetailsHolder } from '@temporalio/common/lib/activity-cancellation-details';
86+
import { Client } from '@temporalio/client';
8587

8688
export {
8789
ActivityFunction,
@@ -313,6 +315,7 @@ export class Context {
313315
cancelled: Promise<never>,
314316
cancellationSignal: AbortSignal,
315317
heartbeat: (details?: any) => void,
318+
private readonly _client: Client | undefined,
316319
log: Logger,
317320
metricMeter: MetricMeter,
318321
details: ActivityCancellationDetailsHolder
@@ -351,6 +354,25 @@ export class Context {
351354
this.heartbeatFn(details);
352355
};
353356

357+
/**
358+
* A Temporal Client, bound to the same Temporal Namespace as the Worker executing this Activity.
359+
*
360+
* May throw an {@link IllegalStateError} if the Activity is running inside a `MockActivityEnvironment`
361+
* that was created without a Client.
362+
*
363+
* @experimental Client support over `NativeConnection` is experimental. Error handling maybe
364+
* incomplete or different from what would be observed using a {@link Connection}
365+
* instead. Client doesn't support cancellation through a Signal.
366+
*/
367+
public get client(): Client {
368+
if (this._client === undefined) {
369+
throw new IllegalStateError(
370+
'No Client available. This may be a MockActivityEnvironment that was created without a Client.'
371+
);
372+
}
373+
return this._client;
374+
}
375+
354376
/**
355377
* Helper function for sleeping in an Activity.
356378
* @param ms Sleep duration: number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
@@ -481,6 +503,22 @@ export function cancellationSignal(): AbortSignal {
481503
return Context.current().cancellationSignal;
482504
}
483505

506+
/**
507+
* A Temporal Client, bound to the same Temporal Namespace as the Worker executing this Activity.
508+
*
509+
* May throw an {@link IllegalStateError} if the Activity is running inside a `MockActivityEnvironment`
510+
* that was created without a Client.
511+
*
512+
* This is a shortcut for `Context.current().client` (see {@link Context.client}).
513+
*
514+
* @experimental Client support over `NativeConnection` is experimental. Error handling maybe
515+
* incomplete or different from what would be observed using a {@link Connection}
516+
* instead. Client doesn't support cancellation through a Signal.
517+
*/
518+
export function getClient(): Client {
519+
return Context.current().client;
520+
}
521+
484522
/**
485523
* Get the metric meter for the current activity, with activity-specific tags.
486524
*

packages/activity/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
"outDir": "./lib",
55
"rootDir": "./src"
66
},
7-
"references": [{ "path": "../common" }],
7+
"references": [{ "path": "../common" }, { "path": "../client" }],
88
"include": ["./src/**/*.ts"]
99
}

packages/client/src/base-client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export function defaultBaseClientOptions(): WithDefaults<BaseClientOptions> {
5353

5454
export class BaseClient {
5555
/**
56-
* The underlying {@link Connection | connection} used by this client.
56+
* The underlying {@link Connection | connection} or {@link NativeConnection | native connection} used by this client.
5757
*
5858
* Clients are cheap to create, but connections are expensive. Where it makes sense,
5959
* a single connection may and should be reused by multiple `Client`s.

packages/client/src/connection.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { type temporal } from '@temporalio/proto';
1212
import { isGrpcServiceError, ServiceError } from './errors';
1313
import { defaultGrpcRetryOptions, makeGrpcRetryInterceptor } from './grpc-retry';
1414
import pkg from './pkg';
15-
import { CallContext, HealthService, Metadata, OperatorService, WorkflowService } from './types';
15+
import { CallContext, HealthService, Metadata, OperatorService, TestService, WorkflowService } from './types';
1616

1717
/**
1818
* The default Temporal Server's TCP port for public gRPC connections.
@@ -138,6 +138,17 @@ export type ConnectionOptionsWithDefaults = Required<
138138
connectTimeoutMs: number;
139139
};
140140

141+
export const InternalConnectionOptionsSymbol = Symbol('__temporal_internal_connection_options');
142+
export type InternalConnectionOptions = ConnectionOptions & {
143+
[InternalConnectionOptionsSymbol]?: {
144+
/**
145+
* A symbol used internally to indicate whether support for TestService should be enabled on this
146+
* connection. This is set to true on connections created internally by the `TestWorkflowEnvironment.createTimeSkipping()`
147+
*/
148+
supportsTestService?: boolean;
149+
};
150+
};
151+
141152
export const LOCAL_TARGET = 'localhost:7233';
142153

143154
function addDefaults(options: ConnectionOptions): ConnectionOptionsWithDefaults {
@@ -239,6 +250,13 @@ export interface ConnectionCtorOptions {
239250
*/
240251
readonly operatorService: OperatorService;
241252

253+
/**
254+
* Raw gRPC access to the Temporal test service.
255+
*
256+
* Will be `undefined` if connected to a server that does not support the test service.
257+
*/
258+
readonly testService: TestService | undefined;
259+
242260
/**
243261
* Raw gRPC access to the standard gRPC {@link https://github.com/grpc/grpc/blob/92f58c18a8da2728f571138c37760a721c8915a2/doc/health-checking.md | health service}.
244262
*/
@@ -286,6 +304,13 @@ export class Connection {
286304
*/
287305
public readonly operatorService: OperatorService;
288306

307+
/**
308+
* Raw gRPC access to the Temporal test service.
309+
*
310+
* Will be `undefined` if connected to a server that does not support the test service.
311+
*/
312+
public readonly testService: TestService | undefined;
313+
289314
/**
290315
* Raw gRPC access to the standard gRPC {@link https://github.com/grpc/grpc/blob/92f58c18a8da2728f571138c37760a721c8915a2/doc/health-checking.md | health service}.
291316
*/
@@ -326,6 +351,7 @@ export class Connection {
326351
apiKeyFnRef,
327352
});
328353
const workflowService = WorkflowService.create(workflowRpcImpl, false, false);
354+
329355
const operatorRpcImpl = this.generateRPCImplementation({
330356
serviceName: 'temporal.api.operatorservice.v1.OperatorService',
331357
client,
@@ -335,6 +361,20 @@ export class Connection {
335361
apiKeyFnRef,
336362
});
337363
const operatorService = OperatorService.create(operatorRpcImpl, false, false);
364+
365+
let testService: TestService | undefined = undefined;
366+
if ((optionsWithDefaults as InternalConnectionOptions)[InternalConnectionOptionsSymbol]?.supportsTestService) {
367+
const testRpcImpl = this.generateRPCImplementation({
368+
serviceName: 'temporal.api.testservice.v1.TestService',
369+
client,
370+
callContextStorage,
371+
interceptors: optionsWithDefaults?.interceptors,
372+
staticMetadata: optionsWithDefaults.metadata,
373+
apiKeyFnRef,
374+
});
375+
testService = TestService.create(testRpcImpl, false, false);
376+
}
377+
338378
const healthRpcImpl = this.generateRPCImplementation({
339379
serviceName: 'grpc.health.v1.Health',
340380
client,
@@ -350,6 +390,7 @@ export class Connection {
350390
callContextStorage,
351391
workflowService,
352392
operatorService,
393+
testService,
353394
healthService,
354395
options: optionsWithDefaults,
355396
apiKeyFnRef,
@@ -414,6 +455,7 @@ export class Connection {
414455
client,
415456
workflowService,
416457
operatorService,
458+
testService,
417459
healthService,
418460
callContextStorage,
419461
apiKeyFnRef,
@@ -422,6 +464,7 @@ export class Connection {
422464
this.client = client;
423465
this.workflowService = this.withNamespaceHeaderInjector(workflowService);
424466
this.operatorService = operatorService;
467+
this.testService = testService;
425468
this.healthService = healthService;
426469
this.callContextStorage = callContextStorage;
427470
this.apiKeyFnRef = apiKeyFnRef;

packages/client/src/types.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ export type WorkflowService = proto.temporal.api.workflowservice.v1.WorkflowServ
9191
export const { WorkflowService } = proto.temporal.api.workflowservice.v1;
9292
export type OperatorService = proto.temporal.api.operatorservice.v1.OperatorService;
9393
export const { OperatorService } = proto.temporal.api.operatorservice.v1;
94+
export type TestService = proto.temporal.api.testservice.v1.TestService;
95+
export const { TestService } = proto.temporal.api.testservice.v1;
9496
export type HealthService = proto.grpc.health.v1.Health;
9597
export const { Health: HealthService } = proto.grpc.health.v1;
9698

@@ -117,9 +119,6 @@ export interface CallContext {
117119

118120
/**
119121
* Connection interface used by high level SDK clients.
120-
*
121-
* NOTE: Currently the SDK only supports grpc-js based connection but in the future
122-
* we might support grpc-web and native Rust connections.
123122
*/
124123
export interface ConnectionLike {
125124
workflowService: WorkflowService;

0 commit comments

Comments
 (0)