Skip to content

Commit 14ae3db

Browse files
authored
Merge branch 'main' into protobuf4j-migration
2 parents af03fe7 + e201a96 commit 14ae3db

File tree

171 files changed

+12408
-1438
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

171 files changed

+12408
-1438
lines changed

.github/workflows/ci-simplification-plan.md

Lines changed: 0 additions & 615 deletions
This file was deleted.

.github/workflows/release-artifacts.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,6 @@ jobs:
146146
uses: actions/setup-node@v4
147147
with:
148148
node-version: 20
149-
cache: 'npm'
150-
cache-dependency-path: 'registry/ui/**/package-lock.json'
151149

152150
- name: Maven Install
153151
run: |
@@ -290,6 +288,9 @@ jobs:
290288
node-version: 20
291289
registry-url: 'https://registry.npmjs.org'
292290

291+
- name: Update npm
292+
run: npm install -g npm@latest
293+
293294
- name: Build and Publish Artifact Type Builtins Package
294295
working-directory: registry/artifact-type-builtins
295296
run: |

.github/workflows/release-sdks.yaml

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ jobs:
101101
- name: Publish to PyPI
102102
working-directory: registry/python-sdk
103103
env:
104-
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }}
105-
run: poetry publish
104+
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
105+
run: make publish
106106

107107
- name: Notify on completion
108108
if: always()
@@ -137,26 +137,41 @@ jobs:
137137
uses: actions/setup-go@v5
138138
with:
139139
go-version: '1.23'
140+
cache: false
140141

141-
- name: Clone Go SDK Repository
142+
- name: Release Go SDK
143+
working-directory: registry
142144
run: |
143-
git clone https://apicurio-ci:${{ secrets.ACCESS_TOKEN }}@github.com/Apicurio/apicurio-registry-go-sdk.git go-sdk
144-
cd go-sdk
145-
git checkout main
146-
147-
- name: Update Go SDK
148-
run: |
149-
# Copy updated SDK files
150-
rm -rf go-sdk/v3/
151-
cp -r registry/go-sdk/* go-sdk/
152-
153-
- name: Create Tag and Push
154-
working-directory: go-sdk
155-
run: |
156-
git add .
157-
git commit -m "Release ${{ needs.prepare.outputs.release-version }}" || true
158-
git tag v${{ needs.prepare.outputs.release-version }}
159-
git push origin main --tags
145+
git tag "v${{ needs.prepare.outputs.release-version }}"
146+
git tag "go-sdk/v${{ needs.prepare.outputs.release-version }}"
147+
git push origin "v${{ needs.prepare.outputs.release-version }}"
148+
git push origin "go-sdk/v${{ needs.prepare.outputs.release-version }}"
149+
150+
# Retry up to 5 times with increasing sleep intervals.
151+
MAX_RETRIES=5
152+
RETRY_COUNT=0
153+
SUCCESS=false
154+
155+
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
156+
echo "Attempt $((RETRY_COUNT + 1)) of $MAX_RETRIES..."
157+
if GOPROXY=proxy.golang.org go list -m "github.com/apicurio/apicurio-registry/go-sdk/v3@v${{ needs.prepare.outputs.release-version }}"; then
158+
SUCCESS=true
159+
echo "Successfully verified Go module."
160+
break
161+
else
162+
RETRY_COUNT=$((RETRY_COUNT + 1))
163+
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
164+
SLEEP_TIME=$((RETRY_COUNT * 10))
165+
echo "Failed. Retrying in $SLEEP_TIME seconds..."
166+
sleep $SLEEP_TIME
167+
fi
168+
fi
169+
done
170+
171+
if [ "$SUCCESS" = false ]; then
172+
echo "ERROR: Failed to verify Go module after $MAX_RETRIES attempts."
173+
exit 1
174+
fi
160175
161176
- name: Notify on completion
162177
if: always()

app/pom.xml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<parent>
55
<groupId>io.apicurio</groupId>
66
<artifactId>apicurio-registry</artifactId>
7-
<version>3.1.6-SNAPSHOT</version>
7+
<version>3.1.7-SNAPSHOT</version>
88
<relativePath>../pom.xml</relativePath>
99
</parent>
1010

@@ -295,6 +295,16 @@
295295
<artifactId>apicurio-registry-v2-java-sdk</artifactId>
296296
<scope>test</scope>
297297
</dependency>
298+
<dependency>
299+
<groupId>io.kiota</groupId>
300+
<artifactId>kiota-http-jdk</artifactId>
301+
<scope>test</scope>
302+
</dependency>
303+
<dependency>
304+
<groupId>io.kiota</groupId>
305+
<artifactId>kiota-http-vertx</artifactId>
306+
<scope>test</scope>
307+
</dependency>
298308
<dependency>
299309
<groupId>io.apicurio</groupId>
300310
<artifactId>apicurio-registry-protobuf-serde-kafka</artifactId>

app/src/main/java/io/apicurio/registry/config/config/impl/DynamicConfigSource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public String getValue(String propertyName) {
8282
pname, Thread.currentThread().getName());
8383
return dto.getValue();
8484
} else {
85-
log.trace(LOG_PREFIX + "Storage returned null.", pname,
85+
log.trace(LOG_PREFIX + "Property not found in storage.", pname,
8686
Thread.currentThread().getName());
8787
}
8888
} else {

app/src/main/java/io/apicurio/registry/types/provider/configured/ConfiguredReferenceFinder.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.apicurio.registry.config.artifactTypes.WebhookProvider;
77
import io.apicurio.registry.content.TypedContent;
88
import io.apicurio.registry.content.refs.ExternalReference;
9+
import io.apicurio.registry.content.refs.ReferenceFinderException;
910
import io.apicurio.registry.content.refs.ReferenceFinder;
1011
import io.apicurio.registry.http.HttpClientService;
1112
import io.apicurio.registry.script.ArtifactTypeScriptProvider;
@@ -61,8 +62,7 @@ public Set<ExternalReference> findExternalReferences(TypedContent content) {
6162
ReferenceFinderResponse responseBody = invokeHook(requestBody, ReferenceFinderResponse.class);
6263
return WebhookBeanUtil.externalReferencesFromWebhookBean(responseBody.getExternalReferences());
6364
} catch (Throwable e) {
64-
log.error("Error invoking webhook", e);
65-
return Set.of();
65+
throw new ReferenceFinderException("Error invoking webhook for reference finder.", e);
6666
}
6767
}
6868
}
@@ -87,8 +87,7 @@ public Set<ExternalReference> findExternalReferences(TypedContent content) {
8787
ReferenceFinderResponse responseBody = scriptProvider.findExternalReferences(requestBody);
8888
return WebhookBeanUtil.externalReferencesFromWebhookBean(responseBody.getExternalReferences());
8989
} catch (Throwable e) {
90-
log.error("Error invoking script", e);
91-
return Set.of();
90+
throw new ReferenceFinderException("Error invoking script for reference finder.", e);
9291
} finally {
9392
closeScriptProvider(scriptProvider);
9493
}

app/src/test/java/io/apicurio/registry/noprofile/rest/v3/AllYamlTest.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@
1818
import io.quarkus.test.junit.QuarkusTest;
1919
import org.junit.jupiter.api.Assertions;
2020
import org.junit.jupiter.api.Test;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
2123

2224
import java.util.UUID;
2325

2426
@QuarkusTest
2527
public class AllYamlTest extends AbstractResourceTestBase {
2628

29+
private static final Logger log = LoggerFactory.getLogger(AllYamlTest.class);
30+
2731
private static String YAML_CONTENT = """
2832
openapi: 3.0.2
2933
info:
@@ -125,8 +129,7 @@ public void testCreateYamlArtifactWithValidity() throws Exception {
125129
YAML_CONTENT, ContentTypes.APPLICATION_YAML);
126130
clientV3.groups().byGroupId(groupId).artifacts().post(createArtifact);
127131
} catch (ProblemDetails e) {
128-
System.out.println("ERROR: " + e.getDetail());
129-
e.getCause().printStackTrace();
132+
log.error("Failed to create YAML artifact: {}", e.getDetail(), e.getCause());
130133
throw e;
131134
}
132135
}

app/src/test/java/io/apicurio/registry/noprofile/rest/v3/GroupsResourceTest.java

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package io.apicurio.registry.noprofile.rest.v3;
22

3+
import com.fasterxml.jackson.databind.JsonNode;
34
import io.apicurio.registry.AbstractResourceTestBase;
5+
import io.apicurio.registry.content.ContentHandle;
6+
import io.apicurio.registry.content.util.ContentTypeUtil;
47
import io.apicurio.registry.model.GroupId;
58
import io.apicurio.registry.rest.client.models.CreateVersion;
69
import io.apicurio.registry.rest.client.models.GroupSearchResults;
@@ -1869,4 +1872,118 @@ public void testGetArtifactVersionWithReferences() throws Exception {
18691872
.body("paths.widgets.get.responses.200.content.json.schema.items.properties.description.type", equalTo("string"));
18701873
}
18711874

1875+
/**
1876+
* Test that verifies the Content-Type header is preserved when using ?references=REWRITE with YAML content.
1877+
* This test reproduces issue #6712 where YAML AsyncAPI content would be rewritten correctly but served with
1878+
* mismatched Content-Type header, causing JSON parsing errors.
1879+
*
1880+
* This test uses the exact structure from the user's report:
1881+
* - AsyncAPI 3.0.0 in YAML format
1882+
* - References to Avro schema files (JSON format)
1883+
* - Repository: https://github.com/ZenWave360/zenwave-playground/tree/main/examples/asyncapi-shopping-cart/apis
1884+
*/
1885+
@Test
1886+
public void testGetArtifactVersionWithReferencesYamlContentType() throws Exception {
1887+
String groupId = TestUtils.generateGroupId();
1888+
String avroSchemaContent = resourceToString("avro/ShoppingCartCreated.avsc");
1889+
String asyncApiContent = resourceToString("asyncapi-shopping-cart.yml");
1890+
1891+
// Create the Avro schema artifact that will be referenced
1892+
createArtifact(groupId, "testYamlReferences/ShoppingCartCreated", ArtifactType.AVRO,
1893+
avroSchemaContent, ContentTypes.APPLICATION_JSON);
1894+
1895+
// Create the AsyncAPI artifact (YAML) that references the Avro schema
1896+
List<ArtifactReference> refs = Collections.singletonList(ArtifactReference.builder()
1897+
.name("./avro/ShoppingCartCreated.avsc").groupId(groupId)
1898+
.artifactId("testYamlReferences/ShoppingCartCreated").version("1").build());
1899+
createArtifactWithReferences(groupId, "testYamlReferences/ShoppingCartAPI",
1900+
ArtifactType.ASYNCAPI, asyncApiContent, ContentTypes.APPLICATION_YAML, refs);
1901+
1902+
// Get the content of the artifact preserving external references
1903+
// This should return YAML with YAML content type
1904+
String preservedContent = given().when().pathParam("groupId", groupId)
1905+
.pathParam("artifactId", "testYamlReferences/ShoppingCartAPI")
1906+
.get("/registry/v3/groups/{groupId}/artifacts/{artifactId}/versions/branch=latest/content")
1907+
.then().statusCode(200)
1908+
.contentType(ContentTypes.APPLICATION_YAML)
1909+
.extract().asString();
1910+
1911+
// Verify the preserved content is valid YAML by parsing it
1912+
try {
1913+
JsonNode preservedYaml = ContentTypeUtil.parseYaml(ContentHandle.create(preservedContent));
1914+
Assertions.assertNotNull(preservedYaml, "Preserved content should be valid YAML");
1915+
Assertions.assertTrue(preservedYaml.has("asyncapi"), "YAML should have 'asyncapi' field");
1916+
Assertions.assertEquals("3.0.0", preservedYaml.get("asyncapi").asText(),
1917+
"Should be AsyncAPI 3.0.0");
1918+
} catch (IOException e) {
1919+
Assertions.fail("Failed to parse preserved content as YAML: " + e.getMessage());
1920+
}
1921+
1922+
// Get the content of the artifact rewriting external references
1923+
// CRITICAL: This should return YAML content with YAML content type (not JSON content type)
1924+
// Bug #6712: Currently this fails because the response uses artifact.getContentType() instead of
1925+
// contentToReturn.getContentType(), causing a mismatch between the content format and Content-Type header
1926+
io.restassured.response.Response rawResponse = given().when().pathParam("groupId", groupId)
1927+
.pathParam("artifactId", "testYamlReferences/ShoppingCartAPI")
1928+
.queryParam("references", "REWRITE")
1929+
.get("/registry/v3/groups/{groupId}/artifacts/{artifactId}/versions/branch=latest/content");
1930+
1931+
// Verify the response status
1932+
Assertions.assertEquals(200, rawResponse.getStatusCode(),
1933+
"Expected 200 OK but got: " + rawResponse.getStatusCode() + " - " + rawResponse.asString());
1934+
1935+
// Verify the Content-Type header is YAML (not JSON)
1936+
String contentType = rawResponse.getContentType();
1937+
Assertions.assertNotNull(contentType, "Content-Type header should not be null");
1938+
Assertions.assertTrue(contentType.contains("yaml") || contentType.contains("yml"),
1939+
"Content-Type should be YAML but was: " + contentType);
1940+
1941+
// Verify the content is valid YAML by parsing it
1942+
String responseBody = rawResponse.asString();
1943+
JsonNode yamlNode = null;
1944+
try {
1945+
yamlNode = ContentTypeUtil.parseYaml(ContentHandle.create(responseBody));
1946+
Assertions.assertNotNull(yamlNode, "Response should be valid YAML");
1947+
Assertions.assertTrue(yamlNode.has("asyncapi"), "YAML should have 'asyncapi' field");
1948+
Assertions.assertEquals("3.0.0", yamlNode.get("asyncapi").asText(),
1949+
"Should be AsyncAPI 3.0.0");
1950+
} catch (IOException e) {
1951+
Assertions.fail("Failed to parse response as YAML: " + e.getMessage() + ". Body starts with: "
1952+
+ responseBody.substring(0, Math.min(100, responseBody.length())));
1953+
}
1954+
1955+
// Verify the reference was rewritten to point to the REST API
1956+
// Navigate to the $ref field: components -> messages -> ShoppingCartCreatedMessage -> payload -> schema -> $ref
1957+
JsonNode components = yamlNode.get("components");
1958+
Assertions.assertNotNull(components, "YAML should have 'components' field");
1959+
1960+
JsonNode messages = components.get("messages");
1961+
Assertions.assertNotNull(messages, "Components should have 'messages' field");
1962+
1963+
JsonNode shoppingCartCreated = messages.get("ShoppingCartCreatedMessage");
1964+
Assertions.assertNotNull(shoppingCartCreated, "Messages should have 'ShoppingCartCreatedMessage' field");
1965+
1966+
JsonNode payload = shoppingCartCreated.get("payload");
1967+
Assertions.assertNotNull(payload, "ShoppingCartCreatedMessage should have 'payload' field");
1968+
1969+
JsonNode schema = payload.get("schema");
1970+
Assertions.assertNotNull(schema, "Payload should have 'schema' field");
1971+
1972+
JsonNode ref = schema.get("$ref");
1973+
Assertions.assertNotNull(ref, "Schema should have '$ref' field");
1974+
1975+
String rewrittenRef = ref.asText();
1976+
Assertions.assertNotNull(rewrittenRef, "Rewritten reference should not be null");
1977+
1978+
// Verify the reference was rewritten to a REST API URL
1979+
Assertions.assertTrue(rewrittenRef.contains("/apis/registry/v3/groups/"),
1980+
"Reference should be rewritten to REST API URL but was: " + rewrittenRef);
1981+
Assertions.assertTrue(rewrittenRef.contains("/artifacts/"),
1982+
"Reference should contain artifact path but was: " + rewrittenRef);
1983+
Assertions.assertTrue(rewrittenRef.contains("testYamlReferences%2FShoppingCartCreated"),
1984+
"Reference should point to the Avro artifact but was: " + rewrittenRef);
1985+
Assertions.assertTrue(rewrittenRef.contains("?references=REWRITE"),
1986+
"Reference should contain references=REWRITE parameter but was: " + rewrittenRef);
1987+
}
1988+
18721989
}

app/src/test/java/io/apicurio/registry/noprofile/rest/v3/ImportExportTest.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import jakarta.inject.Inject;
3131
import org.junit.jupiter.api.Assertions;
3232
import org.junit.jupiter.api.Test;
33+
import org.slf4j.Logger;
34+
import org.slf4j.LoggerFactory;
3335

3436
import java.io.File;
3537
import java.io.FileInputStream;
@@ -47,6 +49,8 @@
4749
@QuarkusTest
4850
public class ImportExportTest extends AbstractResourceTestBase {
4951

52+
private static final Logger log = LoggerFactory.getLogger(ImportExportTest.class);
53+
5054
@Inject
5155
@Current
5256
RegistryStorage storage;
@@ -334,17 +338,17 @@ private static void downloadTo(URL url, File tempFile) {
334338
}
335339

336340
private static void listFiles(File tempFile) {
337-
System.out.println("--- Export ZIP File Listing ---");
341+
log.debug("--- Export ZIP File Listing ---");
338342
try (ZipFile zipFile = new ZipFile(tempFile)) {
339343
Enumeration<? extends ZipEntry> entries = zipFile.entries();
340344
while (entries.hasMoreElements()) {
341345
ZipEntry entry = entries.nextElement();
342-
System.out.println(entry.getName());
346+
log.debug(entry.getName());
343347
}
344348
} catch (IOException e) {
345-
e.printStackTrace();
349+
log.warn("Failed to list ZIP file entries", e);
346350
}
347-
System.out.println("--- Export ZIP File Listing ---");
351+
log.debug("--- Export ZIP File Listing ---");
348352
}
349353

350354
}

0 commit comments

Comments
 (0)