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