UploadsController.java
/*
* Copyright (C) 2024 B3Partners B.V.
*
* SPDX-License-Identifier: MIT
*/
package org.tailormap.api.controller;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.NOT_MODIFIED;
import static org.tailormap.api.util.Constants.UUID_REGEX;
import jakarta.servlet.http.HttpServletRequest;
import java.time.OffsetDateTime;
import java.time.temporal.ChronoField;
import java.util.UUID;
import org.springframework.http.CacheControl;
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.RestController;
import org.springframework.web.server.ResponseStatusException;
import org.tailormap.api.persistence.Upload;
import org.tailormap.api.repository.UploadRepository;
@RestController
public class UploadsController {
private final UploadRepository uploadRepository;
public UploadsController(UploadRepository uploadRepository) {
this.uploadRepository = uploadRepository;
}
// Can't use ${tailormap-api.base-path} because linkTo() won't work
@GetMapping(path = "/api/uploads/{category}/{id}/{filename}")
public ResponseEntity<byte[]> getUpload(
HttpServletRequest request,
@PathVariable String category,
@PathVariable(name = "id") String idString,
@PathVariable(required = false) String filename) {
if (!idString.matches(UUID_REGEX)) {
return ResponseEntity.badRequest().build();
}
UUID id = UUID.fromString(idString);
long ifModifiedSince = request.getDateHeader("If-Modified-Since");
if (ifModifiedSince != -1) {
OffsetDateTime uploadLastModified =
uploadRepository.findLastModifiedById(id).orElseThrow(() -> new ResponseStatusException(NOT_FOUND));
if (ifModifiedSince
>= uploadLastModified
.with(ChronoField.MILLI_OF_SECOND, 0)
.toInstant()
.toEpochMilli()) {
return ResponseEntity.status(NOT_MODIFIED).build();
}
}
Upload upload = uploadRepository
.findWithContentByIdAndCategory(id, category)
.orElseThrow(() -> new ResponseStatusException(NOT_FOUND));
return ResponseEntity.ok()
.header("Content-Type", upload.getMimeType())
.lastModified(upload.getLastModified().toInstant())
.contentLength(upload.getContentLength())
.cacheControl(CacheControl.noCache().cachePublic())
.body(upload.getContent());
}
/**
* Gets the latest upload for a specific category, if any. This is most useful for
* {@code Upload.CATEGORY_DRAWING_STYLE} .
*/
@GetMapping("/api/uploads/{category}/latest")
public ResponseEntity<byte[]> getLatestUpload(@PathVariable String category) {
return uploadRepository
.findFirstWithContentByCategoryOrderByLastModifiedDesc(category)
.map(upload -> ResponseEntity.ok()
.header("Content-Type", upload.getMimeType())
.lastModified(upload.getLastModified().toInstant())
.contentLength(upload.getContentLength())
.cacheControl(CacheControl.noCache().cachePublic())
.body(upload.getContent()))
.orElseThrow(() -> new ResponseStatusException(NOT_FOUND));
}
}