Skip to content
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
<dependency>
<groupId>dev.vality</groupId>
<artifactId>org-management-proto</artifactId>
<version>1.19-e5d3e5f</version>
<version>1.21-23aa5d8</version>
<exclusions>
<exclusion>
<groupId>dev.vality</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package dev.vality.orgmanager.repository;

import dev.vality.orgmanager.entity.InvitationEntity;
import jakarta.persistence.QueryHint;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.QueryHints;
import org.springframework.stereotype.Repository;

import jakarta.persistence.QueryHint;

import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

import static org.hibernate.jpa.QueryHints.HINT_FETCH_SIZE;

@Repository
public interface InvitationRepository extends JpaRepository<InvitationEntity, String> {
public interface InvitationRepository
extends JpaRepository<InvitationEntity, String>, JpaSpecificationExecutor<InvitationEntity> {

List<InvitationEntity> findByOrganizationIdAndStatus(String organizationId, String status);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import dev.vality.orgmanager.entity.MemberEntity;
import dev.vality.orgmanager.service.dto.MemberWithRoleDto;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.NativeQuery;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.List;

@Repository
Expand Down Expand Up @@ -33,10 +35,21 @@ public interface MemberRepository extends JpaRepository<MemberEntity, String> {
List<MemberWithRoleDto> getOrgMemberList(String orgId);

/**
* То же, что {@link #getOrgMemberList(String)}, но джойн ролей внешний: участник без активных
* ролей в организации тоже попадает в выдачу. Административный контракт умеет добавлять
* участника без роли (AddMember), и такой участник не должен пропадать из списка.
* У строк для участника без ролей memberRoleId == null.
* Страница идентификаторов участников организации, отсортированная по возрастанию id.
* continuationToken == null — первая страница, иначе участники строго после указанного id.
*/
@NativeQuery("SELECT m.id " +
" FROM org_manager.member_to_organization mto " +
" JOIN org_manager.member m " +
" ON m.id = mto.member_id " +
" WHERE mto.organization_id = ?1 " +
" AND (CAST(?2 AS VARCHAR) IS NULL OR m.id > ?2) " +
" ORDER BY m.id ")
List<String> getOrgMemberIds(String orgId, String continuationToken, Pageable pageable);

/**
* То же, что {@link #getOrgMemberList(String)}, но джойн ролей внешний и выборка ограничена
* заданными участниками: участник без активных ролей в организации тоже попадает в выдачу.
*/
@NativeQuery("SELECT m.id, " +
" m.email, " +
Expand All @@ -55,8 +68,9 @@ public interface MemberRepository extends JpaRepository<MemberEntity, String> {
" AND mr.active IS TRUE " +
" AND mr.organization_id = mto.organization_id " +
" WHERE mto.organization_id = ?1 " +
" AND m.id IN (?2) " +
" ORDER BY m.id, mr.id ")
List<MemberWithRoleDto> getOrgMemberListWithRoles(String orgId);
List<MemberWithRoleDto> getOrgMemberListWithRoles(String orgId, Collection<String> memberIds);

boolean existsById(String id);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

import dev.vality.orgmanager.entity.OrganizationEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.NativeQuery;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;
import java.util.Set;

@Repository
public interface OrganizationRepository extends JpaRepository<OrganizationEntity, String> {
public interface OrganizationRepository
extends JpaRepository<OrganizationEntity, String>, JpaSpecificationExecutor<OrganizationEntity> {

@NativeQuery(" SELECT * FROM org_manager.organization AS o " +
" WHERE o.id IN " +
Expand Down Expand Up @@ -39,15 +41,6 @@ public interface OrganizationRepository extends JpaRepository<OrganizationEntity

boolean existsByParty(String party);

List<OrganizationEntity> findAllByOrderByIdDesc(Pageable pageable);

List<OrganizationEntity> findByIdLessThanOrderByIdDesc(String id, Pageable pageable);

List<OrganizationEntity> findByStatusOrderByIdDesc(String status, Pageable pageable);

List<OrganizationEntity> findByStatusAndIdLessThanOrderByIdDesc(
String status,
String id,
Pageable pageable);
Optional<OrganizationEntity> findByParty(String party);
Comment thread
jvmdev4 marked this conversation as resolved.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package dev.vality.orgmanager.repository;

import dev.vality.orgmanager.entity.ScopeEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ScopeRepository extends JpaRepository<ScopeEntity, String> {
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,84 @@
package dev.vality.orgmanager.service;

import dev.vality.orgmanagement.InvalidRequest;
import dev.vality.orgmanagement.OrganizationNotFound;
import dev.vality.orgmanagement.RoleScope;
import dev.vality.orgmanager.entity.MemberRoleEntity;
import dev.vality.orgmanager.entity.OrganizationEntity;
import dev.vality.orgmanager.entity.OrganizationRoleEntity;
import dev.vality.orgmanager.entity.ScopeEntity;
import dev.vality.orgmanager.repository.OrganizationRepository;
import dev.vality.orgmanager.repository.OrganizationRoleRepository;
import dev.vality.orgmanager.util.JsonCodec;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class AdminCommonService {

public static final int DEFAULT_PAGE_LIMIT = 20;
public static final int MAX_PAGE_LIMIT = 1000;

private final OrganizationRepository organizationRepository;
private final OrganizationRoleRepository organizationRoleRepository;
private final JsonCodec jsonCodec;

public OrganizationEntity findOrganization(String organizationId) throws OrganizationNotFound {
return organizationRepository.findById(organizationId)
.orElseThrow(OrganizationNotFound::new);
}

public String toStoredMetadata(String metadata) {
public String toStoredMetadata(String metadata) throws InvalidRequest {
if (metadata == null) {
return null;
}
try {
return jsonCodec.toJson(jsonCodec.toMap(metadata));
} catch (RuntimeException exception) {
throw new IllegalArgumentException("Metadata is expected to be a JSON object", exception);
throw new InvalidRequest("Metadata is expected to be a JSON object");
}
}

public String requireText(String value, String field) throws InvalidRequest {
if (value == null || value.isBlank()) {
throw new InvalidRequest(field + " must not be blank");
}
return value;
}

/**
* Проверяет, что назначение роли опирается на каталог ролей организации: сама роль в нём есть,
* а указанный scope_id входит в её список допустимых областей действия.
*/
public void validateRoleAssignment(String organizationId, String roleId, RoleScope scope)
throws InvalidRequest {
requireText(roleId, "Role id");
OrganizationRoleEntity role = organizationRoleRepository
.findByOrganizationIdAndRoleId(organizationId, roleId)
.orElseThrow(() -> new InvalidRequest(
"Role " + roleId + " is not present in the organization role catalog"));
if (scope == null) {
return;
}
String scopeId = requireText(scope.getScopeId(), "Scope id");
if (!allowedScopeIds(role).contains(scopeId)) {
throw new InvalidRequest("Scope " + scopeId + " is not allowed for role " + roleId);
}
}

public Set<String> allowedScopeIds(OrganizationRoleEntity role) {
return collectionOrEmpty(role.getPossibleScopes()).stream()
.map(ScopeEntity::getId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}

public MemberRoleEntity toMemberRoleEntity(String organizationId, String roleId, RoleScope scope) {
Expand All @@ -50,4 +95,11 @@ public MemberRoleEntity toMemberRoleEntity(String organizationId, String roleId,
public static <T> Collection<T> collectionOrEmpty(Collection<T> collection) {
return collection == null ? List.of() : collection;
}

public static int pageLimit(int requested) {
if (requested <= 0) {
return DEFAULT_PAGE_LIMIT;
}
return Math.min(requested, MAX_PAGE_LIMIT);
}
}
Loading
Loading