diff --git a/src/main/java/com/slotline/availability/AvailabilityController.java b/src/main/java/com/slotline/availability/AvailabilityController.java new file mode 100644 index 0000000..cf5ec2b --- /dev/null +++ b/src/main/java/com/slotline/availability/AvailabilityController.java @@ -0,0 +1,80 @@ +package com.slotline.availability; + +import com.slotline.availability.dto.BlackoutRequest; +import com.slotline.availability.dto.BlackoutResponse; +import com.slotline.availability.dto.OperatingHoursRequest; +import com.slotline.availability.dto.OperatingHoursResponse; +import com.slotline.security.AuthenticatedUser; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.DeleteMapping; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/api/resources/{resourceId}") +@Tag(name = "Availability rules") +public class AvailabilityController { + + private final AvailabilityService availabilityService; + + public AvailabilityController(AvailabilityService availabilityService) { + this.availabilityService = availabilityService; + } + + @GetMapping("/operating-hours") + @Operation(summary = "List operating hours for a resource") + public List operatingHours(@AuthenticationPrincipal AuthenticatedUser actor, + @PathVariable Long resourceId) { + return availabilityService.listOperatingHours(actor.organizationId(), resourceId).stream() + .map(OperatingHoursResponse::from) + .toList(); + } + + @PutMapping("/operating-hours") + @Operation(summary = "Replace the operating-hours schedule for a resource (ADMIN only)") + public List replaceOperatingHours(@AuthenticationPrincipal AuthenticatedUser actor, + @PathVariable Long resourceId, + @Valid @RequestBody List windows) { + return availabilityService.replaceOperatingHours(actor.organizationId(), resourceId, windows).stream() + .map(OperatingHoursResponse::from) + .toList(); + } + + @GetMapping("/blackouts") + @Operation(summary = "List blackout ranges for a resource") + public List blackouts(@AuthenticationPrincipal AuthenticatedUser actor, + @PathVariable Long resourceId) { + return availabilityService.listBlackouts(actor.organizationId(), resourceId).stream() + .map(BlackoutResponse::from) + .toList(); + } + + @PostMapping("/blackouts") + @ResponseStatus(HttpStatus.CREATED) + @Operation(summary = "Add a blackout range for a resource (ADMIN only)") + public BlackoutResponse addBlackout(@AuthenticationPrincipal AuthenticatedUser actor, + @PathVariable Long resourceId, + @Valid @RequestBody BlackoutRequest request) { + return BlackoutResponse.from(availabilityService.addBlackout(actor.organizationId(), resourceId, request)); + } + + @DeleteMapping("/blackouts/{blackoutId}") + @ResponseStatus(HttpStatus.NO_CONTENT) + @Operation(summary = "Remove a blackout range (ADMIN only)") + public void removeBlackout(@AuthenticationPrincipal AuthenticatedUser actor, @PathVariable Long resourceId, + @PathVariable Long blackoutId) { + availabilityService.removeBlackout(actor.organizationId(), resourceId, blackoutId); + } +} diff --git a/src/main/java/com/slotline/availability/dto/BlackoutRequest.java b/src/main/java/com/slotline/availability/dto/BlackoutRequest.java new file mode 100644 index 0000000..04d0910 --- /dev/null +++ b/src/main/java/com/slotline/availability/dto/BlackoutRequest.java @@ -0,0 +1,12 @@ +package com.slotline.availability.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +import java.time.LocalDate; + +public record BlackoutRequest( + @NotNull LocalDate startDate, + @NotNull LocalDate endDate, + @Size(max = 256) String reason) { +} diff --git a/src/main/java/com/slotline/availability/dto/BlackoutResponse.java b/src/main/java/com/slotline/availability/dto/BlackoutResponse.java new file mode 100644 index 0000000..ac7bc81 --- /dev/null +++ b/src/main/java/com/slotline/availability/dto/BlackoutResponse.java @@ -0,0 +1,13 @@ +package com.slotline.availability.dto; + +import com.slotline.availability.BlackoutDate; + +import java.time.LocalDate; + +public record BlackoutResponse(Long id, LocalDate startDate, LocalDate endDate, String reason) { + + public static BlackoutResponse from(BlackoutDate blackout) { + return new BlackoutResponse(blackout.getId(), blackout.getStartDate(), blackout.getEndDate(), + blackout.getReason()); + } +} diff --git a/src/main/java/com/slotline/availability/dto/OperatingHoursRequest.java b/src/main/java/com/slotline/availability/dto/OperatingHoursRequest.java new file mode 100644 index 0000000..0f8efff --- /dev/null +++ b/src/main/java/com/slotline/availability/dto/OperatingHoursRequest.java @@ -0,0 +1,13 @@ +package com.slotline.availability.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.validation.constraints.NotNull; + +import java.time.DayOfWeek; +import java.time.LocalTime; + +public record OperatingHoursRequest( + @NotNull DayOfWeek dayOfWeek, + @NotNull @JsonFormat(pattern = "HH:mm") LocalTime openTime, + @NotNull @JsonFormat(pattern = "HH:mm") LocalTime closeTime) { +} diff --git a/src/main/java/com/slotline/availability/dto/OperatingHoursResponse.java b/src/main/java/com/slotline/availability/dto/OperatingHoursResponse.java new file mode 100644 index 0000000..6e27431 --- /dev/null +++ b/src/main/java/com/slotline/availability/dto/OperatingHoursResponse.java @@ -0,0 +1,19 @@ +package com.slotline.availability.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.slotline.availability.OperatingHours; + +import java.time.DayOfWeek; +import java.time.LocalTime; + +public record OperatingHoursResponse( + Long id, + DayOfWeek dayOfWeek, + @JsonFormat(pattern = "HH:mm") LocalTime openTime, + @JsonFormat(pattern = "HH:mm") LocalTime closeTime) { + + public static OperatingHoursResponse from(OperatingHours hours) { + return new OperatingHoursResponse(hours.getId(), hours.getDayOfWeek(), hours.getOpenTime(), + hours.getCloseTime()); + } +}