Skip to content
Open
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
42 changes: 37 additions & 5 deletions docs/collection/skyroster.postman_collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,43 @@
]
},
{
"name": "Admin",
"name": "Aircraft",
"item": [
{
"name": "GET /api/admin/users",
"name": "POST /api/aircraft",
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{token}}",
"type": "text"
},
{
"key": "Content-Type",
"value": "application/json",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"registrationNumber\": \"SP-LRA\",\n \"aircraftTypeCode\": \"B738\",\n \"operationalBaseCode\": \"EPWA\"\n}"
},
"url": {
"raw": "{{base_url}}/api/aircraft",
"host": [
"{{base_url}}"
],
"path": [
"api",
"aircraft"
]
}
},
"response": []
},
{
"name": "GET /api/aircraft",
"request": {
"method": "GET",
"header": [
Expand All @@ -213,14 +246,13 @@
}
],
"url": {
"raw": "{{base_url}}/api/admin/users",
"raw": "{{base_url}}/api/aircraft",
"host": [
"{{base_url}}"
],
"path": [
"api",
"admin",
"users"
"aircraft"
]
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package pl.skyroster.skyroster_backend.application.aircraft;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pl.skyroster.skyroster_backend.domain.exception.AircraftAlreadyExistsException;
import pl.skyroster.skyroster_backend.domain.exception.AircraftTypeNotFoundException;
import pl.skyroster.skyroster_backend.domain.exception.OperationalBaseNotFoundException;
import pl.skyroster.skyroster_backend.domain.model.Aircraft;
import pl.skyroster.skyroster_backend.domain.model.AircraftType;
import pl.skyroster.skyroster_backend.domain.model.OperationalBase;
import pl.skyroster.skyroster_backend.domain.port.AircraftRepository;
import pl.skyroster.skyroster_backend.domain.port.AircraftTypeRepository;
import pl.skyroster.skyroster_backend.domain.port.OperationalBaseRepository;

import java.time.LocalDateTime;
import java.util.UUID;

@Service
public class AddAircraftUseCase {

private final AircraftRepository aircraftRepository;
private final AircraftTypeRepository aircraftTypeRepository;
private final OperationalBaseRepository operationalBaseRepository;

public AddAircraftUseCase(AircraftRepository aircraftRepository,
AircraftTypeRepository aircraftTypeRepository,
OperationalBaseRepository operationalBaseRepository) {
this.aircraftRepository = aircraftRepository;
this.aircraftTypeRepository = aircraftTypeRepository;
this.operationalBaseRepository = operationalBaseRepository;
}

@Transactional
public Aircraft execute(String registrationNumber, String aircraftTypeCode, String operationalBaseCode) {
if (registrationNumber == null || registrationNumber.isBlank()) {
throw new IllegalArgumentException("Registration number must not be blank");
}

if (aircraftRepository.existsByRegistrationNumber(registrationNumber)) {
throw new AircraftAlreadyExistsException(registrationNumber);
}

AircraftType type = aircraftTypeRepository.findByIcaoCode(aircraftTypeCode)
.orElseThrow(() -> new AircraftTypeNotFoundException(aircraftTypeCode));

OperationalBase base = operationalBaseRepository.findByIcaoCode(operationalBaseCode)
.orElseThrow(() -> new OperationalBaseNotFoundException(operationalBaseCode));

Aircraft aircraft = new Aircraft(UUID.randomUUID(), registrationNumber, LocalDateTime.now(), type, base);
return aircraftRepository.save(aircraft);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package pl.skyroster.skyroster_backend.application.aircraft;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pl.skyroster.skyroster_backend.domain.model.Aircraft;
import pl.skyroster.skyroster_backend.domain.port.AircraftRepository;

import java.util.List;

@Service
public class GetAircraftUseCase {

private final AircraftRepository aircraftRepository;

public GetAircraftUseCase(AircraftRepository aircraftRepository) {
this.aircraftRepository = aircraftRepository;
}

@Transactional(readOnly = true)
public List<Aircraft> execute() {
return aircraftRepository.findAll();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package pl.skyroster.skyroster_backend.domain.exception;

public class AircraftAlreadyExistsException extends RuntimeException {
public AircraftAlreadyExistsException(String registrationNumber) {
super("Aircraft with registration number '%s' already exists".formatted(registrationNumber));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package pl.skyroster.skyroster_backend.domain.exception;

public class AircraftTypeNotFoundException extends RuntimeException {
public AircraftTypeNotFoundException(String icaoCode) {
super("Aircraft type with code '%s' not found".formatted(icaoCode));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package pl.skyroster.skyroster_backend.domain.exception;

public class OperationalBaseNotFoundException extends RuntimeException {
public OperationalBaseNotFoundException(String icaoCode) {
super("Operational base with code '%s' not found".formatted(icaoCode));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package pl.skyroster.skyroster_backend.domain.model;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.UUID;

@Entity
@Table(name = "aircraft")
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Aircraft {

@Id
private UUID id;

@Column(name = "registration_number", nullable = false, unique = true, length = 15)
private String registrationNumber;

@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "aircraft_type_id", nullable = false)
private AircraftType aircraftType;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "operational_base_id", nullable = false)
private OperationalBase operationalBase;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package pl.skyroster.skyroster_backend.domain.model;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.UUID;

@Entity
@Table(name = "aircraft_types")
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class AircraftType {

@Id
private UUID id;

@Column(name = "icao_code", nullable = false, unique = true, length = 4)
private String icaoCode;

@Column(nullable = false, length = 100)
private String name;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package pl.skyroster.skyroster_backend.domain.model;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.UUID;

@Entity
@Table(name = "operational_bases")
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class OperationalBase {

@Id
private UUID id;

@Column(name = "icao_code", nullable = false, unique = true, length = 4)
private String icaoCode;

@Column(nullable = false, length = 100)
private String name;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package pl.skyroster.skyroster_backend.domain.port;

import pl.skyroster.skyroster_backend.domain.model.Aircraft;

import java.util.List;

public interface AircraftRepository {
Aircraft save(Aircraft aircraft);
List<Aircraft> findAll();
boolean existsByRegistrationNumber(String registrationNumber);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package pl.skyroster.skyroster_backend.domain.port;

import pl.skyroster.skyroster_backend.domain.model.AircraftType;

import java.util.Optional;

public interface AircraftTypeRepository {
Optional<AircraftType> findByIcaoCode(String icaoCode);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package pl.skyroster.skyroster_backend.domain.port;

import pl.skyroster.skyroster_backend.domain.model.OperationalBase;

import java.util.Optional;

public interface OperationalBaseRepository {
Optional<OperationalBase> findByIcaoCode(String icaoCode);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package pl.skyroster.skyroster_backend.infrastructure.adapter.in.web;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import pl.skyroster.skyroster_backend.application.aircraft.AddAircraftUseCase;
import pl.skyroster.skyroster_backend.application.aircraft.GetAircraftUseCase;
import pl.skyroster.skyroster_backend.domain.model.Aircraft;
import pl.skyroster.skyroster_backend.generated.api.ApiApi;
import pl.skyroster.skyroster_backend.generated.model.AddAircraftRequest;
import pl.skyroster.skyroster_backend.generated.model.AircraftResponse;
import pl.skyroster.skyroster_backend.generated.model.AircraftTypeInfo;
import pl.skyroster.skyroster_backend.generated.model.OperationalBaseInfo;

import java.util.List;

@RestController
public class AircraftController {

private final AddAircraftUseCase addAircraftUseCase;
private final GetAircraftUseCase getAircraftUseCase;

public AircraftController(AddAircraftUseCase addAircraftUseCase, GetAircraftUseCase getAircraftUseCase) {
this.addAircraftUseCase = addAircraftUseCase;
this.getAircraftUseCase = getAircraftUseCase;
}

@PostMapping(ApiApi.PATH_ADD_AIRCRAFT)
public ResponseEntity<AircraftResponse> addAircraft(@RequestBody AddAircraftRequest request) {
Aircraft aircraft = addAircraftUseCase.execute(
request.getRegistrationNumber(),
request.getAircraftTypeCode(),
request.getOperationalBaseCode()
);
return ResponseEntity.status(HttpStatus.CREATED).body(toResponse(aircraft));
}

@GetMapping(ApiApi.PATH_GET_AIRCRAFT)
public ResponseEntity<List<AircraftResponse>> getAircraft() {
List<AircraftResponse> response = getAircraftUseCase.execute().stream()
.map(this::toResponse)
.toList();
return ResponseEntity.ok(response);
}

private AircraftResponse toResponse(Aircraft aircraft) {
return new AircraftResponse()
.id(aircraft.getId())
.registrationNumber(aircraft.getRegistrationNumber())
.aircraftType(new AircraftTypeInfo()
.id(aircraft.getAircraftType().getId())
.icaoCode(aircraft.getAircraftType().getIcaoCode())
.name(aircraft.getAircraftType().getName()))
.operationalBase(new OperationalBaseInfo()
.id(aircraft.getOperationalBase().getId())
.icaoCode(aircraft.getOperationalBase().getIcaoCode())
.name(aircraft.getOperationalBase().getName()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package pl.skyroster.skyroster_backend.infrastructure.adapter.out.persistence;

import org.springframework.data.jpa.repository.JpaRepository;
import pl.skyroster.skyroster_backend.domain.model.Aircraft;
import pl.skyroster.skyroster_backend.domain.port.AircraftRepository;

import java.util.UUID;

public interface JpaAircraftRepository extends JpaRepository<Aircraft, UUID>, AircraftRepository {
}
Loading