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
@@ -0,0 +1,49 @@
package com.angel.autonow.driver;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/drivers")
public class DriverController {

private final DriverService driverService;

@PostMapping
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<DriverResponseDTO> createDriver(@Valid @RequestBody DriverRequestDTO request) {
return driverService.createDriver(request)
.map(driver -> ResponseEntity.status(HttpStatus.CREATED).body(driver))
.orElse(ResponseEntity.badRequest().build());
}

@GetMapping("/{id}")
@PreAuthorize("hasAnyRole('ADMIN', 'CUSTOMER', 'DRIVER')")
public DriverResponseDTO getDriverById(@PathVariable Long id) {
return driverService.getDriverById(id).orElse(null);
}

@GetMapping("/license/{licenseNumber}")
@PreAuthorize("hasAnyRole('ADMIN', 'DRIVER')")
public DriverResponseDTO getDriverByLicenseNumber(@PathVariable String licenseNumber) {
return driverService.getDriverByLicenseNumber(licenseNumber).orElse(null);
Comment thread
StoynovAngel marked this conversation as resolved.
}

@GetMapping
@PreAuthorize("hasAnyRole('ADMIN', 'CUSTOMER', 'DRIVER')")
public List<DriverResponseDTO> getAllDrivers() {
return driverService.getAllDrivers();
}
}
32 changes: 32 additions & 0 deletions backend/src/main/java/com/angel/autonow/driver/DriverMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.angel.autonow.driver;

import com.angel.autonow.vehicle.VehicleEntity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;

import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;

@Mapper(componentModel = "spring")
public interface DriverMapper {

@Mapping(source = "vehicles", target = "vehicleIds", qualifiedByName = "vehiclesToIds")
DriverResponseDTO toDTO(DriverEntity driver);

@Mapping(target = "id", ignore = true)
@Mapping(target = "vehicles", ignore = true)
DriverEntity toEntity(DriverRequestDTO request);

@Named("vehiclesToIds")
default Set<Long> vehiclesToIds(Set<VehicleEntity> vehicles) {
if (vehicles == null) {
return Collections.emptySet();
}

return vehicles.stream()
.map(VehicleEntity::getId)
.collect(Collectors.toSet());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.angel.autonow.driver;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface DriverRepository extends JpaRepository<DriverEntity, Long> {

Optional<DriverEntity> findByLicenseNumber(String licenseNumber);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.angel.autonow.driver;

import com.angel.autonow.expertise.ExpertiseType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;

public record DriverRequestDTO(

@NotBlank(message = "First name is required")
String firstName,

@NotBlank(message = "Last name is required")
String lastName,

@NotBlank(message = "Phone number is required")
@Pattern(regexp = "^\\+?[0-9]{10,15}$", message = "Phone number must be valid")
String phoneNumber,

@NotBlank(message = "License number is required")
String licenseNumber,

@NotNull(message = "Expertise type is required")
ExpertiseType expertiseType,

boolean available,

String imageUrl
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.angel.autonow.driver;

import com.angel.autonow.expertise.ExpertiseType;
import lombok.Builder;

import java.util.Set;

@Builder
public record DriverResponseDTO(
Long id,
String firstName,
String lastName,
String phoneNumber,
String licenseNumber,
ExpertiseType expertiseType,
boolean available,
String imageUrl,
Set<Long> vehicleIds
) {

}
35 changes: 35 additions & 0 deletions backend/src/main/java/com/angel/autonow/driver/DriverService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.angel.autonow.driver;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class DriverService {

private final DriverRepository driverRepository;
private final DriverMapper driverMapper;

public Optional<DriverResponseDTO> createDriver(DriverRequestDTO request) {
DriverEntity driver = driverMapper.toEntity(request);
DriverEntity saved = driverRepository.save(driver);
return Optional.of(driverMapper.toDTO(saved));
}

public Optional<DriverResponseDTO> getDriverById(Long id) {
return driverRepository.findById(id).map(driverMapper::toDTO);
}

public Optional<DriverResponseDTO> getDriverByLicenseNumber(String licenseNumber) {
return driverRepository.findByLicenseNumber(licenseNumber).map(driverMapper::toDTO);
}

public List<DriverResponseDTO> getAllDrivers() {
return driverRepository.findAll().stream()
.map(driverMapper::toDTO)
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -68,13 +72,40 @@ public ProblemDetail handleUserException(UserException e) {
return handle(e, HttpStatus.BAD_REQUEST);
}

@ResponseStatus(HttpStatus.FORBIDDEN)
@ExceptionHandler(AuthorizationDeniedException.class)
public ProblemDetail handleAuthorizationDeniedException(AuthorizationDeniedException e) {
log.warn(e.getMessage(), HttpStatus.FORBIDDEN, e);
return handle(e, HttpStatus.FORBIDDEN);
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ProblemDetail handleIllegalArgumentException(IllegalArgumentException e) {
log.warn(e.getMessage(), HttpStatus.BAD_REQUEST, e);
return handle(e, HttpStatus.BAD_REQUEST);
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageNotReadableException.class)
public ProblemDetail handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
log.warn(e.getMessage(), HttpStatus.BAD_REQUEST, e);
return handle(e, HttpStatus.BAD_REQUEST);
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ProblemDetail handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
log.warn(e.getMessage(), HttpStatus.BAD_REQUEST, e);
return handle(e, HttpStatus.BAD_REQUEST);
}

@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(NoResourceFoundException.class)
public ProblemDetail handleNoResourceFoundException(NoResourceFoundException e) {
return handle(e, HttpStatus.NOT_FOUND);
}

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public ProblemDetail handleException(Exception e) {
Expand Down
49 changes: 49 additions & 0 deletions backend/src/main/java/com/angel/autonow/order/OrderController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.angel.autonow.order;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/orders")
public class OrderController {

private final OrderService orderService;

@PostMapping
@PreAuthorize("hasAnyRole('ADMIN', 'CUSTOMER')")
public ResponseEntity<OrderResponseDTO> createOrder(@Valid @RequestBody OrderRequestDTO request) {
return orderService.createOrder(request)
.map(order -> ResponseEntity.status(HttpStatus.CREATED).body(order))
.orElse(ResponseEntity.badRequest().build());
}

@GetMapping("/{id}")
@PreAuthorize("hasAnyRole('ADMIN', 'CUSTOMER', 'DRIVER')")
public OrderResponseDTO getOrderById(@PathVariable Long id) {
return orderService.getOrderById(id).orElse(null);
}
Comment thread
StoynovAngel marked this conversation as resolved.

@GetMapping("/user/{userId}")
@PreAuthorize("hasAnyRole('ADMIN', 'CUSTOMER')")
public List<OrderResponseDTO> getOrdersByUserId(@PathVariable Long userId) {
return orderService.getOrdersByUserId(userId);
}

@GetMapping
@PreAuthorize("hasRole('ADMIN')")
public List<OrderResponseDTO> getAllOrders() {
return orderService.getAllOrders();
}
}
24 changes: 24 additions & 0 deletions backend/src/main/java/com/angel/autonow/order/OrderMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.angel.autonow.order;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(componentModel = "spring")
public interface OrderMapper {

@Mapping(source = "user.id", target = "userId")
@Mapping(source = "driver.id", target = "driverId")
@Mapping(source = "vehicle.id", target = "vehicleId")
OrderResponseDTO toDTO(OrderEntity order);

@Mapping(target = "id", ignore = true)
@Mapping(target = "user", ignore = true)
@Mapping(target = "driver", ignore = true)
@Mapping(target = "vehicle", ignore = true)
@Mapping(target = "status", ignore = true)
@Mapping(target = "finalPrice", ignore = true)
@Mapping(target = "cancellationReason", ignore = true)
@Mapping(target = "createdAt", ignore = true)
@Mapping(target = "updatedAt", ignore = true)
OrderEntity toEntity(OrderRequestDTO request);
}
12 changes: 12 additions & 0 deletions backend/src/main/java/com/angel/autonow/order/OrderRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.angel.autonow.order;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface OrderRepository extends JpaRepository<OrderEntity, Long> {

List<OrderEntity> findByUserId(Long userId);
}
50 changes: 50 additions & 0 deletions backend/src/main/java/com/angel/autonow/order/OrderRequestDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.angel.autonow.order;

import com.angel.autonow.vehicle.VehicleType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;

public record OrderRequestDTO(

@NotNull(message = "User ID is required")
Long userId,

Long driverId,

Long vehicleId,

@NotNull(message = "Vehicle type is required")
VehicleType vehicleType,

@NotBlank(message = "Pickup address is required")
String pickupAddress,

@NotNull(message = "Pickup latitude is required")
Double pickupLatitude,

@NotNull(message = "Pickup longitude is required")
Double pickupLongitude,

@NotBlank(message = "Dropoff address is required")
String dropoffAddress,

@NotNull(message = "Dropoff latitude is required")
Double dropoffLatitude,

@NotNull(message = "Dropoff longitude is required")
Double dropoffLongitude,

@Positive(message = "Estimated price must be positive")
Double estimatedPrice,

@Positive(message = "Distance must be positive")
Double distanceKm,

@Positive(message = "Estimated duration must be positive")
Integer estimatedDurationMinutes,

String specialRequirements
) {

}
Loading
Loading