diff --git a/endpoint-insights-api/pom.xml b/endpoint-insights-api/pom.xml
index 92a3067..7e9d679 100644
--- a/endpoint-insights-api/pom.xml
+++ b/endpoint-insights-api/pom.xml
@@ -100,7 +100,26 @@
3.5.7
-
+
+ org.mapstruct
+ mapstruct
+ 1.5.5.Final
+
+
+
+ org.mapstruct
+ mapstruct-processor
+ 1.5.5.Final
+ provided
+
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ 0.2.0
+
+
+
@@ -164,6 +183,16 @@
lombok
1.18.42
+
+ org.mapstruct
+ mapstruct-processor
+ 1.5.5.Final
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ 0.2.0
+
diff --git a/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/controller/BatchesController.java b/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/controller/BatchesController.java
index a998f25..3b46bfc 100644
--- a/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/controller/BatchesController.java
+++ b/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/controller/BatchesController.java
@@ -2,14 +2,16 @@
import com.vsp.endpointinsightsapi.dto.BatchRequestDTO;
import com.vsp.endpointinsightsapi.dto.BatchResponseDTO;
-import com.vsp.endpointinsightsapi.model.TestBatch;
import com.vsp.endpointinsightsapi.service.BatchService;
+import com.vsp.endpointinsightsapi.model.TestBatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
+import java.time.LocalDate;
+import java.util.Collections;
import java.util.List;
import java.util.UUID;
@@ -17,30 +19,45 @@
@RequestMapping("/api/batches")
public class BatchesController {
- private static final Logger LOG = LoggerFactory.getLogger(BatchesController.class);
+ private static final Logger LOG = LoggerFactory.getLogger(BatchesController.class);
- private final BatchService batchService;
+ private final BatchService batchService;
- public BatchesController(BatchService batchService) {
- this.batchService = batchService;
- }
+ public BatchesController(BatchService batchService){
+ this.batchService = batchService;
+ }
- // GET /api/batches — stub list (unchanged)
+ // GET /api/batches — stub list (unchanged)
@GetMapping
public ResponseEntity> listBatches() {
- List batches = List.of(
- new BatchResponseDTO(1L, "Daily API Tests", "ACTIVE"),
- new BatchResponseDTO(2L, "Weekly Regression", "INACTIVE")
- );
- return ResponseEntity.ok(batches);
+ List batches = List.of(
+ BatchResponseDTO.builder()
+ .id(UUID.randomUUID())
+ .batchName("Daily API Tests")
+ .scheduleId(334523453L)
+ .startTime(LocalDate.now().minusDays(1))
+ .lastTimeRun(LocalDate.now())
+ .active(true)
+// .jobs(Collections.emptyList())
+ .build(),
+ BatchResponseDTO.builder()
+ .id(UUID.randomUUID())
+ .batchName("Weekly Regression")
+ .scheduleId(42L)
+ .startTime(LocalDate.now().minusWeeks(1))
+ .lastTimeRun(LocalDate.now().minusDays(3))
+ .active(false)
+// .jobs(Collections.emptyList())
+ .build()
+ );
+ return ResponseEntity.ok(batches);
}
// GET /api/batches/{id}
@GetMapping("/{id}")
- public ResponseEntity getBatch(@PathVariable UUID id) {
- return batchService.getBatchById(id)
- .map(b -> ResponseEntity.ok(b))
- .orElseGet(() -> ResponseEntity.notFound().build());
+ public ResponseEntity getBatch(@PathVariable UUID id) {
+ BatchResponseDTO batch = batchService.getBatchById(id);
+ return ResponseEntity.ok(batch);
}
// POST /api/batches
@@ -52,15 +69,20 @@ public ResponseEntity createBatch(@RequestBody BatchRequestDTO reques
// PUT /api/batches/{id} — stubbed
@PutMapping("/{id}")
- public ResponseEntity updateBatch(@PathVariable Long id,
- @RequestBody BatchRequestDTO request) {
- BatchResponseDTO updated = new BatchResponseDTO(id, request.getName(), "UPDATED");
- return ResponseEntity.ok(updated);
+ public ResponseEntity updateBatch(@PathVariable UUID id, @RequestBody BatchRequestDTO request) {
+ BatchResponseDTO updated = BatchResponseDTO.builder()
+ .id(id)
+ .batchName(request.getName())
+ .lastTimeRun(LocalDate.now())
+ .build();
+
+ return ResponseEntity.ok(updated);
}
- // DELETE /api/batches/{id} — stubbed
+ // DELETE /api/batches/{id}
@DeleteMapping("/{id}")
- public ResponseEntity deleteBatch(@PathVariable Long id) {
- return ResponseEntity.noContent().build();
+ public ResponseEntity deleteBatch(@PathVariable UUID id) {
+ batchService.deleteBatchById(id);
+ return ResponseEntity.noContent().build();
}
}
diff --git a/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/dto/BatchResponseDTO.java b/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/dto/BatchResponseDTO.java
index 5e5ebea..0057393 100644
--- a/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/dto/BatchResponseDTO.java
+++ b/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/dto/BatchResponseDTO.java
@@ -1,17 +1,23 @@
package com.vsp.endpointinsightsapi.dto;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
+import com.vsp.endpointinsightsapi.model.Job;
+import lombok.*;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.UUID;
@Getter
@Setter
+@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BatchResponseDTO {
- private Long id;
- private String name;
- private String status;
+ private UUID id;
+ private String batchName;
+ private Long scheduleId;
+ private LocalDate startTime;
+ private LocalDate lastTimeRun;
+ private Boolean active;
}
diff --git a/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/exception/BatchNotFoundException.java b/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/exception/BatchNotFoundException.java
new file mode 100644
index 0000000..e0c40b6
--- /dev/null
+++ b/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/exception/BatchNotFoundException.java
@@ -0,0 +1,14 @@
+package com.vsp.endpointinsightsapi.exception;
+
+import org.springframework.http.HttpStatus;
+
+/**
+ * Thrown when a batch with the given ID does not exist in the database.
+ */
+public class BatchNotFoundException extends CustomException {
+
+ public BatchNotFoundException(String batchId) {
+ super(HttpStatus.NOT_FOUND,
+ new ErrorResponse("BATCH_NOT_FOUND", "Batch not found with ID: " + batchId, null));
+ }
+}
diff --git a/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/mapper/BatchMapper.java b/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/mapper/BatchMapper.java
new file mode 100644
index 0000000..72ad45b
--- /dev/null
+++ b/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/mapper/BatchMapper.java
@@ -0,0 +1,14 @@
+package com.vsp.endpointinsightsapi.mapper;
+
+import com.vsp.endpointinsightsapi.dto.BatchResponseDTO;
+import com.vsp.endpointinsightsapi.model.TestBatch;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+@Mapper(componentModel = "spring")
+public interface BatchMapper {
+
+ // MapStruct will generate the implementation automatically
+ @Mapping(source = "batch_id", target = "id")
+ BatchResponseDTO toDto(TestBatch entity);
+}
diff --git a/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/repository/TestBatchRepository.java b/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/repository/TestBatchRepository.java
index 29fc734..4559307 100644
--- a/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/repository/TestBatchRepository.java
+++ b/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/repository/TestBatchRepository.java
@@ -2,16 +2,10 @@
import com.vsp.endpointinsightsapi.model.TestBatch;
import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
-import java.util.Optional;
import java.util.UUID;
-
@Repository
public interface TestBatchRepository extends JpaRepository {
- @Query("SELECT t FROM TestBatch t where t.batch_id = :id")
- Optional findById(@Param("id")UUID id);
-}
+}
\ No newline at end of file
diff --git a/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/service/BatchService.java b/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/service/BatchService.java
index 99810fb..f2d7c43 100644
--- a/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/service/BatchService.java
+++ b/endpoint-insights-api/src/main/java/com/vsp/endpointinsightsapi/service/BatchService.java
@@ -1,7 +1,9 @@
package com.vsp.endpointinsightsapi.service;
+import com.vsp.endpointinsightsapi.dto.BatchResponseDTO;
+import com.vsp.endpointinsightsapi.exception.BatchNotFoundException;
+import com.vsp.endpointinsightsapi.mapper.BatchMapper;
import java.util.List;
-import java.util.Optional;
import java.util.UUID;
import com.vsp.endpointinsightsapi.dto.BatchRequestDTO;
@@ -12,23 +14,42 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
+import java.util.Objects;
@Service
public class BatchService {
private static final Logger LOG = LoggerFactory.getLogger(BatchService.class);
-
- private final TestBatchRepository batchRepository;
+ private final TestBatchRepository testBatchRepository;
+ private final BatchMapper batchMapper;
private final JobRepository jobRepository;
- public BatchService(TestBatchRepository batchRepository, JobRepository jobRepository) {
- this.batchRepository = batchRepository;
- this.jobRepository = jobRepository;
+ public BatchService(TestBatchRepository testBatchRepository, BatchMapper batchMapper, JobRepository jobRepository) {
+ this.testBatchRepository = Objects.requireNonNull(testBatchRepository, "testBatchRepository must not be null");
+ this.batchMapper = Objects.requireNonNull(batchMapper, "batchMapper must not be null");
+ this.jobRepository = Objects.requireNonNull(jobRepository, "jobRepository must not be null");
+ }
+
+ //Get Batch — used by GET /api/batches/{id}
+ public BatchResponseDTO getBatchById(UUID batchId) {
+ TestBatch b = testBatchRepository.findById(batchId)
+ .orElseThrow(() -> {
+ LOG.debug("Batch {} not found", batchId);
+ return new BatchNotFoundException(batchId.toString());
+ });
+ return batchMapper.toDto(b);
}
- //GET by id — used by BatchesController and its unit test
- public Optional getBatchById(UUID id) {
- return batchRepository.findById(id);
+ //Delete Batch — used by DELETE /api/batches/{id}
+ public void deleteBatchById(UUID batchId) {
+ if (!testBatchRepository.existsById(batchId)) {
+ LOG.debug("Batch {} not found", batchId);
+ throw new BatchNotFoundException(batchId.toString());
+ }
+
+ testBatchRepository.deleteById(batchId);
+ LOG.info("Deleted batch {}", batchId);
+
}
//Create Batch — used by POST /api/batches
@@ -43,6 +64,6 @@ public TestBatch createBatch(BatchRequestDTO request) {
}
batch.setJobs(jobs);
}
- return batchRepository.save(batch);
+ return testBatchRepository.save(batch);
}
}
diff --git a/endpoint-insights-api/src/main/resources/application.properties b/endpoint-insights-api/src/main/resources/application.properties
deleted file mode 100644
index f764801..0000000
--- a/endpoint-insights-api/src/main/resources/application.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-spring:
- application:
- name: endpoint-insights-api
diff --git a/endpoint-insights-api/src/test/java/com/vsp/endpointinsightsapi/controller/BatchesControllerUnitTest.java b/endpoint-insights-api/src/test/java/com/vsp/endpointinsightsapi/controller/BatchesControllerUnitTest.java
index fc2705e..bc8b01a 100644
--- a/endpoint-insights-api/src/test/java/com/vsp/endpointinsightsapi/controller/BatchesControllerUnitTest.java
+++ b/endpoint-insights-api/src/test/java/com/vsp/endpointinsightsapi/controller/BatchesControllerUnitTest.java
@@ -4,6 +4,8 @@
import com.vsp.endpointinsightsapi.dto.BatchRequestDTO;
import com.vsp.endpointinsightsapi.model.TestBatch;
import com.vsp.endpointinsightsapi.service.BatchService;
+import com.vsp.endpointinsightsapi.dto.BatchResponseDTO;
+import com.vsp.endpointinsightsapi.exception.BatchNotFoundException;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
@@ -19,6 +21,7 @@
import java.util.UUID;
import static org.hamcrest.Matchers.*;
+import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@@ -39,33 +42,40 @@ void shouldReturnListOfBatches() throws Exception {
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(greaterThan(0))))
.andExpect(jsonPath("$[0].id", notNullValue()))
- .andExpect(jsonPath("$[0].name", not(emptyString())))
- .andExpect(jsonPath("$[0].status", not(emptyString())));
+ .andExpect(jsonPath("$[0].batchName", not(emptyString())))
+ .andExpect(jsonPath("$[0].scheduleId", notNullValue()))
+ .andExpect(jsonPath("$[0].active", anyOf(is(true), is(false))));
}
@Test
void shouldReturnBatchById() throws Exception {
UUID id = UUID.randomUUID();
- TestBatch batch = new TestBatch(id, null, "Example Batch",
- 1L, LocalDate.now(), LocalDate.now(), true);
- Mockito.when(batchService.getBatchById(any(UUID.class)))
- .thenReturn(Optional.of(batch));
+ BatchResponseDTO dto = BatchResponseDTO.builder()
+ .id(id)
+ .batchName("Daily API Tests")
+ .scheduleId(1001L)
+ .startTime(LocalDate.parse("2025-11-08"))
+ .lastTimeRun(LocalDate.parse("2025-11-09"))
+ .active(true)
+ .build();
- mockMvc.perform(get("/api/batches/" + id))
+ when(batchService.getBatchById(id)).thenReturn(dto);
+
+ mockMvc.perform(get("/api/batches/{id}", id))
.andExpect(status().isOk())
- .andExpect(jsonPath("$.batchName", is("Example Batch")))
- .andExpect(jsonPath("$.active", is(true)));
+ .andExpect(jsonPath("$.id").value(id.toString()))
+ .andExpect(jsonPath("$.batchName").value("Daily API Tests"))
+ .andExpect(jsonPath("$.scheduleId").value(1001))
+ .andExpect(jsonPath("$.active").value(true));
}
@Test
- void shouldReturnNotFoundWhenBatchMissing() throws Exception {
+ void getBatchById_notFound_shouldReturn404() throws Exception {
UUID id = UUID.randomUUID();
+ when(batchService.getBatchById(id)).thenThrow(new BatchNotFoundException(id.toString()));
- Mockito.when(batchService.getBatchById(any(UUID.class)))
- .thenReturn(Optional.empty());
-
- mockMvc.perform(get("/api/batches/" + id))
+ mockMvc.perform(get("/api/batches/{id}", id))
.andExpect(status().isNotFound());
}
@@ -89,18 +99,35 @@ void shouldCreateBatch() throws Exception {
void shouldUpdateBatch() throws Exception {
BatchRequestDTO request = new BatchRequestDTO("Updated Batch", Collections.emptyList());
- mockMvc.perform(put("/api/batches/2")
+ UUID id = UUID.randomUUID();
+
+ mockMvc.perform(put("/api/batches/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk())
- .andExpect(jsonPath("$.id", is(2)))
- .andExpect(jsonPath("$.name", is("Updated Batch")))
- .andExpect(jsonPath("$.status", is("UPDATED")));
+ .andExpect(jsonPath("$.id", is(id.toString())))
+ .andExpect(jsonPath("$.batchName", is("Updated Batch")));
}
@Test
void shouldDeleteBatch() throws Exception {
- mockMvc.perform(delete("/api/batches/1"))
+ UUID id = UUID.randomUUID();
+
+ org.mockito.Mockito.doNothing()
+ .when(batchService).deleteBatchById(id);
+
+ mockMvc.perform(delete("/api/batches/{id}", id))
.andExpect(status().isNoContent());
}
+
+ @Test
+ void shouldReturn404WhenDeletingNonexistentBatch() throws Exception {
+ UUID id = UUID.randomUUID();
+
+ org.mockito.Mockito.doThrow(new BatchNotFoundException(id.toString()))
+ .when(batchService).deleteBatchById(id);
+
+ mockMvc.perform(delete("/api/batches/{id}", id))
+ .andExpect(status().isNotFound());
+ }
}
diff --git a/endpoint-insights-api/src/test/java/com/vsp/endpointinsightsapi/service/BatchServiceTest.java b/endpoint-insights-api/src/test/java/com/vsp/endpointinsightsapi/service/BatchServiceTest.java
index 64cd927..1951368 100644
--- a/endpoint-insights-api/src/test/java/com/vsp/endpointinsightsapi/service/BatchServiceTest.java
+++ b/endpoint-insights-api/src/test/java/com/vsp/endpointinsightsapi/service/BatchServiceTest.java
@@ -1,59 +1,89 @@
+// BatchServiceTest.java
package com.vsp.endpointinsightsapi.service;
+import com.vsp.endpointinsightsapi.dto.BatchResponseDTO;
+import com.vsp.endpointinsightsapi.exception.BatchNotFoundException;
+import com.vsp.endpointinsightsapi.mapper.BatchMapper;
import com.vsp.endpointinsightsapi.model.TestBatch;
import com.vsp.endpointinsightsapi.repository.JobRepository;
import com.vsp.endpointinsightsapi.repository.TestBatchRepository;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import java.time.LocalDate;
import java.util.Optional;
import java.util.UUID;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+@ExtendWith(MockitoExtension.class)
class BatchServiceTest {
- private TestBatchRepository testBatchRepository;
- private JobRepository jobRepository;
- private BatchService batchService;
-
- @BeforeEach
- void setUp() {
- testBatchRepository = Mockito.mock(TestBatchRepository.class);
- jobRepository = Mockito.mock(JobRepository.class);
- batchService = new BatchService(testBatchRepository, jobRepository);
- }
+ @Mock TestBatchRepository testBatchRepository;
+ @Mock BatchMapper batchMapper;
+ @InjectMocks BatchService batchService;
+ @Mock JobRepository jobRepository;
@Test
- void getBatchById_shouldReturnBatchWhenFound() {
+ void getBatchById_returnsDto() {
UUID id = UUID.randomUUID();
- TestBatch mockBatch = new TestBatch();
- mockBatch.setBatch_id(id);
- mockBatch.setBatchName("Sample Batch");
- mockBatch.setActive(true);
+ TestBatch entity = new TestBatch();
+ // adjust getters/setters to your model
+ entity.setBatch_id(id);
+ entity.setBatchName("Example");
+ entity.setScheduleId(1001L);
+ entity.setStartTime(LocalDate.parse("2025-11-08"));
+ entity.setLastTimeRun(LocalDate.parse("2025-11-09"));
+ entity.setActive(true);
+
+ when(testBatchRepository.findById(id)).thenReturn(Optional.of(entity));
- when(testBatchRepository.findById(any(UUID.class)))
- .thenReturn(Optional.of(mockBatch));
+ BatchResponseDTO dto = BatchResponseDTO.builder()
+ .id(id)
+ .batchName("Example")
+ .scheduleId(1001L)
+ .startTime(LocalDate.parse("2025-11-08"))
+ .lastTimeRun(LocalDate.parse("2025-11-09"))
+ .active(true)
+ .build();
+ when(batchMapper.toDto(entity)).thenReturn(dto);
+
+ BatchResponseDTO out = batchService.getBatchById(id);
+
+ assertEquals(id, out.getId());
+ assertEquals("Example", out.getBatchName());
+ verify(testBatchRepository).findById(id);
+ verify(batchMapper).toDto(entity);
+ }
- Optional result = batchService.getBatchById(id);
+ @Test
+ void getBatchById_notFound_throws() {
+ UUID id = UUID.randomUUID();
+ when(testBatchRepository.findById(id)).thenReturn(Optional.empty());
- assertThat(result).isPresent();
- assertThat(result.get().getBatch_id()).isEqualTo(id);
- assertThat(result.get().getBatchName()).isEqualTo("Sample Batch");
+ assertThrows(BatchNotFoundException.class, () -> batchService.getBatchById(id));
}
@Test
- void getBatchById_shouldReturnEmptyWhenNotFound() {
+ void deleteBatchById_Exists() {
UUID id = UUID.randomUUID();
+ when(testBatchRepository.existsById(id)).thenReturn(true);
+
+ batchService.deleteBatchById(id);
- when(testBatchRepository.findById(any(UUID.class)))
- .thenReturn(Optional.empty());
+ verify(testBatchRepository).deleteById(id);
+ }
- Optional result = batchService.getBatchById(id);
+ @Test
+ void deleteBatchById_NotFound() {
+ UUID id = UUID.randomUUID();
+ when(testBatchRepository.existsById(id)).thenReturn(false);
- assertThat(result).isEmpty();
+ assertThrows(BatchNotFoundException.class, () -> batchService.deleteBatchById(id));
}
}