Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import java.util.concurrent.CompletableFuture;
import lombok.RequiredArgsConstructor;
import org.openapitools.api.LampsApi;
import org.openapitools.model.Error;
import org.openapitools.exception.LampNotFoundException;
import org.openapitools.model.Lamp;
import org.openapitools.model.LampCreate;
import org.openapitools.model.LampUpdate;
Expand All @@ -32,49 +32,31 @@ public CompletableFuture<ResponseEntity<Lamp>> createLamp(final LampCreate lampC
lamp.setStatus(lampCreate.getStatus());
final Lamp created = lampService.create(lamp);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
});
},
Runnable::run);
}

@Override
@SuppressWarnings("unchecked")
public CompletableFuture<ResponseEntity<Void>> deleteLamp(final String lampId) {
return CompletableFuture.supplyAsync(
() -> {
try {
final UUID lampUuid = UUID.fromString(lampId);
final boolean deleted = lampService.delete(lampUuid);
if (deleted) {
return ResponseEntity.noContent().<Void>build();
} else {
return ResponseEntity.notFound().<Void>build();
}
} catch (IllegalArgumentException e) {
final Error error = new Error("INVALID_ARGUMENT");
return (ResponseEntity<Void>)
(ResponseEntity<?>) ResponseEntity.badRequest().body(error);
}
});
final UUID lampUuid = UUID.fromString(lampId);
lampService.delete(lampUuid);
return ResponseEntity.noContent().<Void>build();
},
Runnable::run);
}

@Override
@SuppressWarnings("unchecked")
public CompletableFuture<ResponseEntity<Lamp>> getLamp(final String lampId) {
return CompletableFuture.supplyAsync(
() -> {
try {
final UUID lampUuid = UUID.fromString(lampId);
final Lamp lamp = lampService.findById(lampUuid);
if (lamp != null) {
return ResponseEntity.ok().body(lamp);
} else {
return ResponseEntity.notFound().build();
}
} catch (IllegalArgumentException e) {
final Error error = new Error("INVALID_ARGUMENT");
return (ResponseEntity<Lamp>)
(ResponseEntity<?>) ResponseEntity.badRequest().body(error);
}
});
final UUID lampUuid = UUID.fromString(lampId);
final Lamp lamp =
lampService.findById(lampUuid).orElseThrow(() -> new LampNotFoundException(lampUuid));
return ResponseEntity.ok().body(lamp);
},
Runnable::run);
}

@Override
Expand All @@ -87,30 +69,21 @@ public CompletableFuture<ResponseEntity<ListLamps200Response>> listLamps(
response.setData(lamps);
response.setHasMore(false);
return ResponseEntity.ok().body(response);
});
},
Runnable::run);
}

@Override
@SuppressWarnings("unchecked")
public CompletableFuture<ResponseEntity<Lamp>> updateLamp(
final String lampId, final LampUpdate lampUpdate) {
return CompletableFuture.supplyAsync(
() -> {
try {
final UUID lampUuid = UUID.fromString(lampId);
final Lamp lampData = new Lamp();
lampData.setStatus(lampUpdate.getStatus());
final Lamp updated = lampService.update(lampUuid, lampData);
if (updated != null) {
return ResponseEntity.ok().body(updated);
} else {
return ResponseEntity.notFound().build();
}
} catch (IllegalArgumentException e) {
final Error error = new Error("INVALID_ARGUMENT");
return (ResponseEntity<Lamp>)
(ResponseEntity<?>) ResponseEntity.badRequest().body(error);
}
});
final UUID lampUuid = UUID.fromString(lampId);
final Lamp lampData = new Lamp();
lampData.setStatus(lampUpdate.getStatus());
final Lamp updated = lampService.update(lampUuid, lampData);
return ResponseEntity.ok().body(updated);
},
Runnable::run);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@ public ResponseEntity<Error> handleNullPointerException(final NullPointerExcepti
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}

/**
* Handle lamp not found exceptions.
*
* @param ex the lamp not found exception
* @return 404 Not Found
*/
@ExceptionHandler(LampNotFoundException.class)
public ResponseEntity<Void> handleLampNotFoundException(final LampNotFoundException ex) {
return ResponseEntity.notFound().build();
}

/**
* Handle illegal argument exceptions (e.g., invalid UUID format).
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.openapitools.exception;

import java.io.Serial;
import java.util.UUID;

/**
* Exception thrown when a lamp with a given ID cannot be found. This is a domain-level exception
* that maps to HTTP 404 responses.
*/
public class LampNotFoundException extends RuntimeException {

@Serial private static final long serialVersionUID = 1L;

private final UUID lampId;

public LampNotFoundException(final UUID lampId) {
super("Lamp not found: " + lampId);
this.lampId = lampId;
}

public UUID getLampId() {
return lampId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.openapitools.config.OnNoDatabaseUrlCondition;
import org.openapitools.entity.LampEntity;
import org.openapitools.repository.LampRepository;
Expand Down Expand Up @@ -129,15 +128,15 @@ public List<LampEntity> findByStatus(final Boolean isOn) {
return lamps.values().stream()
.filter(lamp -> lamp.getDeletedAt() == null)
.filter(lamp -> lamp.getStatus().equals(isOn))
.collect(Collectors.toList());
.toList();
}

@Override
public List<LampEntity> findAllActive() {
return lamps.values().stream()
.filter(lamp -> lamp.getDeletedAt() == null)
.sorted(Comparator.comparing(LampEntity::getCreatedAt))
.collect(Collectors.toList());
.toList();
}

@Override
Expand Down
37 changes: 17 additions & 20 deletions src/java/src/main/java/org/openapitools/service/LampService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

import java.time.OffsetDateTime;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.openapitools.entity.LampEntity;
import org.openapitools.exception.LampNotFoundException;
import org.openapitools.mapper.LampMapper;
import org.openapitools.model.Lamp;
import org.openapitools.repository.LampRepository;
Expand Down Expand Up @@ -57,10 +58,10 @@ public Lamp create(final Lamp lamp) {
* Find a lamp by its ID.
*
* @param id the lamp ID
* @return the lamp if found, null otherwise
* @return optional containing the lamp if found, empty otherwise
*/
public Lamp findById(final UUID id) {
return repository.findById(id).map(mapper::toModel).orElse(null);
public Optional<Lamp> findById(final UUID id) {
return repository.findById(id).map(mapper::toModel);
}

/**
Expand All @@ -75,7 +76,7 @@ public List<Lamp> findAll(final int offset, final int limit) {
PageRequest.of(offset / limit, limit, Sort.by(Sort.Direction.ASC, "createdAt"));

final Page<LampEntity> page = repository.findAll(pageable);
return page.getContent().stream().map(mapper::toModel).collect(Collectors.toList());
return page.getContent().stream().map(mapper::toModel).toList();
}

/**
Expand All @@ -84,7 +85,7 @@ public List<Lamp> findAll(final int offset, final int limit) {
* @return list of all active lamps ordered by creation time
*/
public List<Lamp> findAllActive() {
return repository.findAllActive().stream().map(mapper::toModel).collect(Collectors.toList());
return repository.findAllActive().stream().map(mapper::toModel).toList();
}

/**
Expand All @@ -94,7 +95,7 @@ public List<Lamp> findAllActive() {
* @return list of lamps with the specified status
*/
public List<Lamp> findByStatus(final Boolean isOn) {
return repository.findByStatus(isOn).stream().map(mapper::toModel).collect(Collectors.toList());
return repository.findByStatus(isOn).stream().map(mapper::toModel).toList();
}

/**
Expand All @@ -111,7 +112,8 @@ public long countActive() {
*
* @param id the lamp ID
* @param lamp the updated lamp data
* @return the updated lamp if found, null otherwise
* @return the updated lamp
* @throws LampNotFoundException if no lamp exists with the given ID
*/
@Transactional
public Lamp update(final UUID id, final Lamp lamp) {
Expand All @@ -123,7 +125,7 @@ public Lamp update(final UUID id, final Lamp lamp) {
// updatedAt is automatically set by @UpdateTimestamp
return mapper.toModel(repository.save(entity));
})
.orElse(null);
.orElseThrow(() -> new LampNotFoundException(id));
}

/**
Expand All @@ -133,18 +135,13 @@ public Lamp update(final UUID id, final Lamp lamp) {
* LampEntity.
*
* @param id the lamp ID to delete
* @return true if the lamp was found and deleted, false otherwise
* @throws LampNotFoundException if no lamp exists with the given ID
*/
@Transactional
public boolean delete(final UUID id) {
return repository
.findById(id)
.map(
entity -> {
entity.setDeletedAt(OffsetDateTime.now());
repository.save(entity);
return true;
})
.orElse(false);
public void delete(final UUID id) {
final LampEntity entity =
repository.findById(id).orElseThrow(() -> new LampNotFoundException(id));
entity.setDeletedAt(OffsetDateTime.now());
repository.save(entity);
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package org.openapitools.controller;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openapitools.exception.LampNotFoundException;
import org.openapitools.model.Lamp;
import org.openapitools.model.LampCreate;
import org.openapitools.model.LampUpdate;
Expand Down Expand Up @@ -65,7 +68,7 @@ void listLamps_ShouldReturnAllLamps() throws Exception {
@Test
void getLamp_WithValidId_ShouldReturnLamp() throws Exception {
// Given
when(lampService.findById(testLampId)).thenReturn(testLamp);
when(lampService.findById(testLampId)).thenReturn(Optional.of(testLamp));

// When & Then
MvcResult result =
Expand All @@ -85,7 +88,7 @@ void getLamp_WithValidId_ShouldReturnLamp() throws Exception {
@Test
void getLamp_WithNonExistentId_ShouldReturn404() throws Exception {
// Given
when(lampService.findById(testLampId)).thenReturn(null);
when(lampService.findById(testLampId)).thenReturn(Optional.empty());

// When & Then
MvcResult result =
Expand Down Expand Up @@ -172,7 +175,8 @@ void updateLamp_WithNonExistentId_ShouldReturn404() throws Exception {
LampUpdate lampUpdate = new LampUpdate();
lampUpdate.setStatus(false);

when(lampService.update(any(UUID.class), any(Lamp.class))).thenReturn(null);
when(lampService.update(any(UUID.class), any(Lamp.class)))
.thenThrow(new LampNotFoundException(testLampId));

// When & Then
MvcResult result =
Expand All @@ -190,8 +194,7 @@ void updateLamp_WithNonExistentId_ShouldReturn404() throws Exception {

@Test
void deleteLamp_WithValidId_ShouldDelete() throws Exception {
// Given
when(lampService.delete(testLampId)).thenReturn(true);
// Given — delete is void, no mock setup needed

// When & Then
MvcResult result =
Expand All @@ -206,7 +209,7 @@ void deleteLamp_WithValidId_ShouldDelete() throws Exception {
@Test
void deleteLamp_WithNonExistentId_ShouldReturn404() throws Exception {
// Given
when(lampService.delete(testLampId)).thenReturn(false);
doThrow(new LampNotFoundException(testLampId)).when(lampService).delete(testLampId);

// When & Then
MvcResult result =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.util.UUID;
import org.junit.jupiter.api.Test;
import org.openapitools.model.Error;
import org.springframework.http.HttpStatus;
Expand All @@ -23,6 +24,16 @@ void handleNullPointerException_ShouldReturnBadRequest() {
assertThat(response.getBody().getError()).isEqualTo("INVALID_ARGUMENT");
}

@Test
void handleLampNotFoundException_ShouldReturnNotFound() {
LampNotFoundException ex = new LampNotFoundException(UUID.randomUUID());

ResponseEntity<Void> response = handler.handleLampNotFoundException(ex);

assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
assertThat(response.getBody()).isNull();
}

@Test
void handleIllegalArgumentException_ShouldReturnBadRequest() {
IllegalArgumentException ex = new IllegalArgumentException("bad argument");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.openapitools.exception;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.UUID;
import org.junit.jupiter.api.Test;

/** Unit tests for LampNotFoundException. */
class LampNotFoundExceptionTest {

@Test
void shouldContainLampIdInMessage() {
final UUID lampId = UUID.randomUUID();

final LampNotFoundException ex = new LampNotFoundException(lampId);

assertThat(ex.getMessage()).contains(lampId.toString());
assertThat(ex.getLampId()).isEqualTo(lampId);
}
}
Loading