View Javadoc
1   package io.apicurio.registry.maven;
2   
3   import com.fasterxml.jackson.core.JsonProcessingException;
4   import com.fasterxml.jackson.databind.JsonNode;
5   import com.fasterxml.jackson.databind.ObjectMapper;
6   import com.github.tomakehurst.wiremock.WireMockServer;
7   import com.github.tomakehurst.wiremock.common.FileSource;
8   import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
9   import com.github.tomakehurst.wiremock.extension.Parameters;
10  import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
11  import com.github.tomakehurst.wiremock.http.Request;
12  import com.github.tomakehurst.wiremock.http.Response;
13  import io.apicurio.registry.rest.v3.beans.IfArtifactExists;
14  import io.apicurio.registry.types.ArtifactType;
15  import org.junit.jupiter.api.AfterEach;
16  import org.junit.jupiter.api.BeforeEach;
17  import org.junit.jupiter.api.Test;
18  
19  import java.io.File;
20  import java.nio.file.Paths;
21  import java.util.HashSet;
22  import java.util.Set;
23  
24  import static com.github.tomakehurst.wiremock.client.WireMock.*;
25  import static org.junit.jupiter.api.Assertions.assertEquals;
26  import static org.junit.jupiter.api.Assertions.assertTrue;
27  
28  public class RegisterAsyncApiAvroAutoRefsTest {
29  
30      private RegisterRegistryMojo mojo;
31      private final File examplesRoot = Paths.get("../../examples/").toAbsolutePath().toFile();
32      private WireMockServer wireMockServer;
33      private Set<String> registeredArtifacts = new HashSet<>();
34  
35      @BeforeEach public void setup() {
36          // Start WireMock server with custom transformer
37          wireMockServer = new WireMockServer(WireMockConfiguration.wireMockConfig().dynamicPort()
38                  .extensions(new CaptureRequestTransformer()));
39          wireMockServer.start();
40  
41          // Mock artifact registration endpoint with debugging transformer
42          wireMockServer.stubFor(post(urlPathMatching("/apis/registry/v3/groups/.*/artifacts")).willReturn(
43                  aResponse().withTransformers("capture-request-transformer").withStatus(200)
44                          .withHeader("Content-Type", "application/json")));
45  
46          mojo = new RegisterRegistryMojo();
47          mojo.setRegistryUrl(wireMockServer.baseUrl() + "/apis/registry/v3");
48  
49          // clear captured registered artifacts
50          registeredArtifacts.clear();
51      }
52  
53      @AfterEach public void tearDown() {
54          if (wireMockServer != null) {
55              wireMockServer.stop();
56          }
57      }
58  
59      @Test public void testAsyncApiWithAvroAutoRefs() throws Exception {
60          File exampleDir = new File(examplesRoot, "asyncapi-avro-maven-with-references-auto");
61          File asyncApiFile = new File(exampleDir, "src/main/resources/schemas/asyncapi-avro-refs.yml");
62  
63          // Create the artifact configuration
64          RegisterArtifact artifact = new RegisterArtifact();
65          artifact.setGroupId("asyncapi-avro-maven-with-references-auto");
66          artifact.setArtifactId("CustomersExample");
67          artifact.setVersion("1.0.0");
68          artifact.setArtifactType(ArtifactType.ASYNCAPI);
69          artifact.setFile(asyncApiFile);
70          artifact.setIfExists(IfArtifactExists.FIND_OR_CREATE_VERSION);
71          artifact.setCanonicalize(true);
72          artifact.setAutoRefs(true);
73  
74          mojo.setArtifacts(java.util.Collections.singletonList(artifact));
75          mojo.execute();
76  
77          assertEquals(6, registeredArtifacts.size());
78          assertTrue(registeredArtifacts.contains("asyncapi-avro-maven-with-references-auto:CustomersExample"));
79          assertTrue(registeredArtifacts.contains(
80                  "asyncapi-avro-maven-with-references-auto:io.example.api.dtos.CustomerEvent"));
81          assertTrue(registeredArtifacts.contains(
82                  "asyncapi-avro-maven-with-references-auto:io.example.api.dtos.Address"));
83          assertTrue(registeredArtifacts.contains(
84                  "asyncapi-avro-maven-with-references-auto:io.example.api.dtos.PaymentMethod"));
85          assertTrue(registeredArtifacts.contains(
86                  "asyncapi-avro-maven-with-references-auto:io.example.api.dtos.PaymentMethodType"));
87          assertTrue(registeredArtifacts.contains(
88                  "asyncapi-avro-maven-with-references-auto:io.example.api.dtos.CustomerDeletedEvent"));
89  
90      }
91  
92      @Test public void testAvroAutoRefs() throws Exception {
93          File exampleDir = new File(examplesRoot, "avro-maven-with-references-auto");
94          File avroFile = new File(exampleDir, "src/main/resources/schemas/TradeRaw.avsc");
95  
96          // Create the artifact configuration
97          RegisterArtifact artifact = new RegisterArtifact();
98          artifact.setGroupId("com.kubetrade.schema.trade");
99          artifact.setArtifactId("TradeRaw");
100         artifact.setVersion("2.0");
101         artifact.setArtifactType(ArtifactType.AVRO);
102         artifact.setFile(avroFile);
103         artifact.setIfExists(IfArtifactExists.FIND_OR_CREATE_VERSION);
104         artifact.setCanonicalize(true);
105         artifact.setAutoRefs(true);
106 
107         mojo.setArtifacts(java.util.Collections.singletonList(artifact));
108         mojo.execute();
109 
110         assertEquals(4, registeredArtifacts.size());
111         assertTrue(registeredArtifacts.contains("com.kubetrade.schema.trade:TradeRaw"));
112         assertTrue(registeredArtifacts.contains("com.kubetrade.schema.trade:TradeKey"));
113         assertTrue(registeredArtifacts.contains("com.kubetrade.schema.trade:TradeValue"));
114         assertTrue(registeredArtifacts.contains("com.kubetrade.schema.common:Exchange"));
115     }
116 
117     @Test public void testAvroAnalyzeDirectory() throws Exception {
118         File exampleDir = new File(examplesRoot, "avro-maven-with-references-auto");
119         File avroFile = new File(exampleDir, "src/main/resources/schemas/TradeRaw.avsc");
120 
121         // Create the artifact configuration
122         RegisterArtifact artifact = new RegisterArtifact();
123         artifact.setGroupId("avro-maven-with-analyze-directory");
124         artifact.setArtifactId("TradeRaw");
125         artifact.setVersion("2.0");
126         artifact.setArtifactType(ArtifactType.AVRO);
127         artifact.setFile(avroFile);
128         artifact.setIfExists(IfArtifactExists.FIND_OR_CREATE_VERSION);
129         artifact.setCanonicalize(true);
130         artifact.setAnalyzeDirectory(true);
131 
132         mojo.setArtifacts(java.util.Collections.singletonList(artifact));
133         mojo.execute();
134 
135         assertEquals(4, registeredArtifacts.size());
136         assertTrue(registeredArtifacts.contains("avro-maven-with-analyze-directory:TradeRaw"));
137         assertTrue(registeredArtifacts.contains(
138                 "avro-maven-with-analyze-directory:com.kubetrade.schema.trade.TradeKey"));
139         assertTrue(registeredArtifacts.contains(
140                 "avro-maven-with-analyze-directory:com.kubetrade.schema.trade.TradeValue"));
141         assertTrue(registeredArtifacts.contains(
142                 "avro-maven-with-analyze-directory:com.kubetrade.schema.common.Exchange"));
143     }
144 
145     // Custom transformer to capture/debug requests and generate dynamic responses
146     class CaptureRequestTransformer extends ResponseTransformer {
147 
148         @Override public String getName() {
149             return "capture-request-transformer";
150         }
151 
152         @Override public Response transform(Request request, Response response, FileSource fileSource,
153                 Parameters parameters) {
154             try {
155                 String responseBody = generateResponseBasedOnRequest(request);
156                 return Response.Builder.like(response).but().body(responseBody).build();
157             } catch (JsonProcessingException e) {
158                 throw new RuntimeException(e);
159             }
160 
161         }
162 
163         private String generateResponseBasedOnRequest(Request request) throws JsonProcessingException {
164             String url = request.getUrl();
165             String body = request.getBodyAsString();
166 
167             String groupId = extractGroupIdFromUrl(url);
168             String artifactId = extractArtifactIdFromBody(body);
169 
170             registeredArtifacts.add(groupId + ":" + artifactId);
171 
172             return """
173                     {
174                       "version": {
175                         "version": "1",
176                         "globalId": 12345,
177                         "contentId": 67890
178                       },
179                       "artifact": {
180                         "groupId": "%s",
181                         "artifactId": "%s"
182                       }
183                     }
184                     """.formatted(groupId, artifactId);
185         }
186 
187         private String extractGroupIdFromUrl(String url) {
188             // Extract groupId from URL pattern: /apis/registry/v3/groups/{groupId}/artifacts
189             String[] parts = url.split("/");
190             for (int i = 0; i < parts.length - 1; i++) {
191                 if ("groups".equals(parts[i]) && i + 1 < parts.length) {
192                     return parts[i + 1];
193                 }
194             }
195             return "default";
196         }
197 
198         private String extractArtifactIdFromBody(String body) throws JsonProcessingException {
199             ObjectMapper mapper = new ObjectMapper();
200             JsonNode jsonNode = mapper.readTree(body);
201 
202             JsonNode artifactIdNode = jsonNode.get("artifactId");
203             if (artifactIdNode != null && !artifactIdNode.isNull()) {
204                 return artifactIdNode.asText();
205             }
206             throw new RuntimeException("ArtifactId not found in request body");
207         }
208 
209     }
210 }
211