feat(backend): add POST /api/documents/{id}/file endpoint to attach file to existing document
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -129,6 +129,20 @@ public class DocumentController {
|
|||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- ATTACH FILE ---
|
||||||
|
|
||||||
|
@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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- QUICK UPLOAD ---
|
// --- QUICK UPLOAD ---
|
||||||
|
|
||||||
private static final Set<String> ALLOWED_CONTENT_TYPES = Set.of(
|
private static final Set<String> ALLOWED_CONTENT_TYPES = Set.of(
|
||||||
|
|||||||
@@ -286,6 +286,23 @@ public class DocumentService {
|
|||||||
return documentRepository.save(doc);
|
return documentRepository.save(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Document attachFile(UUID id, MultipartFile file) throws IOException {
|
||||||
|
Document doc = documentRepository.findById(id)
|
||||||
|
.orElseThrow(() -> DomainException.notFound(ErrorCode.DOCUMENT_NOT_FOUND, "Document not found: " + id));
|
||||||
|
FileService.UploadResult upload = fileService.uploadFile(file, file.getOriginalFilename());
|
||||||
|
doc.setFilePath(upload.s3Key());
|
||||||
|
doc.setFileHash(upload.fileHash());
|
||||||
|
doc.setOriginalFilename(file.getOriginalFilename());
|
||||||
|
doc.setContentType(file.getContentType());
|
||||||
|
if (doc.getStatus() == DocumentStatus.PLACEHOLDER) {
|
||||||
|
doc.setStatus(DocumentStatus.UPLOADED);
|
||||||
|
}
|
||||||
|
Document saved = documentRepository.save(doc);
|
||||||
|
documentVersionService.recordVersion(saved);
|
||||||
|
return saved;
|
||||||
|
}
|
||||||
|
|
||||||
// 0. Zuletzt aktive Dokumente (sortiert nach updatedAt DESC)
|
// 0. Zuletzt aktive Dokumente (sortiert nach updatedAt DESC)
|
||||||
public List<Document> getRecentActivity(int size) {
|
public List<Document> getRecentActivity(int size) {
|
||||||
return documentRepository.findAll(
|
return documentRepository.findAll(
|
||||||
|
|||||||
@@ -563,6 +563,36 @@ class DocumentControllerTest {
|
|||||||
.andExpect(status().isBadRequest());
|
.andExpect(status().isBadRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── POST /api/documents/{id}/file ───────────────────────────────────────
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser
|
||||||
|
void attachFile_returns403_whenMissingWritePermission() throws Exception {
|
||||||
|
org.springframework.mock.web.MockMultipartFile file =
|
||||||
|
new org.springframework.mock.web.MockMultipartFile("file", "brief.pdf", "application/pdf", new byte[]{1});
|
||||||
|
|
||||||
|
mockMvc.perform(multipart("/api/documents/" + UUID.randomUUID() + "/file").file(file))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = "WRITE_ALL")
|
||||||
|
void attachFile_returns200_withUpdatedDocument_whenHasWritePermission() throws Exception {
|
||||||
|
UUID id = UUID.randomUUID();
|
||||||
|
Document doc = Document.builder()
|
||||||
|
.id(id).title("Brief").originalFilename("brief.pdf")
|
||||||
|
.filePath("docs/brief.pdf").status(DocumentStatus.UPLOADED).build();
|
||||||
|
when(documentService.attachFile(eq(id), any())).thenReturn(doc);
|
||||||
|
|
||||||
|
org.springframework.mock.web.MockMultipartFile file =
|
||||||
|
new org.springframework.mock.web.MockMultipartFile("file", "brief.pdf", "application/pdf", new byte[]{1});
|
||||||
|
|
||||||
|
mockMvc.perform(multipart("/api/documents/" + id + "/file").file(file))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.id").value(id.toString()))
|
||||||
|
.andExpect(jsonPath("$.status").value("UPLOADED"));
|
||||||
|
}
|
||||||
|
|
||||||
// ─── GET /api/documents/{id}/versions/{versionId} ────────────────────────
|
// ─── GET /api/documents/{id}/versions/{versionId} ────────────────────────
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user