Repository: appsmithorg/appsmith
File: app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutCollectionServiceCEImpl.java (Java)

Code before:
                                        .saveLastEditInformationInParent(actionCollectionDTO2)
                                        .name(SAVE_ACTION_COLLECTION_LAST_EDIT_INFO)
                                        .tap(Micrometer.observation(observationRegistry))
                                        .thenReturn(actionCollectionDTO2))))
                .flatMap(branchedActionCollection -> sendErrorReportsFromPageToCollection(branchedActionCollection));
    }

    @Override
    public Mono<ActionCollectionDTO> updateUnpublishedActionCollectionWithSpecificActions(String id, ActionCollectionUpdateDTO resource) {
        if (id == null) {
            return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ID));
        }

        Mono<ActionCollection> branchedActionCollectionMono = actionCollectionService
            .findById(id, actionPermission.getEditPermission())
            .switchIfEmpty(Mono.error(
                new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.ACTION_COLLECTION, id)))
            .cache();

        // create duplicate name map
        final Mono<List<String>> duplicateNamesMono = branchedActionCollectionMono
            .flatMap(actionCollection ->
                actionCollectionService.generateActionCollectionByViewMode(actionCollection, false))
            .flatMapMany(actionCollectionDTO -> Flux.fromIterable(actionCollectionDTO.getActions()))
            .filter(actionDTO -> actionDTO.getId() == null)
            .collect(Collectors.groupingBy(ActionDTO::getName, Collectors.counting()))
            .handle((actionNameCountMap, sink) -> {
                List<String> duplicateNames = actionNameCountMap.entrySet().stream()
                    .filter(entry -> entry.getValue() > 1)
                    .map(Map.Entry::getKey)
                    .collect(Collectors.toList());
                if (!duplicateNames.isEmpty()) {
                    sink.error(new AppsmithException(
                        AppsmithError.DUPLICATE_KEY_USER_ERROR, duplicateNames.get(0), FieldName.NAME));
                    return;
                }
                sink.next(duplicateNames);
            });
        ActionCollectionDTO actionCollectionDTO = resource.getActionCollection();
        ActionUpdatesDTO actionUpdatesDTO = resource.getActions();
        Mono<List<ActionDTO>> addedActionsMono = Mono.just(List.of());
        Mono<List<ActionDTO>> deletedActionsMono = Mono.just(List.of());
        Mono<List<ActionDTO>> modifiedActionsMono = Mono.just(List.of());

        if (actionUpdatesDTO.getAdded() != null && !actionUpdatesDTO.getAdded().isEmpty()) {
            addedActionsMono = duplicateNamesMono
                .zipWith(branchedActionCollectionMono)
                .flatMap(tuple2 -> {
                    List<String> duplicateNames = tuple2.getT1();
                    ActionCollection branchedActionCollection = tuple2.getT2();
                    return Flux.fromIterable(actionUpdatesDTO.getAdded())

Review comment (refactor):
It's checking for possible duplicates in the actions-to-be-added set. But what happens if `foo` appears once in the update list and is already in the DB?

Code after:
                                .flatMap(actionCollectionDTO2 -> actionCollectionService
                                        .saveLastEditInformationInParent(actionCollectionDTO2)
                                        .name(SAVE_ACTION_COLLECTION_LAST_EDIT_INFO)
                                        .tap(Micrometer.observation(observationRegistry))
                                        .thenReturn(actionCollectionDTO2))))
                .flatMap(branchedActionCollection -> sendErrorReportsFromPageToCollection(branchedActionCollection));
    }

    @Override
    public Mono<ActionCollectionDTO> updateUnpublishedActionCollectionWithSpecificActions(
            String id, ActionCollectionUpdateDTO resource) {
        if (id == null) {
            return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ID));
        }

        Mono<ActionCollection> branchedActionCollectionMono = actionCollectionService
                .findById(id, actionPermission.getEditPermission())
                .switchIfEmpty(Mono.error(
                        new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.ACTION_COLLECTION, id)))
                .cache();

        ActionCollectionDTO actionCollectionDTO = resource.getActionCollection();
        ActionUpdatesDTO actionUpdatesDTO = resource.getActions();
        Mono<List<ActionDTO>> addedActionsMono = Mono.just(List.of());
        Mono<List<ActionDTO>> deletedActionsMono = Mono.just(List.of());
        Mono<List<ActionDTO>> modifiedActionsMono = Mono.just(List.of());

        if (!CollectionUtils.isNullOrEmpty(actionUpdatesDTO.getAdded())) {
            // create duplicate name map
            final Mono<List<String>> duplicateNamesMono = Flux.fromIterable(actionUpdatesDTO.getAdded())
                    .collect(Collectors.groupingBy(ActionDTO::getName, Collectors.counting()))
                    .handle((actionNameCountMap, sink) -> {
                        List<String> duplicateNames = actionNameCountMap.entrySet().stream()
                                .filter(entry -> entry.getValue() > 1)
                                .map(Map.Entry::getKey)
                                .collect(Collectors.toList());
                        if (!duplicateNames.isEmpty()) {
                            sink.error(new AppsmithException(
                                    AppsmithError.DUPLICATE_KEY_USER_ERROR, duplicateNames.get(0), FieldName.NAME));
                            return;
                        }
                        sink.next(duplicateNames);
                    });
            addedActionsMono = duplicateNamesMono
                    .flatMap(ignored -> branchedActionCollectionMono)
                    .flatMap(branchedActionCollection -> {
                        return Flux.fromIterable(actionUpdatesDTO.getAdded())
                                .flatMap(actionDTO -> {
                                    populateActionFieldsFromCollection(actionDTO, branchedActionCollection);
                                    return layoutActionService
                                            .createSingleAction(actionDTO, Boolean.TRUE)