Skip to content

Commit 177cea7

Browse files
committed
resolve merge conflicts
2 parents 15b1503 + 4155dad commit 177cea7

File tree

4 files changed

+94
-3
lines changed

4 files changed

+94
-3
lines changed

packages/test/src/test-integration-split-two.ts

+55-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
import { msToNumber, tsToMs } from '@temporalio/common/lib/time';
1515
import { decode as payloadDecode, decodeFromPayloadsAtIndex } from '@temporalio/common/lib/internal-non-workflow';
1616

17-
import { condition, defineQuery, setHandler, sleep } from '@temporalio/workflow';
17+
import { condition, defineQuery, defineSignal, setDefaultQueryHandler, setHandler, sleep } from '@temporalio/workflow';
1818
import { configurableHelpers, createTestWorkflowBundle } from './helpers-integration';
1919
import * as activities from './activities';
2020
import * as workflows from './workflows';
@@ -697,3 +697,57 @@ test('Query does not cause condition to be triggered', configMacro, async (t, co
697697
// Worker did not crash
698698
t.pass();
699699
});
700+
701+
const completeSignal = defineSignal('complete');
702+
const definedQuery = defineQuery<QueryNameAndArgs>('query-handler-type');
703+
704+
interface QueryNameAndArgs {
705+
name: string;
706+
queryName?: string;
707+
args: any[];
708+
}
709+
710+
export async function workflowWithMaybeDefinedQuery(useDefinedQuery: boolean): Promise<void> {
711+
let complete = false;
712+
setHandler(completeSignal, () => {
713+
complete = true;
714+
});
715+
setDefaultQueryHandler((queryName: string, ...args: any[]) => {
716+
return { name: 'default', queryName, args };
717+
});
718+
if (useDefinedQuery) {
719+
setHandler(definedQuery, (...args: any[]) => {
720+
return { name: definedQuery.name, args };
721+
});
722+
}
723+
724+
await condition(() => complete);
725+
}
726+
727+
test('default query handler is used if requested query does not exist', configMacro, async (t, config) => {
728+
const { env, createWorkerWithDefaults } = config;
729+
const { startWorkflow } = configurableHelpers(t, t.context.workflowBundle, env);
730+
const worker = await createWorkerWithDefaults(t, { activities });
731+
const handle = await startWorkflow(workflowWithMaybeDefinedQuery, {
732+
args: [false],
733+
});
734+
await worker.runUntil(async () => {
735+
const args = ['test', 'args'];
736+
const result = await handle.query(definedQuery, ...args);
737+
t.deepEqual(result, { name: 'default', queryName: definedQuery.name, args });
738+
});
739+
});
740+
741+
test('default query handler is not used if requested query exists', configMacro, async (t, config) => {
742+
const { env, createWorkerWithDefaults } = config;
743+
const { startWorkflow } = configurableHelpers(t, t.context.workflowBundle, env);
744+
const worker = await createWorkerWithDefaults(t, { activities });
745+
const handle = await startWorkflow(workflowWithMaybeDefinedQuery, {
746+
args: [true],
747+
});
748+
await worker.runUntil(async () => {
749+
const args = ['test', 'args'];
750+
const result = await handle.query('query-handler-type', ...args);
751+
t.deepEqual(result, { name: definedQuery.name, args });
752+
});
753+
});

packages/workflow/src/interfaces.ts

+5
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,11 @@ export type DefaultSignalHandler = (signalName: string, ...args: unknown[]) => v
543543
*/
544544
export type DefaultUpdateHandler = (updateName: string, ...args: unknown[]) => Promise<unknown> | unknown;
545545

546+
/**
547+
* A handler function accepting query calls for non-registered query names.
548+
*/
549+
export type DefaultQueryHandler = (queryName: string, ...args: unknown[]) => unknown;
550+
546551
/**
547552
* A validation function capable of accepting the arguments for a given UpdateDefinition.
548553
*/

packages/workflow/src/internals.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
WorkflowCreateOptionsInternal,
4343
ActivationCompletion,
4444
DefaultUpdateHandler,
45+
DefaultQueryHandler,
4546
} from './interfaces';
4647
import { type SinkCall } from './sinks';
4748
import { untrackPromise } from './stack-helpers';
@@ -195,6 +196,11 @@ export class Activator implements ActivationHandler {
195196
*/
196197
defaultUpdateHandler?: DefaultUpdateHandler;
197198

199+
/**
200+
* A query handler that catches calls for non-registered query names.
201+
*/
202+
defaultQueryHandler?: DefaultQueryHandler;
203+
198204
/**
199205
* Source map file for looking up the source files in response to __enhanced_stack_trace
200206
*/
@@ -617,7 +623,11 @@ export class Activator implements ActivationHandler {
617623

618624
// Intentionally non-async function so this handler doesn't show up in the stack trace
619625
protected queryWorkflowNextHandler({ queryName, args }: QueryInput): Promise<unknown> {
620-
const fn = this.queryHandlers.get(queryName)?.handler;
626+
let fn = this.queryHandlers.get(queryName)?.handler;
627+
if (fn === undefined && this.defaultQueryHandler !== undefined) {
628+
fn = this.defaultQueryHandler.bind(this, queryName);
629+
}
630+
// No handler or default registered, fail.
621631
if (fn === undefined) {
622632
const knownQueryTypes = [...this.queryHandlers.keys()].join(' ');
623633
// Fail the query
@@ -627,6 +637,7 @@ export class Activator implements ActivationHandler {
627637
)
628638
);
629639
}
640+
// Execute handler.
630641
try {
631642
const ret = fn(...args);
632643
if (ret instanceof Promise) {

packages/workflow/src/workflow.ts

+22-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import {
5151
encodeChildWorkflowCancellationType,
5252
encodeParentClosePolicy,
5353
DefaultUpdateHandler,
54+
DefaultQueryHandler,
5455
} from './interfaces';
5556
import { LocalActivityDoBackoff } from './errors';
5657
import { assertInWorkflowContext, getActivator, maybeGetActivator } from './global-attributes';
@@ -1301,7 +1302,7 @@ export function setHandler<
13011302
*
13021303
* Signals are dispatched to the default signal handler in the order that they were accepted by the server.
13031304
*
1304-
* If this function is called multiple times for a given signal or query name the last handler will overwrite any previous calls.
1305+
* If this function is called multiple times for a given signal name the last handler will overwrite any previous calls.
13051306
*
13061307
* @param handler a function that will handle signals for non-registered signal names, or `undefined` to unset the handler.
13071308
*/
@@ -1342,6 +1343,26 @@ export function setDefaultUpdateHandler(handler: DefaultUpdateHandler | undefine
13421343
}
13431344
}
13441345

1346+
/**
1347+
* Set a query handler function that will handle query calls for non-registered query names.
1348+
*
1349+
* Queries are dispatched to the default query handler in the order that they were accepted by the server.
1350+
*
1351+
* If this function is called multiple times for a given query name the last handler will overwrite any previous calls.
1352+
*
1353+
* @param handler a function that will handle queries for non-registered query names, or `undefined` to unset the handler.
1354+
*/
1355+
export function setDefaultQueryHandler(handler: DefaultQueryHandler | undefined): void {
1356+
const activator = assertInWorkflowContext(
1357+
'Workflow.setDefaultQueryHandler(...) may only be used from a Workflow Execution.'
1358+
);
1359+
if (typeof handler === 'function' || handler === undefined) {
1360+
activator.defaultQueryHandler = handler;
1361+
} else {
1362+
throw new TypeError(`Expected handler to be either a function or 'undefined'. Got: '${typeof handler}'`);
1363+
}
1364+
}
1365+
13451366
/**
13461367
* Updates this Workflow's Search Attributes by merging the provided `searchAttributes` with the existing Search
13471368
* Attributes, `workflowInfo().searchAttributes`.

0 commit comments

Comments
 (0)