Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Group level rules #4953

Merged
merged 2 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package io.apicurio.registry.rest.v2;

import io.apicurio.common.apps.config.*;
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,7 +14,13 @@
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.v2.beans.*;
import io.apicurio.registry.rest.v2.beans.ArtifactTypeInfo;
import io.apicurio.registry.rest.v2.beans.ConfigurationProperty;
import io.apicurio.registry.rest.v2.beans.DownloadRef;
import io.apicurio.registry.rest.v2.beans.RoleMapping;
import io.apicurio.registry.rest.v2.beans.Rule;
import io.apicurio.registry.rest.v2.beans.UpdateConfigurationProperty;
import io.apicurio.registry.rest.v2.beans.UpdateRole;
import io.apicurio.registry.rest.v2.shared.DataExporter;
import io.apicurio.registry.rules.DefaultRuleDeletionException;
import io.apicurio.registry.rules.RulesProperties;
Expand Down Expand Up @@ -46,12 +56,19 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.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.utils.DtoUtil.appAuthPropertyToRegistry;
import static io.apicurio.registry.utils.DtoUtil.registryAuthPropertyToApp;

Expand Down Expand Up @@ -117,8 +134,9 @@ public List<ArtifactTypeInfo> listArtifactTypes() {
@Authorized(style = AuthorizedStyle.None, level = AuthorizedLevel.Read)
public List<RuleType> listGlobalRules() {
List<RuleType> rules = storage.getGlobalRules();
List<RuleType> defaultRules = rulesProperties.getFilteredDefaultGlobalRules(rules);
return Stream.concat(rules.stream(), defaultRules.stream()).sorted().collect(Collectors.toList());
Set<RuleType> defaultRules = rulesProperties.getDefaultGlobalRules();
return Stream.concat(rules.stream(), defaultRules.stream()).collect(Collectors.toSet()).stream()
.sorted().collect(Collectors.toList());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -144,8 +145,9 @@ public SnapshotMetaData triggerSnapshot() {
@Authorized(style = AuthorizedStyle.None, level = AuthorizedLevel.Read)
public List<RuleType> listGlobalRules() {
List<RuleType> rules = storage.getGlobalRules();
List<RuleType> defaultRules = rulesProperties.getFilteredDefaultGlobalRules(rules);
return Stream.concat(rules.stream(), defaultRules.stream()).sorted().collect(Collectors.toList());
Set<RuleType> defaultRules = rulesProperties.getDefaultGlobalRules();
return Stream.concat(rules.stream(), defaultRules.stream()).collect(Collectors.toSet()).stream()
.sorted().collect(Collectors.toList());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
import io.apicurio.registry.storage.dto.VersionSearchResultsDto;
import io.apicurio.registry.storage.error.ArtifactAlreadyExistsException;
import io.apicurio.registry.storage.error.ArtifactNotFoundException;
import io.apicurio.registry.storage.error.GroupNotFoundException;
import io.apicurio.registry.storage.error.InvalidArtifactIdException;
import io.apicurio.registry.storage.error.InvalidGroupIdException;
import io.apicurio.registry.storage.error.VersionNotFoundException;
Expand Down Expand Up @@ -307,6 +308,79 @@ public GroupMetaData createGroup(CreateGroup data) {
return V3ApiUtil.groupDtoToGroup(storage.getGroupMetaData(data.getGroupId()));
}

@Override
public List<RuleType> listGroupRules(String groupId) {
requireParameter("groupId", groupId);

return storage.getGroupRules(new GroupId(groupId).getRawGroupIdWithNull());
}

@Override
public void createGroupRule(String groupId, CreateRule data) {
requireParameter("groupId", groupId);
requireParameter("ruleType", data.getRuleType());
requireParameter("config", data.getConfig());

if (data.getConfig() == null || data.getConfig().isEmpty()) {
throw new MissingRequiredParameterException("config");
}

if (new GroupId(groupId).isDefaultGroup()) {
throw new NotAllowedException("Default group is not allowed");
}

RuleConfigurationDto config = new RuleConfigurationDto();
config.setConfiguration(data.getConfig());

if (!storage.isGroupExists(new GroupId(groupId).getRawGroupIdWithNull())) {
throw new GroupNotFoundException(groupId);
}

storage.createGroupRule(new GroupId(groupId).getRawGroupIdWithNull(), data.getRuleType(), config);
}

@Override
public Rule updateGroupRuleConfig(String groupId, RuleType ruleType, Rule data) {
requireParameter("groupId", groupId);
requireParameter("ruleType", ruleType);
requireParameter("config", data.getConfig());

RuleConfigurationDto dto = new RuleConfigurationDto(data.getConfig());
storage.updateGroupRule(new GroupId(groupId).getRawGroupIdWithNull(), ruleType, dto);
Rule rval = new Rule();
rval.setRuleType(ruleType);
rval.setConfig(data.getConfig());
return rval;
}

@Override
public void deleteGroupRules(String groupId) {
requireParameter("groupId", groupId);

storage.deleteGroupRules(new GroupId(groupId).getRawGroupIdWithNull());
}

@Override
public Rule getGroupRuleConfig(String groupId, RuleType ruleType) {
requireParameter("groupId", groupId);
requireParameter("ruleType", ruleType);

RuleConfigurationDto dto = storage.getGroupRule(new GroupId(groupId).getRawGroupIdWithNull(),
ruleType);
Rule rval = new Rule();
rval.setConfig(dto.getConfiguration());
rval.setRuleType(ruleType);
return rval;
}

@Override
public void deleteGroupRule(String groupId, RuleType rule) {
requireParameter("groupId", groupId);
requireParameter("rule", rule);

storage.deleteGroupRule(new GroupId(groupId).getRawGroupIdWithNull(), rule);
}

/**
* @see io.apicurio.registry.rest.v3.GroupsResource#listArtifactRules(java.lang.String, java.lang.String)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import io.apicurio.registry.storage.dto.RuleConfigurationDto;
import io.apicurio.registry.types.RuleType;

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

/**
* A service used to retrieve the default global rules that have been set via registry.rules.global
Expand All @@ -15,15 +15,11 @@
public interface RulesProperties {

/**
* Get the list of configured default global RuleType enums. A list of RuleType enums can be supplied that
* will be filtered out of the returned list.
* Get the list of configured default global RuleType enums.
*
* @param excludeRulesFilter a list of RuleType enums to filter from the returned list. If null, the
* entire configured list of default global RuleTypes is returned.
* @return The list of configured default global RuleTypes with any matching the excludeRules list
* removed.
* @return The list of configured default global RuleTypes.
*/
List<RuleType> getFilteredDefaultGlobalRules(List<RuleType> excludeRulesFilter);
Set<RuleType> getDefaultGlobalRules();

/**
* Whether the supplied RuleType has been configured as a global rule.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import io.apicurio.registry.storage.dto.RuleConfigurationDto;
import io.apicurio.registry.types.RuleType;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

public class RulesPropertiesImpl implements RulesProperties {
Expand All @@ -22,10 +22,8 @@ public RulesPropertiesImpl(Properties properties) {
}

@Override
public List<RuleType> getFilteredDefaultGlobalRules(List<RuleType> excludeRulesFilter) {
return defaultGlobalRules.keySet().stream()
.filter(ruleType -> excludeRulesFilter == null || !excludeRulesFilter.contains(ruleType))
.collect(Collectors.toList());
public Set<RuleType> getDefaultGlobalRules() {
return defaultGlobalRules.keySet();
}

@Override
Expand Down
75 changes: 41 additions & 34 deletions app/src/main/java/io/apicurio/registry/rules/RulesServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
import jakarta.inject.Inject;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Set;

/**
* Implements the {@link RulesService} interface.
Expand Down Expand Up @@ -45,9 +47,9 @@ public void applyRules(String groupId, String artifactId, String artifactType, T
RuleApplicationType ruleApplicationType, List<ArtifactReference> references,
Map<String, TypedContent> resolvedReferences) throws RuleViolationException {
@SuppressWarnings("unchecked")
List<RuleType> rules = Collections.EMPTY_LIST;
Set<RuleType> artifactRules = Collections.EMPTY_SET;
if (ruleApplicationType == RuleApplicationType.UPDATE) {
rules = storage.getArtifactRules(groupId, artifactId);
artifactRules = new HashSet<>(storage.getArtifactRules(groupId, artifactId));
}
LazyContentList currentContent = null;
if (ruleApplicationType == RuleApplicationType.UPDATE) {
Expand All @@ -57,39 +59,44 @@ public void applyRules(String groupId, String artifactId, String artifactType, T
currentContent = new LazyContentList(storage, Collections.emptyList());
}

applyGlobalAndArtifactRules(groupId, artifactId, artifactType, currentContent, content, rules,
references, resolvedReferences);
applyAllRules(groupId, artifactId, artifactType, currentContent, content, artifactRules, references,
resolvedReferences);
}

private void applyGlobalAndArtifactRules(String groupId, String artifactId, String artifactType,
List<TypedContent> currentContent, TypedContent updatedContent, List<RuleType> artifactRules,
private void applyAllRules(String groupId, String artifactId, String artifactType,
List<TypedContent> currentContent, TypedContent updatedContent, Set<RuleType> artifactRules,
List<ArtifactReference> references, Map<String, TypedContent> resolvedReferences) {

Map<RuleType, RuleConfigurationDto> globalOrArtifactRulesMap = artifactRules.stream()
.collect(Collectors.toMap(ruleType -> ruleType,
ruleType -> storage.getArtifactRule(groupId, artifactId, ruleType)));

if (globalOrArtifactRulesMap.isEmpty()) {
List<RuleType> globalRules = storage.getGlobalRules();
globalOrArtifactRulesMap = globalRules.stream()
.collect(Collectors.toMap(ruleType -> ruleType, storage::getGlobalRule));

// Add any default global rules to the map (after filtering out any global rules from
// artifactStore)
Map<RuleType, RuleConfigurationDto> filteredDefaultGlobalRulesMap = rulesProperties
.getFilteredDefaultGlobalRules(globalRules).stream().collect(Collectors
.toMap(ruleType -> ruleType, rulesProperties::getDefaultGlobalRuleConfiguration));
globalOrArtifactRulesMap.putAll(filteredDefaultGlobalRulesMap);
}

if (globalOrArtifactRulesMap.isEmpty()) {
return;
}

for (RuleType ruleType : globalOrArtifactRulesMap.keySet()) {
// TODO Getting the list of rules to apply results in several (admittedly fast) DB calls.
// Can we perhaps do a single DB call to get the map of rules to apply?

Map<RuleType, RuleConfigurationDto> allRules = new HashMap<>();

// Get the group rules (we already have the artifact rules)
Set<RuleType> groupRules = storage.isGroupExists(groupId)
? new HashSet<>(storage.getGroupRules(groupId)) : Set.of();
// Get the global rules
Set<RuleType> globalRules = new HashSet<>(storage.getGlobalRules());
// Get the configured default global rules
Set<RuleType> defaultGlobalRules = rulesProperties.getDefaultGlobalRules();

// Build the map of rules to apply (may be empty)
List.of(RuleType.values()).forEach(rt -> {
if (artifactRules.contains(rt)) {
allRules.put(rt, storage.getArtifactRule(groupId, artifactId, rt));
} else if (groupRules.contains(rt)) {
allRules.put(rt, storage.getGroupRule(groupId, rt));
} else if (globalRules.contains(rt)) {
allRules.put(rt, storage.getGlobalRule(rt));
} else if (defaultGlobalRules.contains(rt)) {
allRules.put(rt, rulesProperties.getDefaultGlobalRuleConfiguration(rt));
}
});

// Apply rules
for (RuleType ruleType : allRules.keySet()) {
applyRule(groupId, artifactId, artifactType, currentContent, updatedContent, ruleType,
globalOrArtifactRulesMap.get(ruleType).getConfiguration(), references,
resolvedReferences);
allRules.get(ruleType).getConfiguration(), references, resolvedReferences);
}
}

Expand Down Expand Up @@ -138,8 +145,8 @@ public void applyRules(String groupId, String artifactId, String artifactVersion
artifactVersion);
TypedContent typedVersionContent = TypedContent.create(versionContent.getContent(),
versionContent.getContentType());
applyGlobalAndArtifactRules(groupId, artifactId, artifactType,
Collections.singletonList(typedVersionContent), updatedContent,
storage.getArtifactRules(groupId, artifactId), references, resolvedReferences);
Set<RuleType> artifactRules = new HashSet<>(storage.getArtifactRules(groupId, artifactId));
applyAllRules(groupId, artifactId, artifactType, Collections.singletonList(typedVersionContent),
updatedContent, artifactRules, references, resolvedReferences);
}
}
Loading
Loading