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..9b801cb9b --- /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,54 @@ +package com.iqser.red.persistence.service.v1.external.api.impl.controller; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +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.resource.UserStatsResource; +import com.iqser.red.service.persistence.service.v1.api.shared.model.UserStats; + +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 ResponseEntity getUserStats(String userId) { + + if (userService.getUserById(userId).isEmpty()) { + return new ResponseEntity(null, HttpStatus.NOT_FOUND); + } + 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 ResponseEntity<>(new UserStats(dossierMemberships.size(), dossierOwnerships.size(), this.fileStatusPersistenceService.getNumberOfAssignedFiles(userId)), + HttpStatus.OK); + } + +} \ No newline at end of file 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..86347d586 --- /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,37 @@ +package com.iqser.red.service.persistence.service.v1.api.external.resource; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +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.shared.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_ID_PARAM = "userId"; + + String USER_ID_PATH_VARIABLE = "/{" + USER_ID_PARAM + "}"; + + String STATS_PATH = "/user-stats"; + + String PATH = ExternalApi.BASE_PATH + STATS_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")}) + ResponseEntity 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); + +} \ No newline at end of file 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 2777ef08e..894179b46 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 @@ -73,6 +73,24 @@ public class DossierACLService extends AbstractACLService { } + 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/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 6c17e7741..1790b52b0 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 @@ -168,6 +168,10 @@ public final class ActionRoles { public static final String READ_APP_CONFIG = "red-read-app-configuration"; public static final String WRITE_APP_CONFIG = "red-write-app-configuration"; + // USER STATS + + public static final String READ_USER_STATS = "red-get-user-stats"; + // License Management public static final String UPDATE_LICENSE = "red-update-license"; public static final String READ_LICENSE = "red-read-license"; 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 aad3cc332..3b69d7c72 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 @@ -18,6 +18,7 @@ import com.iqser.red.service.persistence.management.v1.processor.entity.dossier. import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileAttributeEntity; import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity; import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntityComponentMappingVersionEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.projection.DossierStatsFileProjection; import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException; import com.iqser.red.service.persistence.management.v1.processor.model.FileIdentifier; @@ -26,6 +27,7 @@ import com.iqser.red.service.persistence.management.v1.processor.model.websocket 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; @@ -595,6 +597,13 @@ public class FileStatusPersistenceService { } + public int getNumberOfAssignedFiles(String userId) { + + List files = fileRepository.findFilesByAssignee(userId); + return files.size(); + } + + @Transactional public void deleteFileAttributes(String fileId) { 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 93cdfde12..6156dd593 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 @@ -49,6 +49,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, \ diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/UserStats.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/UserStats.java new file mode 100644 index 000000000..5ce4f62dc --- /dev/null +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/UserStats.java @@ -0,0 +1,18 @@ +package com.iqser.red.service.persistence.service.v1.api.shared.model; + +import lombok.Data; + +@Data +public class UserStats { + + private int numberOfDossierMemberships; + private int numberOfDossierOwnerships; + private int numberOfAssignedFiles; + public UserStats(int numberOfDossierMemberships, int numberOfDossierOwnerships, int numberOfAssignedFiles) { + + this.numberOfDossierMemberships = numberOfDossierMemberships; + this.numberOfDossierOwnerships = numberOfDossierOwnerships; + this.numberOfAssignedFiles = numberOfAssignedFiles; + } + +} \ No newline at end of file