| 1 | package com.edtech.service; | |
| 2 | ||
| 3 | import com.edtech.dto.DocumentResponseDto; | |
| 4 | import com.edtech.model.AuditAction; | |
| 5 | import com.edtech.model.Document; | |
| 6 | import com.edtech.model.DocumentStatus; | |
| 7 | import com.edtech.model.Project; | |
| 8 | import com.edtech.model.ProjectMember; | |
| 9 | import com.edtech.model.ProjectRole; | |
| 10 | import com.edtech.model.User; | |
| 11 | import com.edtech.repository.DocumentCommentRepository; | |
| 12 | import com.edtech.repository.DocumentRepository; | |
| 13 | import com.edtech.repository.ProjectMemberRepository; | |
| 14 | import com.edtech.repository.ProjectRepository; | |
| 15 | import com.edtech.repository.UserRepository; | |
| 16 | import com.fasterxml.jackson.databind.ObjectMapper; | |
| 17 | import java.io.IOException; | |
| 18 | import java.util.Locale; | |
| 19 | import java.util.Map; | |
| 20 | import java.util.Set; | |
| 21 | import java.util.UUID; | |
| 22 | import org.apache.tika.Tika; | |
| 23 | import org.springframework.data.domain.Page; | |
| 24 | import org.springframework.data.domain.Pageable; | |
| 25 | import org.springframework.stereotype.Service; | |
| 26 | import org.springframework.transaction.annotation.Transactional; | |
| 27 | import org.springframework.web.multipart.MultipartFile; | |
| 28 | ||
| 29 | /** Documentacao para DocumentService. */ | |
| 30 | @Service | |
| 31 | public class DocumentService { | |
| 32 | ||
| 33 | private static final String MIME_PDF = "application/pdf"; | |
| 34 | private static final String MIME_CSV = "text/csv"; | |
| 35 | private static final String MIME_JSON = "application/json"; | |
| 36 | private static final Map<String, String> ALLOWED_MIME_BY_EXTENSION = | |
| 37 | Map.of(".pdf", MIME_PDF, ".csv", MIME_CSV, ".json", MIME_JSON); | |
| 38 | private static final Set<String> CSV_COMPATIBLE_DETECTED_TYPES = | |
| 39 | Set.of(MIME_CSV, "text/plain", "application/csv", "application/vnd.ms-excel"); | |
| 40 | private static final Set<String> JSON_COMPATIBLE_DETECTED_TYPES = Set.of(MIME_JSON, "text/plain"); | |
| 41 | ||
| 42 | private final DocumentRepository documentRepository; | |
| 43 | private final ProjectRepository projectRepository; | |
| 44 | private final UserRepository userRepository; | |
| 45 | private final ProjectMemberRepository projectMemberRepository; | |
| 46 | private final DocumentCommentRepository documentCommentRepository; | |
| 47 | private final AuditLogService auditLogService; | |
| 48 | private final StorageService storageService; | |
| 49 | private final NotificationService notificationService; | |
| 50 | private final Tika tika = new Tika(); | |
| 51 | private final ObjectMapper objectMapper = new ObjectMapper(); | |
| 52 | private final ClamAvService clamAvService; | |
| 53 | ||
| 54 | /** Documentacao. */ | |
| 55 | public DocumentService( | |
| 56 | DocumentRepository documentRepository, | |
| 57 | ProjectRepository projectRepository, | |
| 58 | UserRepository userRepository, | |
| 59 | ProjectMemberRepository projectMemberRepository, | |
| 60 | DocumentCommentRepository documentCommentRepository, | |
| 61 | AuditLogService auditLogService, | |
| 62 | StorageService storageService, | |
| 63 | NotificationService notificationService, | |
| 64 | ClamAvService clamAvService) { | |
| 65 | this.documentRepository = documentRepository; | |
| 66 | this.projectRepository = projectRepository; | |
| 67 | this.userRepository = userRepository; | |
| 68 | this.projectMemberRepository = projectMemberRepository; | |
| 69 | this.documentCommentRepository = documentCommentRepository; | |
| 70 | this.auditLogService = auditLogService; | |
| 71 | this.storageService = storageService; | |
| 72 | this.notificationService = notificationService; | |
| 73 | this.clamAvService = clamAvService; | |
| 74 | } | |
| 75 | ||
| 76 | /** Documentacao. */ | |
| 77 | @Transactional | |
| 78 | public DocumentResponseDto uploadDocument( | |
| 79 | MultipartFile file, String title, UUID projectId, UUID authorId) { | |
| 80 | User author = | |
| 81 | userRepository | |
| 82 | .findById(authorId) | |
| 83 |
1
1. lambda$uploadDocument$0 : replaced return value with null for com/edtech/service/DocumentService::lambda$uploadDocument$0 → KILLED |
.orElseThrow(() -> new RuntimeException("Author not found")); |
| 84 | Project project = | |
| 85 | projectRepository | |
| 86 | .findById(projectId) | |
| 87 |
1
1. lambda$uploadDocument$1 : replaced return value with null for com/edtech/service/DocumentService::lambda$uploadDocument$1 → KILLED |
.orElseThrow(() -> new RuntimeException("Project not found")); |
| 88 | ||
| 89 | projectMemberRepository | |
| 90 | .findByProjectIdAndUserId(projectId, authorId) | |
| 91 |
1
1. lambda$uploadDocument$2 : replaced return value with null for com/edtech/service/DocumentService::lambda$uploadDocument$2 → KILLED |
.orElseThrow(() -> new RuntimeException("Author is not a member of the project")); |
| 92 | ||
| 93 | try { | |
| 94 | String originalFilename = file.getOriginalFilename(); | |
| 95 |
1
1. uploadDocument : negated conditional → KILLED |
if (originalFilename == null) { |
| 96 | throw new IllegalArgumentException("Filename cannot be null"); | |
| 97 | } | |
| 98 | ||
| 99 | String contentType = validateAllowedFile(file, originalFilename); | |
| 100 | ||
| 101 |
1
1. uploadDocument : negated conditional → KILLED |
if (!clamAvService.isFileSafe(file)) { |
| 102 | throw new IllegalArgumentException( | |
| 103 | "Upload rejeitado: Assinatura de vírus detectada no arquivo."); | |
| 104 | } | |
| 105 | ||
| 106 | String fileKey = UUID.randomUUID() + "_" + originalFilename; | |
| 107 | ||
| 108 |
1
1. uploadDocument : removed call to com/edtech/service/StorageService::uploadFile → KILLED |
storageService.uploadFile(file, fileKey, contentType); |
| 109 | ||
| 110 | Document document = new Document(); | |
| 111 |
1
1. uploadDocument : removed call to com/edtech/model/Document::setTitle → KILLED |
document.setTitle(title); |
| 112 |
1
1. uploadDocument : removed call to com/edtech/model/Document::setFileUrl → KILLED |
document.setFileUrl(fileKey); |
| 113 |
1
1. uploadDocument : removed call to com/edtech/model/Document::setStatus → KILLED |
document.setStatus(DocumentStatus.PENDING_REVIEW); |
| 114 |
1
1. uploadDocument : removed call to com/edtech/model/Document::setAuthor → KILLED |
document.setAuthor(author); |
| 115 |
1
1. uploadDocument : removed call to com/edtech/model/Document::setProject → KILLED |
document.setProject(project); |
| 116 | ||
| 117 | document = documentRepository.save(document); | |
| 118 | ||
| 119 | auditLogService.logDocumentAction( | |
| 120 | authorId, | |
| 121 | AuditAction.UPLOAD_SUCCESS, | |
| 122 | document.getId(), | |
| 123 | "Documento anexado no Cloud Storage: " + title); | |
| 124 | ||
| 125 | DocumentResponseDto responseDto = mapToDto(document); | |
| 126 |
1
1. uploadDocument : removed call to com/edtech/service/NotificationService::sendToTopic → SURVIVED |
notificationService.sendToTopic( |
| 127 | "/topic/projects/" + projectId, | |
| 128 | Map.of("type", "DOCUMENT_UPLOADED", "document", responseDto)); | |
| 129 | ||
| 130 |
1
1. uploadDocument : replaced return value with null for com/edtech/service/DocumentService::uploadDocument → KILLED |
return responseDto; |
| 131 | } catch (IOException e) { | |
| 132 | throw new RuntimeException("Falha ao analisar o conteudo do arquivo", e); | |
| 133 | } catch (IllegalArgumentException e) { | |
| 134 | throw e; | |
| 135 | } catch (Exception e) { | |
| 136 | throw new RuntimeException("Failed to upload file to Cloud Storage: " + e.getMessage()); | |
| 137 | } | |
| 138 | } | |
| 139 | ||
| 140 | /** Documentacao para o metodo getPresignedUrl. */ | |
| 141 | public String getPresignedUrl(UUID documentId, UUID userId) { | |
| 142 | Document document = | |
| 143 | documentRepository | |
| 144 | .findById(documentId) | |
| 145 |
1
1. lambda$getPresignedUrl$3 : replaced return value with null for com/edtech/service/DocumentService::lambda$getPresignedUrl$3 → NO_COVERAGE |
.orElseThrow(() -> new RuntimeException("Document not found")); |
| 146 | ||
| 147 | projectMemberRepository | |
| 148 | .findByProjectIdAndUserId(document.getProject().getId(), userId) | |
| 149 | .orElseThrow( | |
| 150 |
1
1. lambda$getPresignedUrl$4 : replaced return value with null for com/edtech/service/DocumentService::lambda$getPresignedUrl$4 → KILLED |
() -> new RuntimeException("Access denied: You are not a member of this project")); |
| 151 | ||
| 152 | String presignedUrl; | |
| 153 | try { | |
| 154 | presignedUrl = storageService.getPresignedUrl(document.getFileUrl()); | |
| 155 | } catch (Exception e) { | |
| 156 | throw new RuntimeException("Failed to generate presigned URL", e); | |
| 157 | } | |
| 158 | ||
| 159 | auditLogService.logDocumentAction( | |
| 160 | userId, | |
| 161 | AuditAction.DOWNLOAD, | |
| 162 | documentId, | |
| 163 | "Gerada URL presigned para download: " + document.getTitle()); | |
| 164 |
1
1. getPresignedUrl : replaced return value with "" for com/edtech/service/DocumentService::getPresignedUrl → KILLED |
return presignedUrl; |
| 165 | } | |
| 166 | ||
| 167 | /** Documentacao. */ | |
| 168 | public Page<DocumentResponseDto> listDocumentsByUser( | |
| 169 | UUID userId, UUID projectId, String title, DocumentStatus status, Pageable pageable) { | |
| 170 |
1
1. listDocumentsByUser : replaced return value with null for com/edtech/service/DocumentService::listDocumentsByUser → KILLED |
return documentRepository |
| 171 | .findDocumentsByUserIdAndFilters(userId, projectId, title, status, pageable) | |
| 172 | .map(this::mapToDto); | |
| 173 | } | |
| 174 | ||
| 175 | /** Documentacao. */ | |
| 176 | @Transactional | |
| 177 | public void deleteDocument(UUID documentId, UUID userId) { | |
| 178 | Document document = | |
| 179 | documentRepository | |
| 180 | .findById(documentId) | |
| 181 |
1
1. lambda$deleteDocument$5 : replaced return value with null for com/edtech/service/DocumentService::lambda$deleteDocument$5 → KILLED |
.orElseThrow(() -> new RuntimeException("Document not found")); |
| 182 | ||
| 183 |
1
1. deleteDocument : negated conditional → KILLED |
if (!document.getAuthor().getId().equals(userId)) { |
| 184 | throw new RuntimeException("Only the author can delete this document"); | |
| 185 | } | |
| 186 |
1
1. deleteDocument : negated conditional → KILLED |
if (document.getStatus() == DocumentStatus.APPROVED) { |
| 187 | throw new RuntimeException("APPROVED documents cannot be deleted"); | |
| 188 | } | |
| 189 | ||
| 190 | try { | |
| 191 |
1
1. deleteDocument : removed call to com/edtech/service/StorageService::deleteFile → KILLED |
storageService.deleteFile(document.getFileUrl()); |
| 192 | } catch (Exception e) { | |
| 193 | throw new RuntimeException("Erro ao excluir arquivo fisico: " + e.getMessage()); | |
| 194 | } | |
| 195 | ||
| 196 |
1
1. deleteDocument : removed call to com/edtech/repository/DocumentRepository::delete → KILLED |
documentRepository.delete(document); |
| 197 | auditLogService.logDocumentAction( | |
| 198 | userId, | |
| 199 | AuditAction.DELETE_DOCUMENT, | |
| 200 | documentId, | |
| 201 | "Documento excluido: " + document.getTitle()); | |
| 202 | } | |
| 203 | ||
| 204 | private String validateAllowedFile(MultipartFile file, String originalFilename) | |
| 205 | throws IOException { | |
| 206 | String extension = extractExtension(originalFilename); | |
| 207 | String expectedMimeType = ALLOWED_MIME_BY_EXTENSION.get(extension); | |
| 208 |
1
1. validateAllowedFile : negated conditional → KILLED |
if (expectedMimeType == null) { |
| 209 | throw new IllegalArgumentException( | |
| 210 | "Tipo de arquivo nao permitido. Formatos aceitos: PDF, CSV e JSON."); | |
| 211 | } | |
| 212 | ||
| 213 | String declaredType = normalizeMimeType(file.getContentType()); | |
| 214 |
1
1. validateAllowedFile : negated conditional → KILLED |
if (!expectedMimeType.equals(declaredType)) { |
| 215 | throw new IllegalArgumentException( | |
| 216 | "Content-Type nao permitido para " | |
| 217 | + extension | |
| 218 | + ". Esperado: " | |
| 219 | + expectedMimeType | |
| 220 | + ". Recebido: " | |
| 221 | + declaredType); | |
| 222 | } | |
| 223 | ||
| 224 | String detectedType = normalizeMimeType(tika.detect(file.getInputStream(), originalFilename)); | |
| 225 |
1
1. validateAllowedFile : negated conditional → KILLED |
if (!isDetectedTypeCompatible(expectedMimeType, detectedType)) { |
| 226 | throw new IllegalArgumentException( | |
| 227 | "Conteudo do arquivo nao corresponde ao tipo permitido. Tipo detectado: " + detectedType); | |
| 228 | } | |
| 229 | ||
| 230 |
1
1. validateAllowedFile : negated conditional → KILLED |
if (MIME_JSON.equals(expectedMimeType)) { |
| 231 |
1
1. validateAllowedFile : removed call to com/edtech/service/DocumentService::validateJsonContent → SURVIVED |
validateJsonContent(file); |
| 232 | } | |
| 233 | ||
| 234 |
1
1. validateAllowedFile : replaced return value with "" for com/edtech/service/DocumentService::validateAllowedFile → KILLED |
return expectedMimeType; |
| 235 | } | |
| 236 | ||
| 237 | private String extractExtension(String filename) { | |
| 238 | int lastDotIndex = filename.lastIndexOf('.'); | |
| 239 |
4
1. extractExtension : changed conditional boundary → SURVIVED 2. extractExtension : Replaced integer subtraction with addition → SURVIVED 3. extractExtension : negated conditional → KILLED 4. extractExtension : negated conditional → KILLED |
if (lastDotIndex < 0 || lastDotIndex == filename.length() - 1) { |
| 240 | return ""; | |
| 241 | } | |
| 242 |
1
1. extractExtension : replaced return value with "" for com/edtech/service/DocumentService::extractExtension → KILLED |
return filename.substring(lastDotIndex).toLowerCase(Locale.ROOT); |
| 243 | } | |
| 244 | ||
| 245 | private boolean isDetectedTypeCompatible(String expectedMimeType, String detectedType) { | |
| 246 |
1
1. isDetectedTypeCompatible : negated conditional → KILLED |
if (MIME_CSV.equals(expectedMimeType)) { |
| 247 |
2
1. isDetectedTypeCompatible : replaced boolean return with true for com/edtech/service/DocumentService::isDetectedTypeCompatible → SURVIVED 2. isDetectedTypeCompatible : replaced boolean return with false for com/edtech/service/DocumentService::isDetectedTypeCompatible → KILLED |
return CSV_COMPATIBLE_DETECTED_TYPES.contains(detectedType); |
| 248 | } | |
| 249 |
1
1. isDetectedTypeCompatible : negated conditional → KILLED |
if (MIME_JSON.equals(expectedMimeType)) { |
| 250 |
2
1. isDetectedTypeCompatible : replaced boolean return with true for com/edtech/service/DocumentService::isDetectedTypeCompatible → SURVIVED 2. isDetectedTypeCompatible : replaced boolean return with false for com/edtech/service/DocumentService::isDetectedTypeCompatible → KILLED |
return JSON_COMPATIBLE_DETECTED_TYPES.contains(detectedType); |
| 251 | } | |
| 252 |
2
1. isDetectedTypeCompatible : replaced boolean return with true for com/edtech/service/DocumentService::isDetectedTypeCompatible → SURVIVED 2. isDetectedTypeCompatible : replaced boolean return with false for com/edtech/service/DocumentService::isDetectedTypeCompatible → KILLED |
return expectedMimeType.equals(detectedType); |
| 253 | } | |
| 254 | ||
| 255 | private String normalizeMimeType(String mimeType) { | |
| 256 |
2
1. normalizeMimeType : negated conditional → KILLED 2. normalizeMimeType : negated conditional → KILLED |
if (mimeType == null || mimeType.isBlank()) { |
| 257 | return ""; | |
| 258 | } | |
| 259 |
1
1. normalizeMimeType : replaced return value with "" for com/edtech/service/DocumentService::normalizeMimeType → KILLED |
return mimeType.split(";")[0].trim().toLowerCase(Locale.ROOT); |
| 260 | } | |
| 261 | ||
| 262 | private void validateJsonContent(MultipartFile file) throws IOException { | |
| 263 | objectMapper.readTree(file.getInputStream()); | |
| 264 | } | |
| 265 | ||
| 266 | private DocumentResponseDto mapToDto(Document document) { | |
| 267 | DocumentResponseDto dto = new DocumentResponseDto(); | |
| 268 |
1
1. mapToDto : removed call to com/edtech/dto/DocumentResponseDto::setId → KILLED |
dto.setId(document.getId()); |
| 269 |
1
1. mapToDto : removed call to com/edtech/dto/DocumentResponseDto::setTitle → KILLED |
dto.setTitle(document.getTitle()); |
| 270 |
1
1. mapToDto : removed call to com/edtech/dto/DocumentResponseDto::setFileUrl → KILLED |
dto.setFileUrl(document.getFileUrl()); |
| 271 |
1
1. mapToDto : removed call to com/edtech/dto/DocumentResponseDto::setStatus → KILLED |
dto.setStatus(document.getStatus()); |
| 272 |
1
1. mapToDto : removed call to com/edtech/dto/DocumentResponseDto::setAuthorId → KILLED |
dto.setAuthorId(document.getAuthor().getId()); |
| 273 |
1
1. mapToDto : removed call to com/edtech/dto/DocumentResponseDto::setAuthorName → SURVIVED |
dto.setAuthorName(document.getAuthor().getName()); |
| 274 |
1
1. mapToDto : removed call to com/edtech/dto/DocumentResponseDto::setAuthorEmail → SURVIVED |
dto.setAuthorEmail(document.getAuthor().getEmail()); |
| 275 |
1
1. mapToDto : removed call to com/edtech/dto/DocumentResponseDto::setProjectId → KILLED |
dto.setProjectId(document.getProject().getId()); |
| 276 |
1
1. mapToDto : removed call to com/edtech/dto/DocumentResponseDto::setProjectTitle → SURVIVED |
dto.setProjectTitle(document.getProject().getTitle()); |
| 277 |
1
1. mapToDto : removed call to com/edtech/dto/DocumentResponseDto::setFileType → SURVIVED |
dto.setFileType( |
| 278 | extractExtension(document.getTitle()).replace(".", "").toUpperCase(Locale.ROOT)); | |
| 279 |
1
1. mapToDto : removed call to com/edtech/dto/DocumentResponseDto::setCreatedAt → SURVIVED |
dto.setCreatedAt(document.getCreatedAt()); |
| 280 |
1
1. mapToDto : removed call to com/edtech/dto/DocumentResponseDto::setFeedback → KILLED |
dto.setFeedback(document.getFeedback()); |
| 281 |
1
1. mapToDto : removed call to com/edtech/dto/DocumentResponseDto::setStarred → KILLED |
dto.setStarred(document.isStarred()); |
| 282 |
1
1. mapToDto : replaced return value with null for com/edtech/service/DocumentService::mapToDto → KILLED |
return dto; |
| 283 | } | |
| 284 | ||
| 285 | /** Javadoc. */ | |
| 286 | @Transactional(readOnly = true) | |
| 287 | public java.util.List<com.edtech.dto.CommentResponseDto> getComments( | |
| 288 | UUID documentId, UUID userId) { | |
| 289 | Document document = | |
| 290 | documentRepository | |
| 291 | .findById(documentId) | |
| 292 |
1
1. lambda$getComments$6 : replaced return value with null for com/edtech/service/DocumentService::lambda$getComments$6 → NO_COVERAGE |
.orElseThrow(() -> new RuntimeException("Document not found")); |
| 293 | projectMemberRepository | |
| 294 | .findByProjectIdAndUserId(document.getProject().getId(), userId) | |
| 295 |
1
1. lambda$getComments$7 : replaced return value with null for com/edtech/service/DocumentService::lambda$getComments$7 → NO_COVERAGE |
.orElseThrow(() -> new RuntimeException("Access denied")); |
| 296 | ||
| 297 |
1
1. getComments : replaced return value with Collections.emptyList for com/edtech/service/DocumentService::getComments → KILLED |
return documentCommentRepository.findByDocumentIdOrderByCreatedAtAsc(documentId).stream() |
| 298 | .map( | |
| 299 | c -> { | |
| 300 | com.edtech.dto.CommentResponseDto dto = new com.edtech.dto.CommentResponseDto(); | |
| 301 |
1
1. lambda$getComments$8 : removed call to com/edtech/dto/CommentResponseDto::setId → KILLED |
dto.setId(c.getId()); |
| 302 |
1
1. lambda$getComments$8 : removed call to com/edtech/dto/CommentResponseDto::setContent → KILLED |
dto.setContent(c.getContent()); |
| 303 |
1
1. lambda$getComments$8 : removed call to com/edtech/dto/CommentResponseDto::setCreatedAt → SURVIVED |
dto.setCreatedAt(c.getCreatedAt()); |
| 304 |
1
1. lambda$getComments$8 : removed call to com/edtech/dto/CommentResponseDto::setAuthorId → KILLED |
dto.setAuthorId(c.getAuthor().getId()); |
| 305 |
1
1. lambda$getComments$8 : removed call to com/edtech/dto/CommentResponseDto::setAuthorName → KILLED |
dto.setAuthorName(c.getAuthor().getName()); |
| 306 |
1
1. lambda$getComments$8 : replaced return value with null for com/edtech/service/DocumentService::lambda$getComments$8 → KILLED |
return dto; |
| 307 | }) | |
| 308 | .collect(java.util.stream.Collectors.toList()); | |
| 309 | } | |
| 310 | ||
| 311 | /** Javadoc. */ | |
| 312 | @Transactional | |
| 313 | public com.edtech.dto.CommentResponseDto addComment( | |
| 314 | UUID documentId, UUID userId, String content) { | |
| 315 | Document document = | |
| 316 | documentRepository | |
| 317 | .findById(documentId) | |
| 318 |
1
1. lambda$addComment$9 : replaced return value with null for com/edtech/service/DocumentService::lambda$addComment$9 → NO_COVERAGE |
.orElseThrow(() -> new RuntimeException("Document not found")); |
| 319 | User user = | |
| 320 |
1
1. lambda$addComment$10 : replaced return value with null for com/edtech/service/DocumentService::lambda$addComment$10 → NO_COVERAGE |
userRepository.findById(userId).orElseThrow(() -> new RuntimeException("User not found")); |
| 321 | projectMemberRepository | |
| 322 | .findByProjectIdAndUserId(document.getProject().getId(), userId) | |
| 323 |
1
1. lambda$addComment$11 : replaced return value with null for com/edtech/service/DocumentService::lambda$addComment$11 → NO_COVERAGE |
.orElseThrow(() -> new RuntimeException("Access denied")); |
| 324 | ||
| 325 | com.edtech.model.DocumentComment comment = new com.edtech.model.DocumentComment(); | |
| 326 |
1
1. addComment : removed call to com/edtech/model/DocumentComment::setDocument → SURVIVED |
comment.setDocument(document); |
| 327 |
1
1. addComment : removed call to com/edtech/model/DocumentComment::setAuthor → KILLED |
comment.setAuthor(user); |
| 328 |
1
1. addComment : removed call to com/edtech/model/DocumentComment::setContent → KILLED |
comment.setContent(content); |
| 329 | ||
| 330 | comment = documentCommentRepository.save(comment); | |
| 331 | ||
| 332 | auditLogService.logDocumentAction( | |
| 333 | userId, AuditAction.REVIEW_DOCUMENT, documentId, "Adicionou um comentário ao documento."); | |
| 334 | ||
| 335 | com.edtech.dto.CommentResponseDto dto = new com.edtech.dto.CommentResponseDto(); | |
| 336 |
1
1. addComment : removed call to com/edtech/dto/CommentResponseDto::setId → KILLED |
dto.setId(comment.getId()); |
| 337 |
1
1. addComment : removed call to com/edtech/dto/CommentResponseDto::setContent → KILLED |
dto.setContent(comment.getContent()); |
| 338 |
1
1. addComment : removed call to com/edtech/dto/CommentResponseDto::setCreatedAt → KILLED |
dto.setCreatedAt(comment.getCreatedAt()); |
| 339 |
1
1. addComment : removed call to com/edtech/dto/CommentResponseDto::setAuthorId → KILLED |
dto.setAuthorId(comment.getAuthor().getId()); |
| 340 |
1
1. addComment : removed call to com/edtech/dto/CommentResponseDto::setAuthorName → KILLED |
dto.setAuthorName(comment.getAuthor().getName()); |
| 341 | ||
| 342 |
1
1. addComment : removed call to com/edtech/service/NotificationService::sendToTopic → KILLED |
notificationService.sendToTopic( |
| 343 | "/topic/projects/" + document.getProject().getId(), | |
| 344 | Map.of("type", "NEW_COMMENT", "documentId", documentId, "comment", dto)); | |
| 345 | ||
| 346 |
1
1. addComment : replaced return value with null for com/edtech/service/DocumentService::addComment → KILLED |
return dto; |
| 347 | } | |
| 348 | ||
| 349 | /** Documentacao. */ | |
| 350 | @Transactional | |
| 351 | public DocumentResponseDto reviewDocument( | |
| 352 | UUID documentId, UUID reviewerId, DocumentStatus newStatus, String feedback) { | |
| 353 |
2
1. reviewDocument : negated conditional → KILLED 2. reviewDocument : negated conditional → KILLED |
if (newStatus != DocumentStatus.APPROVED && newStatus != DocumentStatus.REJECTED) { |
| 354 | throw new IllegalArgumentException( | |
| 355 | "Status invalido. Apenas APPROVED ou REJECTED sao permitidos na revisao."); | |
| 356 | } | |
| 357 | Document document = | |
| 358 | documentRepository | |
| 359 | .findById(documentId) | |
| 360 |
1
1. lambda$reviewDocument$12 : replaced return value with null for com/edtech/service/DocumentService::lambda$reviewDocument$12 → NO_COVERAGE |
.orElseThrow(() -> new RuntimeException("Document not found")); |
| 361 | ProjectMember member = | |
| 362 | projectMemberRepository | |
| 363 | .findByProjectIdAndUserId(document.getProject().getId(), reviewerId) | |
| 364 | .orElseThrow( | |
| 365 |
1
1. lambda$reviewDocument$13 : replaced return value with null for com/edtech/service/DocumentService::lambda$reviewDocument$13 → KILLED |
() -> new RuntimeException("Acess denied: You are not a member of this project")); |
| 366 |
1
1. reviewDocument : negated conditional → KILLED |
if (member.getRole() != ProjectRole.ADVISOR) { |
| 367 | throw new RuntimeException("Acess denied: Only an ADVISOR can review documents"); | |
| 368 | } | |
| 369 |
1
1. reviewDocument : negated conditional → KILLED |
if (document.getStatus() != DocumentStatus.PENDING_REVIEW) { |
| 370 | throw new RuntimeException("Document is not pending review"); | |
| 371 | } | |
| 372 |
1
1. reviewDocument : removed call to com/edtech/model/Document::setStatus → KILLED |
document.setStatus(newStatus); |
| 373 |
1
1. reviewDocument : removed call to com/edtech/model/Document::setFeedback → KILLED |
document.setFeedback(feedback); |
| 374 | Document savedDocument = documentRepository.save(document); | |
| 375 | AuditAction action = | |
| 376 |
1
1. reviewDocument : negated conditional → KILLED |
(newStatus == DocumentStatus.APPROVED) |
| 377 | ? AuditAction.DOCUMENT_APPROVED | |
| 378 | : AuditAction.DOCUMENT_REJECTED; | |
| 379 | String details = | |
| 380 | "Status alterado para " | |
| 381 | + newStatus | |
| 382 | + ".Feedback: " | |
| 383 |
2
1. reviewDocument : negated conditional → SURVIVED 2. reviewDocument : negated conditional → KILLED |
+ (feedback != null && !feedback.trim().isEmpty() ? feedback : "Sem feedback"); |
| 384 | auditLogService.logDocumentAction(reviewerId, action, documentId, details); | |
| 385 | ||
| 386 | DocumentResponseDto responseDto = mapToDto(savedDocument); | |
| 387 |
1
1. reviewDocument : removed call to com/edtech/service/NotificationService::sendToTopic → SURVIVED |
notificationService.sendToTopic( |
| 388 | "/topic/projects/" + document.getProject().getId(), | |
| 389 | Map.of("type", "DOCUMENT_REVIEWED", "document", responseDto)); | |
| 390 | ||
| 391 |
1
1. reviewDocument : replaced return value with null for com/edtech/service/DocumentService::reviewDocument → KILLED |
return responseDto; |
| 392 | } | |
| 393 | ||
| 394 | /** Javadoc. */ | |
| 395 | @Transactional | |
| 396 | public DocumentResponseDto toggleStar(UUID documentId, UUID userId) { | |
| 397 | Document document = | |
| 398 | documentRepository | |
| 399 | .findById(documentId) | |
| 400 |
1
1. lambda$toggleStar$14 : replaced return value with null for com/edtech/service/DocumentService::lambda$toggleStar$14 → NO_COVERAGE |
.orElseThrow(() -> new RuntimeException("Document not found")); |
| 401 | ||
| 402 | projectMemberRepository | |
| 403 | .findByProjectIdAndUserId(document.getProject().getId(), userId) | |
| 404 | .orElseThrow( | |
| 405 |
1
1. lambda$toggleStar$15 : replaced return value with null for com/edtech/service/DocumentService::lambda$toggleStar$15 → NO_COVERAGE |
() -> new RuntimeException("Access denied: You are not a member of this project")); |
| 406 | ||
| 407 |
2
1. toggleStar : negated conditional → KILLED 2. toggleStar : removed call to com/edtech/model/Document::setStarred → KILLED |
document.setStarred(!document.isStarred()); |
| 408 | Document savedDocument = documentRepository.save(document); | |
| 409 | ||
| 410 |
1
1. toggleStar : replaced return value with null for com/edtech/service/DocumentService::toggleStar → KILLED |
return mapToDto(savedDocument); |
| 411 | } | |
| 412 | } | |
Mutations | ||
| 83 |
1.1 |
|
| 87 |
1.1 |
|
| 91 |
1.1 |
|
| 95 |
1.1 |
|
| 101 |
1.1 |
|
| 108 |
1.1 |
|
| 111 |
1.1 |
|
| 112 |
1.1 |
|
| 113 |
1.1 |
|
| 114 |
1.1 |
|
| 115 |
1.1 |
|
| 126 |
1.1 |
|
| 130 |
1.1 |
|
| 145 |
1.1 |
|
| 150 |
1.1 |
|
| 164 |
1.1 |
|
| 170 |
1.1 |
|
| 181 |
1.1 |
|
| 183 |
1.1 |
|
| 186 |
1.1 |
|
| 191 |
1.1 |
|
| 196 |
1.1 |
|
| 208 |
1.1 |
|
| 214 |
1.1 |
|
| 225 |
1.1 |
|
| 230 |
1.1 |
|
| 231 |
1.1 |
|
| 234 |
1.1 |
|
| 239 |
1.1 2.2 3.3 4.4 |
|
| 242 |
1.1 |
|
| 246 |
1.1 |
|
| 247 |
1.1 2.2 |
|
| 249 |
1.1 |
|
| 250 |
1.1 2.2 |
|
| 252 |
1.1 2.2 |
|
| 256 |
1.1 2.2 |
|
| 259 |
1.1 |
|
| 268 |
1.1 |
|
| 269 |
1.1 |
|
| 270 |
1.1 |
|
| 271 |
1.1 |
|
| 272 |
1.1 |
|
| 273 |
1.1 |
|
| 274 |
1.1 |
|
| 275 |
1.1 |
|
| 276 |
1.1 |
|
| 277 |
1.1 |
|
| 279 |
1.1 |
|
| 280 |
1.1 |
|
| 281 |
1.1 |
|
| 282 |
1.1 |
|
| 292 |
1.1 |
|
| 295 |
1.1 |
|
| 297 |
1.1 |
|
| 301 |
1.1 |
|
| 302 |
1.1 |
|
| 303 |
1.1 |
|
| 304 |
1.1 |
|
| 305 |
1.1 |
|
| 306 |
1.1 |
|
| 318 |
1.1 |
|
| 320 |
1.1 |
|
| 323 |
1.1 |
|
| 326 |
1.1 |
|
| 327 |
1.1 |
|
| 328 |
1.1 |
|
| 336 |
1.1 |
|
| 337 |
1.1 |
|
| 338 |
1.1 |
|
| 339 |
1.1 |
|
| 340 |
1.1 |
|
| 342 |
1.1 |
|
| 346 |
1.1 |
|
| 353 |
1.1 2.2 |
|
| 360 |
1.1 |
|
| 365 |
1.1 |
|
| 366 |
1.1 |
|
| 369 |
1.1 |
|
| 372 |
1.1 |
|
| 373 |
1.1 |
|
| 376 |
1.1 |
|
| 383 |
1.1 2.2 |
|
| 387 |
1.1 |
|
| 391 |
1.1 |
|
| 400 |
1.1 |
|
| 405 |
1.1 |
|
| 407 |
1.1 2.2 |
|
| 410 |
1.1 |