From e82b14e90abe113b07064762636ffcd7c3a6aa67 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Wed, 30 Oct 2024 15:49:33 +0300 Subject: [PATCH 01/25] Add db changelog for location --- .../resources/db/changelog/changelog-v2.xml | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/main/resources/db/changelog/changelog-v2.xml diff --git a/src/main/resources/db/changelog/changelog-v2.xml b/src/main/resources/db/changelog/changelog-v2.xml new file mode 100644 index 0000000..1da15d0 --- /dev/null +++ b/src/main/resources/db/changelog/changelog-v2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 554658f79c90801ef07f4ef5d0cebd8ffece7b4e Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Wed, 30 Oct 2024 15:51:51 +0300 Subject: [PATCH 02/25] Add location entity class --- .../cameraonboarding/entity/Location.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/main/java/com/onboarding/camera/cameraonboarding/entity/Location.java diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/entity/Location.java b/src/main/java/com/onboarding/camera/cameraonboarding/entity/Location.java new file mode 100644 index 0000000..607b2d5 --- /dev/null +++ b/src/main/java/com/onboarding/camera/cameraonboarding/entity/Location.java @@ -0,0 +1,42 @@ +package com.onboarding.camera.cameraonboarding.entity; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import lombok.Data; +import lombok.ToString; +import org.hibernate.annotations.UuidGenerator; + +import java.util.UUID; + +@Data +@ToString +@Entity +@Table(name = "location") +public class Location { + + @Id + @GeneratedValue + @UuidGenerator + @Column(name = "location_id") + private UUID locationId; + + @Column(name = "latitude", nullable = false) + private Double latitude; + + @Column(name = "longitude", nullable = false) + private Double longitude; + + @Column(name = "address", nullable = false) + private String address; + + @OneToOne + @JoinColumn(name = "camera_id", referencedColumnName = "cam_id") + @JsonBackReference + private Camera camera; +} From b1ba4d0d53f3886da43ee733f75db779da45d4d7 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Wed, 30 Oct 2024 15:52:29 +0300 Subject: [PATCH 03/25] Add dto for location object --- .../camera/cameraonboarding/dto/LocationDto.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationDto.java diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationDto.java b/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationDto.java new file mode 100644 index 0000000..961c3d6 --- /dev/null +++ b/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationDto.java @@ -0,0 +1,11 @@ +package com.onboarding.camera.cameraonboarding.dto; + +import lombok.Data; + +@Data +public class LocationDto { + + private double latitude; + private double longitude; + private String address; +} From 3fac938ea135a64a96ea00a815b5d28fb6648c06 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Wed, 30 Oct 2024 15:54:31 +0300 Subject: [PATCH 04/25] Add custom exception for location --- .../exception/LocationNotAddedException.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/com/onboarding/camera/cameraonboarding/exception/LocationNotAddedException.java diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/exception/LocationNotAddedException.java b/src/main/java/com/onboarding/camera/cameraonboarding/exception/LocationNotAddedException.java new file mode 100644 index 0000000..38b79c7 --- /dev/null +++ b/src/main/java/com/onboarding/camera/cameraonboarding/exception/LocationNotAddedException.java @@ -0,0 +1,7 @@ +package com.onboarding.camera.cameraonboarding.exception; + +public class LocationNotAddedException extends RuntimeException { + public LocationNotAddedException(String message) { + super(message); + } +} From 7e47269cd3f8fb5a3d6db8789a44361473992246 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Wed, 30 Oct 2024 15:55:03 +0300 Subject: [PATCH 05/25] Add location not added handling method --- .../controller/GlobalExceptionHandler.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/controller/GlobalExceptionHandler.java b/src/main/java/com/onboarding/camera/cameraonboarding/controller/GlobalExceptionHandler.java index 936dfaf..30bd025 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/controller/GlobalExceptionHandler.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/controller/GlobalExceptionHandler.java @@ -9,6 +9,7 @@ import com.onboarding.camera.cameraonboarding.exception.ImageNotDownloadedException; import com.onboarding.camera.cameraonboarding.exception.ImageNotFoundException; import com.onboarding.camera.cameraonboarding.exception.ImageNotUploadedException; +import com.onboarding.camera.cameraonboarding.exception.LocationNotAddedException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -153,5 +154,17 @@ public ResponseEntity handleImageNotDownloadedException(ImageNotD return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); } + + @ExceptionHandler(LocationNotAddedException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ResponseBody + public ResponseEntity handleLocationNotAddedException(LocationNotAddedException ex) { + ErrorResponse errorResponse = new ErrorResponse(); + + errorResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); + errorResponse.setMessage(ex.getMessage()); + + return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); + } } From b801398b1de181b644f4d668e32a8c6b29668320 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Wed, 30 Oct 2024 15:55:41 +0300 Subject: [PATCH 06/25] Include changelog-v2 --- src/main/resources/db/changelog/changelog-master.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/resources/db/changelog/changelog-master.xml b/src/main/resources/db/changelog/changelog-master.xml index 651cba4..fa0bdc8 100644 --- a/src/main/resources/db/changelog/changelog-master.xml +++ b/src/main/resources/db/changelog/changelog-master.xml @@ -3,8 +3,9 @@ xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog - http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"> + https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"> + \ No newline at end of file From 7a9a8bb15eb2e6caf6b3387d2f70838f9903616a Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Wed, 30 Oct 2024 15:56:37 +0300 Subject: [PATCH 07/25] Add location relation --- .../onboarding/camera/cameraonboarding/entity/Camera.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/entity/Camera.java b/src/main/java/com/onboarding/camera/cameraonboarding/entity/Camera.java index dc4f30d..2cd3d98 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/entity/Camera.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/entity/Camera.java @@ -1,9 +1,12 @@ package com.onboarding.camera.cameraonboarding.entity; +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import lombok.Data; import lombok.ToString; @@ -47,4 +50,8 @@ public class Camera { @Column(name = "initialized_at") private LocalDateTime initializedAt; + + @OneToOne(mappedBy = "camera", cascade = CascadeType.ALL, orphanRemoval = true) + @JsonManagedReference + private Location location; } From 9ab51ef404f809f4fcd10e5fa6c7cd3fe6a83aed Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Wed, 30 Oct 2024 15:57:29 +0300 Subject: [PATCH 08/25] Add location rest endpoint --- .../controller/CameraRestController.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/controller/CameraRestController.java b/src/main/java/com/onboarding/camera/cameraonboarding/controller/CameraRestController.java index 6ce1de5..622838a 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/controller/CameraRestController.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/controller/CameraRestController.java @@ -3,6 +3,7 @@ import com.onboarding.camera.cameraonboarding.converter.CameraDtoConverter; import com.onboarding.camera.cameraonboarding.dto.CameraDto; import com.onboarding.camera.cameraonboarding.dto.CameraResponse; +import com.onboarding.camera.cameraonboarding.dto.LocationDto; import com.onboarding.camera.cameraonboarding.entity.Camera; import com.onboarding.camera.cameraonboarding.service.CameraService; import jakarta.validation.Valid; @@ -79,4 +80,15 @@ public ResponseEntity downloadImage(@PathVariable UUID cameraId) { .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + camera.getImageId() + ".png") .body(imageData); } + + @PostMapping("/camera/{cameraId}/location") + public ResponseEntity addLocation( + @PathVariable UUID cameraId, + @Valid @RequestBody LocationDto locationDto) { + + Camera updatedCamera = cameraService.handleAddLocation(cameraId, locationDto); + CameraResponse response = cameraDtoConverter.toCameraResponse(updatedCamera); + + return new ResponseEntity<>(response, HttpStatus.CREATED); + } } From 66177b0bd839755c197d4c75bbe54bebf83229a4 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Wed, 30 Oct 2024 15:58:12 +0300 Subject: [PATCH 09/25] Add location handling method --- .../cameraonboarding/service/CameraService.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/CameraService.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/CameraService.java index bce445f..e706b27 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/service/CameraService.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/CameraService.java @@ -1,5 +1,6 @@ package com.onboarding.camera.cameraonboarding.service; +import com.onboarding.camera.cameraonboarding.dto.LocationDto; import com.onboarding.camera.cameraonboarding.entity.Camera; import com.onboarding.camera.cameraonboarding.exception.CameraAlreadyInitializedException; import com.onboarding.camera.cameraonboarding.exception.CameraNotCreatedException; @@ -8,6 +9,7 @@ import com.onboarding.camera.cameraonboarding.exception.ImageNotDownloadedException; import com.onboarding.camera.cameraonboarding.exception.ImageNotFoundException; import com.onboarding.camera.cameraonboarding.exception.ImageNotUploadedException; +import com.onboarding.camera.cameraonboarding.exception.LocationNotAddedException; import java.util.UUID; @@ -70,4 +72,16 @@ public interface CameraService { */ byte[] handleDownloadImage(UUID cameraId); + + /** + * this method is used for add location information to camera + * + * @param cameraId camera id + * @param locationDto camera location + * @return updated camera + * @throws CameraNotFoundException if camera is not found with id + * @throws LocationNotAddedException if unexpected error occurs while adding location + */ + + Camera handleAddLocation(UUID cameraId, LocationDto locationDto); } From e505c39ce371dc19478a601e34f603e82f43711c Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Wed, 30 Oct 2024 15:58:45 +0300 Subject: [PATCH 10/25] Add location handling method --- .../service/impl/CameraServiceImpl.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java index 0418f91..69e0e4a 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java @@ -1,6 +1,8 @@ package com.onboarding.camera.cameraonboarding.service.impl; +import com.onboarding.camera.cameraonboarding.dto.LocationDto; import com.onboarding.camera.cameraonboarding.entity.Camera; +import com.onboarding.camera.cameraonboarding.entity.Location; import com.onboarding.camera.cameraonboarding.exception.CameraAlreadyInitializedException; import com.onboarding.camera.cameraonboarding.exception.CameraNotCreatedException; import com.onboarding.camera.cameraonboarding.exception.CameraNotFoundException; @@ -9,6 +11,7 @@ import com.onboarding.camera.cameraonboarding.exception.ImageNotDownloadedException; import com.onboarding.camera.cameraonboarding.exception.ImageNotFoundException; import com.onboarding.camera.cameraonboarding.exception.ImageNotUploadedException; +import com.onboarding.camera.cameraonboarding.exception.LocationNotAddedException; import com.onboarding.camera.cameraonboarding.repository.CameraRepository; import com.onboarding.camera.cameraonboarding.service.BlobStorageService; import com.onboarding.camera.cameraonboarding.service.CameraService; @@ -120,6 +123,33 @@ public byte[] handleDownloadImage(UUID cameraId) { } } + @Override + public Camera handleAddLocation(UUID cameraId, LocationDto locationDto) { + Camera camera = getCameraById(cameraId); + + try { + Location location = camera.getLocation(); + if (location != null) { + location.setLatitude(locationDto.getLatitude()); + location.setLongitude(locationDto.getLongitude()); + location.setAddress(locationDto.getAddress()); + } else { + location = new Location(); + location.setLatitude(locationDto.getLatitude()); + location.setLongitude(locationDto.getLongitude()); + location.setAddress(locationDto.getAddress()); + + location.setCamera(camera); + camera.setLocation(location); + } + cameraRepository.save(camera); + return camera; + } catch (Exception ex) { + log.error("Exception occurred while adding location, camera:{}:ex:{}", cameraId, ex.getMessage()); + throw new LocationNotAddedException(String.format("Error occurred while adding location: %s", ex.getMessage())); + } + } + /** * Validates if the camera has been onboarded and initialized * From cb10faaee3ac47b702a6bdb57f62095b1c597e68 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Mon, 4 Nov 2024 18:33:05 +0300 Subject: [PATCH 11/25] Add location dto converter --- .../converter/LocationDtoConverter.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/com/onboarding/camera/cameraonboarding/converter/LocationDtoConverter.java diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/converter/LocationDtoConverter.java b/src/main/java/com/onboarding/camera/cameraonboarding/converter/LocationDtoConverter.java new file mode 100644 index 0000000..00f18d6 --- /dev/null +++ b/src/main/java/com/onboarding/camera/cameraonboarding/converter/LocationDtoConverter.java @@ -0,0 +1,17 @@ +package com.onboarding.camera.cameraonboarding.converter; + +import com.onboarding.camera.cameraonboarding.dto.LocationResponse; +import com.onboarding.camera.cameraonboarding.entity.Location; +import org.springframework.stereotype.Component; + +@Component +public class LocationDtoConverter { + + public LocationResponse toLocationResponse(Location location) { + LocationResponse response = new LocationResponse(); + response.setLatitude(location.getLatitude()); + response.setLongitude(location.getLongitude()); + response.setAddress(location.getAddress()); + return response; + } +} From 7534e15426f5e87ef10a9c235b9627280af7cc06 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Mon, 4 Nov 2024 18:33:35 +0300 Subject: [PATCH 12/25] Add location response --- .../camera/cameraonboarding/dto/LocationResponse.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationResponse.java diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationResponse.java b/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationResponse.java new file mode 100644 index 0000000..97de16a --- /dev/null +++ b/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationResponse.java @@ -0,0 +1,10 @@ +package com.onboarding.camera.cameraonboarding.dto; + +import lombok.Data; + +@Data +public class LocationResponse { + private double latitude; + private double longitude; + private String address; +} From 8b412f9bf89995bf6144cc9145c021f008699cf4 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Mon, 4 Nov 2024 18:53:27 +0300 Subject: [PATCH 13/25] Refactor addLocation method --- .../controller/CameraRestController.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/controller/CameraRestController.java b/src/main/java/com/onboarding/camera/cameraonboarding/controller/CameraRestController.java index 622838a..9c47e52 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/controller/CameraRestController.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/controller/CameraRestController.java @@ -1,10 +1,13 @@ package com.onboarding.camera.cameraonboarding.controller; import com.onboarding.camera.cameraonboarding.converter.CameraDtoConverter; +import com.onboarding.camera.cameraonboarding.converter.LocationDtoConverter; import com.onboarding.camera.cameraonboarding.dto.CameraDto; import com.onboarding.camera.cameraonboarding.dto.CameraResponse; import com.onboarding.camera.cameraonboarding.dto.LocationDto; +import com.onboarding.camera.cameraonboarding.dto.LocationResponse; import com.onboarding.camera.cameraonboarding.entity.Camera; +import com.onboarding.camera.cameraonboarding.entity.Location; import com.onboarding.camera.cameraonboarding.service.CameraService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -33,6 +36,8 @@ public class CameraRestController { private final CameraDtoConverter cameraDtoConverter; + private final LocationDtoConverter locationDtoConverter; + @PostMapping("/onboard") public ResponseEntity saveCamera(@Valid @RequestBody CameraDto cameraDto) { @@ -82,12 +87,13 @@ public ResponseEntity downloadImage(@PathVariable UUID cameraId) { } @PostMapping("/camera/{cameraId}/location") - public ResponseEntity addLocation( + public ResponseEntity addLocation( @PathVariable UUID cameraId, @Valid @RequestBody LocationDto locationDto) { Camera updatedCamera = cameraService.handleAddLocation(cameraId, locationDto); - CameraResponse response = cameraDtoConverter.toCameraResponse(updatedCamera); + Location cameraLocation = updatedCamera.getLocation(); + LocationResponse response = locationDtoConverter.toLocationResponse(cameraLocation); return new ResponseEntity<>(response, HttpStatus.CREATED); } From 95db1de95eb103a8da7dad8786a422dd2cf98862 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Mon, 4 Nov 2024 18:54:23 +0300 Subject: [PATCH 14/25] Add Jakarta validation annotations --- .../camera/cameraonboarding/dto/LocationDto.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationDto.java b/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationDto.java index 961c3d6..fbbd9b8 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationDto.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationDto.java @@ -1,11 +1,23 @@ package com.onboarding.camera.cameraonboarding.dto; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; @Data +@AllArgsConstructor +@NoArgsConstructor public class LocationDto { + @NotBlank(message = "Latitude cannot be blank") private double latitude; + + @NotBlank(message = "Longitude cannot be blank") private double longitude; + + @NotBlank(message = "Address version cannot be blank") + @Size(min = 10, max = 100, message = "Address should be up to 100 characters") private String address; } From 0c26d2df8ae2ef9f534ff037fad3501e8128d760 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Mon, 4 Nov 2024 18:55:00 +0300 Subject: [PATCH 15/25] Refactor handle add location method --- .../service/impl/CameraServiceImpl.java | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java index 69e0e4a..2da41a4 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/service/impl/CameraServiceImpl.java @@ -19,8 +19,10 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.io.ByteArrayOutputStream; +import java.util.Optional; import java.util.UUID; @Slf4j @@ -124,25 +126,21 @@ public byte[] handleDownloadImage(UUID cameraId) { } @Override + @Transactional public Camera handleAddLocation(UUID cameraId, LocationDto locationDto) { Camera camera = getCameraById(cameraId); try { - Location location = camera.getLocation(); - if (location != null) { - location.setLatitude(locationDto.getLatitude()); - location.setLongitude(locationDto.getLongitude()); - location.setAddress(locationDto.getAddress()); - } else { - location = new Location(); - location.setLatitude(locationDto.getLatitude()); - location.setLongitude(locationDto.getLongitude()); - location.setAddress(locationDto.getAddress()); - - location.setCamera(camera); - camera.setLocation(location); - } + Location location = Optional.ofNullable(camera.getLocation()).orElse(new Location()); + location.setLatitude(locationDto.getLatitude()); + location.setLongitude(locationDto.getLongitude()); + location.setAddress(locationDto.getAddress()); + + location.setCamera(camera); + camera.setLocation(location); + cameraRepository.save(camera); + log.info("Location added/updated successfully for Camera ID: {}", cameraId); return camera; } catch (Exception ex) { log.error("Exception occurred while adding location, camera:{}:ex:{}", cameraId, ex.getMessage()); From 5d30ef1cf997daaa3fd88946e838714a8c3757b2 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Mon, 4 Nov 2024 18:56:40 +0300 Subject: [PATCH 16/25] Import necessary component --- .../cameraonboarding/controller/CameraRestControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java b/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java index 19a1c40..bb10f85 100644 --- a/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java +++ b/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java @@ -36,7 +36,7 @@ @WebMvcTest(controllers = CameraRestController.class) @AutoConfigureMockMvc(addFilters = false) @ExtendWith(MockitoExtension.class) -@Import(CameraDtoConverter.class) +@Import({CameraDtoConverter.class, LocationDtgitoConverter.class}) class CameraRestControllerTest { @Autowired From 9ee10789003e9a19f268fbfd5be4e8f431cb2814 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Mon, 4 Nov 2024 18:57:17 +0300 Subject: [PATCH 17/25] Add necessary component --- .../cameraonboarding/controller/CameraRestControllerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java b/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java index bb10f85..eed0e7a 100644 --- a/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java +++ b/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.onboarding.camera.cameraonboarding.converter.CameraDtoConverter; +import com.onboarding.camera.cameraonboarding.converter.LocationDtoConverter; import com.onboarding.camera.cameraonboarding.dto.CameraDto; import com.onboarding.camera.cameraonboarding.entity.Camera; import com.onboarding.camera.cameraonboarding.exception.CameraAlreadyInitializedException; @@ -36,7 +37,7 @@ @WebMvcTest(controllers = CameraRestController.class) @AutoConfigureMockMvc(addFilters = false) @ExtendWith(MockitoExtension.class) -@Import({CameraDtoConverter.class, LocationDtgitoConverter.class}) +@Import({CameraDtoConverter.class, LocationDtoConverter.class}) class CameraRestControllerTest { @Autowired From bc8c0c783cba0326ef68bd236cf85816b0996500 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Mon, 4 Nov 2024 19:01:27 +0300 Subject: [PATCH 18/25] Import necessary component --- .../cameraonboarding/controller/CameraRestControllerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java b/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java index bb10f85..eed0e7a 100644 --- a/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java +++ b/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.onboarding.camera.cameraonboarding.converter.CameraDtoConverter; +import com.onboarding.camera.cameraonboarding.converter.LocationDtoConverter; import com.onboarding.camera.cameraonboarding.dto.CameraDto; import com.onboarding.camera.cameraonboarding.entity.Camera; import com.onboarding.camera.cameraonboarding.exception.CameraAlreadyInitializedException; @@ -36,7 +37,7 @@ @WebMvcTest(controllers = CameraRestController.class) @AutoConfigureMockMvc(addFilters = false) @ExtendWith(MockitoExtension.class) -@Import({CameraDtoConverter.class, LocationDtgitoConverter.class}) +@Import({CameraDtoConverter.class, LocationDtoConverter.class}) class CameraRestControllerTest { @Autowired From 261740c06f04f15c2adaeddb0cf15f728e68568c Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Mon, 4 Nov 2024 19:06:27 +0300 Subject: [PATCH 19/25] Import necessary component --- .../cameraonboarding/controller/CameraRestControllerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java b/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java index 19a1c40..eed0e7a 100644 --- a/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java +++ b/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.onboarding.camera.cameraonboarding.converter.CameraDtoConverter; +import com.onboarding.camera.cameraonboarding.converter.LocationDtoConverter; import com.onboarding.camera.cameraonboarding.dto.CameraDto; import com.onboarding.camera.cameraonboarding.entity.Camera; import com.onboarding.camera.cameraonboarding.exception.CameraAlreadyInitializedException; @@ -36,7 +37,7 @@ @WebMvcTest(controllers = CameraRestController.class) @AutoConfigureMockMvc(addFilters = false) @ExtendWith(MockitoExtension.class) -@Import(CameraDtoConverter.class) +@Import({CameraDtoConverter.class, LocationDtoConverter.class}) class CameraRestControllerTest { @Autowired From 650ad92489dfbfe37a709786894386d3abb3485a Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Tue, 12 Nov 2024 21:36:01 +0300 Subject: [PATCH 20/25] Enhance validation with jakarta --- .../camera/cameraonboarding/dto/LocationDto.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationDto.java b/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationDto.java index fbbd9b8..91e9fa6 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationDto.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/dto/LocationDto.java @@ -1,6 +1,9 @@ package com.onboarding.camera.cameraonboarding.dto; +import jakarta.validation.constraints.DecimalMax; +import jakarta.validation.constraints.DecimalMin; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; import lombok.Data; @@ -11,13 +14,17 @@ @NoArgsConstructor public class LocationDto { - @NotBlank(message = "Latitude cannot be blank") + @NotNull(message = "Latitude cannot be null") + @DecimalMin(value = "-90.0", message = "Latitude must be >= -90") + @DecimalMax(value = "90.0", message = "Latitude must be <= 90") private double latitude; - @NotBlank(message = "Longitude cannot be blank") + @NotNull(message = "Longitude cannot be null") + @DecimalMin(value = "-180.0", message = "Longitude must be >= -180") + @DecimalMax(value = "180.0", message = "Longitude must be <= 180") private double longitude; - @NotBlank(message = "Address version cannot be blank") + @NotBlank(message = "Address cannot be blank") @Size(min = 10, max = 100, message = "Address should be up to 100 characters") private String address; } From 289d2582110f06091558e263d884e0a54bda0832 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Tue, 12 Nov 2024 21:37:48 +0300 Subject: [PATCH 21/25] Fix lombok @ToString bug --- .../com/onboarding/camera/cameraonboarding/entity/Camera.java | 2 +- .../com/onboarding/camera/cameraonboarding/entity/Location.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/entity/Camera.java b/src/main/java/com/onboarding/camera/cameraonboarding/entity/Camera.java index 2cd3d98..389f5df 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/entity/Camera.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/entity/Camera.java @@ -16,7 +16,7 @@ import java.util.UUID; @Data -@ToString +@ToString(exclude = "location") @Entity @Table(name = "camera_metadata") public class Camera { diff --git a/src/main/java/com/onboarding/camera/cameraonboarding/entity/Location.java b/src/main/java/com/onboarding/camera/cameraonboarding/entity/Location.java index 607b2d5..52e72fc 100644 --- a/src/main/java/com/onboarding/camera/cameraonboarding/entity/Location.java +++ b/src/main/java/com/onboarding/camera/cameraonboarding/entity/Location.java @@ -15,7 +15,7 @@ import java.util.UUID; @Data -@ToString +@ToString(exclude = "camera") @Entity @Table(name = "location") public class Location { From 2e269ad702fbe145ec3bd5f4303e3d361c2485da Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Tue, 12 Nov 2024 21:38:27 +0300 Subject: [PATCH 22/25] Add location dto converter unit test --- .../converter/LocationDtoConverterTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/test/java/com/onboarding/camera/cameraonboarding/converter/LocationDtoConverterTest.java diff --git a/src/test/java/com/onboarding/camera/cameraonboarding/converter/LocationDtoConverterTest.java b/src/test/java/com/onboarding/camera/cameraonboarding/converter/LocationDtoConverterTest.java new file mode 100644 index 0000000..80e3c17 --- /dev/null +++ b/src/test/java/com/onboarding/camera/cameraonboarding/converter/LocationDtoConverterTest.java @@ -0,0 +1,39 @@ +package com.onboarding.camera.cameraonboarding.converter; + +import com.onboarding.camera.cameraonboarding.dto.LocationResponse; +import com.onboarding.camera.cameraonboarding.entity.Location; +import org.assertj.core.api.AssertionsForClassTypes; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class LocationDtoConverterTest { + + private LocationDtoConverter locationDtoConverter; + + private final Double LATITUDE = 51.232; + private final Double LONGITUDE = -51.232; + private final String ADDRESS = "long enough address"; + + @BeforeEach + void setUp() { + locationDtoConverter = new LocationDtoConverter(); + } + + @Test + void expect_convert_withValidLocation_returnLocationResponse() { + // arrange + Location location = new Location(); + location.setLatitude(LATITUDE); + location.setLongitude(LONGITUDE); + location.setAddress(ADDRESS); + + // act + LocationResponse response = locationDtoConverter.toLocationResponse(location); + + // assert + AssertionsForClassTypes.assertThat(response).isNotNull(); + AssertionsForClassTypes.assertThat(response.getLatitude()).isEqualTo(LATITUDE); + AssertionsForClassTypes.assertThat(response.getLongitude()).isEqualTo(LONGITUDE); + AssertionsForClassTypes.assertThat(response.getAddress()).isEqualTo(ADDRESS); + } +} \ No newline at end of file From 373ddb0962a87d468ba1d4f71ae46e9a39c41356 Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Tue, 12 Nov 2024 21:39:22 +0300 Subject: [PATCH 23/25] Add handle add location unit test --- .../service/CameraServiceImplTest.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/test/java/com/onboarding/camera/cameraonboarding/service/CameraServiceImplTest.java b/src/test/java/com/onboarding/camera/cameraonboarding/service/CameraServiceImplTest.java index e80a86a..89c0bbb 100644 --- a/src/test/java/com/onboarding/camera/cameraonboarding/service/CameraServiceImplTest.java +++ b/src/test/java/com/onboarding/camera/cameraonboarding/service/CameraServiceImplTest.java @@ -1,6 +1,8 @@ package com.onboarding.camera.cameraonboarding.service; +import com.onboarding.camera.cameraonboarding.dto.LocationDto; import com.onboarding.camera.cameraonboarding.entity.Camera; +import com.onboarding.camera.cameraonboarding.entity.Location; import com.onboarding.camera.cameraonboarding.exception.CameraAlreadyInitializedException; import com.onboarding.camera.cameraonboarding.exception.CameraNotCreatedException; import com.onboarding.camera.cameraonboarding.exception.CameraNotFoundException; @@ -9,6 +11,7 @@ import com.onboarding.camera.cameraonboarding.exception.ImageNotDownloadedException; import com.onboarding.camera.cameraonboarding.exception.ImageNotFoundException; import com.onboarding.camera.cameraonboarding.exception.ImageNotUploadedException; +import com.onboarding.camera.cameraonboarding.exception.LocationNotAddedException; import com.onboarding.camera.cameraonboarding.repository.CameraRepository; import com.onboarding.camera.cameraonboarding.service.impl.BlobStorageServiceImpl; import com.onboarding.camera.cameraonboarding.service.impl.CameraServiceImpl; @@ -52,6 +55,8 @@ class CameraServiceImplTest { private ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + private LocationDto locationDto; + private final String CAMERA_NAME = "Camera 1"; private final String FIRMWARE_VERSION = "v1.0"; private final UUID CAMERA_ID = UUID.randomUUID(); @@ -64,6 +69,9 @@ class CameraServiceImplTest { private final String CONTAINER_NAME = "test_container"; private final UUID IMAGE_ID = UUID.randomUUID(); private final UUID NULL_UUID = null; + private final Double LATITUDE = 51.232; + private final Double LONGITUDE = -51.232; + private final String ADDRESS = "long enough address"; @BeforeEach void setUp() { @@ -73,6 +81,7 @@ void setUp() { camera.setFirmwareVersion(FIRMWARE_VERSION); camera.setCreatedAt(CREATED_AT); camera.setOnboardedAt(ONBOARDED_AT); + locationDto = new LocationDto(); } @Test @@ -405,4 +414,57 @@ void expect_handleDownloadImage_withNotInitializedCamera_throwsException() { Mockito.verify(blobStorageService, Mockito.never()).getBlob(Mockito.any(), Mockito.anyString(), Mockito.anyString()); } + + @Test + void expect_handleAddLocation_withValidLocation_returnCamera() { + // arrange + locationDto.setLatitude(LATITUDE); + locationDto.setLongitude(LONGITUDE); + locationDto.setAddress(ADDRESS); + Mockito.when(cameraRepository.findById(CAMERA_ID)).thenReturn(Optional.of(camera)); + + // act + Camera updatedCamera = cameraService.handleAddLocation(CAMERA_ID, locationDto); + Location location = updatedCamera.getLocation(); + + // assert + Assertions.assertThat(location).isNotNull(); + Assertions.assertThat(location.getLatitude()).isNotNull(); + Assertions.assertThat(location.getLatitude()).isEqualTo(LATITUDE); + Assertions.assertThat(location.getLongitude()).isNotNull(); + Assertions.assertThat(location.getLongitude()).isEqualTo(LONGITUDE); + Assertions.assertThat(location.getAddress()).isNotNull(); + Assertions.assertThat(location.getAddress()).isEqualTo(ADDRESS); + + Mockito.verify(cameraRepository).save(camera); + Mockito.verify(cameraRepository).findById(CAMERA_ID); + } + + @Test + void expect_handleAddLocation_withNonExistingCamera_throwsException() { + // arrange + Mockito.when(cameraRepository.findById(CAMERA_ID)).thenReturn(Optional.empty()); + + // act and assert + Assertions.assertThatThrownBy(() -> cameraService.handleAddLocation(CAMERA_ID, locationDto)) + .isInstanceOf(CameraNotFoundException.class) + .hasMessageContaining("Camera not found with id: " + CAMERA_ID); + + Mockito.verify(cameraRepository).findById(CAMERA_ID); + } + + @Test + void expect_handleAddLocation_withLocationNotAddedError_throwsException() { + // arrange + Mockito.when(cameraRepository.findById(CAMERA_ID)).thenReturn(Optional.of(camera)); + Mockito.doThrow(new LocationNotAddedException("Error occurred while adding location")) + .when(cameraRepository).save(camera); + + // act and assert + Assertions.assertThatThrownBy(() -> cameraService.handleAddLocation(CAMERA_ID, locationDto)) + .isInstanceOf(LocationNotAddedException.class) + .hasMessageContaining("Error occurred while adding location"); + + Mockito.verify(cameraRepository).findById(CAMERA_ID); + } } From f4b0f5886eb03c323457306bd47c36dd66b8d5aa Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Tue, 12 Nov 2024 21:40:57 +0300 Subject: [PATCH 24/25] Add add location REST endpoint unit test --- .../controller/CameraRestControllerTest.java | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java b/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java index eed0e7a..e1fc176 100644 --- a/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java +++ b/src/test/java/com/onboarding/camera/cameraonboarding/controller/CameraRestControllerTest.java @@ -4,11 +4,14 @@ import com.onboarding.camera.cameraonboarding.converter.CameraDtoConverter; import com.onboarding.camera.cameraonboarding.converter.LocationDtoConverter; import com.onboarding.camera.cameraonboarding.dto.CameraDto; +import com.onboarding.camera.cameraonboarding.dto.LocationDto; import com.onboarding.camera.cameraonboarding.entity.Camera; +import com.onboarding.camera.cameraonboarding.entity.Location; import com.onboarding.camera.cameraonboarding.exception.CameraAlreadyInitializedException; import com.onboarding.camera.cameraonboarding.exception.CameraNotFoundException; import com.onboarding.camera.cameraonboarding.exception.CameraNotInitializedException; import com.onboarding.camera.cameraonboarding.exception.ImageAlreadyUploadedException; +import com.onboarding.camera.cameraonboarding.exception.LocationNotAddedException; import com.onboarding.camera.cameraonboarding.service.CameraService; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; @@ -53,12 +56,19 @@ class CameraRestControllerTest { private Camera camera; + private LocationDto locationDto; + + private Location location; + private final String CAMERA_NAME = "Camera 1"; private final String FIRMWARE_VERSION = "v1.0"; private final UUID CAMERA_ID = UUID.randomUUID(); private final String INVALID_UUID = "invalid-uuid"; private final UUID IMAGE_ID = UUID.randomUUID(); private final String IMAGE_DATA = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAAECAYAAACk7+45AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAXSURBVBhXY5STV/rPAARMIAIE0BkMDAAtLgFmLE0FhAAAAABJRU5ErkJggg=="; + private final Double LATITUDE = 51.232; + private final Double LONGITUDE = -51.232; + private final String ADDRESS = "long enough address"; @BeforeEach void setUp() { @@ -66,6 +76,15 @@ void setUp() { cameraDto.setCameraName(CAMERA_NAME); cameraDto.setFirmwareVersion(FIRMWARE_VERSION); camera = new Camera(); + locationDto = new LocationDto(); + locationDto.setLatitude(LATITUDE); + locationDto.setLongitude(LONGITUDE); + locationDto.setAddress(ADDRESS); + location = new Location(); + location.setLatitude(locationDto.getLatitude()); + location.setLongitude(locationDto.getLongitude()); + location.setAddress(locationDto.getAddress()); + } @Test @@ -394,4 +413,127 @@ public void expect_handleDownloadImage_withNotInitializedCamera_returnInternalSe Mockito.verify(cameraService).handleDownloadImage(CAMERA_ID); } + + @Test + void expect_handleAddLocation_withValidInput_returnCreated() throws Exception { + // arrange + camera.setLocation(location); + Mockito.when(cameraService.handleAddLocation(CAMERA_ID, locationDto)) + .thenReturn(camera); + + // act + ResultActions response = mockMvc.perform(MockMvcRequestBuilders + .post("/api/v1/camera/{cameraId}/location", CAMERA_ID) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(locationDto))); + + // assert + response.andExpect(MockMvcResultMatchers.status().isCreated()) + .andExpect(MockMvcResultMatchers.jsonPath("$.latitude", CoreMatchers.is(locationDto.getLatitude()))) + .andExpect(MockMvcResultMatchers.jsonPath("$.longitude", CoreMatchers.is(locationDto.getLongitude()))) + .andExpect(MockMvcResultMatchers.jsonPath("$.address", CoreMatchers.is(locationDto.getAddress()))); + + Mockito.verify(cameraService).handleAddLocation(CAMERA_ID, locationDto); + } + + @Test + void expect_handleAddLocation_withInvalidUUID_returnClientError() throws Exception { + // act + ResultActions response = mockMvc.perform(MockMvcRequestBuilders + .post("/api/v1/camera/{cameraId}/location", INVALID_UUID) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(locationDto))); + + // assert + response.andExpect(MockMvcResultMatchers.status().is4xxClientError()); + + Mockito.verify(cameraService, Mockito.never()).handleAddLocation(Mockito.any(), Mockito.any()); + } + + @Test + void expect_handleAddLocation_withInvalidLatitude_returnBadRequest() throws Exception { + // arrange + locationDto.setLatitude(1800.0); + + // act + ResultActions response = mockMvc.perform(MockMvcRequestBuilders + .post("/api/v1/camera/{cameraId}/location", CAMERA_ID) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(locationDto))); + + // assert + response.andExpect(MockMvcResultMatchers.status().isBadRequest()); + + Mockito.verify(cameraService, Mockito.never()).handleAddLocation(Mockito.any(), Mockito.any()); + } + + @Test + void expect_handleAddLocation_withInvalidLongitude_returnBadRequest() throws Exception { + // arrange + locationDto.setLongitude(1800.0); + + // act + ResultActions response = mockMvc.perform(MockMvcRequestBuilders + .post("/api/v1/camera/{cameraId}/location", CAMERA_ID) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(locationDto))); + + // assert + response.andExpect(MockMvcResultMatchers.status().isBadRequest()); + + Mockito.verify(cameraService, Mockito.never()).handleAddLocation(Mockito.any(), Mockito.any()); + } + + @Test + void expect_handleAddLocation_withBlankAddress_returnBadRequest() throws Exception { + // arrange + locationDto.setAddress(""); + + // act + ResultActions response = mockMvc.perform(MockMvcRequestBuilders + .post("/api/v1/camera/{cameraId}/location", CAMERA_ID) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(locationDto))); + + // assert + response.andExpect(MockMvcResultMatchers.status().isBadRequest()); + + Mockito.verify(cameraService, Mockito.never()).handleAddLocation(Mockito.any(), Mockito.any()); + } + + @Test + void expect_handleAddLocation_withCameraNotFound_returnNotFound() throws Exception { + // arrange + Mockito.doThrow(new CameraNotFoundException("Camera not found with id: " + CAMERA_ID)) + .when(cameraService).handleAddLocation(CAMERA_ID, locationDto); + + // act + ResultActions response = mockMvc.perform(MockMvcRequestBuilders + .post("/api/v1/camera/{cameraId}/location", CAMERA_ID) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(locationDto))); + + // assert + response.andExpect(MockMvcResultMatchers.status().isNotFound()); + + Mockito.verify(cameraService).handleAddLocation(ArgumentMatchers.eq(CAMERA_ID), ArgumentMatchers.eq(locationDto)); + } + + @Test + void expect_handleAddLocation_withLocationNotAddedException_returnInternalServerError() throws Exception { + // arrange + Mockito.doThrow(new LocationNotAddedException("Error occurred while adding location")) + .when(cameraService).handleAddLocation(CAMERA_ID, locationDto); + + // act + ResultActions response = mockMvc.perform(MockMvcRequestBuilders + .post("/api/v1/camera/{cameraId}/location", CAMERA_ID) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(locationDto))); + + // assert + response.andExpect(MockMvcResultMatchers.status().isInternalServerError()); + + Mockito.verify(cameraService).handleAddLocation(ArgumentMatchers.eq(CAMERA_ID), ArgumentMatchers.eq(locationDto)); + } } \ No newline at end of file From 237a220909a2a0be8a87ca287a8f252e5b43b0be Mon Sep 17 00:00:00 2001 From: Selahattin Sert Date: Thu, 14 Nov 2024 12:40:04 +0300 Subject: [PATCH 25/25] Improve assertion approach --- .../service/CameraServiceImplTest.java | 50 +++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/src/test/java/com/onboarding/camera/cameraonboarding/service/CameraServiceImplTest.java b/src/test/java/com/onboarding/camera/cameraonboarding/service/CameraServiceImplTest.java index 89c0bbb..753084c 100644 --- a/src/test/java/com/onboarding/camera/cameraonboarding/service/CameraServiceImplTest.java +++ b/src/test/java/com/onboarding/camera/cameraonboarding/service/CameraServiceImplTest.java @@ -97,13 +97,24 @@ void expect_handleSaveCamera_withValidCamera_returnSavedCamera() { final Camera savedCamera = cameraService.handleSaveCamera(camera); // assert - Assertions.assertThat(savedCamera).isNotNull(); - Assertions.assertThat(savedCamera.getCreatedAt()).isNotNull(); - Assertions.assertThat(savedCamera.getCreatedAt()).isEqualTo(CREATED_AT); - Assertions.assertThat(savedCamera.getCameraName()).isNotNull(); - Assertions.assertThat(savedCamera.getCameraName()).isEqualTo(CAMERA_NAME); - Assertions.assertThat(savedCamera.getFirmwareVersion()).isNotNull(); - Assertions.assertThat(savedCamera.getFirmwareVersion()).isEqualTo(FIRMWARE_VERSION); + Assertions.assertThat(savedCamera) + .isNotNull() + .satisfies(cam -> { + Assertions.assertThat(cam.getCreatedAt()) + .as("Check Created At") + .isNotNull() + .isEqualTo(CREATED_AT); + + Assertions.assertThat(cam.getCameraName()) + .as("Check Camera Name") + .isNotNull() + .isEqualTo(CAMERA_NAME); + + Assertions.assertThat(cam.getFirmwareVersion()) + .as("Check Fırmware Version") + .isNotNull() + .isEqualTo(FIRMWARE_VERSION); + }); Mockito.verify(dateTimeFactory, Mockito.times(2)).now(); Mockito.verify(cameraRepository).save(camera); @@ -428,13 +439,24 @@ void expect_handleAddLocation_withValidLocation_returnCamera() { Location location = updatedCamera.getLocation(); // assert - Assertions.assertThat(location).isNotNull(); - Assertions.assertThat(location.getLatitude()).isNotNull(); - Assertions.assertThat(location.getLatitude()).isEqualTo(LATITUDE); - Assertions.assertThat(location.getLongitude()).isNotNull(); - Assertions.assertThat(location.getLongitude()).isEqualTo(LONGITUDE); - Assertions.assertThat(location.getAddress()).isNotNull(); - Assertions.assertThat(location.getAddress()).isEqualTo(ADDRESS); + Assertions.assertThat(location) + .isNotNull() + .satisfies(loc -> { + Assertions.assertThat(loc.getLatitude()) + .as("Check Latitude") + .isNotNull() + .isEqualTo(LATITUDE); + + Assertions.assertThat(loc.getLongitude()) + .as("Check Longitude") + .isNotNull() + .isEqualTo(LONGITUDE); + + Assertions.assertThat(loc.getAddress()) + .as("Check Address") + .isNotNull() + .isEqualTo(ADDRESS); + }); Mockito.verify(cameraRepository).save(camera); Mockito.verify(cameraRepository).findById(CAMERA_ID);