66import access .jira .JiraIssue ;
77import access .manage .ChangeRequest ;
88import access .manage .ConnectionProviderConverter ;
9+ import access .manage .ListMerger ;
910import access .manage .Manage ;
1011import access .manage .PathUpdateType ;
1112import access .manage .RequestType ;
1920import access .repository .ApplicationRepository ;
2021import access .repository .ConnectionRepository ;
2122import access .repository .UserRepository ;
23+ import com .fasterxml .jackson .databind .ObjectMapper ;
2224import io .swagger .v3 .oas .annotations .security .SecurityRequirement ;
2325import lombok .SneakyThrows ;
2426import org .apache .commons .logging .Log ;
4446
4547import java .time .Instant ;
4648import java .util .Collections ;
49+ import java .util .HashMap ;
4750import java .util .List ;
4851import java .util .Map ;
4952import java .util .Optional ;
53+ import java .util .stream .Collectors ;
5054
5155import static access .SwaggerOpenIdConfig .API_TOKENS_SCHEME_NAME ;
5256import static access .SwaggerOpenIdConfig .OPEN_ID_SCHEME_NAME ;
@@ -72,19 +76,22 @@ public class ConnectionController implements UserAccessRights {
7276 private final PasswordGenerator passwordGenerator = new PasswordGenerator ();
7377 private final List <CharacterRule > rules = initPasswordGeneratorRules ();
7478 private final ConnectionProviderConverter connectionProviderConverter ;
79+ private final ObjectMapper objectMapper ;
7580
7681 public ConnectionController (ConnectionRepository connectionRepository ,
7782 ApplicationRepository applicationRepository ,
7883 UserRepository userRepository ,
7984 Manage manage ,
8085 JiraClient jiraClient ,
81- ConnectionProviderConverter connectionProviderConverter ) {
86+ ConnectionProviderConverter connectionProviderConverter ,
87+ ObjectMapper objectMapper ) {
8288 this .connectionRepository = connectionRepository ;
8389 this .applicationRepository = applicationRepository ;
8490 this .userRepository = userRepository ;
8591 this .manage = manage ;
8692 this .jiraClient = jiraClient ;
8793 this .connectionProviderConverter = connectionProviderConverter ;
94+ this .objectMapper = objectMapper ;
8895 }
8996
9097 private List <CharacterRule > initPasswordGeneratorRules () {
@@ -322,11 +329,42 @@ private Connection productionReadyChangeRequests(Connection connection, User use
322329 //Now we need to ensure that previous change requests, with the same pathUpdate and value a List, does not overwrite changes
323330 //And therefore we don't create a new change request, but update the existing one
324331 Map <String , Object > existingChangeRequest = existingChangeRequests .getFirst ();
325- ChangeRequest changeRequest = changeRequestOptional .get ();
326- existingChangeRequest .get ("pathUpdates" );
327- //TODO , add / override all new pathUpdates, except if the value is a List, then sort out the difference
328-
329-
332+ ChangeRequest newChangeRequest = changeRequestOptional .get ();
333+ Map <String , Object > existingPathUpdates = (Map <String , Object >) existingChangeRequest .get ("pathUpdates" );
334+ Map <String , Object > newPathUpdates = newChangeRequest .getPathUpdates ();
335+ newPathUpdates .forEach ((key , value ) -> {
336+ if (key .equals ("arp" ) && existingPathUpdates .containsKey (key )) {
337+ //three way merge on the attributes and profile, motivation from the latest change
338+ Map <String , Object > attributes = (Map <String , Object >) ((Map <String , Object >)value ).get ("attributes" );
339+ List <String > attibuteNames = attributes .keySet ().stream ().toList ();
340+
341+ Map <String , Object > arpPath = (Map <String , Object >) existingPathUpdates .get ("arp" );
342+ Map <String , Object > pathAttributes = (Map <String , Object >) arpPath .get ("attributes" );
343+ List <String > pathValues = pathAttributes .keySet ().stream ().toList ();
344+
345+ Map <String , Object > baseArp = (Map <String , Object >) getData (provider ).get ("arp" );
346+ Map <String , Object > baseAttributes = (Map <String , Object >) baseArp .get ("attributes" );
347+ List <String > baseValues = baseAttributes .keySet ().stream ().toList ();
348+
349+ List <String > newValues = ListMerger .threeWayMerge (baseValues , pathValues , attibuteNames );
350+ //Now we need to construct a new attributes Map with all the values from the three attributes Map
351+ Map <String , Object > newAttributes = newValues .stream ().collect (Collectors .toMap (
352+ attrName -> attrName ,
353+ attrName -> attributes .getOrDefault (attrName , pathAttributes .getOrDefault (attrName , baseAttributes .get (attrName )))));
354+ arpPath .put ("attributes" , newAttributes );
355+ } else if (value instanceof List && existingPathUpdates .containsKey (key )) {
356+ //three way merge
357+ List <String > pathUpdateValue = (List <String >) existingPathUpdates .get (key );
358+ List <String > base = (List <String >) getMetaDataFields (getData (provider )).get (key .substring (key .indexOf ("." ) + 1 ));
359+ List <String > newValues = ListMerger .threeWayMerge (base , pathUpdateValue , (List <String >) value );
360+ existingPathUpdates .put (key , newValues );
361+ } else {
362+ //simply override
363+ existingPathUpdates .put (key , value );
364+ }
365+ });
366+ ChangeRequest changeRequest = objectMapper .convertValue (existingChangeRequest , ChangeRequest .class );
367+ manage .updateChangeRequest (Environment .PROD , changeRequest );
330368 }
331369 }
332370
0 commit comments