EquipmentQueryController.java
package com.github.jenkaby.bikerental.equipment.web.query.controller;
import com.github.jenkaby.bikerental.equipment.application.usecase.GetEquipmentByIdUseCase;
import com.github.jenkaby.bikerental.equipment.application.usecase.GetEquipmentBySerialNumberUseCase;
import com.github.jenkaby.bikerental.equipment.application.usecase.GetEquipmentByUidUseCase;
import com.github.jenkaby.bikerental.equipment.application.usecase.SearchEquipmentsUseCase;
import com.github.jenkaby.bikerental.equipment.domain.model.Equipment;
import com.github.jenkaby.bikerental.equipment.shared.domain.model.vo.SerialNumber;
import com.github.jenkaby.bikerental.equipment.shared.domain.model.vo.Uid;
import com.github.jenkaby.bikerental.equipment.web.query.dto.EquipmentResponse;
import com.github.jenkaby.bikerental.equipment.web.query.mapper.EquipmentQueryMapper;
import com.github.jenkaby.bikerental.shared.config.OpenApiConfig;
import com.github.jenkaby.bikerental.shared.domain.model.vo.Page;
import com.github.jenkaby.bikerental.shared.exception.ResourceNotFoundException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ProblemDetail;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@Validated
@Slf4j
@RestController
@RequestMapping("/api/equipments")
@Tag(name = OpenApiConfig.Tags.EQUIPMENT)
public class EquipmentQueryController {
private final GetEquipmentByIdUseCase getById;
private final GetEquipmentByUidUseCase getByUid;
private final GetEquipmentBySerialNumberUseCase getBySerial;
private final SearchEquipmentsUseCase searchUseCase;
private final EquipmentQueryMapper mapper;
EquipmentQueryController(GetEquipmentByIdUseCase getById,
GetEquipmentByUidUseCase getByUid,
GetEquipmentBySerialNumberUseCase getBySerial,
SearchEquipmentsUseCase searchUseCase,
EquipmentQueryMapper mapper) {
this.getById = getById;
this.getByUid = getByUid;
this.getBySerial = getBySerial;
this.searchUseCase = searchUseCase;
this.mapper = mapper;
}
@GetMapping("/{id}")
@Operation(summary = "Get equipment by ID")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Equipment found",
content = @Content(schema = @Schema(implementation = EquipmentResponse.class))),
@ApiResponse(responseCode = "404", description = "Equipment not found",
content = @Content(schema = @Schema(implementation = ProblemDetail.class)))
})
public ResponseEntity<EquipmentResponse> getEquipmentById(
@Parameter(description = "Equipment ID", example = "1") @PathVariable("id") Long id) {
log.info("[GET] Get equipment by id {}", id);
return getById.execute(id)
.map(mapper::toResponse)
.map(ResponseEntity::ok)
.orElseThrow(() -> new ResourceNotFoundException(Equipment.class, id));
}
@GetMapping("/by-uid/{uid}")
@Operation(summary = "Get equipment by UID")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Equipment found",
content = @Content(schema = @Schema(implementation = EquipmentResponse.class))),
@ApiResponse(responseCode = "404", description = "Equipment not found",
content = @Content(schema = @Schema(implementation = ProblemDetail.class)))
})
public ResponseEntity<EquipmentResponse> getEquipmentByUid(
@Parameter(description = "Equipment UID", example = "BIKE-001") @PathVariable("uid") String uid) {
log.info("[GET] Get equipment by uid {}", uid);
var result = getByUid.execute(new Uid(uid));
return result.map(mapper::toResponse)
.map(ResponseEntity::ok)
.orElseThrow(() -> new ResourceNotFoundException(Equipment.class, uid));
}
@GetMapping("/by-serial/{serialNumber}")
@Operation(summary = "Get equipment by serial number")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Equipment found",
content = @Content(schema = @Schema(implementation = EquipmentResponse.class))),
@ApiResponse(responseCode = "404", description = "Equipment not found",
content = @Content(schema = @Schema(implementation = ProblemDetail.class)))
})
public ResponseEntity<EquipmentResponse> getEquipmentBySerial(
@Parameter(description = "Serial number", example = "SN-123456") @PathVariable("serialNumber") String serialNumber) {
log.info("[GET] Get equipment by serial number{}", serialNumber);
var result = getBySerial.execute(new SerialNumber(serialNumber));
return result.map(mapper::toResponse)
.map(ResponseEntity::ok)
.orElseThrow(() -> new ResourceNotFoundException(Equipment.class, serialNumber));
}
@GetMapping
@Operation(summary = "Search equipment", description = "Returns paginated equipment list filtered by status and/or type")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Equipment page returned"),
@ApiResponse(responseCode = "400", description = "Invalid filter parameters",
content = @Content(schema = @Schema(implementation = ProblemDetail.class)))
})
public ResponseEntity<Page<EquipmentResponse>> searchEquipments(
@Parameter(description = "Status slug filter", example = "available") @RequestParam(name = "status", required = false) String status,
@Parameter(description = "Type slug filter", example = "bike") @RequestParam(name = "type", required = false) String type,
@PageableDefault(size = 20, sort = "serialNumber", direction = Sort.Direction.ASC) Pageable pageable) {
log.info("[GET] Search equipments filters status={} type={}", status, type);
var query = mapper.toSearchQuery(status, type, pageable);
var page = searchUseCase.execute(query).map(mapper::toResponse);
return ResponseEntity.ok(page);
}
}