Skip to content

Commit 131a0d3

Browse files
committed
1.0
1 parent 6671829 commit 131a0d3

File tree

56 files changed

+850
-861
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+850
-861
lines changed

.idea/misc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rest/src/main/java/at/fhtw/rest/api/DocumentController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package at.fhtw.rest.api;
22

3-
import at.fhtw.rest.core.imp.IDocumentService;
3+
import at.fhtw.rest.core.DocumentService;
44
import io.swagger.v3.oas.annotations.Operation;
55
import jakarta.validation.constraints.NotBlank;
66
import lombok.RequiredArgsConstructor;
@@ -22,7 +22,7 @@
2222
@RequiredArgsConstructor
2323
public class DocumentController {
2424

25-
private final IDocumentService documentService;
25+
private final DocumentService documentService;
2626

2727
@Operation(summary = "Upload a new document")
2828
@PostMapping
Lines changed: 8 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,134 +1,16 @@
11
package at.fhtw.rest.core;
22

33
import at.fhtw.rest.api.DocumentRequest;
4-
import at.fhtw.rest.core.imp.IDocumentService;
5-
import at.fhtw.rest.core.imp.IElasticsearchService;
6-
import at.fhtw.rest.infrastructure.mapper.imp.IDocumentMapper;
7-
import at.fhtw.rest.message.imp.IProcessingEventDispatcher;
8-
import at.fhtw.rest.persistence.DocumentEntity;
9-
import at.fhtw.rest.persistence.imp.IDocumentRepository;
10-
import at.fhtw.rest.persistence.imp.IMinioStorageService;
11-
import lombok.RequiredArgsConstructor;
12-
import lombok.extern.slf4j.Slf4j;
13-
import org.springframework.stereotype.Service;
144
import org.springframework.web.multipart.MultipartFile;
15-
165
import java.io.IOException;
17-
import java.time.LocalDateTime;
18-
import java.util.Collections;
196
import java.util.List;
20-
import java.util.Optional;
21-
import java.util.UUID;
22-
import java.util.stream.Collectors;
23-
24-
/**
25-
* <p>
26-
* For more details on the underlying technologies, refer to:
27-
* <a href="https://min.io/docs/minio/linux/developers/java/minio-java.html" target="_blank">MinIO Documentation</a>,
28-
* <a href="https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/index.html" target="_blank">
29-
* Elasticsearch Java API Documentation</a>,
30-
* <a href="https://spring.io/projects/spring-framework" target="_blank">Spring Framework Documentation</a>,
31-
* and <a href="https://www.rabbitmq.com/docs/queues" target="_blank">RabbitMQ Documentation</a>.
32-
* </p>
33-
*/
34-
35-
@Slf4j
36-
@Service
37-
@RequiredArgsConstructor
38-
public class DocumentService implements IDocumentService {
39-
40-
private final IDocumentRepository documentRepository;
41-
private final IDocumentMapper mapper;
42-
private final IMinioStorageService minioStorageService;
43-
private final IProcessingEventDispatcher processingEventDispatcher;
44-
private final IElasticsearchService elasticsearchService;
45-
46-
@Override
47-
public DocumentRequest uploadFile(MultipartFile file) throws IOException {
48-
if (file.isEmpty()) {
49-
throw new IllegalArgumentException("File must not be empty");
50-
}
51-
String docId = generateDocId();
52-
minioStorageService.storeFile(docId, file);
53-
DocumentEntity entity = new DocumentEntity();
54-
entity.setId(docId);
55-
entity.setFilename(file.getOriginalFilename());
56-
entity.setUploadDate(LocalDateTime.now());
57-
entity.setFilesize(file.getSize());
58-
entity.setFiletype(file.getContentType());
59-
documentRepository.save(entity);
60-
DocumentRequest request = mapper.toDto(entity);
61-
processingEventDispatcher.sendProcessingRequest(docId, file.getOriginalFilename());
62-
return request;
63-
}
64-
65-
@Override
66-
public DocumentRequest renameFile(String docId, String newName) {
67-
String sanitized = ensurePdfExtension(newName);
68-
elasticsearchService.updateFilename(docId, sanitized);
69-
DocumentEntity entity = documentRepository.findById(docId)
70-
.orElseThrow(() -> new IllegalArgumentException("Document not found: " + docId));
71-
entity.setFilename(sanitized);
72-
documentRepository.save(entity);
73-
return mapper.toDto(entity);
74-
}
75-
76-
@Override
77-
public byte[] getFileBytes(String docId) {
78-
return minioStorageService.loadFile(docId)
79-
.orElseThrow(() -> new IllegalArgumentException("File not found: " + docId));
80-
}
81-
82-
@Override
83-
public void deleteDocument(String docId) {
84-
minioStorageService.deleteFile(docId);
85-
documentRepository.deleteById(docId);
86-
elasticsearchService.deleteDocument(docId);
87-
}
88-
89-
@Override
90-
public List<DocumentRequest> getAllDocuments() {
91-
return documentRepository.findAll().stream().map(mapper::toDto).collect(Collectors.toList());
92-
}
93-
94-
@Override
95-
public DocumentRequest getDocument(String docId) {
96-
DocumentEntity entity = documentRepository.findById(docId)
97-
.orElseThrow(() -> new IllegalArgumentException("Document not found: " + docId));
98-
return mapper.toDto(entity);
99-
}
100-
101-
@Override
102-
public List<DocumentRequest> searchDocuments(String query) {
103-
List<String> docIds = elasticsearchService.searchIdsByQuery(query);
104-
if (docIds == null) {
105-
docIds = Collections.emptyList();
106-
}
107-
return docIds.stream()
108-
.map(documentRepository::findById)
109-
.filter(Optional::isPresent)
110-
.map(Optional::get)
111-
.map(mapper::toDto)
112-
.collect(Collectors.toList());
113-
}
114-
115-
private String generateDocId() {
116-
return UUID.randomUUID().toString();
117-
}
1187

119-
public String ensurePdfExtension(String name) {
120-
String lower = name.toLowerCase();
121-
while (lower.endsWith(".pdf.pdf")) {
122-
name = name.substring(0, name.length() - 4);
123-
lower = name.toLowerCase();
124-
}
125-
if (lower.endsWith(".pdf")) {
126-
return name;
127-
}
128-
int dotPos = name.lastIndexOf('.');
129-
if (dotPos != -1) {
130-
name = name.substring(0, dotPos);
131-
}
132-
return name + ".pdf";
133-
}
8+
public interface DocumentService {
9+
DocumentRequest uploadFile(MultipartFile file) throws IOException;
10+
DocumentRequest renameFile(String docId, String newName) throws IOException;
11+
byte[] getFileBytes(String docId);
12+
void deleteDocument(String docId);
13+
DocumentRequest getDocument(String docId);
14+
List<DocumentRequest> searchDocuments(String query);
15+
List<DocumentRequest> getAllDocuments();
13416
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package at.fhtw.rest.core;
2+
3+
import at.fhtw.rest.api.DocumentRequest;
4+
import at.fhtw.rest.infrastructure.mapper.DocumentMapper;
5+
import at.fhtw.rest.message.ProcessingEventDispatcher;
6+
import at.fhtw.rest.persistence.DocumentEntity;
7+
import at.fhtw.rest.persistence.DocumentRepository;
8+
import at.fhtw.rest.persistence.MinioStorageService;
9+
import lombok.RequiredArgsConstructor;
10+
import lombok.extern.slf4j.Slf4j;
11+
import org.springframework.stereotype.Service;
12+
import org.springframework.web.multipart.MultipartFile;
13+
14+
import java.io.IOException;
15+
import java.time.LocalDateTime;
16+
import java.util.Collections;
17+
import java.util.List;
18+
import java.util.Optional;
19+
import java.util.UUID;
20+
import java.util.stream.Collectors;
21+
22+
/**
23+
* <p>
24+
* For more details on the underlying technologies, refer to:
25+
* <a href="https://min.io/docs/minio/linux/developers/java/minio-java.html" target="_blank">MinIO Documentation</a>,
26+
* <a href="https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/index.html" target="_blank">
27+
* Elasticsearch Java API Documentation</a>,
28+
* <a href="https://spring.io/projects/spring-framework" target="_blank">Spring Framework Documentation</a>,
29+
* and <a href="https://www.rabbitmq.com/docs/queues" target="_blank">RabbitMQ Documentation</a>.
30+
* </p>
31+
*/
32+
33+
@Slf4j
34+
@Service
35+
@RequiredArgsConstructor
36+
public class DocumentServiceImp implements DocumentService {
37+
38+
private final DocumentRepository documentRepository;
39+
private final DocumentMapper mapper;
40+
private final MinioStorageService minioStorageService;
41+
private final ProcessingEventDispatcher processingEventDispatcher;
42+
private final ElasticsearchService elasticsearchService;
43+
44+
@Override
45+
public DocumentRequest uploadFile(MultipartFile file) throws IOException {
46+
if (file.isEmpty()) {
47+
throw new IllegalArgumentException("File must not be empty");
48+
}
49+
String docId = generateDocId();
50+
minioStorageService.storeFile(docId, file);
51+
DocumentEntity entity = new DocumentEntity();
52+
entity.setId(docId);
53+
entity.setFilename(file.getOriginalFilename());
54+
entity.setUploadDate(LocalDateTime.now());
55+
entity.setFilesize(file.getSize());
56+
entity.setFiletype(file.getContentType());
57+
documentRepository.save(entity);
58+
DocumentRequest request = mapper.toDto(entity);
59+
processingEventDispatcher.sendProcessingRequest(docId, file.getOriginalFilename());
60+
return request;
61+
}
62+
63+
@Override
64+
public DocumentRequest renameFile(String docId, String newName) {
65+
String sanitized = ensurePdfExtension(newName);
66+
elasticsearchService.updateFilename(docId, sanitized);
67+
DocumentEntity entity = documentRepository.findById(docId)
68+
.orElseThrow(() -> new IllegalArgumentException("Document not found: " + docId));
69+
entity.setFilename(sanitized);
70+
documentRepository.save(entity);
71+
return mapper.toDto(entity);
72+
}
73+
74+
@Override
75+
public byte[] getFileBytes(String docId) {
76+
return minioStorageService.loadFile(docId)
77+
.orElseThrow(() -> new IllegalArgumentException("File not found: " + docId));
78+
}
79+
80+
@Override
81+
public void deleteDocument(String docId) {
82+
minioStorageService.deleteFile(docId);
83+
documentRepository.deleteById(docId);
84+
elasticsearchService.deleteDocument(docId);
85+
}
86+
87+
@Override
88+
public List<DocumentRequest> getAllDocuments() {
89+
return documentRepository.findAll().stream().map(mapper::toDto).collect(Collectors.toList());
90+
}
91+
92+
@Override
93+
public DocumentRequest getDocument(String docId) {
94+
DocumentEntity entity = documentRepository.findById(docId)
95+
.orElseThrow(() -> new IllegalArgumentException("Document not found: " + docId));
96+
return mapper.toDto(entity);
97+
}
98+
99+
@Override
100+
public List<DocumentRequest> searchDocuments(String query) {
101+
List<String> docIds = elasticsearchService.searchIdsByQuery(query);
102+
if (docIds == null) {
103+
docIds = Collections.emptyList();
104+
}
105+
return docIds.stream()
106+
.map(documentRepository::findById)
107+
.filter(Optional::isPresent)
108+
.map(Optional::get)
109+
.map(mapper::toDto)
110+
.collect(Collectors.toList());
111+
}
112+
113+
private String generateDocId() {
114+
return UUID.randomUUID().toString();
115+
}
116+
117+
public String ensurePdfExtension(String name) {
118+
String lower = name.toLowerCase();
119+
while (lower.endsWith(".pdf.pdf")) {
120+
name = name.substring(0, name.length() - 4);
121+
lower = name.toLowerCase();
122+
}
123+
if (lower.endsWith(".pdf")) {
124+
return name;
125+
}
126+
int dotPos = name.lastIndexOf('.');
127+
if (dotPos != -1) {
128+
name = name.substring(0, dotPos);
129+
}
130+
return name + ".pdf";
131+
}
132+
}
Lines changed: 5 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,9 @@
11
package at.fhtw.rest.core;
22

3-
import at.fhtw.rest.core.imp.IElasticsearchService;
4-
import co.elastic.clients.elasticsearch.ElasticsearchClient;
5-
import co.elastic.clients.elasticsearch._types.ElasticsearchException;
6-
import co.elastic.clients.elasticsearch.core.DeleteRequest;
7-
import co.elastic.clients.elasticsearch.core.UpdateRequest;
8-
import co.elastic.clients.elasticsearch.core.search.Hit;
9-
import lombok.extern.slf4j.Slf4j;
10-
import org.springframework.beans.factory.annotation.Value;
11-
import org.springframework.stereotype.Service;
12-
import java.io.IOException;
13-
import java.time.Instant;
14-
import java.util.*;
3+
import java.util.List;
154

16-
@Slf4j
17-
@Service
18-
public class ElasticsearchService implements IElasticsearchService {
19-
20-
private final ElasticsearchClient esClient;
21-
private final String indexName;
22-
23-
public ElasticsearchService(ElasticsearchClient esClient,
24-
@Value("${elasticsearch.index-name:documents}") String indexName) {
25-
this.esClient = esClient;
26-
this.indexName = indexName;
27-
}
28-
29-
@Override
30-
public void updateFilename(String docId, String newFilename) {
31-
Map<String, Object> updateFields = new HashMap<>();
32-
updateFields.put("filename", newFilename);
33-
updateFields.put("last_modified", Instant.now().toString());
34-
try {
35-
esClient.update(
36-
UpdateRequest.of(u -> u
37-
.index(indexName)
38-
.id(docId)
39-
.doc(updateFields)
40-
),
41-
Map.class
42-
);
43-
} catch (IOException e) {
44-
log.error("Error updating filename for document {}: {}", docId, e.getMessage());
45-
}
46-
}
47-
48-
@Override
49-
public void deleteDocument(String docId) {
50-
try {
51-
esClient.delete(DeleteRequest.of(d -> d.index(indexName).id(docId)));
52-
} catch (IOException e) {
53-
log.error("Error deleting document {}: {}", docId, e.getMessage());
54-
}
55-
}
56-
57-
@Override
58-
public List<String> searchIdsByQuery(String query) {
59-
if (query == null || query.isBlank()) {
60-
return Collections.emptyList();
61-
}
62-
try {
63-
var response = esClient.search(
64-
s -> s
65-
.index(indexName)
66-
.allowNoIndices(true)
67-
.ignoreUnavailable(true)
68-
.query(q -> q.multiMatch(mm -> mm
69-
.fields("filename", "ocrText")
70-
.query(query)
71-
.fuzziness("AUTO")
72-
))
73-
.size(50),
74-
Map.class
75-
);
76-
return response.hits().hits().stream().map(Hit::id).toList();
77-
} catch (ElasticsearchException | IOException e) {
78-
log.error("Search failed for query {}: {}", query, e.getMessage());
79-
return Collections.emptyList();
80-
}
81-
}
5+
public interface ElasticsearchService {
6+
void updateFilename(String docId, String newFilename);
7+
void deleteDocument(String docId);
8+
List<String> searchIdsByQuery(String query);
829
}

0 commit comments

Comments
 (0)