@@ -10,10 +10,17 @@ import {
10
10
ScheduleHandle ,
11
11
ScheduleSummary ,
12
12
ScheduleUpdateOptions ,
13
+ ScheduleDescription ,
13
14
} from '@temporalio/client' ;
14
15
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' ;
23
+ import { defaultSAKeys } from './helpers-integration' ;
17
24
18
25
export interface Context {
19
26
client : Client ;
@@ -168,9 +175,7 @@ if (RUN_INTEGRATION_TESTS) {
168
175
searchAttributes : {
169
176
CustomKeywordField : [ 'test-value2' ] ,
170
177
} ,
171
- typedSearchAttributes : new TypedSearchAttributes ( [
172
- { key : { name : 'CustomIntField' , type : SearchAttributeType . INT } , value : 42 } ,
173
- ] ) ,
178
+ typedSearchAttributes : new TypedSearchAttributes ( [ { key : defaultSAKeys . CustomIntField , value : 42 } ] ) ,
174
179
} ,
175
180
} ) ;
176
181
@@ -188,8 +193,8 @@ if (RUN_INTEGRATION_TESTS) {
188
193
t . deepEqual (
189
194
describedSchedule . action . typedSearchAttributes ,
190
195
new TypedSearchAttributes ( [
191
- { key : { name : 'CustomIntField' , type : SearchAttributeType . INT } , value : 42 } ,
192
- { key : { name : 'CustomKeywordField' , type : SearchAttributeType . KEYWORD } , value : 'test-value2' } ,
196
+ { key : defaultSAKeys . CustomIntField , value : 42 } ,
197
+ { key : defaultSAKeys . CustomKeywordField , value : 'test-value2' } ,
193
198
] )
194
199
) ;
195
200
} finally {
@@ -216,9 +221,7 @@ if (RUN_INTEGRATION_TESTS) {
216
221
searchAttributes : {
217
222
CustomKeywordField : [ 'test-value2' ] ,
218
223
} ,
219
- typedSearchAttributes : new TypedSearchAttributes ( [
220
- { key : { name : 'CustomIntField' , type : SearchAttributeType . INT } , value : 42 } ,
221
- ] ) ,
224
+ typedSearchAttributes : new TypedSearchAttributes ( [ { key : defaultSAKeys . CustomIntField , value : 42 } ] ) ,
222
225
} ,
223
226
} ) ;
224
227
@@ -237,8 +240,8 @@ if (RUN_INTEGRATION_TESTS) {
237
240
t . deepEqual (
238
241
describedSchedule . action . typedSearchAttributes ,
239
242
new TypedSearchAttributes ( [
240
- { key : { name : 'CustomIntField' , type : SearchAttributeType . INT } , value : 42 } ,
241
- { key : { name : 'CustomKeywordField' , type : SearchAttributeType . KEYWORD } , value : 'test-value2' } ,
243
+ { key : defaultSAKeys . CustomIntField , value : 42 } ,
244
+ { key : defaultSAKeys . CustomKeywordField , value : 'test-value2' } ,
242
245
] )
243
246
) ;
244
247
} finally {
@@ -351,9 +354,7 @@ if (RUN_INTEGRATION_TESTS) {
351
354
searchAttributes : {
352
355
CustomKeywordField : [ 'test-value2' ] ,
353
356
} ,
354
- typedSearchAttributes : new TypedSearchAttributes ( [
355
- { key : { name : 'CustomIntField' , type : SearchAttributeType . INT } , value : 42 } ,
356
- ] ) ,
357
+ typedSearchAttributes : new TypedSearchAttributes ( [ { key : defaultSAKeys . CustomIntField , value : 42 } ] ) ,
357
358
} ,
358
359
} ) ;
359
360
@@ -598,9 +599,7 @@ if (RUN_INTEGRATION_TESTS) {
598
599
taskQueue,
599
600
} ,
600
601
searchAttributes,
601
- typedSearchAttributes : new TypedSearchAttributes ( [
602
- { key : { name : 'CustomIntField' , type : SearchAttributeType . INT } , value : 42 } ,
603
- ] ) ,
602
+ typedSearchAttributes : new TypedSearchAttributes ( [ { key : defaultSAKeys . CustomIntField , value : 42 } ] ) ,
604
603
} )
605
604
) ;
606
605
}
@@ -759,6 +758,131 @@ if (RUN_INTEGRATION_TESTS) {
759
758
}
760
759
} ) ;
761
760
761
+ = === ===
762
+ test . serial ( 'Can update search attributes of a schedule' , async ( t ) => {
763
+ const { client } = t . context ;
764
+ const scheduleId = `can-update-search-attributes-of-schedule-${ randomUUID ( ) } ` ;
765
+
766
+ // Helper to wait for search attribute changes to propagate.
767
+ const waitForAttributeChange = async (
768
+ handle : ScheduleHandle ,
769
+ attributeName : string ,
770
+ shouldExist : boolean
771
+ ) : Promise < ScheduleDescription > => {
772
+ await waitUntil ( async ( ) => {
773
+ const desc = await handle . describe ( ) ;
774
+ const exists =
775
+ desc . typedSearchAttributes . getAll ( ) . find ( ( pair ) => pair . key . name === attributeName ) !== undefined ;
776
+ return exists === shouldExist ;
777
+ } , 300 ) ;
778
+ return await handle . describe ( ) ;
779
+ } ;
780
+
781
+ // Create a schedule with search attributes.
782
+ const handle = await client . schedule . create ( {
783
+ scheduleId,
784
+ spec : {
785
+ calendars : [ { hour : { start : 2 , end : 7 , step : 1 } } ] ,
786
+ } ,
787
+ action : {
788
+ type : 'startWorkflow' ,
789
+ workflowType : dummyWorkflow ,
790
+ taskQueue,
791
+ } ,
792
+ searchAttributes : {
793
+ CustomKeywordField : [ 'keyword-one' ] ,
794
+ } ,
795
+ typedSearchAttributes : [ { key : defineSearchAttributeKey ( 'CustomIntField' , SearchAttributeType . INT ) , value : 1 } ] ,
796
+ } ) ;
797
+
798
+ // Check the search attributes are part of the schedule description.
799
+ const desc = await handle . describe ( ) ;
800
+ // eslint-disable-next-line deprecation/deprecation
801
+ t . deepEqual ( desc . searchAttributes , {
802
+ CustomKeywordField : [ 'keyword-one' ] ,
803
+ CustomIntField : [ 1 ] ,
804
+ } ) ;
805
+ t . deepEqual (
806
+ desc . typedSearchAttributes ,
807
+ new TypedSearchAttributes ( [
808
+ { key : defineSearchAttributeKey ( 'CustomIntField' , SearchAttributeType . INT ) , value : 1 } ,
809
+ { key : defineSearchAttributeKey ( 'CustomKeywordField' , SearchAttributeType . KEYWORD ) , value : 'keyword-one' } ,
810
+ ] )
811
+ ) ;
812
+
813
+ // Perform a series of updates to schedule's search attributes.
814
+ try {
815
+ // Update existing search attributes, add new ones.
816
+ await handle . update ( ( desc ) => ( {
817
+ ...desc ,
818
+ searchAttributes : {
819
+ CustomKeywordField : [ 'keyword-two' ] ,
820
+ // Add a new search attribute.
821
+ CustomDoubleField : [ 1.5 ] ,
822
+ } ,
823
+ typedSearchAttributes : [
824
+ { key : defineSearchAttributeKey ( 'CustomIntField' , SearchAttributeType . INT ) , value : 2 } ,
825
+ // Add a new typed search attribute.
826
+ { key : defineSearchAttributeKey ( 'CustomTextField' , SearchAttributeType . TEXT ) , value : 'new-text' } ,
827
+ ] ,
828
+ } ) ) ;
829
+
830
+ let desc = await waitForAttributeChange ( handle , 'CustomTextField' , true ) ;
831
+ // eslint-disable-next-line deprecation/deprecation
832
+ t . deepEqual ( desc . searchAttributes , {
833
+ CustomKeywordField : [ 'keyword-two' ] ,
834
+ CustomIntField : [ 2 ] ,
835
+ CustomDoubleField : [ 1.5 ] ,
836
+ CustomTextField : [ 'new-text' ] ,
837
+ } ) ;
838
+ t . deepEqual (
839
+ desc . typedSearchAttributes ,
840
+ new TypedSearchAttributes ( [
841
+ { key : defineSearchAttributeKey ( 'CustomIntField' , SearchAttributeType . INT ) , value : 2 } ,
842
+ { key : defineSearchAttributeKey ( 'CustomKeywordField' , SearchAttributeType . KEYWORD ) , value : 'keyword-two' } ,
843
+ { key : defineSearchAttributeKey ( 'CustomTextField' , SearchAttributeType . TEXT ) , value : 'new-text' } ,
844
+ { key : defineSearchAttributeKey ( 'CustomDoubleField' , SearchAttributeType . DOUBLE ) , value : 1.5 } ,
845
+ ] )
846
+ ) ;
847
+
848
+ // Update and remove some search attributes. We remove a search attribute by omitting an existing key from the update.
849
+ await handle . update ( ( desc ) => ( {
850
+ ...desc ,
851
+ searchAttributes : {
852
+ CustomKeywordField : [ 'keyword-three' ] ,
853
+ } ,
854
+ typedSearchAttributes : [ { key : defineSearchAttributeKey ( 'CustomIntField' , SearchAttributeType . INT ) , value : 3 } ] ,
855
+ } ) ) ;
856
+
857
+ desc = await waitForAttributeChange ( handle , 'CustomTextField' , false ) ;
858
+ // eslint-disable-next-line deprecation/deprecation
859
+ t . deepEqual ( desc . searchAttributes , {
860
+ CustomKeywordField : [ 'keyword-three' ] ,
861
+ CustomIntField : [ 3 ] ,
862
+ } ) ;
863
+ t . deepEqual (
864
+ desc . typedSearchAttributes ,
865
+ new TypedSearchAttributes ( [
866
+ { key : defineSearchAttributeKey ( 'CustomIntField' , SearchAttributeType . INT ) , value : 3 } ,
867
+ { key : defineSearchAttributeKey ( 'CustomKeywordField' , SearchAttributeType . KEYWORD ) , value : 'keyword-three' } ,
868
+ ] )
869
+ ) ;
870
+
871
+ // Remove all search attributes.
872
+ await handle . update ( ( desc ) => ( {
873
+ ...desc ,
874
+ searchAttributes : { } ,
875
+ typedSearchAttributes : [ ] ,
876
+ } ) ) ;
877
+
878
+ desc = await waitForAttributeChange ( handle , 'CustomIntField' , false ) ;
879
+ t . deepEqual ( desc . searchAttributes , { } ) ; // eslint-disable-line deprecation/deprecation
880
+ t . deepEqual ( desc . typedSearchAttributes , new TypedSearchAttributes ( [ ] ) ) ;
881
+ } finally {
882
+ await handle . delete ( ) ;
883
+ }
884
+ } ) ;
885
+
762
886
test . serial ( 'User metadata on schedule' , async ( t ) => {
763
887
const { client } = t . context ;
764
888
const scheduleId = `schedule-with-user-metadata-${ randomUUID ( ) } ` ;
@@ -769,7 +893,7 @@ if (RUN_INTEGRATION_TESTS) {
769
893
type : 'startWorkflow' ,
770
894
workflowType : dummyWorkflow ,
771
895
taskQueue,
772
- staticSummary : 'schedule static summary' ,
896
+ staticSummary : 'schedule static summary' ,
773
897
staticDetails : 'schedule static details' ,
774
898
} ,
775
899
} ) ;
0 commit comments