Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e82b14e
Add db changelog for location
SelahattinSert Oct 30, 2024
554658f
Add location entity class
SelahattinSert Oct 30, 2024
b1ba4d0
Add dto for location object
SelahattinSert Oct 30, 2024
3fac938
Add custom exception for location
SelahattinSert Oct 30, 2024
7e47269
Add location not added handling method
SelahattinSert Oct 30, 2024
b801398
Include changelog-v2
SelahattinSert Oct 30, 2024
7a9a8bb
Add location relation
SelahattinSert Oct 30, 2024
9ab51ef
Add location rest endpoint
SelahattinSert Oct 30, 2024
66177b0
Add location handling method
SelahattinSert Oct 30, 2024
e505c39
Add location handling method
SelahattinSert Oct 30, 2024
cb10faa
Add location dto converter
SelahattinSert Nov 4, 2024
7534e15
Add location response
SelahattinSert Nov 4, 2024
8b412f9
Refactor addLocation method
SelahattinSert Nov 4, 2024
95db1de
Add Jakarta validation annotations
SelahattinSert Nov 4, 2024
0c26d2d
Refactor handle add location method
SelahattinSert Nov 4, 2024
5d30ef1
Import necessary component
SelahattinSert Nov 4, 2024
9ee1078
Add necessary component
SelahattinSert Nov 4, 2024
bc8c0c7
Import necessary component
SelahattinSert Nov 4, 2024
0133d18
Merge remote-tracking branch 'origin/task/add-location-object' into t…
SelahattinSert Nov 4, 2024
261740c
Import necessary component
SelahattinSert Nov 4, 2024
14bb462
Merge remote-tracking branch 'origin/task/add-location-object' into t…
SelahattinSert Nov 4, 2024
650ad92
Enhance validation with jakarta
SelahattinSert Nov 12, 2024
289d258
Fix lombok @ToString bug
SelahattinSert Nov 12, 2024
2e269ad
Add location dto converter unit test
SelahattinSert Nov 12, 2024
373ddb0
Add handle add location unit test
SelahattinSert Nov 12, 2024
f4b0f58
Add add location REST endpoint unit test
SelahattinSert Nov 12, 2024
237a220
Improve assertion approach
SelahattinSert Nov 14, 2024
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
@@ -1,9 +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;
Expand Down Expand Up @@ -32,6 +36,8 @@ public class CameraRestController {

private final CameraDtoConverter cameraDtoConverter;

private final LocationDtoConverter locationDtoConverter;

@PostMapping("/onboard")
public ResponseEntity<CameraResponse> saveCamera(@Valid @RequestBody CameraDto cameraDto) {

Expand Down Expand Up @@ -79,4 +85,16 @@ public ResponseEntity<byte[]> downloadImage(@PathVariable UUID cameraId) {
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + camera.getImageId() + ".png")
.body(imageData);
}

@PostMapping("/camera/{cameraId}/location")
public ResponseEntity<LocationResponse> addLocation(
@PathVariable UUID cameraId,
@Valid @RequestBody LocationDto locationDto) {

Camera updatedCamera = cameraService.handleAddLocation(cameraId, locationDto);
Location cameraLocation = updatedCamera.getLocation();
LocationResponse response = locationDtoConverter.toLocationResponse(cameraLocation);

return new ResponseEntity<>(response, HttpStatus.CREATED);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -153,5 +154,17 @@ public ResponseEntity<ErrorResponse> handleImageNotDownloadedException(ImageNotD

return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}

@ExceptionHandler(LocationNotAddedException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ResponseEntity<ErrorResponse> 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);
}
}

Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +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;
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.onboarding.camera.cameraonboarding.exception;

public class LocationNotAddedException extends RuntimeException {
public LocationNotAddedException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -9,15 +11,18 @@
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;
import com.onboarding.camera.cameraonboarding.util.DateTimeFactory;
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
Expand Down Expand Up @@ -120,6 +125,29 @@ public byte[] handleDownloadImage(UUID cameraId) {
}
}

@Override
@Transactional
public Camera handleAddLocation(UUID cameraId, LocationDto locationDto) {
Camera camera = getCameraById(cameraId);

try {
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());
throw new LocationNotAddedException(String.format("Error occurred while adding location: %s", ex.getMessage()));
}
}

/**
* Validates if the camera has been onboarded and initialized
*
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/db/changelog/changelog-master.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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">

<include file="/db/changelog/changelog-v1.xml"/>
<include file="/db/changelog/changelog-v2.xml"/>

</databaseChangeLog>
41 changes: 41 additions & 0 deletions src/main/resources/db/changelog/changelog-v2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
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
https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

<changeSet id="2" author="selahattin" runOnChange="true">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="location"/>
</not>
</preConditions>
<createTable tableName="location">
<column name="location_id" type="UUID">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="latitude" type="DOUBLE">
<constraints nullable="false"/>
</column>
<column name="longitude" type="DOUBLE">
<constraints nullable="false"/>
</column>
<column name="address" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="camera_id" type="UUID">
<constraints nullable="false"/>
</column>
</createTable>

<addForeignKeyConstraint
baseTableName="location"
baseColumnNames="camera_id"
referencedTableName="camera_metadata"
referencedColumnNames="cam_id"
constraintName="fk_location_camera_id"/>

</changeSet>

</databaseChangeLog>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down