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
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package pl.skyroster.skyroster_backend.application.flight;

import lombok.val;
import org.springframework.stereotype.Service;
import pl.skyroster.skyroster_backend.domain.exception.FlightNotFoundException;
import pl.skyroster.skyroster_backend.domain.exception.FlightTimeConflictException;
import pl.skyroster.skyroster_backend.domain.port.FlightRepository;

import java.time.OffsetDateTime;
import java.util.UUID;

@Service
public class DeleteFlightUseCase {
private final FlightRepository flightRepository;

public DeleteFlightUseCase(FlightRepository flightRepository) {
this.flightRepository = flightRepository;
}

public void execute(UUID flightId) {
val flight = flightRepository.findById(flightId).orElseThrow(FlightNotFoundException::new);
val isHappening = flight.getFlightStart().isBefore(OffsetDateTime.now());
if (isHappening) throw new FlightTimeConflictException("Deleting ongoing flight is not allowed");
flightRepository.delete(flight);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package pl.skyroster.skyroster_backend.application.rule;

import org.springframework.stereotype.Service;
import pl.skyroster.skyroster_backend.domain.model.Rule;
import pl.skyroster.skyroster_backend.domain.port.RuleRepository;
import pl.skyroster.skyroster_backend.generated.model.RuleRequest;
import pl.skyroster.skyroster_backend.infrastructure.mappers.RuleMapper;

@Service
public class CreateRuleUseCase {
private final RuleRepository ruleRepository;

public CreateRuleUseCase(RuleRepository ruleRepository) {
this.ruleRepository = ruleRepository;
}

public Rule execute(RuleRequest ruleRequest) {
Rule rule = RuleMapper.fromRequest(ruleRequest);
return ruleRepository.save(rule);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package pl.skyroster.skyroster_backend.domain.exception;

public class FlightNotFoundException extends RuntimeException {
public FlightNotFoundException() {
super("Flight not found");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@

public interface RuleRepository {
List<Rule> findAll();
Rule save(Rule rule);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@

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 org.springframework.web.bind.annotation.*;
import pl.skyroster.skyroster_backend.application.flight.CreateFlightUseCase;
import pl.skyroster.skyroster_backend.application.flight.DeleteFlightUseCase;
import pl.skyroster.skyroster_backend.application.flight.GetFlightsUseCase;
import pl.skyroster.skyroster_backend.domain.model.Flight;
import pl.skyroster.skyroster_backend.generated.api.ApiApi;
Expand All @@ -15,16 +13,19 @@
import pl.skyroster.skyroster_backend.infrastructure.mappers.FlightResponseMapper;

import java.util.List;
import java.util.UUID;

@RestController
public class FlightController {

private final GetFlightsUseCase getFlightsUseCase;
private final CreateFlightUseCase createFlightUseCase;
private final DeleteFlightUseCase deleteFlightUseCase;

public FlightController(GetFlightsUseCase getFlightsUseCase, CreateFlightUseCase createFlightUseCase) {
public FlightController(GetFlightsUseCase getFlightsUseCase, CreateFlightUseCase createFlightUseCase, DeleteFlightUseCase deleteFlightUseCase) {
this.getFlightsUseCase = getFlightsUseCase;
this.createFlightUseCase = createFlightUseCase;
this.deleteFlightUseCase = deleteFlightUseCase;
}

@GetMapping(ApiApi.PATH_DISPLAY_FLIGHTS)
Expand All @@ -45,4 +46,10 @@ public ResponseEntity<FlightResponse> createFlight(@RequestBody CreateFlightRequ
);
return ResponseEntity.status(HttpStatus.CREATED).body(FlightResponseMapper.map(flight));
}

@DeleteMapping(ApiApi.PATH_DELETE_FLIGHT)
public ResponseEntity<Void> deleteAircraft(@PathVariable("id") UUID id) {
deleteFlightUseCase.execute(id);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
package pl.skyroster.skyroster_backend.infrastructure.adapter.in.web;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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.rule.CreateRuleUseCase;
import pl.skyroster.skyroster_backend.application.rule.GetRulesUseCase;
import pl.skyroster.skyroster_backend.domain.model.Rule;
import pl.skyroster.skyroster_backend.generated.api.ApiApi;
import pl.skyroster.skyroster_backend.generated.model.RuleRequest;
import pl.skyroster.skyroster_backend.generated.model.RuleResponse;
import pl.skyroster.skyroster_backend.infrastructure.mappers.RuleResponseMapper;
import pl.skyroster.skyroster_backend.infrastructure.mappers.RuleMapper;

import java.util.List;

Expand All @@ -16,12 +23,20 @@
public class RuleController {

private final GetRulesUseCase getRulesUseCase;
private final CreateRuleUseCase createRuleUseCase;

@GetMapping(ApiApi.PATH_GET_RULES)
public ResponseEntity<List<RuleResponse>> getRules() {
List<RuleResponse> response = getRulesUseCase.execute().stream()
.map(RuleResponseMapper::map)
.map(RuleMapper::toResponse)
.toList();
return ResponseEntity.ok(response);
}

@PostMapping(ApiApi.PATH_CREATE_RULE)
public ResponseEntity<RuleResponse> createRule(@RequestBody @Valid RuleRequest request) {
Rule rule = createRuleUseCase.execute(request);
return ResponseEntity.status(HttpStatus.CREATED)
.body(RuleMapper.toResponse(rule));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ public ResponseEntity<ErrorResponse> handleAircraftHasAssignedFlights(AircraftHa
return buildResponse(HttpStatus.CONFLICT, ex.getMessage(), request);
}

@ExceptionHandler(FlightNotFoundException.class)
public ResponseEntity<ErrorResponse> handleFlightNotFound(FlightNotFoundException ex,
HttpServletRequest request) {
return buildResponse(HttpStatus.NOT_FOUND, ex.getMessage(), request);
}

@ExceptionHandler(FlightTimeConflictException.class)
public ResponseEntity<ErrorResponse> handleFlightTimeConflict(FlightTimeConflictException ex,
HttpServletRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) {
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.requestMatchers("/api/compliance/**").hasRole("compliance_officer")
.requestMatchers("/api/planning/**").hasRole("schedule_planner")
.requestMatchers(HttpMethod.DELETE,"/api/flights/**").hasRole("schedule_planner")
.requestMatchers(HttpMethod.POST, "/api/aircraft/**").hasRole("operations_administrator")
.requestMatchers(HttpMethod.GET, "/api/aircraft/**").authenticated()
.requestMatchers(HttpMethod.DELETE, "/api/pilots/**").hasRole("operations_administrator")
.requestMatchers(HttpMethod.PATCH, "/api/pilots/**").hasRole("operations_administrator")
.requestMatchers(HttpMethod.POST, "/api/pilots/**").hasRole("operations_administrator")
.requestMatchers(HttpMethod.POST,"/api/rule/**").hasRole("compliance_officer")
.requestMatchers("/api/**").authenticated()
.anyRequest().denyAll()
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package pl.skyroster.skyroster_backend.infrastructure.mappers;

import pl.skyroster.skyroster_backend.domain.model.Rule;
import pl.skyroster.skyroster_backend.generated.model.RulePeriod;
import pl.skyroster.skyroster_backend.generated.model.RuleResponse;
import pl.skyroster.skyroster_backend.generated.model.RuleType;

import java.util.UUID;

public class RuleMapper {
public static RuleResponse toResponse(Rule rule) {
return new RuleResponse(
rule.getId(),
rule.getName(),
RuleType.valueOf(rule.getType().name()),
rule.getValue(),
RulePeriod.valueOf(rule.getPeriod().name())
).description(rule.getDescription());
}

public static Rule fromRequest(pl.skyroster.skyroster_backend.generated.model.RuleRequest request) {
pl.skyroster.skyroster_backend.domain.model.RuleType type = switch (request.getType()){
case MAX_WORK_TIME -> pl.skyroster.skyroster_backend.domain.model.RuleType.MAX_WORK_TIME;
case MIN_REST_TIME -> pl.skyroster.skyroster_backend.domain.model.RuleType.MIN_REST_TIME;
case MIN_FLIGHT_TIME -> pl.skyroster.skyroster_backend.domain.model.RuleType.MIN_FLIGHT_TIME;
};
pl.skyroster.skyroster_backend.domain.model.RulePeriod period = switch (request.getPeriod()){
case DAY -> pl.skyroster.skyroster_backend.domain.model.RulePeriod.DAY;
case WEEK -> pl.skyroster.skyroster_backend.domain.model.RulePeriod.WEEK;
case MONTH -> pl.skyroster.skyroster_backend.domain.model.RulePeriod.MONTH;
};
return new Rule(
UUID.randomUUID(),
request.getName(),
type,
request.getValue(),
period,
request.getDescription()
);
}
}

This file was deleted.

102 changes: 102 additions & 0 deletions skyroster-backend/src/main/resources/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,41 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
post:
summary: Create a new rule
operationId: createRule
tags: [ Rules ]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RuleRequest'
example:
type: MAX_WORK_TIME
period: WEEK
value: 40
name: Weekly work limit
description: Maximum work time per week in hours
responses:
'201':
description: Rule created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/RuleResponse'
'400':
description: Invalid request body
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'409':
description: Rule with given name already exists
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'

/api/flights:
get:
Expand Down Expand Up @@ -440,6 +475,46 @@ paths:
schema:
$ref: '#/components/schemas/RuleViolationErrorResponse'

/api/flights/{id}:
delete:
operationId: deleteFlight
tags: [ Flights ]
summary: Delete a flight
parameters:
- in: path
name: id
required: true
schema:
type: string
format: uuid
responses:
'204':
description: Flight deleted
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: Flight not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'409':
description: Flight is already happening
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'

/api/aircraft/{id}:
put:
operationId: updateAircraft
Expand Down Expand Up @@ -997,6 +1072,33 @@ components:
description:
type: string

RuleRequest:
type: object
required:
- type
- period
- value
- name
properties:
type:
$ref: '#/components/schemas/RuleType'
period:
$ref: '#/components/schemas/RulePeriod'
value:
type: integer
description: Numeric value of the rule (e.g. hours, minutes)
example: 40
name:
type: string
maxLength: 100
description: Optional human-readable name of the rule
example: Weekly work limit
description:
type: string
maxLength: 500
description: Optional detailed description of the rule
example: Maximum work time per week in hours

FlightResponse:
type: object
required:
Expand Down
Loading