Skip to content

Commit

Permalink
Add support for pagination in the get-role-mappings REST operation (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
EricWittmann authored Feb 15, 2024
1 parent 4f4d569 commit 843e87f
Show file tree
Hide file tree
Showing 27 changed files with 619 additions and 244 deletions.
102 changes: 54 additions & 48 deletions app/src/main/java/io/apicurio/registry/rest/v3/AdminResourceImpl.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,36 @@
package io.apicurio.registry.rest.v3;

import io.apicurio.common.apps.config.*;
import static io.apicurio.common.apps.logging.audit.AuditingConstants.KEY_FOR_BROWSER;
import static io.apicurio.common.apps.logging.audit.AuditingConstants.KEY_NAME;
import static io.apicurio.common.apps.logging.audit.AuditingConstants.KEY_PRINCIPAL_ID;
import static io.apicurio.common.apps.logging.audit.AuditingConstants.KEY_ROLE_MAPPING;
import static io.apicurio.common.apps.logging.audit.AuditingConstants.KEY_RULE;
import static io.apicurio.common.apps.logging.audit.AuditingConstants.KEY_RULE_TYPE;
import static io.apicurio.common.apps.logging.audit.AuditingConstants.KEY_UPDATE_ROLE;
import static io.apicurio.registry.util.DtoUtil.appAuthPropertyToRegistry;
import static io.apicurio.registry.util.DtoUtil.registryAuthPropertyToApp;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipInputStream;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.slf4j.Logger;

import io.apicurio.common.apps.config.Dynamic;
import io.apicurio.common.apps.config.DynamicConfigPropertyDef;
import io.apicurio.common.apps.config.DynamicConfigPropertyDto;
import io.apicurio.common.apps.config.DynamicConfigPropertyIndex;
import io.apicurio.common.apps.config.Info;
import io.apicurio.common.apps.logging.Logged;
import io.apicurio.common.apps.logging.audit.Audited;
import io.apicurio.registry.auth.Authorized;
Expand All @@ -10,21 +40,28 @@
import io.apicurio.registry.metrics.health.liveness.ResponseErrorLivenessCheck;
import io.apicurio.registry.metrics.health.readiness.ResponseTimeoutReadinessCheck;
import io.apicurio.registry.rest.MissingRequiredParameterException;
import io.apicurio.registry.rest.v3.beans.*;
import io.apicurio.registry.rest.v3.beans.ArtifactTypeInfo;
import io.apicurio.registry.rest.v3.beans.ConfigurationProperty;
import io.apicurio.registry.rest.v3.beans.DownloadRef;
import io.apicurio.registry.rest.v3.beans.RoleMapping;
import io.apicurio.registry.rest.v3.beans.RoleMappingSearchResults;
import io.apicurio.registry.rest.v3.beans.Rule;
import io.apicurio.registry.rest.v3.beans.UpdateConfigurationProperty;
import io.apicurio.registry.rest.v3.beans.UpdateRole;
import io.apicurio.registry.rest.v3.shared.DataExporter;
import io.apicurio.registry.rules.DefaultRuleDeletionException;
import io.apicurio.registry.rules.RulesProperties;
import io.apicurio.registry.storage.RegistryStorage;
import io.apicurio.registry.storage.dto.DownloadContextDto;
import io.apicurio.registry.storage.dto.DownloadContextType;
import io.apicurio.registry.storage.dto.RoleMappingDto;
import io.apicurio.registry.storage.dto.RoleMappingSearchResultsDto;
import io.apicurio.registry.storage.dto.RuleConfigurationDto;
import io.apicurio.registry.storage.error.ConfigPropertyNotFoundException;
import io.apicurio.registry.storage.error.InvalidPropertyValueException;
import io.apicurio.registry.storage.error.RuleNotFoundException;
import io.apicurio.registry.storage.impexp.EntityInputStream;
import io.apicurio.registry.types.Current;
import io.apicurio.registry.types.RoleType;
import io.apicurio.registry.types.RuleType;
import io.apicurio.registry.types.provider.ArtifactTypeUtilProviderFactory;
import io.apicurio.registry.utils.impexp.Entity;
Expand All @@ -36,24 +73,6 @@
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.slf4j.Logger;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipInputStream;

import static io.apicurio.common.apps.logging.audit.AuditingConstants.*;
import static io.apicurio.registry.util.DtoUtil.appAuthPropertyToRegistry;
import static io.apicurio.registry.util.DtoUtil.registryAuthPropertyToApp;

@ApplicationScoped
@Interceptors({ResponseErrorLivenessCheck.class, ResponseTimeoutReadinessCheck.class})
Expand Down Expand Up @@ -287,16 +306,21 @@ public void createRoleMapping(RoleMapping data) {
}

/**
* @see io.apicurio.registry.rest.v3.AdminResource#listRoleMappings()
* @see io.apicurio.registry.rest.v3.AdminResource#listRoleMappings(java.math.BigInteger, java.math.BigInteger)
*/
@Override
@Authorized(style=AuthorizedStyle.None, level=AuthorizedLevel.Admin)
@RoleBasedAccessApiOperation
public List<RoleMapping> listRoleMappings() {
List<RoleMappingDto> mappings = storage.getRoleMappings();
return mappings.stream().map(dto -> {
return dtoToRoleMapping(dto);
}).collect(Collectors.toList());
public RoleMappingSearchResults listRoleMappings(BigInteger limit, BigInteger offset) {
if (offset == null) {
offset = BigInteger.valueOf(0);
}
if (limit == null) {
limit = BigInteger.valueOf(20);
}

RoleMappingSearchResultsDto dto = storage.searchRoleMappings(offset.intValue(), limit.intValue());
return V3ApiUtil.dtoToRoleMappingSearchResults(dto);
}

/**
Expand All @@ -307,7 +331,7 @@ public List<RoleMapping> listRoleMappings() {
@RoleBasedAccessApiOperation
public RoleMapping getRoleMapping(String principalId) {
RoleMappingDto dto = storage.getRoleMapping(principalId);
return dtoToRoleMapping(dto);
return V3ApiUtil.dtoToRoleMapping(dto);
}

/**
Expand Down Expand Up @@ -352,7 +376,7 @@ public List<ConfigurationProperty> listConfigProperties() {
// on whether the value is actually configured and stored in the DB or not).
return dynamicPropertyIndex.getAcceptedPropertyNames().stream()
.sorted((pname1, pname2) -> pname1.compareTo(pname2))
.map(pname -> propsI.containsKey(pname) ? dtoToConfigurationProperty(dynamicPropertyIndex.getProperty(pname), propsI.get(pname)) : defToConfigurationProperty(dynamicPropertyIndex.getProperty(pname)))
.map(pname -> propsI.containsKey(pname) ? V3ApiUtil.dtoToConfigurationProperty(dynamicPropertyIndex.getProperty(pname), propsI.get(pname)) : defToConfigurationProperty(dynamicPropertyIndex.getProperty(pname)))
.collect(Collectors.toList());
}

Expand All @@ -369,7 +393,7 @@ public ConfigurationProperty getConfigProperty(String propertyName) {
if (dto == null) {
return defToConfigurationProperty(def);
} else {
return dtoToConfigurationProperty(def, dto);
return V3ApiUtil.dtoToConfigurationProperty(def, dto);
}
}

Expand Down Expand Up @@ -401,14 +425,6 @@ public void resetConfigProperty(String propertyName) {
storage.deleteConfigProperty(propertyName);
}

private static RoleMapping dtoToRoleMapping(RoleMappingDto dto) {
RoleMapping mapping = new RoleMapping();
mapping.setPrincipalId(dto.getPrincipalId());
mapping.setRole(RoleType.valueOf(dto.getRole()));
mapping.setPrincipalName(dto.getPrincipalName());
return mapping;
}


private static boolean isNullOrTrue(Boolean value) {
return value == null || value;
Expand All @@ -418,16 +434,6 @@ private String createDownloadHref(String downloadId) {
return "/apis/registry/v3/downloads/" + downloadId;
}

private static ConfigurationProperty dtoToConfigurationProperty(DynamicConfigPropertyDef def, DynamicConfigPropertyDto dto) {
ConfigurationProperty rval = new ConfigurationProperty();
rval.setName(def.getName());
rval.setValue(dto.getValue());
rval.setType(def.getType().getName());
rval.setLabel(def.getLabel());
rval.setDescription(def.getDescription());
return rval;
}

private ConfigurationProperty defToConfigurationProperty(DynamicConfigPropertyDef def) {
String propertyValue = config.getOptionalValue(def.getName(), String.class).orElse(def.getDefaultValue());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ public void deleteGroupById(String groupId) {
* @see io.apicurio.registry.rest.v3.GroupsResource#updateGroupById(java.lang.String, io.apicurio.registry.rest.v3.beans.EditableGroupMetaData)
*/
@Override
@Authorized(style = AuthorizedStyle.GroupOnly, level = AuthorizedLevel.Write)
public void updateGroupById(String groupId, EditableGroupMetaData data) {
requireParameter("groupId", groupId);

Expand Down
61 changes: 58 additions & 3 deletions app/src/main/java/io/apicurio/registry/rest/v3/V3ApiUtil.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,41 @@
package io.apicurio.registry.rest.v3;

import io.apicurio.registry.rest.v3.beans.*;
import io.apicurio.registry.storage.dto.*;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.Optional;
import java.util.stream.Collectors;

import io.apicurio.common.apps.config.DynamicConfigPropertyDef;
import io.apicurio.common.apps.config.DynamicConfigPropertyDto;
import io.apicurio.registry.rest.v3.beans.ArtifactMetaData;
import io.apicurio.registry.rest.v3.beans.ArtifactReference;
import io.apicurio.registry.rest.v3.beans.ArtifactSearchResults;
import io.apicurio.registry.rest.v3.beans.Comment;
import io.apicurio.registry.rest.v3.beans.ConfigurationProperty;
import io.apicurio.registry.rest.v3.beans.GroupMetaData;
import io.apicurio.registry.rest.v3.beans.GroupSearchResults;
import io.apicurio.registry.rest.v3.beans.RoleMapping;
import io.apicurio.registry.rest.v3.beans.RoleMappingSearchResults;
import io.apicurio.registry.rest.v3.beans.SearchedArtifact;
import io.apicurio.registry.rest.v3.beans.SearchedGroup;
import io.apicurio.registry.rest.v3.beans.SearchedVersion;
import io.apicurio.registry.rest.v3.beans.SortOrder;
import io.apicurio.registry.rest.v3.beans.VersionMetaData;
import io.apicurio.registry.rest.v3.beans.VersionSearchResults;
import io.apicurio.registry.storage.dto.ArtifactMetaDataDto;
import io.apicurio.registry.storage.dto.ArtifactReferenceDto;
import io.apicurio.registry.storage.dto.ArtifactSearchResultsDto;
import io.apicurio.registry.storage.dto.ArtifactVersionMetaDataDto;
import io.apicurio.registry.storage.dto.CommentDto;
import io.apicurio.registry.storage.dto.EditableArtifactMetaDataDto;
import io.apicurio.registry.storage.dto.GroupMetaDataDto;
import io.apicurio.registry.storage.dto.GroupSearchResultsDto;
import io.apicurio.registry.storage.dto.RoleMappingDto;
import io.apicurio.registry.storage.dto.RoleMappingSearchResultsDto;
import io.apicurio.registry.storage.dto.VersionSearchResultsDto;
import io.apicurio.registry.types.RoleType;

public final class V3ApiUtil {

private V3ApiUtil() {
Expand Down Expand Up @@ -296,4 +323,32 @@ public static Comment commentDtoToComment(CommentDto dto) {
.value(dto.getValue())
.build();
}

public static RoleMapping dtoToRoleMapping(RoleMappingDto dto) {
RoleMapping mapping = new RoleMapping();
mapping.setPrincipalId(dto.getPrincipalId());
mapping.setRole(RoleType.valueOf(dto.getRole()));
mapping.setPrincipalName(dto.getPrincipalName());
return mapping;
}

public static RoleMappingSearchResults dtoToRoleMappingSearchResults(RoleMappingSearchResultsDto dto) {
RoleMappingSearchResults results = new RoleMappingSearchResults();
results.setCount((int) dto.getCount());
results.setRoleMappings(dto.getRoleMappings().stream().map(rm -> {
return dtoToRoleMapping(rm);
}).collect(Collectors.toList()));
return results;
}

public static ConfigurationProperty dtoToConfigurationProperty(DynamicConfigPropertyDef def, DynamicConfigPropertyDto dto) {
ConfigurationProperty rval = new ConfigurationProperty();
rval.setName(def.getName());
rval.setValue(dto.getValue());
rval.setType(def.getType().getName());
rval.setLabel(def.getLabel());
rval.setDescription(def.getDescription());
return rval;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,13 @@ void updateArtifactRule(String groupId, String artifactId, RuleType rule, RuleCo
*/
List<RoleMappingDto> getRoleMappings() throws RegistryStorageException;

/**
* Search for role mappings.
* @param offset the number of artifacts to skip
* @param limit the result size limit
*/
RoleMappingSearchResultsDto searchRoleMappings(int offset, int limit) throws RegistryStorageException;

/**
* Gets the details of a single role mapping.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,11 @@ public String getRoleForPrincipal(String principalId) throws RegistryStorageExce
public List<RoleMappingDto> getRoleMappings() throws RegistryStorageException {
return delegate.getRoleMappings();
}

@Override
public RoleMappingSearchResultsDto searchRoleMappings(int offset, int limit) throws RegistryStorageException {
return delegate.searchRoleMappings(offset, limit);
}


@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
@ToString
public class ArtifactSearchResultsDto {

@Builder.Default
private List<SearchedArtifactDto> artifacts = new ArrayList<>();
private long count;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
@ToString
public class GroupSearchResultsDto {

@Builder.Default
private List<SearchedGroupDto> groups = new ArrayList<SearchedGroupDto>();

private Integer count;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.apicurio.registry.storage.dto;

import java.util.ArrayList;
import java.util.List;

import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@EqualsAndHashCode
@ToString
@RegisterForReflection
public class RoleMappingSearchResultsDto {

@Builder.Default
private List<RoleMappingDto> roleMappings = new ArrayList<>();
private long count;

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
@ToString
public class VersionSearchResultsDto {

private long count;
@Builder.Default
private List<SearchedVersionDto> versions = new ArrayList<>();
private long count;
}
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,11 @@ public long countTotalArtifactVersions() {
public List<RoleMappingDto> getRoleMappings() {
return proxy(RegistryStorage::getRoleMappings);
}

@Override
public RoleMappingSearchResultsDto searchRoleMappings(int offset, int limit) throws RegistryStorageException {
return proxy(storage -> storage.searchRoleMappings(offset, limit));
}


@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import io.apicurio.registry.storage.dto.OrderBy;
import io.apicurio.registry.storage.dto.OrderDirection;
import io.apicurio.registry.storage.dto.RoleMappingDto;
import io.apicurio.registry.storage.dto.RoleMappingSearchResultsDto;
import io.apicurio.registry.storage.dto.RuleConfigurationDto;
import io.apicurio.registry.storage.dto.SearchFilter;
import io.apicurio.registry.storage.dto.SearchFilterType;
Expand Down Expand Up @@ -2419,6 +2420,27 @@ public List<RoleMappingDto> getRoleMappings() throws RegistryStorageException {
});
}

@Override
@Transactional
public RoleMappingSearchResultsDto searchRoleMappings(int offset, int limit) throws RegistryStorageException {
log.debug("Searching role mappings.");
return handles.withHandleNoException(handle -> {
String query = sqlStatements.selectRoleMappings() + " LIMIT ? OFFSET ?";
String countQuery = sqlStatements.countRoleMappings();
List<RoleMappingDto> mappings = handle.createQuery(query)
.bind(0, limit)
.bind(1, offset)
.map(RoleMappingDtoMapper.instance)
.list();
Integer count = handle.createQuery(countQuery)
.mapTo(Integer.class)
.one();
return RoleMappingSearchResultsDto.builder()
.count(count)
.roleMappings(mappings)
.build();
});
}

@Override
@Transactional
Expand Down
Loading

0 comments on commit 843e87f

Please sign in to comment.