@@ -495,6 +495,47 @@ fn default_response_validation() -> bool {
495495 true
496496}
497497
498+ /// Update the target context based on the context returned from the coprocessor.
499+ /// This function handles both updates/inserts and deletions:
500+ /// - Keys present in the returned context (with non-null values) are updated/inserted
501+ /// - Keys that were sent to the coprocessor but are missing from the returned context are deleted
502+ pub ( crate ) fn update_context_from_coprocessor (
503+ target_context : & Context ,
504+ context_returned : Context ,
505+ context_config : & ContextConf ,
506+ ) -> Result < ( ) , BoxError > {
507+ // Collect keys that are in the returned context
508+ let mut keys_returned = HashSet :: with_capacity ( context_returned. len ( ) ) ;
509+
510+ for ( mut key, value) in context_returned. try_into_iter ( ) ? {
511+ // Handle deprecated key names - convert back to actual key names
512+ if let ContextConf :: NewContextConf ( NewContextConf :: Deprecated ) = context_config {
513+ key = context_key_from_deprecated ( key) ;
514+ }
515+
516+ keys_returned. insert ( key. clone ( ) ) ;
517+ target_context. insert_json_value ( key, value) ;
518+ }
519+
520+ // Delete keys that were sent but are missing from the returned context
521+ // If the context config is selective, only delete keys that are in the selective list
522+ match context_config {
523+ ContextConf :: NewContextConf ( NewContextConf :: Selective ( context_keys) ) => {
524+ target_context. retain ( |key, _v| {
525+ if keys_returned. contains ( key) {
526+ return true ;
527+ } else if context_keys. contains ( key) {
528+ return false ;
529+ }
530+ true
531+ } ) ;
532+ }
533+ _ => target_context. retain ( |key, _v| keys_returned. contains ( key) ) ,
534+ }
535+
536+ Ok ( ( ) )
537+ }
538+
498539fn record_coprocessor_duration ( stage : PipelineStep , duration : Duration ) {
499540 f64_histogram ! (
500541 "apollo.router.operations.coprocessor.duration" ,
@@ -1025,16 +1066,7 @@ where
10251066 }
10261067
10271068 if let Some ( context) = co_processor_output. context {
1028- for ( mut key, value) in context. try_into_iter ( ) ? {
1029- if let ContextConf :: NewContextConf ( NewContextConf :: Deprecated ) =
1030- & response_config. context
1031- {
1032- key = context_key_from_deprecated ( key) ;
1033- }
1034- response
1035- . context
1036- . upsert_json_value ( key, move |_current| value) ;
1037- }
1069+ update_context_from_coprocessor ( & response. context , context, & response_config. context ) ?;
10381070 }
10391071
10401072 if let Some ( headers) = co_processor_output. headers {
@@ -1099,14 +1131,11 @@ where
10991131 } ;
11001132
11011133 if let Some ( context) = co_processor_output. context {
1102- for ( mut key, value) in context. try_into_iter ( ) ? {
1103- if let ContextConf :: NewContextConf ( NewContextConf :: Deprecated ) =
1104- & context_conf
1105- {
1106- key = context_key_from_deprecated ( key) ;
1107- }
1108- generator_map_context. upsert_json_value ( key, move |_current| value) ;
1109- }
1134+ update_context_from_coprocessor (
1135+ & generator_map_context,
1136+ context,
1137+ & context_conf,
1138+ ) ?;
11101139 }
11111140
11121141 // We return the final_bytes into our stream of response chunks
@@ -1368,16 +1397,7 @@ where
13681397 }
13691398
13701399 if let Some ( context) = co_processor_output. context {
1371- for ( mut key, value) in context. try_into_iter ( ) ? {
1372- if let ContextConf :: NewContextConf ( NewContextConf :: Deprecated ) =
1373- & response_config. context
1374- {
1375- key = context_key_from_deprecated ( key) ;
1376- }
1377- response
1378- . context
1379- . upsert_json_value ( key, move |_current| value) ;
1380- }
1400+ update_context_from_coprocessor ( & response. context , context, & response_config. context ) ?;
13811401 }
13821402
13831403 if let Some ( headers) = co_processor_output. headers {
0 commit comments