From ca01fb657f92aeafd5f61cd1921ba94902f72925 Mon Sep 17 00:00:00 2001
From: Radek Davidek
Date: Wed, 1 Oct 2025 18:00:27 +0200
Subject: [PATCH] big refactor
---
.../cz/trask/apioperator/impl/Import.java | 544 +++++++++---------
1 file changed, 283 insertions(+), 261 deletions(-)
diff --git a/src/main/java/cz/trask/apioperator/impl/Import.java b/src/main/java/cz/trask/apioperator/impl/Import.java
index 9723827..94e9acd 100644
--- a/src/main/java/cz/trask/apioperator/impl/Import.java
+++ b/src/main/java/cz/trask/apioperator/impl/Import.java
@@ -1,20 +1,25 @@
package cz.trask.apioperator.impl;
import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
+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.JsonParser;
+import com.google.gson.reflect.TypeToken;
import cz.trask.apioperator.AbstractProcess;
import cz.trask.apioperator.model.APIInfo;
@@ -32,335 +37,352 @@ 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.VersionMetaData;
import io.apicurio.registry.rest.v2.beans.VersionSearchResults;
import io.apicurio.registry.types.RuleType;
+/**
+ * Import class – reads APIs from WSO2 APIM and publishes them to Apicurio.
+ *
+ *
+ * All major improvements from the original code:
+ *
+ * - Thread‑safe counter using {@link AtomicInteger}
+ * - Parameterized Log4j messages
+ * - Safe JSON deserialization with {@code TypeToken}
+ * - Try‑with‑resources for all streams
+ * - Artifact creation – references first, then parent
+ * - Cleaner metadata handling (no trailing spaces)
+ * - Specific exception handling and wrapping in a runtime exception
+ *
+ *
+ */
public class Import extends AbstractProcess {
- private static Logger log = LogManager.getLogger(Import.class);
+ private static final Logger log = LogManager.getLogger(Import.class);
- StartParameters sp;
- RegistryClient client;
- static int i = 1;
+ private final AtomicInteger apiCounter = new AtomicInteger(1);
+ private final Gson gson = new Gson();
+
+ private final StartParameters sp;
+ private final RegistryClient client;
public Import(StartParameters sp) throws Exception {
this.sp = sp;
- client = RegistryClientFactory.create(config.getApicurioApiUrl());
+ this.client = RegistryClientFactory.create(config.getApicurioApiUrl());
}
/**
- * Process Export API
- *
- * @throws Exception - all exceptions in the code are thrown for handling in
- * CICD purpose
+ * Main entry point for the import process.
+ *
+ * @throws RuntimeException if any error occurs
*/
- public void process() throws Exception {
- log.info("exporting APIs...");
+ public void process() {
try {
- log.info("Register user for calling Admins APIs...");
- RegisterResponse registerResponse = register(config.getSourceRegistrationApiUrl(),
- config.getSourceWso2User());
- log.info("Registered with clientId: " + registerResponse.getClientId());
+ log.info("Starting API export…");
- log.info("Getting token for clientId:" + registerResponse.getClientId());
- TokenResponse tokenResponse = getToken(config.getSourcePublisherTokenUrl(), config.getSourceWso2User(),
- registerResponse,
- "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("Token for clientId is: " + tokenResponse.getAccess_token());
+ RegisterResponse register = register(config.getSourceRegistrationApiUrl(), config.getSourceWso2User());
- log.info("Getting list of APIs ...");
- APIList apis = getList(config.getSourcePublisherApiUrl(), tokenResponse);
+ 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 Exception(
- "There is no APIs to export that meets yours criteria! Please, check the name of API you want to export and try again!");
+ 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.getCount() + " APIs");
+ log.info("Found {} APIs", apis.getCount());
int maxThreads = config.getMaxThreads();
- log.info("Starting API processing with " + maxThreads + " threads");
ExecutorService executor = Executors.newFixedThreadPool(maxThreads);
+
for (APIInfo api : apis.getList()) {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- processApi(api, tokenResponse, i++, apis.getCount());
- }
- });
+ final int index = apiCounter.getAndIncrement();
+ executor.submit(() -> processApi(api, token, index, apis.getCount()));
}
+
executor.shutdown();
- while (!executor.isTerminated()) {
- Thread.sleep(500);
+ 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 Exception("Error while exporting APIs!");
+ log.error("Error while exporting APIs.", e);
+ throw new RuntimeException("Export failed", e);
}
}
- private void processApi(APIInfo api, TokenResponse tokenResponse, int i, int apiCount) {
+ /**
+ * 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 apiStatus = api.getLifeCycleStatus();
- if (!apiStatus.contains("PUBLISHED") && !apiStatus.contains("DEPRECATED")) {
- log.info("Skipping API " + i + " of " + apiCount + "with ID " + api.getId()
- + " because it is not published.");
+ 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 " + i + " of " + apiCount);
+ log.info("Processing API {} of {}", index, total);
- Map httpHeaders = new HashMap<>();
- Map params = new HashMap<>();
+ Map httpHeaders = Collections.singletonMap("Authorization",
+ "Bearer " + tokenResponse.getAccess_token());
- httpHeaders.put("Authorization", "Bearer ".concat(tokenResponse.getAccess_token()));
+ String type = mapApiType(api.getType());
- String type = api.getType();
+ // 1) Retrieve basic information
+ HttpResponse apiInfoResp = makeRequest("GET", config.getSourceDevportalApiUrl() + "/apis/" + api.getId(),
+ httpHeaders, Collections.emptyMap());
- switch (type) {
- case "HTTP":
- type = "OPENAPI";
- break;
- case "GRAPHQL":
- type = "GRAPHQL";
- break;
- case "WS":
- type = "ASYNCAPI";
- break;
- case "SOAP":
- type = "WSDL";
- break;
- default:
+ HttpResponse subsResp = makeRequest("GET",
+ config.getSourcePublisherApiUrl() + "/subscriptions?apiId=" + api.getId(), httpHeaders,
+ Collections.emptyMap());
+
+ HttpResponse swaggerResp = makeRequest("GET",
+ config.getSourcePublisherApiUrl() + "/apis/" + api.getId() + "/swagger", 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 zipEntries = ZipExtractor.extractFilesFromZip(exportedZip.getResponseBytes());
+
+ // 3) Deserialize JSON responses
+ TypeToken