fix(backend): move IOException into service, add content-type whitelist to attachFile
- DocumentService.attachFile() now catches IOException internally and re-throws as DomainException.internal — the IOException no longer leaks through the service boundary - DocumentController.attachFile() is now a plain delegate (no try/catch) - ALLOWED_CONTENT_TYPES whitelist (PDF/JPEG/PNG/TIFF) is now enforced on the attachFile endpoint, matching the existing quick-upload validation - Added 5 DocumentService unit tests for attachFile (notFound, status transition PLACEHOLDER→UPLOADED, no-change when already UPLOADED, field assignment from upload result, IOException→DomainException) - Added controller tests: 400 on disallowed content type, 404 on missing doc Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -131,23 +131,23 @@ public class DocumentController {
|
||||
|
||||
// --- ATTACH FILE ---
|
||||
|
||||
private static final Set<String> ALLOWED_CONTENT_TYPES = Set.of(
|
||||
"application/pdf", "image/jpeg", "image/png", "image/tiff");
|
||||
|
||||
@PostMapping(value = "/{id}/file", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
@RequirePermission(Permission.WRITE_ALL)
|
||||
public Document attachFile(
|
||||
@PathVariable UUID id,
|
||||
@RequestPart("file") MultipartFile file) {
|
||||
try {
|
||||
return documentService.attachFile(id, file);
|
||||
} catch (IOException e) {
|
||||
throw DomainException.internal(ErrorCode.FILE_UPLOAD_FAILED, "Failed to upload file: " + e.getMessage());
|
||||
String contentType = file.getContentType();
|
||||
if (contentType == null || !ALLOWED_CONTENT_TYPES.contains(contentType)) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Unsupported file type: " + contentType);
|
||||
}
|
||||
return documentService.attachFile(id, file);
|
||||
}
|
||||
|
||||
// --- QUICK UPLOAD ---
|
||||
|
||||
private static final Set<String> ALLOWED_CONTENT_TYPES = Set.of(
|
||||
"application/pdf", "image/jpeg", "image/png", "image/tiff");
|
||||
|
||||
public record UploadError(String filename, String code) {}
|
||||
public record QuickUploadResult(List<Document> created, List<Document> updated, List<UploadError> errors) {}
|
||||
|
||||
|
||||
@@ -287,10 +287,15 @@ public class DocumentService {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Document attachFile(UUID id, MultipartFile file) throws IOException {
|
||||
public Document attachFile(UUID id, MultipartFile file) {
|
||||
Document doc = documentRepository.findById(id)
|
||||
.orElseThrow(() -> DomainException.notFound(ErrorCode.DOCUMENT_NOT_FOUND, "Document not found: " + id));
|
||||
FileService.UploadResult upload = fileService.uploadFile(file, file.getOriginalFilename());
|
||||
FileService.UploadResult upload;
|
||||
try {
|
||||
upload = fileService.uploadFile(file, file.getOriginalFilename());
|
||||
} catch (IOException e) {
|
||||
throw DomainException.internal(ErrorCode.FILE_UPLOAD_FAILED, "Failed to upload file: " + e.getMessage());
|
||||
}
|
||||
doc.setFilePath(upload.s3Key());
|
||||
doc.setFileHash(upload.fileHash());
|
||||
doc.setOriginalFilename(file.getOriginalFilename());
|
||||
|
||||
Reference in New Issue
Block a user