Skip to content

Commit a0470c9

Browse files
authored
Add search attributes to dev server (temporalio#1624)
1 parent 8ed034c commit a0470c9

7 files changed

+79
-30
lines changed

packages/core-bridge/ts/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { LogLevel, Duration } from '@temporalio/common';
1+
import { LogLevel, Duration, SearchAttributeType } from '@temporalio/common';
22
import type { TLSConfig, ProxyConfig, HttpConnectProxyConfig } from '@temporalio/common/lib/internal-non-workflow';
33
import { WorkerTuner } from './worker-tuner';
4+
import { SearchAttributeKey } from '@temporalio/common/src/search-attributes';
45

56
export {
67
WorkerTuner,
@@ -511,6 +512,10 @@ export interface DevServerConfig {
511512
* be supported in the future.
512513
*/
513514
extraArgs?: string[];
515+
/**
516+
* Search attributes to be registered with the dev server.
517+
*/
518+
searchAttributes?: SearchAttributeKey<SearchAttributeType>[];
514519
}
515520

516521
/**

packages/test/src/helpers-integration-multi-codec.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
createLocalTestEnvironment,
1111
makeConfigurableEnvironmentTestFn,
1212
} from './helpers-integration';
13-
import { ByteSkewerPayloadCodec, registerDefaultCustomSearchAttributes, Worker } from './helpers';
13+
import { ByteSkewerPayloadCodec, Worker } from './helpers';
1414

1515
// Note: re-export shared workflows (or long workflows)
1616
export * from './workflows';
@@ -43,7 +43,6 @@ export function makeTestFn(makeBundle: () => Promise<WorkflowBundle>): TestFn<Te
4343
const env = await createLocalTestEnvironment({
4444
client: { dataConverter },
4545
});
46-
await registerDefaultCustomSearchAttributes(env.connection);
4746

4847
configs.push({
4948
loadedDataConverter,

packages/test/src/helpers-integration.ts

+12-8
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,9 @@ import {
2727
} from '@temporalio/worker';
2828
import * as workflow from '@temporalio/workflow';
2929
import { temporal } from '@temporalio/proto';
30+
import { defineSearchAttributeKey, SearchAttributeType } from '@temporalio/common/lib/search-attributes';
3031
import { ConnectionInjectorInterceptor } from './activities/interceptors';
31-
import {
32-
Worker,
33-
TestWorkflowEnvironment,
34-
test as anyTest,
35-
bundlerOptions,
36-
registerDefaultCustomSearchAttributes,
37-
} from './helpers';
32+
import { Worker, TestWorkflowEnvironment, test as anyTest, bundlerOptions } from './helpers';
3833

3934
export interface Context {
4035
env: TestWorkflowEnvironment;
@@ -89,12 +84,22 @@ export async function createTestWorkflowBundle({
8984
});
9085
}
9186

87+
export const defaultSAKeys = {
88+
CustomIntField: defineSearchAttributeKey('CustomIntField', SearchAttributeType.INT),
89+
CustomBoolField: defineSearchAttributeKey('CustomBoolField', SearchAttributeType.BOOL),
90+
CustomKeywordField: defineSearchAttributeKey('CustomKeywordField', SearchAttributeType.KEYWORD),
91+
CustomTextField: defineSearchAttributeKey('CustomTextField', SearchAttributeType.TEXT),
92+
CustomDatetimeField: defineSearchAttributeKey('CustomDatetimeField', SearchAttributeType.DATETIME),
93+
CustomDoubleField: defineSearchAttributeKey('CustomDoubleField', SearchAttributeType.DOUBLE),
94+
};
95+
9296
export async function createLocalTestEnvironment(
9397
opts?: LocalTestWorkflowEnvironmentOptions
9498
): Promise<TestWorkflowEnvironment> {
9599
return await TestWorkflowEnvironment.createLocal({
96100
...(opts || {}), // Use provided options or default to an empty object
97101
server: {
102+
searchAttributes: Object.values(defaultSAKeys),
98103
...(opts?.server || {}), // Use provided server options or default to an empty object
99104
extraArgs: [
100105
...defaultDynamicConfigOptions.flatMap((opt) => ['--dynamic-config-value', opt]),
@@ -130,7 +135,6 @@ export function makeTestFunction(opts: {
130135
recordedLogs: opts.recordedLogs,
131136
createTestContext: async (_t: ExecutionContext): Promise<Context> => {
132137
const env = await createLocalTestEnvironment(opts.workflowEnvironmentOpts);
133-
await registerDefaultCustomSearchAttributes(env.connection);
134138
return {
135139
workflowBundle: await createTestWorkflowBundle({
136140
workflowsPath: opts.workflowsPath,

packages/test/src/test-integration-workflows.ts

+36-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,19 @@ import { CancelReason } from '@temporalio/worker/lib/activity';
1010
import * as workflow from '@temporalio/workflow';
1111
import { defineQuery, defineSignal } from '@temporalio/workflow';
1212
import { SdkFlags } from '@temporalio/workflow/lib/flags';
13-
import { ActivityCancellationType, ApplicationFailure, WorkflowExecutionAlreadyStartedError } from '@temporalio/common';
13+
import {
14+
ActivityCancellationType,
15+
ApplicationFailure,
16+
defineSearchAttributeKey,
17+
SearchAttributePair,
18+
SearchAttributeType,
19+
TypedSearchAttributes,
20+
WorkflowExecutionAlreadyStartedError,
21+
} from '@temporalio/common';
1422
import { signalSchedulingWorkflow } from './activities/helpers';
1523
import { activityStartedSignal } from './workflows/definitions';
1624
import * as workflows from './workflows';
17-
import { Context, helpers, makeTestFunction } from './helpers-integration';
25+
import { Context, createLocalTestEnvironment, helpers, makeTestFunction } from './helpers-integration';
1826
import { overrideSdkInternalFlag } from './mock-internal-flags';
1927
import { asSdkLoggerSink, loadHistory, RUN_TIME_SKIPPING_TESTS } from './helpers';
2028

@@ -1303,3 +1311,29 @@ test('Count workflow executions', async (t) => {
13031311
],
13041312
});
13051313
});
1314+
1315+
test('can register search attributes to dev server', async (t) => {
1316+
const key = defineSearchAttributeKey('new-search-attr', SearchAttributeType.INT);
1317+
const newSearchAttribute: SearchAttributePair = { key, value: 12 };
1318+
1319+
// Create new test environment with search attribute registered.
1320+
const env = await createLocalTestEnvironment({
1321+
server: {
1322+
searchAttributes: [key],
1323+
},
1324+
});
1325+
1326+
const newClient = env.client;
1327+
// Expect workflow with search attribute to start without error.
1328+
const handle = await newClient.workflow.start(completableWorkflow, {
1329+
args: [true],
1330+
workflowId: randomUUID(),
1331+
taskQueue: 'new-env-tq',
1332+
typedSearchAttributes: [newSearchAttribute],
1333+
});
1334+
// Expect workflow description to have search attribute.
1335+
const desc = await handle.describe();
1336+
t.deepEqual(desc.typedSearchAttributes, new TypedSearchAttributes([newSearchAttribute]));
1337+
t.deepEqual(desc.searchAttributes, { 'new-search-attr': [12] }); // eslint-disable-line deprecation/deprecation
1338+
await env.teardown();
1339+
});

packages/test/src/test-schedules.ts

+10-17
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import {
1212
ScheduleUpdateOptions,
1313
} from '@temporalio/client';
1414
import { msToNumber } from '@temporalio/common/lib/time';
15-
import { SearchAttributes, SearchAttributeType, TypedSearchAttributes } from '@temporalio/common';
15+
import { SearchAttributes, TypedSearchAttributes } from '@temporalio/common';
1616
import { registerDefaultCustomSearchAttributes, RUN_INTEGRATION_TESTS } from './helpers';
17+
import { defaultSAKeys } from './helpers-integration';
1718

1819
export interface Context {
1920
client: Client;
@@ -168,9 +169,7 @@ if (RUN_INTEGRATION_TESTS) {
168169
searchAttributes: {
169170
CustomKeywordField: ['test-value2'],
170171
},
171-
typedSearchAttributes: new TypedSearchAttributes([
172-
{ key: { name: 'CustomIntField', type: SearchAttributeType.INT }, value: 42 },
173-
]),
172+
typedSearchAttributes: new TypedSearchAttributes([{ key: defaultSAKeys.CustomIntField, value: 42 }]),
174173
},
175174
});
176175

@@ -188,8 +187,8 @@ if (RUN_INTEGRATION_TESTS) {
188187
t.deepEqual(
189188
describedSchedule.action.typedSearchAttributes,
190189
new TypedSearchAttributes([
191-
{ key: { name: 'CustomIntField', type: SearchAttributeType.INT }, value: 42 },
192-
{ key: { name: 'CustomKeywordField', type: SearchAttributeType.KEYWORD }, value: 'test-value2' },
190+
{ key: defaultSAKeys.CustomIntField, value: 42 },
191+
{ key: defaultSAKeys.CustomKeywordField, value: 'test-value2' },
193192
])
194193
);
195194
} finally {
@@ -216,9 +215,7 @@ if (RUN_INTEGRATION_TESTS) {
216215
searchAttributes: {
217216
CustomKeywordField: ['test-value2'],
218217
},
219-
typedSearchAttributes: new TypedSearchAttributes([
220-
{ key: { name: 'CustomIntField', type: SearchAttributeType.INT }, value: 42 },
221-
]),
218+
typedSearchAttributes: new TypedSearchAttributes([{ key: defaultSAKeys.CustomIntField, value: 42 }]),
222219
},
223220
});
224221

@@ -237,8 +234,8 @@ if (RUN_INTEGRATION_TESTS) {
237234
t.deepEqual(
238235
describedSchedule.action.typedSearchAttributes,
239236
new TypedSearchAttributes([
240-
{ key: { name: 'CustomIntField', type: SearchAttributeType.INT }, value: 42 },
241-
{ key: { name: 'CustomKeywordField', type: SearchAttributeType.KEYWORD }, value: 'test-value2' },
237+
{ key: defaultSAKeys.CustomIntField, value: 42 },
238+
{ key: defaultSAKeys.CustomKeywordField, value: 'test-value2' },
242239
])
243240
);
244241
} finally {
@@ -351,9 +348,7 @@ if (RUN_INTEGRATION_TESTS) {
351348
searchAttributes: {
352349
CustomKeywordField: ['test-value2'],
353350
},
354-
typedSearchAttributes: new TypedSearchAttributes([
355-
{ key: { name: 'CustomIntField', type: SearchAttributeType.INT }, value: 42 },
356-
]),
351+
typedSearchAttributes: new TypedSearchAttributes([{ key: defaultSAKeys.CustomIntField, value: 42 }]),
357352
},
358353
});
359354

@@ -598,9 +593,7 @@ if (RUN_INTEGRATION_TESTS) {
598593
taskQueue,
599594
},
600595
searchAttributes,
601-
typedSearchAttributes: new TypedSearchAttributes([
602-
{ key: { name: 'CustomIntField', type: SearchAttributeType.INT }, value: 42 },
603-
]),
596+
typedSearchAttributes: new TypedSearchAttributes([{ key: defaultSAKeys.CustomIntField, value: 42 }]),
604597
})
605598
);
606599
}

packages/test/src/test-typed-search-attributes.ts

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const test = makeTestFunction({
2828
workflowEnvironmentOpts: {
2929
server: {
3030
namespace: 'test-typed-search-attributes',
31+
searchAttributes: [],
3132
},
3233
},
3334
});

packages/testing/src/index.ts

+13
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
Logger,
2828
defaultFailureConverter,
2929
defaultPayloadConverter,
30+
TypedSearchAttributes,
3031
} from '@temporalio/common';
3132
import { msToNumber, msToTs, tsToMs } from '@temporalio/common/lib/time';
3233
import { ActivityInterceptorsFactory, DefaultLogger, NativeConnection, Runtime } from '@temporalio/worker';
@@ -302,6 +303,18 @@ export class TestWorkflowEnvironment {
302303
): Promise<TestWorkflowEnvironment> {
303304
const { supportsTimeSkipping, namespace, ...rest } = opts;
304305
const optsWithDefaults = addDefaults(filterNullAndUndefined(rest));
306+
307+
// Add search attributes to CLI server arguments
308+
if ('searchAttributes' in optsWithDefaults.server && optsWithDefaults.server.searchAttributes) {
309+
let newArgs: string[] = [];
310+
for (const { name, type } of optsWithDefaults.server.searchAttributes) {
311+
newArgs.push('--search-attribute');
312+
newArgs.push(`${name}=${TypedSearchAttributes.toMetadataType(type)}`);
313+
}
314+
newArgs = newArgs.concat(optsWithDefaults.server.extraArgs ?? []);
315+
optsWithDefaults.server.extraArgs = newArgs;
316+
}
317+
305318
const server = await Runtime.instance().createEphemeralServer(optsWithDefaults.server);
306319
const address = getEphemeralServerTarget(server);
307320

0 commit comments

Comments
 (0)