diff --git a/src/main/java/snackscription/subscriptionadmin/config/AsyncConfiguration.java b/src/main/java/snackscription/subscriptionadmin/config/AsyncConfiguration.java index 7237d9a..0ea1381 100644 --- a/src/main/java/snackscription/subscriptionadmin/config/AsyncConfiguration.java +++ b/src/main/java/snackscription/subscriptionadmin/config/AsyncConfiguration.java @@ -1,22 +1,22 @@ package snackscription.subscriptionadmin.config; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -import java.util.concurrent.Executor; - @Configuration @EnableAsync -public class AsyncConfiguration { - @Bean("asyncTaskExecutor") - public Executor asyncTaskExecutor(){ - ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); - taskExecutor.setCorePoolSize(4); - taskExecutor.setQueueCapacity(150); - taskExecutor.setThreadNamePrefix("AsyncTaskThread-"); - taskExecutor.initialize(); - return taskExecutor; +public class AsyncConfiguration implements AsyncConfigurer { + @Override + public AsyncTaskExecutor getAsyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); + executor.setMaxPoolSize(10); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("Async-Executor-"); + executor.initialize(); + return executor; } -} +} \ No newline at end of file diff --git a/src/main/java/snackscription/subscriptionadmin/controller/AdminController.java b/src/main/java/snackscription/subscriptionadmin/controller/AdminController.java index 59f55c6..de7d02e 100644 --- a/src/main/java/snackscription/subscriptionadmin/controller/AdminController.java +++ b/src/main/java/snackscription/subscriptionadmin/controller/AdminController.java @@ -1,6 +1,5 @@ package snackscription.subscriptionadmin.controller; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import snackscription.subscriptionadmin.dto.AdminDTO; @@ -8,6 +7,9 @@ import snackscription.subscriptionadmin.service.AdminService; import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; @RestController @RequestMapping("/admin") @@ -20,32 +22,46 @@ public AdminController(AdminService adminService) { } @PostMapping("/create") - public ResponseEntity create(@RequestBody AdminDTO adminDTO) { - AdminSubscription adminSubscription = adminService.create(adminDTO); - return new ResponseEntity<>(adminSubscription, HttpStatus.CREATED); + public CompletableFuture> create(@RequestBody AdminDTO adminDTO) { + return adminService.create(adminDTO).thenApply(ResponseEntity::ok) + .exceptionally(ex -> ResponseEntity.badRequest().build()); + } @GetMapping("/list") - public ResponseEntity> findAll() { - List adminDTOList = adminService.findAll(); - return new ResponseEntity<>(adminDTOList, HttpStatus.OK); + public CompletableFuture>> findAll() { + return adminService.findAll().thenApply(ResponseEntity::ok); } @GetMapping("/{subscriptionId}") - public ResponseEntity findById(@PathVariable String subscriptionId) { - AdminDTO adminDTO = adminService.findById(subscriptionId); - return new ResponseEntity<>(adminDTO, HttpStatus.OK); + public CompletableFuture> findById(@PathVariable String subscriptionId) { + try { + UUID.fromString(subscriptionId); + } catch (IllegalArgumentException e) { + return CompletableFuture.completedFuture(ResponseEntity.badRequest().build()); + } + return adminService.findById(subscriptionId).thenApply(ResponseEntity::ok) + .exceptionally(ex -> ResponseEntity.notFound().build()); } @PutMapping("/update") - public ResponseEntity update(@RequestBody AdminDTO adminDTO) { - AdminSubscription adminSubscription = adminService.update(adminDTO); - return new ResponseEntity<>(adminSubscription, HttpStatus.OK); + public CompletableFuture> update(@RequestBody AdminDTO adminDTO) { + if (adminDTO.getSubscriptionId() == null) { + return CompletableFuture.completedFuture(ResponseEntity.badRequest().build()); + } + + return adminService.update(adminDTO).thenApply(ResponseEntity::ok) + .exceptionally(ex -> ResponseEntity.notFound().build()); } @DeleteMapping("/{subscriptionId}") - public ResponseEntity delete(@PathVariable String subscriptionId) { - adminService.delete(subscriptionId); - return new ResponseEntity<>(HttpStatus.NO_CONTENT); + public CompletableFuture> delete(@PathVariable String subscriptionId) { + try { + UUID.fromString(subscriptionId); + } catch (IllegalArgumentException e) { + return CompletableFuture.completedFuture(ResponseEntity.badRequest().build()); + } + return adminService.delete(subscriptionId).thenApply(deleted -> ResponseEntity.ok("DELETE SUCCESS")) + .exceptionally(ex -> ResponseEntity.notFound().build()); } } \ No newline at end of file diff --git a/src/main/java/snackscription/subscriptionadmin/dto/DTOMapper.java b/src/main/java/snackscription/subscriptionadmin/dto/DTOMapper.java index e73a195..b81c1db 100644 --- a/src/main/java/snackscription/subscriptionadmin/dto/DTOMapper.java +++ b/src/main/java/snackscription/subscriptionadmin/dto/DTOMapper.java @@ -10,14 +10,15 @@ public class DTOMapper { public static AdminDTO convertModelToDto(AdminSubscription adminSubscription) { - return new AdminDTO( - adminSubscription.getSubscriptionId(), - adminSubscription.getUniqueCode(), - adminSubscription.getSubscriberName(), - adminSubscription.getSubscriberId(), - adminSubscription.getSubscriptionBoxId(), - adminSubscription.getSubscriptionStatus() - ); + AdminDTO adminDTO = new AdminDTO(); + adminDTO.setSubscriptionId(adminSubscription.getSubscriptionId()); + adminDTO.setSubscriptionType(adminSubscription.getSubscriptionType()); + adminDTO.setSubscriberName(adminSubscription.getSubscriberName()); + adminDTO.setSubscriberId(adminSubscription.getSubscriberId()); + adminDTO.setSubscriptionBoxId(adminSubscription.getSubscriptionBoxId()); + adminDTO.setSubscriptionStatus(adminSubscription.getSubscriptionStatus()); + adminDTO.setUniqueCode(adminSubscription.getUniqueCode()); + return adminDTO; } public static AdminSubscription convertDTOtoModel(AdminDTO adminDTO) { diff --git a/src/main/java/snackscription/subscriptionadmin/service/AdminService.java b/src/main/java/snackscription/subscriptionadmin/service/AdminService.java index 48774b6..57641e7 100644 --- a/src/main/java/snackscription/subscriptionadmin/service/AdminService.java +++ b/src/main/java/snackscription/subscriptionadmin/service/AdminService.java @@ -3,12 +3,15 @@ import snackscription.subscriptionadmin.dto.AdminDTO; import snackscription.subscriptionadmin.model.AdminSubscription; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + import java.util.List; public interface AdminService { - AdminSubscription create(AdminDTO adminDTO); - List findAll(); - AdminDTO findById(String subscriptionId); - AdminSubscription update(AdminDTO adminDTO); - void delete(String subscriptionId); + CompletableFuture create(AdminDTO adminDTO); + CompletableFuture> findAll(); + CompletableFuture findById(String subscriptionId); + CompletableFuture update(AdminDTO adminDTO); + CompletableFuture delete(String subscriptionId); } \ No newline at end of file diff --git a/src/main/java/snackscription/subscriptionadmin/service/AdminServiceImpl.java b/src/main/java/snackscription/subscriptionadmin/service/AdminServiceImpl.java index 6085aaa..8e4d0eb 100644 --- a/src/main/java/snackscription/subscriptionadmin/service/AdminServiceImpl.java +++ b/src/main/java/snackscription/subscriptionadmin/service/AdminServiceImpl.java @@ -5,11 +5,15 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import snackscription.subscriptionadmin.dto.AdminDTO; +import org.springframework.scheduling.annotation.Async; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; import snackscription.subscriptionadmin.dto.DTOMapper; + @Service public class AdminServiceImpl implements AdminService{ @@ -17,54 +21,60 @@ public class AdminServiceImpl implements AdminService{ private AdminRepository adminRepository; @Override - public AdminSubscription create(AdminDTO adminDTO) { + @Async + public CompletableFuture create(AdminDTO adminDTO) { AdminSubscription adminSubscription = DTOMapper.convertDTOtoModel(adminDTO); - return adminRepository.create(adminSubscription); + return CompletableFuture.completedFuture(adminRepository.create(adminSubscription)); } @Override - public List findAll() { - return adminRepository.findAll() - .stream() + @Async + public CompletableFuture> findAll() { + List adminSubscriptions = adminRepository.findAll(); + List adminDtos = adminSubscriptions.stream() .map(DTOMapper::convertModelToDto) - .toList(); + .collect(Collectors.toList()); + return CompletableFuture.completedFuture(adminDtos); } @Override - public AdminDTO findById(String subscriptionId) { + @Async + public CompletableFuture findById(String subscriptionId) { if(subscriptionId == null || subscriptionId.isBlank()){ throw new IllegalArgumentException("ID cannot be null or empty"); } - AdminSubscription adminSubscription = adminRepository.findById(subscriptionId) + return adminRepository.findById(subscriptionId) + .map(DTOMapper::convertModelToDto) + .map(CompletableFuture::completedFuture) .orElseThrow(() -> new IllegalArgumentException("Subscription not found")); - - return DTOMapper.convertModelToDto(adminSubscription); } @Override - public AdminSubscription update(AdminDTO adminDTO) { + @Async + public CompletableFuture update(AdminDTO adminDTO) { if(adminDTO == null) { throw new IllegalArgumentException("AdminDTO cannot be null"); } - AdminSubscription adminSubscription = adminRepository.findById(adminDTO.getSubscriptionId()) - .orElseThrow(() -> new IllegalArgumentException("Subscription not found")); - - DTOMapper.updateAdminSubscription(adminSubscription, adminDTO); - return adminRepository.update(adminSubscription); + return adminRepository.findById(adminDTO.getSubscriptionId()). + map(adminSubscription -> { + DTOMapper.updateAdminSubscription(adminSubscription, adminDTO); + return CompletableFuture.completedFuture(adminRepository.update(adminSubscription)); + }).orElseThrow(() -> new IllegalArgumentException("Subscription not found")); } @Override - public void delete(String subscriptionId) { + @Async + public CompletableFuture delete(String subscriptionId) { if(subscriptionId == null || subscriptionId.isBlank()){ throw new IllegalArgumentException("ID cannot be null or empty"); } - if(adminRepository.findById(subscriptionId).isEmpty()){ + if (adminRepository.findById(subscriptionId).isEmpty()) { throw new IllegalArgumentException("Subscription not found"); } - adminRepository.delete(subscriptionId); + return CompletableFuture.completedFuture(null); } } \ No newline at end of file diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index ffe5daf..137369d 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -1,4 +1,4 @@ -spring.datasource.url=jdbc:postgresql://localhost:5432/subscription-admin/ +spring.datasource.url=jdbc:postgresql://postgres:5432/subscription-admin/ spring.datasource.username=postgres spring.datasource.password=postgres spring.jpa.hibernate.ddl-auto=create-drop diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties index 52b03e0..209fd70 100644 --- a/src/main/resources/application-prod.properties +++ b/src/main/resources/application-prod.properties @@ -1,4 +1,4 @@ -spring.datasource.url=jdbc:postgresql://aws-0-ap-southeast-1.pooler.supabase.com:5432/postgres?user=postgres.thnfvhxtvcbfxndlxdze&password=YouMakeStrayKidsStay +spring.datasource.url=jdbc:postgresql://aws-0-ap-southeast-1.pooler.supabase.com:5432/postgres spring.datasource.username=postgres.thnfvhxtvcbfxndlxdze spring.datasource.password=YouMakeStrayKidsStay spring.jpa.hibernate.ddl-auto=update diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties index 0a5230d..207a451 100644 --- a/src/main/resources/application-test.properties +++ b/src/main/resources/application-test.properties @@ -1,4 +1,4 @@ -spring.datasource.url=jdbc:postgresql://postgres:5432/subscription-admin +spring.datasource.url=jdbc:postgresql://postgres:5432/subscription-admin/ spring.datasource.username=postgres spring.datasource.password=postgres spring.jpa.hibernate.ddl-auto=create diff --git a/src/test/java/snackscription/subscriptionadmin/controller/AdminControllerTest.java b/src/test/java/snackscription/subscriptionadmin/controller/AdminControllerTest.java index 62b8345..1468c42 100644 --- a/src/test/java/snackscription/subscriptionadmin/controller/AdminControllerTest.java +++ b/src/test/java/snackscription/subscriptionadmin/controller/AdminControllerTest.java @@ -1,10 +1,9 @@ package snackscription.subscriptionadmin.controller; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import snackscription.subscriptionadmin.dto.AdminDTO; import snackscription.subscriptionadmin.model.AdminSubscription; @@ -12,8 +11,10 @@ import java.util.Collections; import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class AdminControllerTest { @@ -24,16 +25,79 @@ public class AdminControllerTest { @InjectMocks private AdminController adminController; - public AdminControllerTest() { + private AdminDTO adminDTO; + private AdminSubscription adminSubscription; + + @BeforeEach + void setUp(){ MockitoAnnotations.openMocks(this); + + adminDTO = new AdminDTO(); + adminDTO.setSubscriptionId("1"); + adminDTO.setSubscriptionType("MONTHLY"); + adminDTO.setSubscriberName("Stray Kids"); + adminDTO.setSubscriberId("0325"); + adminDTO.setSubscriptionBoxId("143ily"); + adminDTO.setSubscriptionStatus("PENDING"); + + adminSubscription = new AdminSubscription(); + adminSubscription.setSubscriptionId("1"); + adminSubscription.setSubscriptionType("MONTHLY"); + adminSubscription.setSubscriberName("Stray Kids"); + adminSubscription.setSubscriberId("0325"); + adminSubscription.setSubscriptionBoxId("143ily"); + adminSubscription.setSubscriptionStatus("PENDING"); + } + + @Test + void testCreate(){ + when(adminService.create(adminDTO)).thenReturn(CompletableFuture.completedFuture(adminSubscription)); + + CompletableFuture> response = adminController.create(adminDTO); + assertNotNull(response); + assertEquals(ResponseEntity.ok(adminSubscription), response.join()); } @Test - void testCreate() { - List adminDTOList = Collections.singletonList(new AdminDTO()); + void testFindAll(){ + List adminDTOList = Collections.singletonList(adminDTO); + when(adminService.findAll()).thenReturn(CompletableFuture.completedFuture(adminDTOList)); - when(adminService.findAll()).thenReturn(adminDTOList); + CompletableFuture>> response = adminController.findAll(); + assertNotNull(response); + assertEquals(ResponseEntity.ok(adminDTOList), response.join()); + } + + @Test + void testFindById(){ + String validUUID = "8a56e04b-d0c8-4e43-b2e0-fdf43e304d9e"; + adminDTO.setSubscriptionId(validUUID); + adminSubscription.setSubscriptionId(validUUID); + when(adminService.findById(validUUID)).thenReturn(CompletableFuture.completedFuture(adminDTO)); + + CompletableFuture> result = adminController.findById(validUUID); + + assertNotNull(result); + assertTrue(result.isDone()); + assertEquals(ResponseEntity.ok(adminDTO), result.join()); + } + + @Test + void testUpdate(){ + when(adminService.update(adminDTO)).thenReturn(CompletableFuture.completedFuture(adminSubscription)); + + CompletableFuture> response = adminController.update(adminDTO); + assertNotNull(response); + assertEquals(ResponseEntity.ok(adminSubscription), response.join()); + } + + @Test + void testDelete(){ + String validUUID = "8a56e04b-d0c8-4e43-b2e0-fdf43e304d9e"; + when(adminService.delete(validUUID)).thenReturn(CompletableFuture.completedFuture(null)); - ResponseEntity> responseEntity = adminController.findAll(); + CompletableFuture> response = adminController.delete(validUUID); + assertNotNull(response); + assertEquals(ResponseEntity.ok("DELETE SUCCESS"), response.join()); } } diff --git a/src/test/java/snackscription/subscriptionadmin/service/AdminServiceImplTest.java b/src/test/java/snackscription/subscriptionadmin/service/AdminServiceImplTest.java index cd95a40..e96829f 100644 --- a/src/test/java/snackscription/subscriptionadmin/service/AdminServiceImplTest.java +++ b/src/test/java/snackscription/subscriptionadmin/service/AdminServiceImplTest.java @@ -5,17 +5,23 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.ResponseEntity; +import snackscription.subscriptionadmin.controller.AdminController; import snackscription.subscriptionadmin.dto.AdminDTO; import snackscription.subscriptionadmin.factory.AdminSubscriptionFactory; import snackscription.subscriptionadmin.model.AdminSubscription; import snackscription.subscriptionadmin.repository.AdminRepository; -import java.util.Collections; import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +@ExtendWith(MockitoExtension.class) public class AdminServiceImplTest { @Mock @@ -26,6 +32,7 @@ public class AdminServiceImplTest { @InjectMocks private AdminServiceImpl adminService; + private AdminController adminController; private AdminSubscription adminSubscription; private AdminDTO adminDTO; @@ -49,39 +56,65 @@ void setUp() { adminSubscription.setSubscriberId("0325"); adminSubscription.setSubscriptionBoxId("143ily"); adminSubscription.setSubscriptionStatus("PENDING"); - - when(adminSubscriptionFactory.create(anyString(), anyString(), anyString(), anyString())).thenReturn(adminSubscription); } + @Test void testCreate() { when(adminRepository.create(any(AdminSubscription.class))).thenReturn(adminSubscription); - AdminSubscription result = adminService.create(adminDTO); + CompletableFuture result = adminService.create(adminDTO); assertNotNull(result); - assertEquals(adminSubscription, result); + assertTrue(result.isDone()); + assertEquals(adminSubscription, result.join()); } @Test void testFindAll() { - when(adminRepository.findAll()).thenReturn(Collections.singletonList(adminSubscription)); + List adminSubscriptions = List.of(adminSubscription); + when(adminRepository.findAll()).thenReturn(adminSubscriptions); - List result = adminService.findAll(); + CompletableFuture> result = adminService.findAll(); - assertEquals(1, result.size()); - assertEquals(adminSubscription.getSubscriptionId(), result.get(0).getSubscriptionId()); + assertNotNull(result); + assertTrue(result.isDone()); + assertEquals(List.of(adminDTO), result.join()); } @Test void testFindById() { - String subscriptionId = "1"; + String id = "1"; + when(adminRepository.findById(id)).thenReturn(Optional.of(adminSubscription)); + + CompletableFuture result = adminService.findById(id); + + assertNotNull(result); + assertTrue(result.isDone()); + assertEquals(adminDTO, result.join()); + } - when(adminRepository.findById(subscriptionId)).thenReturn(java.util.Optional.of(adminSubscription)); + @Test + void testUpdate() { + when(adminRepository.findById(adminDTO.getSubscriptionId())).thenReturn(Optional.of(adminSubscription)); + when(adminRepository.update(adminSubscription)).thenReturn(adminSubscription); + + CompletableFuture result = adminService.update(adminDTO); + + assertNotNull(result); + assertTrue(result.isDone()); + assertEquals(adminSubscription, result.join()); + } + + @Test + void testDelete() { + String id = "1"; + when(adminRepository.findById(id)).thenReturn(Optional.of(adminSubscription)); - AdminDTO result = adminService.findById(subscriptionId); + CompletableFuture result = adminService.delete(id); assertNotNull(result); - assertEquals(adminSubscription.getSubscriptionId(), result.getSubscriptionId()); + assertTrue(result.isDone()); + assertNull(result.join()); } }