diff --git a/src/main/java/cz/trask/apioperator/AbstractProcess.java b/src/main/java/cz/trask/apioperator/AbstractProcess.java index 1996e37..9190844 100644 --- a/src/main/java/cz/trask/apioperator/AbstractProcess.java +++ b/src/main/java/cz/trask/apioperator/AbstractProcess.java @@ -1,6 +1,7 @@ package cz.trask.apioperator; import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; import java.io.File; import java.io.InputStream; import java.io.OutputStream; @@ -309,4 +310,79 @@ public abstract class AbstractProcess { } return listOfApis; } + + /** + * Common function used for upload API + * + * @param urlStr - url to dev poral + * @param httpHeaders + * @param params - currently is not used + * @param api - zip file to upload + * @throws Exception + */ + protected static HttpResponse makeFileRequest(String method, String urlStr, Map httpHeaders, + byte[] buff, String attachmentFileName) throws Exception { + + if (buff==null) { + log.error("Cannot send NULL payload to rest service."); + } + + String crlf = "\r\n"; + + String twoHyphens = "--"; + String boundary = "----" + System.currentTimeMillis() + "----"; + + URL url = new URL(urlStr); + + HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); + con.setUseCaches(false); + con.setDoOutput(true); + + con.setRequestMethod(method); + con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); + con.setRequestProperty("User-Agent", "curl/7.55.1"); + con.setRequestProperty("Accept", "*/*"); + + for (String key : httpHeaders.keySet()) { + con.addRequestProperty(key, httpHeaders.get(key)); + } + + DataOutputStream request = new DataOutputStream(con.getOutputStream()); + request.writeBytes(crlf); + request.writeBytes(twoHyphens + boundary + crlf); + request.writeBytes( + "Content-Disposition: form-data; name=\"file\"; filename=\"" + attachmentFileName + "\"" + crlf); + request.writeBytes("Content-Type: application/octet-stream" + crlf); + request.writeBytes("Content-Transfer-Encoding: binary" + crlf); + request.writeBytes(crlf); + request.write(buff); + request.writeBytes(crlf); + request.writeBytes(twoHyphens + boundary + twoHyphens + crlf); + request.flush(); + request.close(); + + String res = ""; + byte[] buf = new byte[4096]; + + HttpResponse resp = new HttpResponse(); + InputStream in; + int responseCode = con.getResponseCode(); + + if (responseCode == 200 || responseCode == 201) { + in = con.getInputStream(); + } else { + in = con.getErrorStream(); + } + + while (in.available() > 0) { + int read = in.read(buf); + res = res.concat(new String(buf, 0, read)); + } + + resp.setHeaders(con.getHeaderFields()); + resp.setResponse(res); + resp.setResponseCode(responseCode); + + return resp; + } } diff --git a/src/main/java/cz/trask/apioperator/impl/ExportToWso2.java b/src/main/java/cz/trask/apioperator/impl/ExportToWso2.java index a82db76..2fe8320 100644 --- a/src/main/java/cz/trask/apioperator/impl/ExportToWso2.java +++ b/src/main/java/cz/trask/apioperator/impl/ExportToWso2.java @@ -1,10 +1,18 @@ package cz.trask.apioperator.impl; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +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 java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -12,10 +20,13 @@ import org.apache.logging.log4j.Logger; import com.google.gson.Gson; import cz.trask.apioperator.AbstractProcess; +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 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.ArtifactReference; import io.apicurio.registry.rest.v2.beans.ArtifactSearchResults; import io.apicurio.registry.rest.v2.beans.SearchedArtifact; @@ -90,17 +101,25 @@ public class ExportToWso2 extends AbstractProcess { try { log.info("Processing API {} of {}", index, total); - - VersionSearchResults versions = client.listArtifactVersions(config.getDefaultApiGroup(), api.getId(), null, null); - + + VersionSearchResults versions = client.listArtifactVersions(config.getDefaultApiGroup(), api.getId(), null, + null); + for (SearchedVersion ver : versions.getVersions()) { log.info(" - Found version: {}", ver.getVersion()); - List ref = client.getArtifactReferencesByCoordinates(config.getDefaultApiGroup(), api.getId(), ver.getVersion()); + List ref = client.getArtifactReferencesByCoordinates(config.getDefaultApiGroup(), + api.getId(), ver.getVersion()); if (ref != null && !ref.isEmpty()) { log.info("Artifact has {} references", ref.size()); + byte[] data = prepareApiZipFile(ver, ref); + String fileName = api.getName() + "-" + ver.getVersion() + ".zip"; + if (data != null && data.length > 0 && fileName != null && !fileName.isEmpty()) { + publishApiToWso2(fileName, data, tokenResponse); + } } } - + long end = System.currentTimeMillis(); + log.info("Finished processing API {} of {} in {} ms", index, total, (end - start)); } catch (Exception e) { log.error("IO error while importing API {}: {}", api.getId(), e.getMessage(), e); } @@ -110,4 +129,64 @@ public class ExportToWso2 extends AbstractProcess { /* Helper methods */ /* --------------------------------------------------------------------- */ + private int publishApiToWso2(String fileName, byte[] data, TokenResponse tokenResponse) { + int responseCode = -1; + try { + String url = config.getTargetPublisherApiUrl().concat(String.format("?preserveProvider=false&overwrite=true")); + + log.info("API Import URL: " + url); + + Map httpHeaders = new HashMap<>(); + + httpHeaders.put("Authorization", "Bearer " + tokenResponse.getAccess_token()); + + HttpResponse response = makeFileRequest("POST", url, httpHeaders, data, fileName); + + responseCode = response.getResponseCode(); + + if (response.getResponseCode() != 201 && response.getResponseCode() != 200) { + log.info("Cannot import API file: " + fileName + ", response code: " + response.getResponseCode()); + } + } catch (Exception e) { + log.error("IO error while importing API file: " + fileName + ", error: " + e.getMessage(), e); + } + return responseCode; + } + + private byte[] prepareApiZipFile(SearchedVersion ver, List ref) throws IOException { + + String baseDir = ver.getName() + "-" + ver.getVersion() + "/"; + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ZipOutputStream zos = new ZipOutputStream(baos)) { + + for (ArtifactReference r : ref) { + log.info(" - Reference: {} {} {}", r.getGroupId(), r.getArtifactId(), r.getVersion()); + + ArtifactMetaData amd = client.getArtifactMetaData(r.getGroupId(), r.getArtifactId()); + + String subDir = ""; + + if (FileType.OPENAPI.toString().equals(amd.getGroupId())) { + subDir = "Definitions/"; + } else if (FileType.POLICY.toString().equals(amd.getGroupId())) { + subDir = "Policies/"; + } + + String fileName = baseDir + subDir + r.getName(); + log.info(" - Adding file: {}", fileName); + + zos.putNextEntry(new ZipEntry(fileName)); + + try (InputStream is = client.getContentByGlobalId(amd.getGlobalId())) { + zos.write(is.readAllBytes()); + } + + zos.closeEntry(); + } + zos.finish(); + return baos.toByteArray(); + } + } + } diff --git a/src/main/resources/api-operator.properties b/src/main/resources/api-operator.properties index d33805b..52333ff 100644 --- a/src/main/resources/api-operator.properties +++ b/src/main/resources/api-operator.properties @@ -5,7 +5,7 @@ SOURCE_PUBLISHER_TOKEN_URL = https://localhost:9443/oauth2/token SOURCE_WSO2_USER = YWRtaW46YWRtaW4= TARGET_REGISTRATION_API_URL = https://localhost:9443/client-registration/v0.17/register -TARGET_PUBLISHER_API_URL = https://localhost:9443/api/am/publisher +TARGET_PUBLISHER_API_URL = https://localhost:9443/api/am/publisher/v3/apis/import TARGET_DEVPORTAL_API_URL = https://localhost:9443/api/am/devportal TARGET_PUBLISHER_TOKEN_URL = https://localhost:9443/oauth2/token TARGET_WSO2_USER = YWRtaW46YWRtaW4=