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
@@ -1,17 +1,22 @@
package com.shipping.freightops.controller;

import com.shipping.freightops.dto.ContainerLabelResponse;
import com.shipping.freightops.dto.ContainerResponse;
import com.shipping.freightops.dto.CreateContainerRequest;
import com.shipping.freightops.entity.Container;
import com.shipping.freightops.enums.ContainerSize;
import com.shipping.freightops.enums.ContainerType;
import com.shipping.freightops.service.ContainerService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import java.net.URI;
import java.util.List;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/containers")
Expand All @@ -23,6 +28,46 @@ public ContainerController(ContainerService containerService) {
this.containerService = containerService;
}

/** Register a new container. */
@Operation(summary = "Register a new container")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "Container successfully created"),
@ApiResponse(responseCode = "400", description = "Invalid request data"),
@ApiResponse(responseCode = "409", description = "Container code already exists")
})
@PostMapping
public ResponseEntity<ContainerResponse> create(
@Valid @RequestBody CreateContainerRequest request) {
Container container = containerService.createContainer(request);
ContainerResponse body = ContainerResponse.fromEntity(container);
URI location = URI.create("/api/v1/containers/" + container.getId());
return ResponseEntity.created(location).body(body);
}

/** List all containers, optionally filtered by size and/or type. */
@Operation(summary = "List all containers with optional size and type filters")
@ApiResponses({@ApiResponse(responseCode = "200", description = "List of containers returned")})
@GetMapping
public ResponseEntity<List<ContainerResponse>> list(
@RequestParam(required = false) ContainerSize size,
@RequestParam(required = false) ContainerType type) {
List<Container> containers = containerService.getAllContainers(size, type);
List<ContainerResponse> body = containers.stream().map(ContainerResponse::fromEntity).toList();
return ResponseEntity.ok(body);
}

/** Get a single container by ID. */
@Operation(summary = "Get a container by ID")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Container found"),
@ApiResponse(responseCode = "404", description = "Container not found")
})
@GetMapping("/{id}")
public ResponseEntity<ContainerResponse> getById(@PathVariable Long id) {
Container container = containerService.getContainerById(id);
return ResponseEntity.ok(ContainerResponse.fromEntity(container));
}

@Operation(
summary = "Get PDF label for a container by ID",
description = "Returns a PDF document containing the container label")
Expand Down
53 changes: 53 additions & 0 deletions src/main/java/com/shipping/freightops/dto/ContainerResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.shipping.freightops.dto;

import com.shipping.freightops.entity.Container;
import com.shipping.freightops.enums.ContainerSize;
import com.shipping.freightops.enums.ContainerType;
import java.time.LocalDateTime;

/** Read-only view of a container returned by the API. */
public class ContainerResponse {

private Long id;
private String containerCode;
private ContainerSize size;
private ContainerType type;
private int teu;
private LocalDateTime createdAt;

/** Factory method to map entity → response DTO. */
public static ContainerResponse fromEntity(Container container) {
ContainerResponse dto = new ContainerResponse();
dto.id = container.getId();
dto.containerCode = container.getContainerCode();
dto.size = container.getSize();
dto.type = container.getType();
dto.teu = container.getTeu();
dto.createdAt = container.getCreatedAt();
return dto;
}

public Long getId() {
return id;
}

public String getContainerCode() {
return containerCode;
}

public ContainerSize getSize() {
return size;
}

public ContainerType getType() {
return type;
}

public int getTeu() {
return teu;
}

public LocalDateTime getCreatedAt() {
return createdAt;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.shipping.freightops.dto;

import com.shipping.freightops.enums.ContainerSize;
import com.shipping.freightops.enums.ContainerType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public class CreateContainerRequest {

@NotBlank(message = "Container code is required")
@Size(min = 11, max = 11, message = "Container code must be exactly 11 characters")
private String containerCode;

/** Must be one of: TWENTY_FOOT, FORTY_FOOT. */
@NotNull(message = "Container size is required")
private ContainerSize size;

/** Must be one of: DRY, REEFER, OPEN_TOP, FLAT_RACK, TANK. */
@NotNull(message = "Container type is required")
private ContainerType type;

public String getContainerCode() {
return containerCode;
}

public void setContainerCode(String containerCode) {
this.containerCode = containerCode;
}

public ContainerSize getSize() {
return size;
}

public void setSize(ContainerSize size) {
this.size = size;
}

public ContainerType getType() {
return type;
}

public void setType(ContainerType type) {
this.type = type;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
package com.shipping.freightops.repository;

import com.shipping.freightops.entity.Container;
import com.shipping.freightops.enums.ContainerSize;
import com.shipping.freightops.enums.ContainerType;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ContainerRepository extends JpaRepository<Container, Long> {

Optional<Container> findByContainerCode(String containerCode);

boolean existsByContainerCode(String containerCode);

List<Container> findBySize(ContainerSize size);

List<Container> findByType(ContainerType type);

List<Container> findBySizeAndType(ContainerSize size, ContainerType type);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@
import com.itextpdf.text.pdf.PdfWriter;
import com.shipping.freightops.config.AppProperties;
import com.shipping.freightops.dto.ContainerLabelResponse;
import com.shipping.freightops.dto.CreateContainerRequest;
import com.shipping.freightops.entity.Container;
import com.shipping.freightops.entity.FreightOrder;
import com.shipping.freightops.enums.ContainerSize;
import com.shipping.freightops.enums.ContainerType;
import com.shipping.freightops.enums.OrderStatus;
import com.shipping.freightops.exception.PdfGenerationException;
import com.shipping.freightops.repository.ContainerRepository;
import com.shipping.freightops.repository.FreightOrderRepository;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -57,6 +61,39 @@ public ContainerService(
this.appProperties = appProperties;
}

@Transactional
public Container createContainer(CreateContainerRequest request) {
if (containerRepository.existsByContainerCode(request.getContainerCode())) {
throw new IllegalStateException(
"Container code already exists: " + request.getContainerCode());
}

Container container =
new Container(request.getContainerCode(), request.getSize(), request.getType());
return containerRepository.save(container);
}

@Transactional(readOnly = true)
public List<Container> getAllContainers(ContainerSize size, ContainerType type) {
if (size != null && type != null) {
return containerRepository.findBySizeAndType(size, type);
}
if (size != null) {
return containerRepository.findBySize(size);
}
if (type != null) {
return containerRepository.findByType(type);
}
return containerRepository.findAll();
}

@Transactional(readOnly = true)
public Container getContainerById(Long id) {
return containerRepository
.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Container not found: " + id));
}

@Transactional(readOnly = true)
public ContainerLabelResponse generateContainerLabel(long containerId) {

Expand Down
Loading
Loading