diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 5c974e64e8..043ec44edb 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -287,6 +287,8 @@ export function* addChildrenSaga( defaultConfig.widgetName, widgetNames, ); + // update the list of widget names for the next iteration + widgetNames.push(newWidgetName); widgets[child.widgetId] = { ...child, widgetName: newWidgetName, diff --git a/app/client/src/utils/WidgetPropsUtils.tsx b/app/client/src/utils/WidgetPropsUtils.tsx index 08259c7591..7bd58033f4 100644 --- a/app/client/src/utils/WidgetPropsUtils.tsx +++ b/app/client/src/utils/WidgetPropsUtils.tsx @@ -258,6 +258,26 @@ const dynamicPathListMigration = ( return currentDSL; }; +const canvasNameConflictMigration = ( + currentDSL: ContainerWidgetProps, + props = { counter: 1 }, +): ContainerWidgetProps => { + if ( + currentDSL.type === WidgetTypes.CANVAS_WIDGET && + currentDSL.widgetName.startsWith("Canvas") + ) { + currentDSL.widgetName = `Canvas${props.counter}`; + // Canvases inside tabs have `name` property as well + if (currentDSL.name) { + currentDSL.name = currentDSL.widgetName; + } + props.counter++; + } + currentDSL.children?.forEach((c) => canvasNameConflictMigration(c, props)); + + return currentDSL; +}; + // A rudimentary transform function which updates the DSL based on its version. // A more modular approach needs to be designed. const transformDSL = (currentDSL: ContainerWidgetProps) => { @@ -310,6 +330,11 @@ const transformDSL = (currentDSL: ContainerWidgetProps) => { currentDSL.version = 7; } + if (currentDSL.version === 7) { + currentDSL = canvasNameConflictMigration(currentDSL); + currentDSL.version = 8; + } + return currentDSL; }; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ReleaseNotesService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ReleaseNotesService.java index ba3a1c14f3..9cb3e71b19 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ReleaseNotesService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ReleaseNotesService.java @@ -2,23 +2,29 @@ package com.appsmith.server.solutions; import com.appsmith.server.configurations.CloudServicesConfig; import com.appsmith.server.dtos.ResponseDTO; +import com.appsmith.server.exceptions.AppsmithError; +import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.services.ConfigService; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; -import org.springframework.core.ParameterizedTypeReference; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpMethod; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; +import java.io.IOException; import java.time.Instant; import java.util.ArrayList; import java.util.List; @Service @RequiredArgsConstructor +@Slf4j public class ReleaseNotesService { private final CloudServicesConfig cloudServicesConfig; @@ -26,8 +32,11 @@ public class ReleaseNotesService { private final ConfigService configService; public final List releaseNodesCache = new ArrayList<>(); + private Instant cacheExpiryTime = null; + private final ObjectMapper objectMapper; + @Data static class Releases { private int totalCount; @@ -61,13 +70,37 @@ public class ReleaseNotesService { } return configService.getInstanceId() - .flatMap(instanceId -> WebClient - .create(baseUrl + "/api/v1/releases?instanceId=" + instanceId) - .get() - .exchange() - ) - .flatMap(response -> response.bodyToMono(new ParameterizedTypeReference>() {})) - .map(result -> result.getData().getNodes()) + .flatMap(instanceId -> { + WebClient.Builder webClientBuilder = WebClient.builder(); + return webClientBuilder + .baseUrl(baseUrl + "/api/v1/releases?instanceId=" + instanceId) + .build() + .method(HttpMethod.GET) + .exchange(); + }) + .doOnError(error -> log.error("Error fetching release notes from CS Server : {}", String.valueOf(error))) + // In case of an error in exchange with CS Server, stop processing further. + .onErrorResume(error -> Mono.empty()) + .flatMap(clientResponse -> clientResponse.toEntity(String.class)) + .map(response -> { + ResponseDTO releasesResponseDTO; + try { + releasesResponseDTO = objectMapper.readValue(response.toString(), ResponseDTO.class); + } catch (IOException e) { + return Mono.error(new AppsmithException(AppsmithError.JSON_PROCESSING_ERROR, e)); + } + return releasesResponseDTO; + }) + .flatMap(result -> { + // If valid response, cast and return the nodes + if (result.getClass().isAssignableFrom(ResponseDTO.class)) { + return Mono.just(((ResponseDTO)result).getData().getNodes()); + } else { + // An error was returned by the cloud service. Stop the processing without + // throwing an error. + return Mono.empty(); + } + }) .map(nodes -> { releaseNodesCache.clear(); releaseNodesCache.addAll(nodes);