diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index c434f63..737b346 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -29,6 +29,3 @@ jobs: - name: Build with Gradle run: ./gradlew clean build - - - name: Test with Gradle - run: ./gradlew clean test diff --git a/app/src/main/java/env/service/App.java b/app/src/main/java/env/service/App.java index 420ee32..1bfaec0 100644 --- a/app/src/main/java/env/service/App.java +++ b/app/src/main/java/env/service/App.java @@ -16,9 +16,11 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; @Slf4j @SpringBootApplication +@EnableScheduling public class App { public static void main(final String[] args) { diff --git a/app/src/main/java/env/service/app/controller/EnvDetailsController.java b/app/src/main/java/env/service/app/controller/EnvDetailsController.java index ca02ed5..c027b22 100644 --- a/app/src/main/java/env/service/app/controller/EnvDetailsController.java +++ b/app/src/main/java/env/service/app/controller/EnvDetailsController.java @@ -13,6 +13,7 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.DeleteMapping; @@ -32,6 +33,20 @@ public class EnvDetailsController { private final MongoTemplate mongoTemplate; + @Scheduled(cron = "0 0 22 * * *") + void cleanupEmptyCollections() { + try { + for (String collectionName : mongoTemplate.getCollectionNames()) { + if (mongoTemplate.getCollection(collectionName).countDocuments() == 0) { + mongoTemplate.dropCollection(collectionName); + log.info("Cleaned Up Empty Collection: [{}]", collectionName); + } + } + } catch (Exception ex) { + log.error("Cleanup Empty Collections Exception", ex); + } + } + @GetMapping("/appNames") public ResponseEntity getAllAppNames() { try { @@ -53,6 +68,26 @@ public ResponseEntity getAllAppNames() { } } + @DeleteMapping("/appNames") + public ResponseEntity deleteEmptyAppNames() { + try { + for (String collectionName : mongoTemplate.getCollectionNames()) { + if (mongoTemplate.getCollection(collectionName).countDocuments() == 0) { + mongoTemplate.dropCollection(collectionName); + log.info("Dropped Empty Collection: [{}]", collectionName); + } + } + return ResponseEntity.ok().build(); + } catch (Exception ex) { + log.error("Delete Empty App Names Exception", ex); + return ResponseEntity.internalServerError() + .body( + EnvDetailsResponse.builder() + .errMsg("Delete Empty App Names Exception: " + ex.getMessage()) + .build()); + } + } + @PostMapping("/{appName}") public ResponseEntity create( @PathVariable final String appName, @RequestBody final EnvDetails envDetails) { diff --git a/app/src/test/java/env/service/app/controller/EnvDetailsControllerTest.java b/app/src/test/java/env/service/app/controller/EnvDetailsControllerTest.java index 9f90b6c..c906321 100644 --- a/app/src/test/java/env/service/app/controller/EnvDetailsControllerTest.java +++ b/app/src/test/java/env/service/app/controller/EnvDetailsControllerTest.java @@ -7,6 +7,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -16,6 +19,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.mongodb.MongoInternalException; +import com.mongodb.client.MongoCollection; import com.mongodb.client.result.DeleteResult; import env.service.app.config.TestSecurityConfig; import env.service.app.model.EnvDetails; @@ -29,9 +33,7 @@ import java.util.Set; import org.bson.types.ObjectId; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -52,7 +54,6 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("springboottest") @Import({TestSecurityConfig.class}) -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @EnableAutoConfiguration(exclude = MongoAutoConfiguration.class) @AutoConfigureMockMvc public class EnvDetailsControllerTest { @@ -161,6 +162,69 @@ void test_GetAllAppNames_Failure_Exception() throws Exception { "Look App Names Exception: Mongo Internal Exception", envDetailsResponse.getErrMsg()); } + @Test + void test_DeleteEmptyAppNames_Success() throws Exception { + when(mongoTemplate.getCollectionNames()).thenReturn(Set.of("app_one", "app_two", "app_three")); + when(mongoTemplate.getCollection("app_one")).thenReturn(mock(MongoCollection.class)); + when(mongoTemplate.getCollection("app_two")).thenReturn(mock(MongoCollection.class)); + when(mongoTemplate.getCollection("app_three")).thenReturn(mock(MongoCollection.class)); + when(mongoTemplate.getCollection("app_one").countDocuments()).thenReturn(0L); + when(mongoTemplate.getCollection("app_two").countDocuments()).thenReturn(1L); + when(mongoTemplate.getCollection("app_three").countDocuments()).thenReturn(2L); + + mockMvc + .perform( + delete("/api/v1/appNames") + .with( + SecurityMockMvcRequestPostProcessors.httpBasic( + ConstantUtils.AUTH_USR, ConstantUtils.AUTH_PWD))) + .andExpect(status().isOk()) + .andReturn(); + + verify(mongoTemplate).dropCollection("app_one"); + verify(mongoTemplate, never()).dropCollection("app_two"); + verify(mongoTemplate, never()).dropCollection("app_three"); + } + + @Test + void test_DeleteEmptyAppNames_Failure_Unauthorized() throws Exception { + mockMvc + .perform( + delete("/api/v1/appNames") + .with( + SecurityMockMvcRequestPostProcessors.httpBasic( + ConstantUtils.AUTH_USR, "invalid_password"))) + .andExpect(status().isUnauthorized()) + .andReturn(); + } + + @Test + void test_DeleteEmptyAppNames_Failure_Exception() throws Exception { + when(mongoTemplate.getCollectionNames()) + .thenThrow(new MongoInternalException("Mongo Internal Exception")); + + MvcResult mvcResult = + mockMvc + .perform( + delete("/api/v1/appNames") + .with( + SecurityMockMvcRequestPostProcessors.httpBasic( + ConstantUtils.AUTH_USR, ConstantUtils.AUTH_PWD))) + .andExpect(status().isInternalServerError()) + .andReturn(); + + EnvDetailsResponse envDetailsResponse = + objectMapper() + .readValue(mvcResult.getResponse().getContentAsString(), EnvDetailsResponse.class); + + assertNotNull(envDetailsResponse); + assertNull(envDetailsResponse.getEnvDetails()); + assertNotNull(envDetailsResponse.getErrMsg()); + assertEquals( + "Delete Empty App Names Exception: Mongo Internal Exception", + envDetailsResponse.getErrMsg()); + } + @Test void test_Create_Success() throws Exception { when(mongoTemplate.save(any(EnvDetails.class), eq("app_" + TEST_COLLECTION_NAME)))