Skip to content

Commit 2594115

Browse files
committed
Allow schedules to update search attributes
1 parent d9df892 commit 2594115

File tree

4 files changed

+140
-4
lines changed

4 files changed

+140
-4
lines changed

packages/client/src/schedule-client.ts

+6
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,12 @@ export class ScheduleClient extends BaseClient {
307307
},
308308
identity: this.options.identity,
309309
requestId: uuid4(),
310+
searchAttributes:
311+
opts.searchAttributes || opts.typedSearchAttributes // eslint-disable-line deprecation/deprecation
312+
? {
313+
indexedFields: encodeUnifiedSearchAttributes(opts.searchAttributes, opts.typedSearchAttributes), // eslint-disable-line deprecation/deprecation
314+
}
315+
: undefined,
310316
};
311317
try {
312318
return await this.workflowService.updateSchedule(req);

packages/client/src/schedule-types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ export type CompiledScheduleOptions = Replace<
142142
* The specification of an updated Schedule, as expected by {@link ScheduleHandle.update}.
143143
*/
144144
export type ScheduleUpdateOptions<A extends ScheduleOptionsAction = ScheduleOptionsAction> = Replace<
145-
Omit<ScheduleOptions, 'scheduleId' | 'memo' | 'searchAttributes' | 'typedSearchAttributes'>,
145+
Omit<ScheduleOptions, 'scheduleId' | 'memo'>,
146146
{
147147
action: A;
148148
state: Omit<ScheduleOptions['state'], 'triggerImmediately' | 'backfill'>;

packages/core-bridge/sdk-core

Submodule sdk-core updated 81 files

packages/test/src/test-schedules.ts

+132-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,16 @@ import {
1010
ScheduleHandle,
1111
ScheduleSummary,
1212
ScheduleUpdateOptions,
13+
ScheduleDescription,
1314
} from '@temporalio/client';
1415
import { msToNumber } from '@temporalio/common/lib/time';
15-
import { SearchAttributes, SearchAttributeType, TypedSearchAttributes } from '@temporalio/common';
16-
import { registerDefaultCustomSearchAttributes, RUN_INTEGRATION_TESTS } from './helpers';
16+
import {
17+
SearchAttributeType,
18+
SearchAttributes,
19+
TypedSearchAttributes,
20+
defineSearchAttributeKey,
21+
} from '@temporalio/common';
22+
import { registerDefaultCustomSearchAttributes, RUN_INTEGRATION_TESTS, waitUntil } from './helpers';
1723

1824
export interface Context {
1925
client: Client;
@@ -758,4 +764,128 @@ if (RUN_INTEGRATION_TESTS) {
758764
await handle.delete();
759765
}
760766
});
767+
768+
test.serial('Can update search attributes of a schedule', async (t) => {
769+
const { client } = t.context;
770+
const scheduleId = `can-update-search-attributes-of-schedule-${randomUUID()}`;
771+
772+
// Helper to wait for search attribute changes to propagate.
773+
const waitForAttributeChange = async (
774+
handle: ScheduleHandle,
775+
attributeName: string,
776+
shouldExist: boolean
777+
): Promise<ScheduleDescription> => {
778+
await waitUntil(async () => {
779+
const desc = await handle.describe();
780+
const exists =
781+
desc.typedSearchAttributes.getAll().find((pair) => pair.key.name === attributeName) !== undefined;
782+
return exists === shouldExist;
783+
}, 300);
784+
return await handle.describe();
785+
};
786+
787+
// Create a schedule with search attributes.
788+
const handle = await client.schedule.create({
789+
scheduleId,
790+
spec: {
791+
calendars: [{ hour: { start: 2, end: 7, step: 1 } }],
792+
},
793+
action: {
794+
type: 'startWorkflow',
795+
workflowType: dummyWorkflow,
796+
taskQueue,
797+
},
798+
searchAttributes: {
799+
CustomKeywordField: ['keyword-one'],
800+
},
801+
typedSearchAttributes: [{ key: defineSearchAttributeKey('CustomIntField', SearchAttributeType.INT), value: 1 }],
802+
});
803+
804+
// Check the search attributes are part of the schedule description.
805+
const desc = await handle.describe();
806+
// eslint-disable-next-line deprecation/deprecation
807+
t.deepEqual(desc.searchAttributes, {
808+
CustomKeywordField: ['keyword-one'],
809+
CustomIntField: [1],
810+
});
811+
t.deepEqual(
812+
desc.typedSearchAttributes,
813+
new TypedSearchAttributes([
814+
{ key: defineSearchAttributeKey('CustomIntField', SearchAttributeType.INT), value: 1 },
815+
{ key: defineSearchAttributeKey('CustomKeywordField', SearchAttributeType.KEYWORD), value: 'keyword-one' },
816+
])
817+
);
818+
819+
// Perform a series of updates to schedule's search attributes.
820+
try {
821+
// Update existing search attributes, add new ones.
822+
await handle.update((desc) => ({
823+
...desc,
824+
searchAttributes: {
825+
CustomKeywordField: ['keyword-two'],
826+
// Add a new search attribute.
827+
CustomDoubleField: [1.5],
828+
},
829+
typedSearchAttributes: [
830+
{ key: defineSearchAttributeKey('CustomIntField', SearchAttributeType.INT), value: 2 },
831+
// Add a new typed search attribute.
832+
{ key: defineSearchAttributeKey('CustomTextField', SearchAttributeType.TEXT), value: 'new-text' },
833+
],
834+
}));
835+
836+
let desc = await waitForAttributeChange(handle, 'CustomTextField', true);
837+
// eslint-disable-next-line deprecation/deprecation
838+
t.deepEqual(desc.searchAttributes, {
839+
CustomKeywordField: ['keyword-two'],
840+
CustomIntField: [2],
841+
CustomDoubleField: [1.5],
842+
CustomTextField: ['new-text'],
843+
});
844+
t.deepEqual(
845+
desc.typedSearchAttributes,
846+
new TypedSearchAttributes([
847+
{ key: defineSearchAttributeKey('CustomIntField', SearchAttributeType.INT), value: 2 },
848+
{ key: defineSearchAttributeKey('CustomKeywordField', SearchAttributeType.KEYWORD), value: 'keyword-two' },
849+
{ key: defineSearchAttributeKey('CustomTextField', SearchAttributeType.TEXT), value: 'new-text' },
850+
{ key: defineSearchAttributeKey('CustomDoubleField', SearchAttributeType.DOUBLE), value: 1.5 },
851+
])
852+
);
853+
854+
// Update and remove some search attributes. We remove a search attribute by omitting an existing key from the update.
855+
await handle.update((desc) => ({
856+
...desc,
857+
searchAttributes: {
858+
CustomKeywordField: ['keyword-three'],
859+
},
860+
typedSearchAttributes: [{ key: defineSearchAttributeKey('CustomIntField', SearchAttributeType.INT), value: 3 }],
861+
}));
862+
863+
desc = await waitForAttributeChange(handle, 'CustomTextField', false);
864+
// eslint-disable-next-line deprecation/deprecation
865+
t.deepEqual(desc.searchAttributes, {
866+
CustomKeywordField: ['keyword-three'],
867+
CustomIntField: [3],
868+
});
869+
t.deepEqual(
870+
desc.typedSearchAttributes,
871+
new TypedSearchAttributes([
872+
{ key: defineSearchAttributeKey('CustomIntField', SearchAttributeType.INT), value: 3 },
873+
{ key: defineSearchAttributeKey('CustomKeywordField', SearchAttributeType.KEYWORD), value: 'keyword-three' },
874+
])
875+
);
876+
877+
// Remove all search attributes.
878+
await handle.update((desc) => ({
879+
...desc,
880+
searchAttributes: {},
881+
typedSearchAttributes: [],
882+
}));
883+
884+
desc = await waitForAttributeChange(handle, 'CustomIntField', false);
885+
t.deepEqual(desc.searchAttributes, {}); // eslint-disable-line deprecation/deprecation
886+
t.deepEqual(desc.typedSearchAttributes, new TypedSearchAttributes([]));
887+
} finally {
888+
await handle.delete();
889+
}
890+
});
761891
}

0 commit comments

Comments
 (0)