added export class, some refactor
This commit is contained in:
parent
d3eaa644b3
commit
4c8135e801
@ -3,7 +3,8 @@ package cz.trask.apioperator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import cz.trask.apioperator.impl.Import;
|
||||
import cz.trask.apioperator.impl.ExportToWso2;
|
||||
import cz.trask.apioperator.impl.ImportToApicurio;
|
||||
import cz.trask.apioperator.model.StartParameters;
|
||||
|
||||
public class ApiSync {
|
||||
@ -22,10 +23,18 @@ public class ApiSync {
|
||||
StartParameters sp = StartParameters.parse(commandLine);
|
||||
log.info("Parsed parameters: " + sp);
|
||||
|
||||
if (sp.getCommand().equalsIgnoreCase("import")) {
|
||||
if (sp.getCommand().equals("import")) {
|
||||
log.info("Import command selected.");
|
||||
Import imp = new Import();
|
||||
ImportToApicurio imp = new ImportToApicurio();
|
||||
imp.process();
|
||||
} else if (sp.getCommand().equals("export")) {
|
||||
log.info("Export command selected.");
|
||||
ExportToWso2 exp = new ExportToWso2();
|
||||
exp.process();
|
||||
log.error("Export command not implemented yet.");
|
||||
} else {
|
||||
log.error("Unknown command: " + sp.getCommand());
|
||||
printHelp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,16 +39,16 @@ import io.apicurio.registry.rest.v2.beans.Rule;
|
||||
import io.apicurio.registry.rest.v2.beans.VersionSearchResults;
|
||||
import io.apicurio.registry.types.RuleType;
|
||||
|
||||
public class Import extends AbstractProcess {
|
||||
public class ExportToWso2 extends AbstractProcess {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(Import.class);
|
||||
private static final Logger log = LogManager.getLogger(ExportToWso2.class);
|
||||
|
||||
private final AtomicInteger apiCounter = new AtomicInteger(1);
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
private final RegistryClient client;
|
||||
|
||||
public Import() throws Exception {
|
||||
public ExportToWso2() throws Exception {
|
||||
this.client = RegistryClientFactory.create(config.getApicurioApiUrl());
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ public class Import extends AbstractProcess {
|
||||
*/
|
||||
public void process() {
|
||||
try {
|
||||
log.info("Starting API export…");
|
||||
log.info("Starting API export to WSO2 from Apicurio...");
|
||||
|
||||
RegisterResponse register = register(config.getSourceRegistrationApiUrl(), config.getSourceWso2User());
|
||||
|
||||
358
src/main/java/cz/trask/apioperator/impl/ImportToApicurio.java
Normal file
358
src/main/java/cz/trask/apioperator/impl/ImportToApicurio.java
Normal file
@ -0,0 +1,358 @@
|
||||
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;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
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.VersionSearchResults;
|
||||
import io.apicurio.registry.types.RuleType;
|
||||
|
||||
public class ImportToApicurio extends AbstractProcess {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(ImportToApicurio.class);
|
||||
|
||||
private final AtomicInteger apiCounter = new AtomicInteger(1);
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
private final RegistryClient client;
|
||||
|
||||
public ImportToApicurio() throws Exception {
|
||||
this.client = RegistryClientFactory.create(config.getApicurioApiUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point for the import process.
|
||||
*
|
||||
* @throws RuntimeException if any error occurs
|
||||
*/
|
||||
public void process() {
|
||||
try {
|
||||
log.info("Starting API import to Apicurio from WSO2...");
|
||||
|
||||
RegisterResponse register = register(config.getSourceRegistrationApiUrl(), config.getSourceWso2User());
|
||||
|
||||
String clientId = register.getClientId();
|
||||
log.info("Registered with clientId: {}", clientId);
|
||||
|
||||
TokenResponse token = getToken(config.getSourcePublisherTokenUrl(), config.getSourceWso2User(), register,
|
||||
"apim:api_view apim:api_create apim:api_manage apim:api_delete apim:api_publish "
|
||||
+ "apim:subscription_view apim:subscription_block apim:subscription_manage apim:external_services_discover "
|
||||
+ "apim:threat_protection_policy_create apim:threat_protection_policy_manage apim:document_create apim:document_manage "
|
||||
+ "apim:mediation_policy_view apim:mediation_policy_create apim:mediation_policy_manage apim:client_certificates_view "
|
||||
+ "apim:client_certificates_add apim:client_certificates_update apim:ep_certificates_view apim:ep_certificates_add "
|
||||
+ "apim:ep_certificates_update apim:publisher_settings apim:pub_alert_manage apim:shared_scope_manage apim:app_import_export "
|
||||
+ "apim:api_import_export apim:api_product_import_export apim:api_generate_key apim:common_operation_policy_view "
|
||||
+ "apim:common_operation_policy_manage apim:comment_write apim:comment_view apim:admin");
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
log.info("Found {} APIs", apis.getCount());
|
||||
|
||||
int maxThreads = config.getMaxThreads();
|
||||
ExecutorService executor = Executors.newFixedThreadPool(maxThreads);
|
||||
|
||||
for (APIInfo api : apis.getList()) {
|
||||
final int index = apiCounter.getAndIncrement();
|
||||
executor.submit(() -> processApi(api, token, index, apis.getCount()));
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
if (!executor.awaitTermination(10, TimeUnit.MINUTES)) {
|
||||
log.warn("Timeout waiting for API import tasks to finish");
|
||||
}
|
||||
log.info("Finished processing APIs.");
|
||||
} catch (Exception e) {
|
||||
log.error("Error while exporting APIs.", e);
|
||||
throw new RuntimeException("Export failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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());
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,7 @@ public class StartParameters {
|
||||
}
|
||||
|
||||
public void setCommand(String command) {
|
||||
this.command = command;
|
||||
this.command = command.toLowerCase();
|
||||
}
|
||||
|
||||
public static StartParameters parse(String commandLine) throws Exception {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user