diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/MockDataServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/MockDataServiceImpl.java index 5fb62fb3c4..874741cf66 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/MockDataServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/MockDataServiceImpl.java @@ -15,6 +15,7 @@ import com.appsmith.server.dtos.ResponseDTO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.ParameterizedTypeReference; +import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.springframework.web.reactive.function.client.WebClient; @@ -90,7 +91,7 @@ public class MockDataServiceImpl implements MockDataService { datasource.setPluginId(mockDataSource.getPluginId()); datasource.setName(mockDataSource.getName().toUpperCase(Locale.ROOT)+" - Mock"); datasource.setDatasourceConfiguration(datasourceConfiguration); - return datasourceService.create(datasource); + return createSuffixedDatasource(datasource); }); } @@ -158,4 +159,29 @@ public class MockDataServiceImpl implements MockDataService { return datasourceConfiguration; } + private Mono createSuffixedDatasource(Datasource datasource) { + return createSuffixedDatasource(datasource, datasource.getName(), 0); + } + + /** + * Tries to create the given datasource with the name, over and over again with an incremented suffix, but **only** + * if the error is because of a name clash. + * @param datasource Datasource to try create. + * @param name Name of the datasource, to which numbered suffixes will be appended. + * @param suffix Suffix used for appending, recursion artifact. Usually set to 0. + * @return A Mono that yields the created datasource. + */ + private Mono createSuffixedDatasource(Datasource datasource, String name, int suffix) { + final String actualName = name + (suffix == 0 ? "" : " (" + suffix + ")"); + datasource.setName(actualName); + return datasourceService.create(datasource) + .onErrorResume(DuplicateKeyException.class, error -> { + if (error.getMessage() != null + && error.getMessage().contains("organization_datasource_deleted_compound_index")) { + return createSuffixedDatasource(datasource, name, 1 + suffix); + } + throw error; + }); + } + } diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/MockDataServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/MockDataServiceTest.java index 2e679f19ad..d7305e1869 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/MockDataServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/MockDataServiceTest.java @@ -3,6 +3,7 @@ package com.appsmith.server.services; import com.appsmith.external.models.DBAuth; import com.appsmith.external.models.Policy; import com.appsmith.server.acl.AclPermission; +import com.appsmith.server.domains.Datasource; import com.appsmith.server.domains.Organization; import com.appsmith.server.domains.Plugin; import com.appsmith.server.dtos.MockDataSource; @@ -96,7 +97,7 @@ public class MockDataServiceTest { .assertNext(createdDatasource -> { assertThat(createdDatasource.getId()).isNotEmpty(); assertThat(createdDatasource.getPluginId()).isEqualTo(pluginMono.getId()); - assertThat(createdDatasource.getName()).isEqualTo("MOVIES - Mock"); + assertThat(createdDatasource.getName()).isEqualTo("MOVIES - Mock (2)"); Policy manageDatasourcePolicy = Policy.builder().permission(MANAGE_DATASOURCES.getValue()) .users(Set.of("api_user")) .build(); @@ -154,4 +155,47 @@ public class MockDataServiceTest { }) .verifyComplete(); } + + @Test + @WithUserDetails(value = "api_user") + public void testCreateMockDataSetsDuplicateName() { + + Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor())); + + Plugin pluginMono = pluginService.findByName("Installed Plugin Name").block(); + MockDataSource mockDataSource = new MockDataSource(); + mockDataSource.setName("movies"); + mockDataSource.setOrganizationId(orgId); + mockDataSource.setPackageName("mongo-plugin"); + mockDataSource.setPluginId(pluginMono.getId()); + + Mono datasourceMono = mockDataService.createMockDataSet(mockDataSource) + .flatMap(datasource -> mockDataService.createMockDataSet(mockDataSource)); + + StepVerifier + .create(datasourceMono) + .assertNext(createdDatasource -> { + assertThat(createdDatasource.getId()).isNotEmpty(); + assertThat(createdDatasource.getPluginId()).isEqualTo(pluginMono.getId()); + assertThat(createdDatasource.getName()).isEqualTo("MOVIES - Mock (1)"); + Policy manageDatasourcePolicy = Policy.builder().permission(MANAGE_DATASOURCES.getValue()) + .users(Set.of("api_user")) + .build(); + Policy readDatasourcePolicy = Policy.builder().permission(READ_DATASOURCES.getValue()) + .users(Set.of("api_user")) + .build(); + Policy executeDatasourcePolicy = Policy.builder().permission(EXECUTE_DATASOURCES.getValue()) + .users(Set.of("api_user")) + .build(); + + DBAuth auth = (DBAuth) createdDatasource.getDatasourceConfiguration().getAuthentication(); + assertThat(createdDatasource.getPolicies()).isNotEmpty(); + assertThat(createdDatasource.getPolicies()).containsAll(Set.of(manageDatasourcePolicy, readDatasourcePolicy, executeDatasourcePolicy)); + assertThat(createdDatasource.getDatasourceConfiguration().getProperties().get(0).getValue()).isEqualTo("Yes"); + assertThat(createdDatasource.getDatasourceConfiguration().getProperties().get(0).getKey()).isEqualTo("Use Mongo Connection String URI"); + assertThat(auth.getDatabaseName()).isEqualTo("movies"); + assertThat(auth.getUsername()).isEqualTo("mockdb-admin"); + }) + .verifyComplete(); + } }