retrieving apicurio artifacts with versions and references

This commit is contained in:
Radek Davidek 2025-10-02 15:37:04 +02:00
parent 4c8135e801
commit c164ff7802

View File

@ -1,12 +1,6 @@
package cz.trask.apioperator.impl;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@ -16,28 +10,17 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import cz.trask.apioperator.AbstractProcess;
import cz.trask.apioperator.model.APIInfo;
import cz.trask.apioperator.model.APIList;
import cz.trask.apioperator.model.FileType;
import cz.trask.apioperator.model.HttpResponse;
import cz.trask.apioperator.model.RegisterResponse;
import cz.trask.apioperator.model.TokenResponse;
import cz.trask.apioperator.model.ZipEntryData;
import cz.trask.apioperator.utils.ZipExtractor;
import io.apicurio.registry.rest.client.RegistryClient;
import io.apicurio.registry.rest.client.RegistryClientFactory;
import io.apicurio.registry.rest.client.exception.VersionAlreadyExistsException;
import io.apicurio.registry.rest.v2.beans.ArtifactMetaData;
import io.apicurio.registry.rest.v2.beans.ArtifactReference;
import io.apicurio.registry.rest.v2.beans.EditableMetaData;
import io.apicurio.registry.rest.v2.beans.Rule;
import io.apicurio.registry.rest.v2.beans.ArtifactSearchResults;
import io.apicurio.registry.rest.v2.beans.SearchedArtifact;
import io.apicurio.registry.rest.v2.beans.SearchedVersion;
import io.apicurio.registry.rest.v2.beans.VersionSearchResults;
import io.apicurio.registry.types.RuleType;
public class ExportToWso2 extends AbstractProcess {
@ -78,18 +61,15 @@ public class ExportToWso2 extends AbstractProcess {
log.debug("Access token received {}", token.getAccess_token());
APIList apis = getList(config.getSourcePublisherApiUrl(), token);
if (apis == null || apis.getList() == null || apis.getList().length == 0) {
throw new IllegalStateException(
"No APIs to export that match your criteria! Check the name of the API you want to export.");
}
ArtifactSearchResults apis = client.searchArtifacts(config.getDefaultApiGroup(), null, null, null, null,
null, null, null, null);
log.info("Found {} APIs", apis.getCount());
int maxThreads = config.getMaxThreads();
ExecutorService executor = Executors.newFixedThreadPool(maxThreads);
for (APIInfo api : apis.getList()) {
for (SearchedArtifact api : apis.getArtifacts()) {
final int index = apiCounter.getAndIncrement();
executor.submit(() -> processApi(api, token, index, apis.getCount()));
}
@ -105,138 +85,24 @@ public class ExportToWso2 extends AbstractProcess {
}
}
/**
* Process a single API fetches the data, creates or updates the corresponding
* artifact in Apicurio.
*/
private void processApi(APIInfo api, TokenResponse tokenResponse, int index, int total) {
private void processApi(SearchedArtifact api, TokenResponse tokenResponse, int index, int total) {
long start = System.currentTimeMillis();
String status = api.getLifeCycleStatus();
if (!status.contains("PUBLISHED") && !status.contains("DEPRECATED")) {
log.info("Skipping API {} of {} not published (ID={})", index, total, api.getId());
return;
}
try {
log.info("Processing API {} of {}", index, total);
Map<String, String> httpHeaders = Collections.singletonMap("Authorization",
"Bearer " + tokenResponse.getAccess_token());
VersionSearchResults versions = client.listArtifactVersions(config.getDefaultApiGroup(), api.getId(), null, null);
// 1) Retrieve basic information
HttpResponse apiInfoResp = makeRequest("GET", config.getSourceDevportalApiUrl() + "/apis/" + api.getId(),
httpHeaders, Collections.emptyMap());
HttpResponse subsResp = makeRequest("GET",
config.getSourcePublisherApiUrl() + "/subscriptions?apiId=" + api.getId(), httpHeaders,
Collections.emptyMap());
// 2) Export the API as a zip
HttpResponse exportedZip = makeRequest("GET",
config.getSourcePublisherApiUrl() + "/apis/export?apiId=" + api.getId(), httpHeaders,
Collections.emptyMap(), true);
List<ZipEntryData> zipEntries = ZipExtractor.extractFilesFromZip(exportedZip.getResponseBytes());
String swagger = null;
for (ZipEntryData e : zipEntries) {
if (e.getType().toString().equals(FileType.OPENAPI.toString())) {
log.debug("Found main API definition file: {}", e.getName());
swagger = new String(e.getContent());
break;
for (SearchedVersion ver : versions.getVersions()) {
log.info(" - Found version: {}", ver.getVersion());
List<ArtifactReference> ref = client.getArtifactReferencesByCoordinates(config.getDefaultApiGroup(), api.getId(), ver.getVersion());
if (ref != null && !ref.isEmpty()) {
log.info("Artifact has {} references", ref.size());
}
}
// 3) Deserialize JSON responses
TypeToken<Map<String, Object>> mapType = new TypeToken<>() {
};
Map<String, Object> apiMap = gson.fromJson(apiInfoResp.getResponse(), mapType.getType());
Map<String, Object> subsMap = gson.fromJson(subsResp.getResponse(), mapType.getType());
@SuppressWarnings("unchecked")
List<String> tagsList = (List<String>) apiMap.get("tags");
// 4) Build the properties map
Map<String, String> props = new LinkedHashMap<>();
props.put("version", api.getVersion());
props.put("status", status);
addSubscriptionsToProps(props, subsMap);
addEndpointsToProps(props, apiMap);
addTagsToProps(props, tagsList);
// 5) Build the description that contains the publisher & devportal URLs
String baseDesc = api.getDescription() != null ? api.getDescription() : "";
String pubUrl = config.getPublisherUrlPattern().replace("{API_ID}", api.getId());
String devPortUrl = config.getDevportalUrlPattern().replace("{API_ID}", api.getId());
String fullDesc = baseDesc + " ***** PUBLISHER URL ***** " + pubUrl + " ***** DEVPORTAL URL ***** "
+ devPortUrl;
// 6) Update the swagger with the description and servers
Map<String, Object> swaggerMap = yaml.load(swagger);
JsonObject swaggerObj = gson.toJsonTree(swaggerMap).getAsJsonObject();
updateSwagger(swaggerObj, apiMap, fullDesc);
// 7) Prepare artifact creation/update
String group = config.getDefaultApiGroup();
String mainArtifactId = api.getName() + api.getContext();
VersionSearchResults existingArtifacts;
try {
existingArtifacts = client.listArtifactVersions(group, mainArtifactId, 0, Integer.MAX_VALUE);
} catch (Exception e) {
log.debug("No API {} exists will create it", api.getContext());
existingArtifacts = null;
}
if (existingArtifacts == null) {
// Create new artifact
List<ArtifactReference> references = createReferencesFromZip(zipEntries, group, api);
ArtifactMetaData meta = client.createArtifact(group, mainArtifactId, api.getVersion(), null, null, null,
api.getName(), fullDesc, null, null, null,
new ByteArrayInputStream(swaggerObj.toString().getBytes()), references);
setMetaAndRules(meta, props, tagsList);
// Create the three required rules
createRule(meta, "NONE", RuleType.COMPATIBILITY);
createRule(meta, "NONE", RuleType.VALIDITY);
createRule(meta, "NONE", RuleType.INTEGRITY);
} else {
// Artifact exists check if the version exists
boolean versionExists = false;
try {
client.getArtifactVersionMetaData(group, mainArtifactId, api.getVersion());
versionExists = true;
} catch (Exception e) {
// Version missing will create it below
}
List<ArtifactReference> references = createReferencesFromZip(zipEntries, group, api);
if (!versionExists) {
ArtifactMetaData meta = client.updateArtifact(group, mainArtifactId, api.getVersion(),
api.getName(), fullDesc, new ByteArrayInputStream(swaggerObj.toString().getBytes()),
references);
setMetaAndRules(meta, props, tagsList);
} else {
// Version already exists no action needed
log.warn("API {} with version {} already exists. Skipping import.", api.getContext(),
api.getVersion());
}
}
log.info("Successfully imported API '{}' ({}). Took {} ms", api.getName(), api.getVersion(),
System.currentTimeMillis() - start);
} catch (IOException e) {
log.error("IO error while importing API {}: {}", api.getId(), e.getMessage(), e);
} catch (VersionAlreadyExistsException e) {
log.warn("API version already exists for {}: {}. Skipping.", api.getName(), api.getVersion());
} catch (Exception e) {
log.error("Cannot export API '{}':{}", api.getName(), api.getVersion(), e);
log.error("IO error while importing API {}: {}", api.getId(), e.getMessage(), e);
}
}
@ -244,115 +110,4 @@ public class ExportToWso2 extends AbstractProcess {
/* Helper methods */
/* --------------------------------------------------------------------- */
private void updateSwagger(JsonObject swagger, Map<String, Object> apiMap, String description) {
JsonObject info = swagger.getAsJsonObject("info");
if (info != null) {
info.addProperty("description", description);
}
// Build servers array
JsonArray servers = new JsonArray();
@SuppressWarnings("unchecked")
List<Map<String, Object>> endpoints = (List<Map<String, Object>>) apiMap.get("endpointURLs");
if (endpoints != null) {
for (Map<String, Object> env : endpoints) {
@SuppressWarnings("unchecked")
Map<String, String> urls = (Map<String, String>) env.get("URLs");
if (urls == null || urls.isEmpty())
continue;
JsonObject server = new JsonObject();
urls.forEach((k, v) -> {
if (v != null && !v.isBlank()) {
if (k.equals("https")) {
server.addProperty("url", v);
} else if (k.equals("wss")) {
server.addProperty("url", v);
}
}
});
server.addProperty("description", "Gateway: " + env.getOrDefault("environmentName", ""));
servers.add(server);
}
}
swagger.remove("servers");
swagger.add("servers", servers);
}
private void addSubscriptionsToProps(Map<String, String> props, Map<String, Object> subsMap) {
if (subsMap == null || !subsMap.containsKey("list"))
return;
@SuppressWarnings("unchecked")
List<Map<String, Object>> list = (List<Map<String, Object>>) subsMap.get("list");
int i = 1;
for (Map<String, Object> sub : list) {
@SuppressWarnings("unchecked")
Map<String, String> appInfo = (Map<String, String>) sub.get("applicationInfo");
if (appInfo == null)
continue;
props.put("subscription" + i,
appInfo.getOrDefault("name", "") + " (Owner: " + appInfo.getOrDefault("subscriber", "") + ")");
i++;
}
}
private void addEndpointsToProps(Map<String, String> props, Map<String, Object> apiMap) {
if (apiMap == null || !apiMap.containsKey("endpointURLs"))
return;
@SuppressWarnings("unchecked")
List<Map<String, Object>> envs = (List<Map<String, Object>>) apiMap.get("endpointURLs");
for (Map<String, Object> env : envs) {
@SuppressWarnings("unchecked")
Map<String, String> urls = (Map<String, String>) env.get("URLs");
if (urls == null)
continue;
urls.forEach((k, v) -> props.put(k + " Endpoint", v));
}
}
private void addTagsToProps(Map<String, String> props, List<String> tags) {
if (tags != null && !tags.isEmpty()) {
props.put("tags", String.join(", ", tags));
}
}
private List<ArtifactReference> createReferencesFromZip(List<ZipEntryData> zipEntries, String group, APIInfo api)
throws IOException {
List<ArtifactReference> references = new ArrayList<>();
for (ZipEntryData entry : zipEntries) {
String artifactId = api.getName() + "/" + api.getVersion() + "/" + entry.getName();
// Create the artifact (versioned)
try (ByteArrayInputStream is = new ByteArrayInputStream(entry.getContent())) {
client.createArtifactWithVersion(entry.getType().toString(), artifactId, api.getVersion(), is);
}
ArtifactReference ref = new ArtifactReference();
ref.setName(entry.getName());
ref.setGroupId(entry.getType().toString());
ref.setArtifactId(artifactId);
ref.setVersion(api.getVersion());
references.add(ref);
}
return references;
}
private void setMetaAndRules(ArtifactMetaData meta, Map<String, String> props, List<String> tags) {
EditableMetaData metaData = new EditableMetaData();
metaData.setName(meta.getName());
metaData.setDescription(meta.getDescription());
metaData.setProperties(props);
metaData.setLabels(tags);
client.updateArtifactMetaData(meta.getGroupId(), meta.getId(), metaData);
}
private void createRule(ArtifactMetaData meta, String config, RuleType type) {
Rule rule = new Rule();
rule.setConfig(config);
rule.setType(type);
client.createArtifactRule(meta.getGroupId(), meta.getId(), rule);
}
}