apicurio app to wso2 - draft

This commit is contained in:
Radek Davidek 2025-11-04 16:03:43 +01:00
parent 8b14aa04b3
commit ba02eb001c
6 changed files with 245 additions and 49 deletions

View File

@ -6,10 +6,8 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.KeyStore; import java.security.KeyStore;
import java.util.Base64; import java.util.Base64;
import java.util.HashMap; import java.util.HashMap;
@ -90,7 +88,8 @@ public abstract class AbstractProcess {
protected void setTrustStoreCredentials() { protected void setTrustStoreCredentials() {
File trustStoreFile = new File(config.getTrustStore().getPath()); File trustStoreFile = new File(config.getTrustStore().getPath());
if (!trustStoreFile.exists() || !trustStoreFile.isFile()) { if (!trustStoreFile.exists() || !trustStoreFile.isFile()) {
log.warn("Truststore file '{}' does not exist. Skipping truststore setup.", config.getTrustStore().getPath()); log.warn("Truststore file '{}' does not exist. Skipping truststore setup.",
config.getTrustStore().getPath());
return; return;
} }
log.info("Setting truststore: " + trustStoreFile.getAbsolutePath()); log.info("Setting truststore: " + trustStoreFile.getAbsolutePath());
@ -175,7 +174,7 @@ public abstract class AbstractProcess {
String data = "grant_type=password&username=".concat(username).concat("&password=") String data = "grant_type=password&username=".concat(username).concat("&password=")
.concat(URLEncoder.encode(password, "UTF-8")).concat("&scope=").concat(scope); .concat(URLEncoder.encode(password, "UTF-8")).concat("&scope=").concat(scope);
HttpResponse response = makeDataRequest(publisherurl, httpHeaders, data); HttpResponse response = makeDataRequest(publisherurl, httpHeaders, data.getBytes());
log.debug("Token response: HTTP Code " + response.getResponseCode() + " Json: " + response.getResponse()); log.debug("Token response: HTTP Code " + response.getResponseCode() + " Json: " + response.getResponse());
@ -204,7 +203,7 @@ public abstract class AbstractProcess {
String data = "{\"callbackUrl\": \"www.google.lk\",\"clientName\": \"rest_api_publisher" + decodeduser String data = "{\"callbackUrl\": \"www.google.lk\",\"clientName\": \"rest_api_publisher" + decodeduser
+ "\",\"owner\": \"" + decodeduser + "\",\"grantType\": \"password refresh_token\",\"saasApp\": true}"; + "\",\"owner\": \"" + decodeduser + "\",\"grantType\": \"password refresh_token\",\"saasApp\": true}";
HttpResponse response = makeDataRequest(publisherurl, httpHeaders, data); HttpResponse response = makeDataRequest(publisherurl, httpHeaders, data.getBytes());
log.debug( log.debug(
"Register API response: HTTP Code " + response.getResponseCode() + " Json: " + response.getResponse()); "Register API response: HTTP Code " + response.getResponseCode() + " Json: " + response.getResponse());
@ -311,11 +310,9 @@ public abstract class AbstractProcess {
* @param data - request data * @param data - request data
* @throws Exception * @throws Exception
*/ */
protected HttpResponse makeDataRequest(String urlStr, Map<String, String> httpHeaders, String data) protected HttpResponse makeDataRequest(String urlStr, Map<String, String> httpHeaders, byte[] data)
throws Exception { throws Exception {
byte[] json = data.getBytes(Charset.forName("UTF-8"));
URL url = new URL(urlStr); URL url = new URL(urlStr);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
@ -328,10 +325,12 @@ public abstract class AbstractProcess {
con.addRequestProperty(key, httpHeaders.get(key)); con.addRequestProperty(key, httpHeaders.get(key));
} }
con.addRequestProperty("Content-Length", "" + json.length); con.addRequestProperty("Content-Length", "" + data.length);
OutputStream out = con.getOutputStream(); OutputStream out = con.getOutputStream();
out.write(json); out.write(data);
out.flush();
out.close();
InputStream in = con.getInputStream(); InputStream in = con.getInputStream();

View File

@ -5,7 +5,8 @@ import org.apache.logging.log4j.Logger;
import cz.trask.migration.impl.v32.Wso2AppsToApicurio; import cz.trask.migration.impl.v32.Wso2AppsToApicurio;
import cz.trask.migration.impl.v32.Wso2v32ToApicurio; import cz.trask.migration.impl.v32.Wso2v32ToApicurio;
import cz.trask.migration.impl.v45.ExportToWso2FromV32; import cz.trask.migration.impl.v45.ExportApisToWso2FromV32;
import cz.trask.migration.impl.v45.ExportAppsToWso2FromV32;
import cz.trask.migration.model.StartParameters; import cz.trask.migration.model.StartParameters;
public class ApiSync { public class ApiSync {
@ -28,7 +29,7 @@ public class ApiSync {
imp.process(); imp.process();
} else if (sp.getCommand().equalsIgnoreCase("apicurioApisToWso2")) { } else if (sp.getCommand().equalsIgnoreCase("apicurioApisToWso2")) {
log.info("apicurioApisToWso2 command selected."); log.info("apicurioApisToWso2 command selected.");
ExportToWso2FromV32 exp = new ExportToWso2FromV32(); ExportApisToWso2FromV32 exp = new ExportApisToWso2FromV32();
exp.process(); exp.process();
} else if (sp.getCommand().equalsIgnoreCase("wso2AppsToApicurio")) { } else if (sp.getCommand().equalsIgnoreCase("wso2AppsToApicurio")) {
log.info("wso2AppsToApicurio command selected."); log.info("wso2AppsToApicurio command selected.");
@ -36,7 +37,7 @@ public class ApiSync {
apps.process(); apps.process();
} else if (sp.getCommand().equalsIgnoreCase("apicurioAppsToWso2")) { } else if (sp.getCommand().equalsIgnoreCase("apicurioAppsToWso2")) {
log.info("apicurioAppsToWso2 command selected."); log.info("apicurioAppsToWso2 command selected.");
ExportToWso2FromV32 exp = new ExportToWso2FromV32(); ExportAppsToWso2FromV32 exp = new ExportAppsToWso2FromV32();
exp.process(); exp.process();
} else { } else {
log.error("Unknown command: " + sp.getCommand()); log.error("Unknown command: " + sp.getCommand());

View File

@ -29,8 +29,6 @@ import cz.trask.migration.model.v45.ApiDefinition45;
import cz.trask.migration.model.v45.Documents45; import cz.trask.migration.model.v45.Documents45;
import cz.trask.migration.model.v45.EndpointCertificates45; import cz.trask.migration.model.v45.EndpointCertificates45;
import cz.trask.migration.model.v45.OperationPolicySpecification45; import cz.trask.migration.model.v45.OperationPolicySpecification45;
import io.apicurio.registry.rest.client.RegistryClient;
import io.apicurio.registry.rest.client.RegistryClientFactory;
import io.apicurio.registry.rest.v2.beans.ArtifactMetaData; import io.apicurio.registry.rest.v2.beans.ArtifactMetaData;
import io.apicurio.registry.rest.v2.beans.ArtifactReference; import io.apicurio.registry.rest.v2.beans.ArtifactReference;
import io.apicurio.registry.rest.v2.beans.ArtifactSearchResults; import io.apicurio.registry.rest.v2.beans.ArtifactSearchResults;
@ -40,16 +38,10 @@ import io.apicurio.registry.rest.v2.beans.VersionSearchResults;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
@Log4j2 @Log4j2
public class ExportToWso2FromV32 extends AbstractProcess { public class ExportApisToWso2FromV32 extends AbstractProcess {
private final AtomicInteger apiCounter = new AtomicInteger(1); private final AtomicInteger apiCounter = new AtomicInteger(1);
private final RegistryClient client;
public ExportToWso2FromV32() throws Exception {
this.client = RegistryClientFactory.create(config.getApicurio().getApiUrl());
}
/** /**
* Main entry point for the import process. * Main entry point for the import process.
* *
@ -100,7 +92,7 @@ public class ExportToWso2FromV32 extends AbstractProcess {
config.getApicurio().getDefaultApiGroup(), api.getId(), ver.getVersion()); config.getApicurio().getDefaultApiGroup(), api.getId(), ver.getVersion());
if (ref != null && !ref.isEmpty()) { if (ref != null && !ref.isEmpty()) {
log.info("Artifact has {} references", ref.size()); log.info("Artifact has {} references", ref.size());
byte[] data = prepareApiZipFile32to45(client, ver, ref); byte[] data = prepareApiZipFile32to45(ver, ref);
String fileName = api.getName() + "-" + ver.getVersion() + ".zip"; String fileName = api.getName() + "-" + ver.getVersion() + ".zip";
FileOutputStream fos = new FileOutputStream(fileName); FileOutputStream fos = new FileOutputStream(fileName);
@ -156,7 +148,7 @@ public class ExportToWso2FromV32 extends AbstractProcess {
return responseCode; return responseCode;
} }
public byte[] prepareApiZipFile32to45(RegistryClient client, SearchedVersion ver, List<ArtifactReference> ref) public byte[] prepareApiZipFile32to45(SearchedVersion ver, List<ArtifactReference> ref)
throws Exception { throws Exception {
String baseDir = ver.getName() + "-" + ver.getVersion() + "/"; String baseDir = ver.getName() + "-" + ver.getVersion() + "/";

View File

@ -0,0 +1,152 @@
package cz.trask.migration.impl.v45;
import java.io.FileOutputStream;
import java.util.Base64;
import java.util.HashMap;
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 cz.trask.migration.AbstractProcess;
import cz.trask.migration.model.HttpResponse;
import cz.trask.migration.model.TokenResponse;
import cz.trask.migration.model.v32.ApplicationDetail;
import cz.trask.migration.model.v45.ApplicationCreateRequest;
import io.apicurio.registry.rest.v2.beans.ArtifactMetaData;
import io.apicurio.registry.rest.v2.beans.ArtifactReference;
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 lombok.extern.log4j.Log4j2;
@Log4j2
public class ExportAppsToWso2FromV32 extends AbstractProcess {
private final AtomicInteger appCounter = new AtomicInteger(1);
/**
* Main entry point for the import process.
*
* @throws RuntimeException if any error occurs
*/
public void process() {
try {
log.info("Starting App export to WSO2 from Apicurio...");
TokenResponse token = authenticateToWso2AndGetToken();
ArtifactSearchResults apps = client.searchArtifacts(ARTIFACT_GROUP_APPLICATIONS, null, null,
null, null, null, null, null, null);
log.info("Found {} Apps", apps.getCount());
int maxThreads = config.getMaxThreads();
ExecutorService executor = Executors.newFixedThreadPool(maxThreads);
for (SearchedArtifact app : apps.getArtifacts()) {
final int index = appCounter.getAndIncrement();
executor.submit(() -> processApp(app, token, index, apps.getCount()));
}
executor.shutdown();
if (!executor.awaitTermination(10, TimeUnit.MINUTES)) {
log.warn("Timeout waiting for App import tasks to finish");
}
log.info("Finished processing Apps.");
} catch (Exception e) {
log.error("Error while exporting Apps.", e);
throw new RuntimeException("Export failed", e);
}
}
private void processApp(SearchedArtifact app, TokenResponse tokenResponse, int index, int total) {
long start = System.currentTimeMillis();
String endpoint = config.getTarget().getDevPortalApiUrl()+ "/v3/applications";
try {
log.info("Processing App {} of {}", index, total);
VersionSearchResults versions = client.listArtifactVersions(ARTIFACT_GROUP_APPLICATIONS,
app.getId(), null, null);
for (SearchedVersion ver : versions.getVersions()) {
log.info(" - Found version: {}", ver.getVersion());
ArtifactMetaData amd = client.getArtifactMetaData(app.getGroupId(), app.getId());
byte[] content = client.getContentByGlobalId(amd.getGlobalId()).readAllBytes();
if (content != null && content.length > 0) {
ApplicationDetail appDetail = mapper
.readValue(content, ApplicationDetail.class);
ApplicationCreateRequest appCreateRequest = mapAppDetailToCreateRequest(appDetail);
byte[] data = mapper.writeValueAsBytes(appCreateRequest);
log.info(" - Prepared application data for WSO2: {} bytes", data.length);
// Publish the application data to WSO2
byte[] decoded = Base64.getDecoder().decode(config.getTarget().getWso2User());
String decodedstring = new String(decoded);
String[] decodedstringparts = decodedstring.split(":");
String username = decodedstringparts[0];
String password = decodedstringparts[1];
Map<String, String> httpHeaders = new HashMap<>();
httpHeaders.put("Authorization", "Basic ".concat(Base64.getEncoder()
.encodeToString(username.concat(":").concat(password).getBytes())));
httpHeaders.put("Content-Type", "application/json");
HttpResponse response = makeDataRequest(endpoint, httpHeaders, data);
if (response.getResponseCode() == 200 || response.getResponseCode() == 201) {
log.info(" - Application version {} imported successfully", ver.getVersion());
} else {
log.warn(" - Application version {} import failed with response code {}",
ver.getVersion(), response.getResponseCode());
}
}
// String fileName = api.getName() + "-" + ver.getVersion() + ".zip";
// FileOutputStream fos = new FileOutputStream(fileName);
// fos.write(data);
// fos.flush();
// fos.close();
// System.exit(0);
// if (data != null && data.length > 0 && fileName != null &&
// !fileName.isEmpty()) {
// int responseCode = publishAppToWso2(fileName, data, tokenResponse);
// if (responseCode == 200 || responseCode == 201) {
// log.info(" - API version {} imported successfully", ver.getVersion());
// } else {
// log.warn(" - API version {} import failed with response code {}",
// ver.getVersion(),
// responseCode);
// }
// }
}
long end = System.currentTimeMillis();
log.info("Finished processing App {} of {} in {} ms", index, total, (end - start));
} catch (Exception e) {
log.error("IO error while importing App {}: {}", app.getId(), e.getMessage(), e);
}
}
private ApplicationCreateRequest mapAppDetailToCreateRequest(ApplicationDetail appDetail) {
ApplicationCreateRequest request = new ApplicationCreateRequest();
request.setName(appDetail.getName());
request.setDescription(appDetail.getDescription());
request.setThrottlingPolicy(appDetail.getTier());
request.setTokenType(appDetail.getTokenType());
request.setGroups(List.of(appDetail.getGroupId()));
return request;
}
}

View File

@ -18,6 +18,7 @@ public class ApplicationDetail {
private List<?> keys; private List<?> keys;
private Map<String, Map<String, KeyManagerApp>> keyManagerWiseOAuthApp; private Map<String, Map<String, KeyManagerApp>> keyManagerWiseOAuthApp;
private String tier; private String tier;
private String description;
private String status; private String status;
private String groupId; private String groupId;
private String owner; private String owner;

View File

@ -0,0 +1,51 @@
package cz.trask.migration.model.v45;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import javax.validation.constraints.*;
import java.util.List;
import java.util.Map;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApplicationCreateRequest {
@NotNull
@Size(min = 1, max = 100)
private String name;
@NotNull
private String throttlingPolicy;
@Size(max = 512)
private String description;
@Pattern(regexp = "JWT|OAUTH")
private String tokenType = "JWT";
private List<String> groups;
private Map<String, Object> attributes;
private List<SubscriptionScope> subscriptionScopes;
@Pattern(regexp = "PRIVATE|SHARED_WITH_ORG")
private String visibility;
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class SubscriptionScope {
@NotNull
private String key;
@NotNull
private String name;
private List<String> roles;
private String description;
}
}