Skip to content

Commit 7d9875d

Browse files
authored
[3.0] Forward port the "group concatenation" feature of the ccompat API introduced in 2.6.2 (#5189)
* Ported the "group concatenation" feature of the ccompat API introduced in 2.6.2 to main * spotless:apply * Generated docs
1 parent 9a88ef5 commit 7d9875d

File tree

9 files changed

+421
-134
lines changed

9 files changed

+421
-134
lines changed

app/src/main/java/io/apicurio/registry/ccompat/rest/v7/impl/AbstractResource.java

+52-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.apicurio.registry.ccompat.rest.v7.impl;
22

3+
import io.apicurio.common.apps.util.Pair;
34
import io.apicurio.registry.ccompat.dto.SchemaReference;
45
import io.apicurio.registry.ccompat.rest.error.ConflictException;
56
import io.apicurio.registry.ccompat.rest.error.UnprocessableEntityException;
@@ -19,6 +20,7 @@
1920
import io.apicurio.registry.storage.dto.ContentWrapperDto;
2021
import io.apicurio.registry.storage.dto.EditableArtifactMetaDataDto;
2122
import io.apicurio.registry.storage.dto.EditableVersionMetaDataDto;
23+
import io.apicurio.registry.storage.dto.SearchedArtifactDto;
2224
import io.apicurio.registry.storage.dto.StoredArtifactVersionDto;
2325
import io.apicurio.registry.storage.error.ArtifactNotFoundException;
2426
import io.apicurio.registry.storage.error.RuleNotFoundException;
@@ -31,6 +33,7 @@
3133
import io.apicurio.registry.types.provider.ArtifactTypeUtilProvider;
3234
import io.apicurio.registry.types.provider.ArtifactTypeUtilProviderFactory;
3335
import jakarta.inject.Inject;
36+
import jakarta.ws.rs.BadRequestException;
3437
import org.apache.avro.AvroTypeException;
3538
import org.apache.avro.SchemaParseException;
3639
import org.apache.commons.codec.digest.DigestUtils;
@@ -63,7 +66,37 @@ public abstract class AbstractResource {
6366
@Inject
6467
ArtifactTypeUtilProviderFactory factory;
6568

66-
protected ArtifactVersionMetaDataDto createOrUpdateArtifact(String subject, String schema,
69+
protected String toSubjectWithGroupConcat(String groupId, String artifactId) {
70+
return (groupId == null ? "" : groupId) + cconfig.groupConcatSeparator + artifactId;
71+
}
72+
73+
protected String toSubjectWithGroupConcat(SearchedArtifactDto dto) {
74+
return toSubjectWithGroupConcat(dto.getGroupId(), dto.getArtifactId());
75+
}
76+
77+
private Pair<String, String> toGAFromGroupConcatSubject(String subject) {
78+
int sepIdx = subject.indexOf(cconfig.groupConcatSeparator);
79+
if (sepIdx < 1) {
80+
throw new BadRequestException("Invalid subject format. Should be: groupId"
81+
+ cconfig.groupConcatSeparator + "artifactId");
82+
}
83+
String groupId = subject.substring(0, sepIdx);
84+
String artifactId = subject.substring(sepIdx + cconfig.groupConcatSeparator.length());
85+
return new Pair<>(groupId, artifactId);
86+
}
87+
88+
protected GA getGA(String groupId, String artifactId) {
89+
String gid = groupId;
90+
String aid = artifactId;
91+
if (cconfig.groupConcatEnabled) {
92+
Pair<String, String> ga = toGAFromGroupConcatSubject(artifactId);
93+
gid = ga.getLeft();
94+
aid = ga.getRight();
95+
}
96+
return new GA(gid, aid);
97+
}
98+
99+
protected ArtifactVersionMetaDataDto createOrUpdateArtifact(String artifactId, String schema,
67100
String artifactType, List<SchemaReference> references, String groupId) {
68101
ArtifactVersionMetaDataDto res;
69102
final List<ArtifactReferenceDto> parsedReferences = parseReferences(references, groupId);
@@ -80,9 +113,9 @@ protected ArtifactVersionMetaDataDto createOrUpdateArtifact(String subject, Stri
80113
contentType = ContentTypes.APPLICATION_PROTOBUF;
81114
}
82115

83-
if (!doesArtifactExist(subject, groupId)) {
116+
if (!doesArtifactExist(artifactId, groupId)) {
84117
TypedContent typedSchemaContent = TypedContent.create(schemaContent, contentType);
85-
rulesService.applyRules(groupId, subject, artifactType, typedSchemaContent,
118+
rulesService.applyRules(groupId, artifactId, artifactType, typedSchemaContent,
86119
RuleApplicationType.CREATE, artifactReferences, resolvedReferences);
87120

88121
EditableArtifactMetaDataDto artifactMetaData = EditableArtifactMetaDataDto.builder().build();
@@ -91,15 +124,15 @@ protected ArtifactVersionMetaDataDto createOrUpdateArtifact(String subject, Stri
91124
ContentWrapperDto firstVersionContent = ContentWrapperDto.builder().content(schemaContent)
92125
.contentType(contentType).references(parsedReferences).build();
93126

94-
res = storage.createArtifact(groupId, subject, artifactType, artifactMetaData, null,
127+
res = storage.createArtifact(groupId, artifactId, artifactType, artifactMetaData, null,
95128
firstVersionContent, firstVersionMetaData, null, false).getValue();
96129
} else {
97130
TypedContent typedSchemaContent = TypedContent.create(schemaContent, contentType);
98-
rulesService.applyRules(groupId, subject, artifactType, typedSchemaContent,
131+
rulesService.applyRules(groupId, artifactId, artifactType, typedSchemaContent,
99132
RuleApplicationType.UPDATE, artifactReferences, resolvedReferences);
100133
ContentWrapperDto versionContent = ContentWrapperDto.builder().content(schemaContent)
101134
.contentType(contentType).references(parsedReferences).build();
102-
res = storage.createArtifactVersion(groupId, subject, null, artifactType, versionContent,
135+
res = storage.createArtifactVersion(groupId, artifactId, null, artifactType, versionContent,
103136
EditableVersionMetaDataDto.builder().build(), List.of(), false);
104137
}
105138
} catch (RuleViolationException ex) {
@@ -112,7 +145,7 @@ protected ArtifactVersionMetaDataDto createOrUpdateArtifact(String subject, Stri
112145
return res;
113146
}
114147

115-
protected ArtifactVersionMetaDataDto lookupSchema(String groupId, String subject, String schema,
148+
protected ArtifactVersionMetaDataDto lookupSchema(String groupId, String artifactId, String schema,
116149
List<SchemaReference> schemaReferences, String schemaType, boolean normalize) {
117150
// FIXME simplify logic
118151
try {
@@ -126,7 +159,7 @@ protected ArtifactVersionMetaDataDto lookupSchema(String groupId, String subject
126159

127160
if (cconfig.canonicalHashModeEnabled.get() || normalize) {
128161
try {
129-
amd = storage.getArtifactVersionMetaDataByContent(groupId, subject, true,
162+
amd = storage.getArtifactVersionMetaDataByContent(groupId, artifactId, true,
130163
typedSchemaContent, artifactReferences);
131164
} catch (ArtifactNotFoundException ex) {
132165
if (type.equals(ArtifactType.AVRO)) {
@@ -137,9 +170,9 @@ protected ArtifactVersionMetaDataDto lookupSchema(String groupId, String subject
137170
// exception.
138171
// This approach only works for schema types with dereference support (for now, only
139172
// Avro in the ccompat API).
140-
amd = storage.getArtifactVersions(groupId, subject).stream().filter(version -> {
173+
amd = storage.getArtifactVersions(groupId, artifactId).stream().filter(version -> {
141174
StoredArtifactVersionDto artifactVersion = storage
142-
.getArtifactVersionContent(groupId, subject, version);
175+
.getArtifactVersionContent(groupId, artifactId, version);
143176
TypedContent typedArtifactVersion = TypedContent
144177
.create(artifactVersion.getContent(), artifactVersion.getContentType());
145178
Map<String, TypedContent> artifactVersionReferences = storage
@@ -149,17 +182,17 @@ protected ArtifactVersionMetaDataDto lookupSchema(String groupId, String subject
149182
.dereference(typedArtifactVersion, artifactVersionReferences)
150183
.getContent().content());
151184
return dereferencedExistingContentSha.equals(DigestUtils.sha256Hex(schema));
152-
}).findAny()
153-
.map(version -> storage.getArtifactVersionMetaData(groupId, subject, version))
185+
}).findAny().map(
186+
version -> storage.getArtifactVersionMetaData(groupId, artifactId, version))
154187
.orElseThrow(() -> ex);
155188
} else {
156189
throw ex;
157190
}
158191
}
159192

160193
} else {
161-
amd = storage.getArtifactVersionMetaDataByContent(groupId, subject, false, typedSchemaContent,
162-
artifactReferences);
194+
amd = storage.getArtifactVersionMetaDataByContent(groupId, artifactId, false,
195+
typedSchemaContent, artifactReferences);
163196
}
164197

165198
return amd;
@@ -193,18 +226,18 @@ protected Map<String, TypedContent> resolveReferences(List<SchemaReference> refe
193226
return resolvedReferences;
194227
}
195228

196-
protected boolean isArtifactActive(String subject, String groupId) {
197-
long count = storage.countActiveArtifactVersions(groupId, subject);
229+
protected boolean isArtifactActive(String artifactId, String groupId) {
230+
long count = storage.countActiveArtifactVersions(groupId, artifactId);
198231
return count > 0;
199232
}
200233

201-
protected String getLatestArtifactVersionForSubject(String subject, String groupId) {
234+
protected String getLatestArtifactVersionForSubject(String artifactId, String groupId) {
202235
try {
203-
GAV latestGAV = storage.getBranchTip(new GA(groupId, subject), BranchId.LATEST,
236+
GAV latestGAV = storage.getBranchTip(new GA(groupId, artifactId), BranchId.LATEST,
204237
RetrievalBehavior.SKIP_DISABLED_LATEST);
205238
return latestGAV.getRawVersionId();
206239
} catch (ArtifactNotFoundException ex) {
207-
throw new VersionNotFoundException(groupId, subject, "latest");
240+
throw new VersionNotFoundException(groupId, artifactId, "latest");
208241
}
209242
}
210243

app/src/main/java/io/apicurio/registry/ccompat/rest/v7/impl/CCompatConfig.java

+8
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,12 @@ public class CCompatConfig {
2525
@Info(category = "ccompat", description = "Maximum number of Subjects returned (compatibility API)", availableSince = "2.4.2.Final")
2626
Supplier<Integer> maxSubjects;
2727

28+
@ConfigProperty(name = "apicurio.ccompat.group-concat.enabled", defaultValue = "false")
29+
@Info(category = "ccompat", description = "Enable group support via concatenation in subject (compatibility API)", availableSince = "2.6.2.Final")
30+
boolean groupConcatEnabled;
31+
32+
@ConfigProperty(name = "apicurio.ccompat.group-concat.separator", defaultValue = ":")
33+
@Info(category = "ccompat", description = "Separator to use when group concatenation is enabled (compatibility API)", availableSince = "2.6.2.Final")
34+
String groupConcatSeparator;
35+
2836
}

app/src/main/java/io/apicurio/registry/ccompat/rest/v7/impl/CompatibilityResourceImpl.java

+36-29
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import io.apicurio.registry.content.TypedContent;
1313
import io.apicurio.registry.metrics.health.liveness.ResponseErrorLivenessCheck;
1414
import io.apicurio.registry.metrics.health.readiness.ResponseTimeoutReadinessCheck;
15+
import io.apicurio.registry.model.GA;
1516
import io.apicurio.registry.rules.RuleViolationException;
1617
import io.apicurio.registry.rules.UnprocessableSchemaException;
1718
import io.apicurio.registry.storage.dto.ArtifactVersionMetaDataDto;
@@ -30,21 +31,24 @@ public class CompatibilityResourceImpl extends AbstractResource implements Compa
3031
@Authorized(style = AuthorizedStyle.ArtifactOnly, level = AuthorizedLevel.Write)
3132
public CompatibilityCheckResponse testCompatibilityBySubjectName(String subject, SchemaContent request,
3233
Boolean verbose, String groupId) throws Exception {
34+
final GA ga = getGA(groupId, subject);
3335
final boolean fverbose = verbose == null ? Boolean.FALSE : verbose;
3436
try {
35-
final List<String> versions = storage.getArtifactVersions(groupId, subject);
37+
final List<String> versions = storage.getArtifactVersions(ga.getRawGroupIdWithNull(),
38+
ga.getRawArtifactId());
3639
for (String version : versions) {
37-
final ArtifactVersionMetaDataDto artifactVersionMetaData = storage
38-
.getArtifactVersionMetaData(groupId, subject, version);
40+
final ArtifactVersionMetaDataDto artifactVersionMetaData = storage.getArtifactVersionMetaData(
41+
ga.getRawGroupIdWithNull(), ga.getRawArtifactId(), version);
3942
// Assume the content type of the SchemaContent is the same as the previous version.
4043
String contentType = ContentTypes.APPLICATION_JSON;
4144
if (artifactVersionMetaData.getArtifactType().equals(ArtifactType.PROTOBUF)) {
4245
contentType = ContentTypes.APPLICATION_PROTOBUF;
4346
}
4447
TypedContent typedContent = TypedContent.create(ContentHandle.create(request.getSchema()),
4548
contentType);
46-
rulesService.applyRules(groupId, subject, version, artifactVersionMetaData.getArtifactType(),
47-
typedContent, Collections.emptyList(), Collections.emptyMap());
49+
rulesService.applyRules(ga.getRawGroupIdWithNull(), ga.getRawArtifactId(), version,
50+
artifactVersionMetaData.getArtifactType(), typedContent, Collections.emptyList(),
51+
Collections.emptyMap());
4852
}
4953
return CompatibilityCheckResponse.IS_COMPATIBLE;
5054
} catch (RuleViolationException ex) {
@@ -63,30 +67,33 @@ public CompatibilityCheckResponse testCompatibilityBySubjectName(String subject,
6367
public CompatibilityCheckResponse testCompatibilityByVersion(String subject, String versionString,
6468
SchemaContent request, Boolean verbose, String groupId) throws Exception {
6569
final boolean fverbose = verbose == null ? Boolean.FALSE : verbose;
70+
final GA ga = getGA(groupId, subject);
6671

67-
return parseVersionString(subject, versionString, groupId, v -> {
68-
try {
69-
final ArtifactVersionMetaDataDto artifact = storage.getArtifactVersionMetaData(groupId,
70-
subject, v);
71-
// Assume the content type of the SchemaContent is correct based on the artifact type.
72-
String contentType = ContentTypes.APPLICATION_JSON;
73-
if (artifact.getArtifactType().equals(ArtifactType.PROTOBUF)) {
74-
contentType = ContentTypes.APPLICATION_PROTOBUF;
75-
}
76-
TypedContent typedContent = TypedContent.create(ContentHandle.create(request.getSchema()),
77-
contentType);
78-
rulesService.applyRules(groupId, subject, v, artifact.getArtifactType(), typedContent,
79-
Collections.emptyList(), Collections.emptyMap());
80-
return CompatibilityCheckResponse.IS_COMPATIBLE;
81-
} catch (RuleViolationException ex) {
82-
if (fverbose) {
83-
return new CompatibilityCheckResponse(false, ex.getMessage());
84-
} else {
85-
return CompatibilityCheckResponse.IS_NOT_COMPATIBLE;
86-
}
87-
} catch (UnprocessableSchemaException ex) {
88-
throw new UnprocessableEntityException(ex.getMessage());
89-
}
90-
});
72+
return parseVersionString(ga.getRawArtifactId(), versionString, ga.getRawGroupIdWithNull(),
73+
version -> {
74+
try {
75+
final ArtifactVersionMetaDataDto artifact = storage.getArtifactVersionMetaData(
76+
ga.getRawGroupIdWithNull(), ga.getRawArtifactId(), version);
77+
// Assume the content type of the SchemaContent is correct based on the artifact type.
78+
String contentType = ContentTypes.APPLICATION_JSON;
79+
if (artifact.getArtifactType().equals(ArtifactType.PROTOBUF)) {
80+
contentType = ContentTypes.APPLICATION_PROTOBUF;
81+
}
82+
TypedContent typedContent = TypedContent
83+
.create(ContentHandle.create(request.getSchema()), contentType);
84+
rulesService.applyRules(ga.getRawGroupIdWithNull(), ga.getRawArtifactId(), version,
85+
artifact.getArtifactType(), typedContent, Collections.emptyList(),
86+
Collections.emptyMap());
87+
return CompatibilityCheckResponse.IS_COMPATIBLE;
88+
} catch (RuleViolationException ex) {
89+
if (fverbose) {
90+
return new CompatibilityCheckResponse(false, ex.getMessage());
91+
} else {
92+
return CompatibilityCheckResponse.IS_NOT_COMPATIBLE;
93+
}
94+
} catch (UnprocessableSchemaException ex) {
95+
throw new UnprocessableEntityException(ex.getMessage());
96+
}
97+
});
9198
}
9299
}

0 commit comments

Comments
 (0)