diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/UserStatsController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/UserStatsController.java new file mode 100644 index 000000000..43f7aac23 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/UserStatsController.java @@ -0,0 +1,51 @@ +package com.iqser.red.persistence.service.v1.external.api.impl.controller; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.web.bind.annotation.RestController; + +import com.iqser.red.service.persistence.management.v1.processor.acl.custom.dossier.DossierACLService; +import com.iqser.red.service.persistence.management.v1.processor.service.DossierService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService; +import com.iqser.red.service.persistence.management.v1.processor.service.users.UserService; +import com.iqser.red.service.persistence.service.v1.api.external.model.UserStats; +import com.iqser.red.service.persistence.service.v1.api.external.resource.UserStatsResource; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RestController +@RequiredArgsConstructor +public class UserStatsController implements UserStatsResource { + + private final UserService userService; + private final DossierService dossierService; + private final FileStatusPersistenceService fileStatusPersistenceService; + private final DossierACLService dossierACLService; + + + @Override + public UserStats getUserStats(String userId) { + + if (userService.getUserById(userId).isEmpty()) { + return null; + } + List dossierMemberships = new ArrayList<>(); + List dossierOwnerships = new ArrayList<>(); + dossierService.getAllDossiers() + .stream() + .forEach(d -> { + if (dossierACLService.getMembers(d.getId()).contains(userId)) { + dossierMemberships.add(d.getId()); + } + if (dossierACLService.getOwners(d.getId()).contains(userId)) { + dossierOwnerships.add(d.getId()); + } + }); + + return new UserStats(dossierMemberships.size(), dossierOwnerships.size(), this.fileStatusPersistenceService.getNumberOfAssignedFiles(userId)); + } + +} diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/model/UserStats.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/model/UserStats.java new file mode 100644 index 000000000..a71fc0805 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/model/UserStats.java @@ -0,0 +1,14 @@ +package com.iqser.red.persistence.service.v1.external.api.impl.model; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class UserStats { + + private int numberOfDossierMemberships; + private int numberOfDossierOwnerships; + private int numberOfAssignedFiles; + +} diff --git a/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/model/UserStats.java b/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/model/UserStats.java new file mode 100644 index 000000000..568d185b8 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/model/UserStats.java @@ -0,0 +1,20 @@ +package com.iqser.red.service.persistence.service.v1.api.external.model; + +import lombok.Data; + +@Data +public class UserStats { + + public UserStats(int numberOfDossierMemberships, int numberOfDossierOwnerships, int numberOfAssignedFiles) { + + this.numberOfDossierMemberships = numberOfDossierMemberships; + this.numberOfDossierOwnerships = numberOfDossierOwnerships; + this.numberOfAssignedFiles = numberOfAssignedFiles; + } + + + private int numberOfDossierMemberships; + private int numberOfDossierOwnerships; + private int numberOfAssignedFiles; + +} diff --git a/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/UserStatsResource.java b/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/UserStatsResource.java new file mode 100644 index 000000000..3ee71c2cd --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/UserStatsResource.java @@ -0,0 +1,38 @@ +package com.iqser.red.service.persistence.service.v1.api.external.resource; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import com.iqser.red.service.persistence.service.v1.api.external.model.UserStats; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; + +@ApiResponses(value = {@ApiResponse(responseCode = "429", description = "Too many requests.")}) +public interface UserStatsResource { + + String USER_PATH = "/users"; + + String USER_ID_PARAM = "userId"; + + String USER_ID_PATH_VARIABLE = "/{" + USER_ID_PARAM + "}"; + + String STATS_PATH = "/stats"; + + String PATH = ExternalApi.BASE_PATH + STATS_PATH + USER_PATH; + + + @ResponseStatus(value = HttpStatus.OK) + @ResponseBody + @GetMapping(value = PATH+ USER_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Gets user stats for user specified by id.", description = "") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found")}) + UserStats getUserStats(@Parameter(name = USER_ID_PARAM, description = "The unique identifier of the user whose statistics we want to retrieve.", required = true) @PathVariable(USER_ID_PARAM) String userId); + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/acl/custom/dossier/DossierACLService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/acl/custom/dossier/DossierACLService.java index b60076a01..b0abc0fb2 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/acl/custom/dossier/DossierACLService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/acl/custom/dossier/DossierACLService.java @@ -71,6 +71,23 @@ public class DossierACLService extends AbstractACLService { return members; } + public Set getOwners(String dossierId) { + + ObjectIdentityImpl dossierIdentity = new ObjectIdentityImpl(getIdentifier(), dossierId); + var acl = mutableAclService.readAclById(dossierIdentity); + Set members = new HashSet<>(); + acl.getEntries() + .forEach(entry -> { + if (entry.getSid() instanceof PrincipalSid) { + var principal = ((PrincipalSid) entry.getSid()).getPrincipal(); + if (entry.getPermission().getMask() == RedPermission.OWNER.getMask()) { + members.add(principal); + } + } + }); + return members; + } + @Override public String getIdentifier() { diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/DossierEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/DossierEntity.java index feeb1226b..e0bd18fe9 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/DossierEntity.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/entity/dossier/DossierEntity.java @@ -6,20 +6,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Convert; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToMany; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; - import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; @@ -31,6 +17,20 @@ import com.iqser.red.service.persistence.management.v1.processor.utils.JSONStrin import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierVisibility; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/roles/ActionRoles.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/roles/ActionRoles.java index cb81b38d3..3db46c5a1 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/roles/ActionRoles.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/roles/ActionRoles.java @@ -171,6 +171,10 @@ public final class ActionRoles { // RSS public static final String GET_RSS = "red-get-rss"; + // USER STATS + + public static final String READ_USER_STATS = "red-get-user-stats"; + // Multitenancy public static final String CREATE_TENANT = "red-create-tenant"; public static final String GET_TENANTS = "red-get-tenants"; diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java index 4ca594587..673fb5523 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java @@ -22,9 +22,9 @@ import com.iqser.red.service.persistence.management.v1.processor.exception.NotFo import com.iqser.red.service.persistence.management.v1.processor.model.FileIdentifier; import com.iqser.red.service.persistence.management.v1.processor.model.OCRStatusUpdateResponse; import com.iqser.red.service.persistence.management.v1.processor.model.websocket.AnalyseStatus; -import com.iqser.red.service.persistence.management.v1.processor.service.websocket.WebsocketService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileAttributesRepository; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.websocket.WebsocketService; import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute; import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileErrorInfo; @@ -403,7 +403,10 @@ public class FileStatusPersistenceService { public List getStatusesForDossierTemplate(String dossierTemplateId) { return dossierService.findAllDossiersForDossierTemplateId(dossierTemplateId) - .stream().map(dossier -> fileRepository.findByDossierId(dossier.getId())).flatMap(List::stream).toList(); + .stream() + .map(dossier -> fileRepository.findByDossierId(dossier.getId())) + .flatMap(List::stream) + .toList(); } @@ -434,6 +437,13 @@ public class FileStatusPersistenceService { } + public int getNumberOfAssignedFiles(String userId) { + + List files = fileRepository.findFilesByAssignee(userId); + return files.size(); + } + + public List getSoftDeletedFiles(List dossierIds) { return fileRepository.getSoftDeletedFiles(dossierIds); @@ -453,7 +463,7 @@ public class FileStatusPersistenceService { @Transactional public void softDelete(String fileId, OffsetDateTime softDeletedTime) { - fileRepository.setSoftDelete(fileId, ProcessingStatus.PROCESSED, OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS), softDeletedTime); + fileRepository.setSoftDelete(fileId, ProcessingStatus.PROCESSED, OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS), softDeletedTime); } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java index 16d4538b8..b7b00c339 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java @@ -46,6 +46,10 @@ public interface FileRepository extends JpaRepository { @Param("hasUpdates") boolean hasUpdates); + @Query("select f from FileEntity f where f.assignee = :assignee") + List findFilesByAssignee(@Param("assignee") String assignee); + + @Modifying @Query(""" update FileEntity f set f.numberOfPages = :numberOfPages, \