diff --git a/src/main/java/cz/trask/migration/AbstractProcess.java b/src/main/java/cz/trask/migration/AbstractProcess.java index 5b460b8..917f5f1 100644 --- a/src/main/java/cz/trask/migration/AbstractProcess.java +++ b/src/main/java/cz/trask/migration/AbstractProcess.java @@ -20,6 +20,7 @@ import org.apache.logging.log4j.Logger; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; import cz.trask.migration.config.ConfigManager; import cz.trask.migration.model.APIList; @@ -43,7 +44,10 @@ public abstract class AbstractProcess { protected AbstractProcess() { mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - mapperYaml = new ObjectMapper(new YAMLFactory()); + YAMLFactory yamlFactory = new YAMLFactory(); + yamlFactory.configure(YAMLGenerator.Feature.WRITE_DOC_START_MARKER, false); + yamlFactory.configure(YAMLGenerator.Feature.MINIMIZE_QUOTES, true); + mapperYaml = new ObjectMapper(yamlFactory); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); setTrustStoreCredentials(); diff --git a/src/main/java/cz/trask/migration/impl/v32/ZipUtils.java b/src/main/java/cz/trask/migration/impl/v32/ZipUtils.java index 53e0bac..b7aa984 100644 --- a/src/main/java/cz/trask/migration/impl/v32/ZipUtils.java +++ b/src/main/java/cz/trask/migration/impl/v32/ZipUtils.java @@ -13,6 +13,8 @@ import java.util.zip.ZipOutputStream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import cz.trask.migration.mapper.ApiDefinitionMapper; +import cz.trask.migration.model.ApiDefinition32; import cz.trask.migration.model.FileType; import cz.trask.migration.model.ZipEntryData; import io.apicurio.registry.rest.client.RegistryClient; @@ -76,41 +78,4 @@ public class ZipUtils { buffer.flush(); return buffer.toByteArray(); } - - public static byte[] prepareApiZipFile32to45(RegistryClient client, 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_IN.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/java/cz/trask/migration/impl/v45/ExportToWso2FromV32.java b/src/main/java/cz/trask/migration/impl/v45/ExportToWso2FromV32.java index e90b8c1..3e22c36 100644 --- a/src/main/java/cz/trask/migration/impl/v45/ExportToWso2FromV32.java +++ b/src/main/java/cz/trask/migration/impl/v45/ExportToWso2FromV32.java @@ -1,5 +1,8 @@ package cz.trask.migration.impl.v45; +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -7,17 +10,23 @@ 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; import cz.trask.migration.AbstractProcess; -import cz.trask.migration.impl.v32.ZipUtils; +import cz.trask.migration.mapper.ApiDefinitionMapper; +import cz.trask.migration.model.ApiDefinition32; +import cz.trask.migration.model.ApiDefinition45; +import cz.trask.migration.model.FileType; import cz.trask.migration.model.HttpResponse; import cz.trask.migration.model.RegisterResponse; import cz.trask.migration.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; @@ -101,8 +110,9 @@ public class ExportToWso2FromV32 extends AbstractProcess { api.getId(), ver.getVersion()); if (ref != null && !ref.isEmpty()) { log.info("Artifact has {} references", ref.size()); - byte[] data = ZipUtils.prepareApiZipFile32to45(client, ver, ref); + byte[] data = prepareApiZipFile32to45(client, ver, ref); String fileName = api.getName() + "-" + ver.getVersion() + ".zip"; + if (data != null && data.length > 0 && fileName != null && !fileName.isEmpty()) { int responseCode = publishApiToWso2(fileName, data, tokenResponse); if (responseCode == 200 || responseCode == 201) { @@ -149,4 +159,50 @@ public class ExportToWso2FromV32 extends AbstractProcess { } return responseCode; } + + public static byte[] prepareApiZipFile32to45(RegistryClient client, 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_IN.toString().equals(amd.getGroupId())) { + subDir = "Policies/"; + } + + String fileName = baseDir + subDir + r.getName(); + log.info(" - Adding file: {}", fileName); + + byte[] content = client.getContentByGlobalId(amd.getGlobalId()).readAllBytes(); + + if (FileType.APIDEF.toString().equals(amd.getGroupId())) { + ApiDefinition32 apiDef = mapperYaml.readValue(content, ApiDefinition32.class); + ApiDefinition45 apiDef45 = ApiDefinitionMapper.map(apiDef); + content = mapperYaml.writeValueAsBytes(apiDef45); + FileOutputStream fos = new FileOutputStream(r.getName()); + fos.write(content); + fos.flush(); + fos.close(); + System.exit(0); + } + + zos.putNextEntry(new ZipEntry(fileName)); + zos.write(content); + zos.closeEntry(); + } + zos.finish(); + return baos.toByteArray(); + } + } } diff --git a/src/main/java/cz/trask/migration/mapper/ApiDefinitionMapper.java b/src/main/java/cz/trask/migration/mapper/ApiDefinitionMapper.java index 99391ca..9319a19 100644 --- a/src/main/java/cz/trask/migration/mapper/ApiDefinitionMapper.java +++ b/src/main/java/cz/trask/migration/mapper/ApiDefinitionMapper.java @@ -1,5 +1,226 @@ package cz.trask.migration.mapper; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import cz.trask.migration.model.ApiDefinition32; +import cz.trask.migration.model.ApiDefinition45; + public class ApiDefinitionMapper { + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + public static ApiDefinition45 map(ApiDefinition32 oldApi) { + if (oldApi == null) + return null; + + ApiDefinition45 newApi = new ApiDefinition45(); + newApi.setType("api"); + newApi.setVersion("v4.5.0"); + + ApiDefinition45.DataSection data = new ApiDefinition45.DataSection(); + + // ---------- základní metadata ---------- + data.setId(oldApi.getUuid()); + data.setName(oldApi.getId() != null ? oldApi.getId().getApiName() : null); + // data.setDescription(oldApi.getDescription()); + data.setVersion(oldApi.getId() != null ? oldApi.getId().getVersion() : null); + data.setProvider(oldApi.getId() != null ? oldApi.getId().getProviderName() : null); + data.setContext(oldApi.getContext()); + data.setLifeCycleStatus(oldApi.getStatus()); + data.setDefaultVersion(oldApi.isDefaultVersion()); + data.setRevision(false); + data.setEnableSchemaValidation(oldApi.isEnableSchemaValidation()); + data.setAuthorizationHeader(oldApi.getAuthorizationHeader()); + data.setTransport( + oldApi.getTransports() != null ? List.of(oldApi.getTransports().split(",")) : Collections.emptyList()); + data.setTags(oldApi.getTags()); + data.setVisibility(oldApi.getVisibility().toUpperCase()); + data.setVisibleRoles(Collections.emptyList()); + data.setVisibleTenants(Collections.emptyList()); + data.setAccessControl(oldApi.getAccessControl()); + data.setOrganizationPolicies(Collections.emptyList()); + data.setType(oldApi.getType()); + data.setAudiences(Arrays.asList("all")); + + List policies = new ArrayList<>(); + for (ApiDefinition32.Tier tier : oldApi.getAvailableTiers()) { + if (tier.getName() != null && !tier.getName().isEmpty()) { + policies.add(tier.getName()); + } + } + data.setPolicies(policies); + + if (oldApi.getApiSecurity() != null && !oldApi.getApiSecurity().isEmpty()) { + data.setSecurityScheme( + Arrays.stream(oldApi.getApiSecurity().split(",")).map(String::trim).collect(Collectors.toList())); + } else { + data.setSecurityScheme(Collections.emptyList()); + } + + data.setApiKeyHeader("ApiKey"); + data.setOrganizationId("carbon.super"); + data.setAsyncTransportProtocols(Collections.emptyList()); + data.setCategories(Collections.emptyList()); + + + // ---------- cache ---------- + data.setResponseCachingEnabled( + oldApi.getResponseCache() != null && !"disabled".equalsIgnoreCase(oldApi.getResponseCache())); + data.setCacheTimeout(oldApi.getCacheTimeout()); + + // ---------- CORS ---------- + data.setCorsConfiguration(mapCors(oldApi.getCorsConfiguration())); + + // ---------- endpoint ---------- + data.setEndpointConfig(mapEndpointConfig(oldApi.getEndpointConfig())); + data.setEndpointImplementationType(oldApi.getImplementation()); + + // ---------- API policies ---------- + // data.setApiPolicies(mapApiPolicies(oldApi.getAvailableTiers())); + + // ---------- key managers ---------- + data.setKeyManagers(oldApi.getKeyManagers()); + + // ---------- advertise info ---------- + ApiDefinition45.AdvertiseInfo ai = new ApiDefinition45.AdvertiseInfo(); + ai.setAdvertised(oldApi.isAdvertiseOnly()); + ai.setApiOwner(oldApi.getId() != null ? oldApi.getId().getProviderName() : null); + ai.setVendor("WSO2"); + data.setAdvertiseInfo(ai); + + // ---------- gateway ---------- + data.setGatewayVendor("wso2"); + data.setGatewayType("wso2/synapse"); + + // ---------- business & monetization ---------- + data.setBusinessInformation(oldApi.getMonetizationProperties()); + data.setAdditionalPropertiesMap(oldApi.getAdditionalProperties()); + + // ---------- subscription ---------- + data.setSubscriptionAvailability(oldApi.getSubscriptionAvailability()); + + // ---------- operations ---------- + data.setOperations(mapOperations(oldApi)); + + // ---------- additional metadata ---------- + data.setCreatedTime(oldApi.getCreatedTime()); + data.setLastUpdatedTime(oldApi.getLastUpdated()); + + // ---------- mediace ---------- + data.setMediationPolicies(oldApi.getDocuments()); + + newApi.setData(data); + return newApi; + } + + private static ApiDefinition45.CorsConfiguration mapCors(ApiDefinition32.CorsConfiguration oldCors) { + if (oldCors == null) + return null; + ApiDefinition45.CorsConfiguration cors = new ApiDefinition45.CorsConfiguration(); + cors.setCorsConfigurationEnabled(oldCors.isCorsConfigurationEnabled()); + cors.setAccessControlAllowOrigins(oldCors.getAccessControlAllowOrigins()); + cors.setAccessControlAllowHeaders(oldCors.getAccessControlAllowHeaders()); + cors.setAccessControlAllowMethods(oldCors.getAccessControlAllowMethods()); + cors.setAccessControlAllowCredentials(oldCors.isAccessControlAllowCredentials()); + return cors; + } + + private static ApiDefinition45.EndpointConfig mapEndpointConfig(ApiDefinition32.EndpointConfig oldEndpoint) { + if (oldEndpoint == null) + return null; + + ApiDefinition45.EndpointConfig newEndpoint = new ApiDefinition45.EndpointConfig(); + newEndpoint.setEndpoint_type(oldEndpoint.getEndpointType()); + + if (oldEndpoint.getSandboxEndpoints() != null) { + ApiDefinition45.EndpointGroup sandbox = new ApiDefinition45.EndpointGroup(); + sandbox.setUrl(oldEndpoint.getSandboxEndpoints().getUrl()); + newEndpoint.setSandbox_endpoints(sandbox); + } + + if (oldEndpoint.getProductionEndpoints() != null) { + ApiDefinition45.EndpointGroup production = new ApiDefinition45.EndpointGroup(); + production.setUrl(oldEndpoint.getProductionEndpoints().getUrl()); + newEndpoint.setProduction_endpoints(production); + } + + if (oldEndpoint.getEndpointSecurity() != null) { + ApiDefinition45.EndpointSecurity security = new ApiDefinition45.EndpointSecurity(); + security.setSandbox(mapSecurityEnv(oldEndpoint.getEndpointSecurity().getSandbox())); + security.setProduction(mapSecurityEnv(oldEndpoint.getEndpointSecurity().getProduction())); + newEndpoint.setEndpoint_security(security); + } + + return newEndpoint; + } + + private static ApiDefinition45.SecurityEnv mapSecurityEnv(ApiDefinition32.SecurityEnvironment oldSec) { + if (oldSec == null) + return null; + + ApiDefinition45.SecurityEnv newSec = new ApiDefinition45.SecurityEnv(); + newSec.setType(oldSec.getType()); + newSec.setTokenUrl(oldSec.getTokenUrl()); + newSec.setClientId(oldSec.getClientId()); + newSec.setClientSecret(oldSec.getClientSecret()); + newSec.setUsername(oldSec.getUsername()); + newSec.setPassword(oldSec.getPassword()); + newSec.setGrantType(oldSec.getGrantType()); + newSec.setEnabled(oldSec.isEnabled()); + newSec.setConnectionTimeoutDuration(0); + newSec.setSocketTimeoutDuration(0); + newSec.setConnectionRequestTimeoutDuration(0); + newSec.setProxyConfigs(new ApiDefinition45.ProxyConfigs()); + + // ---------- parse customParameters JSON string ---------- + if (oldSec.getCustomParameters() != null && !oldSec.getCustomParameters().isEmpty()) { + try { + Map map = OBJECT_MAPPER.readValue(oldSec.getCustomParameters(), new TypeReference<>() { + }); + newSec.setCustomParameters(map); + } catch (Exception e) { + newSec.setCustomParameters(Collections.emptyMap()); + } + } else { + newSec.setCustomParameters(Collections.emptyMap()); + } + + // ---------- parse additionalProperties JSON string ---------- + newSec.setAdditionalProperties(Collections.emptyMap()); + + return newSec; + } + + private static List mapOperations(ApiDefinition32 oldApi) { + if (oldApi.getUriTemplates() == null || oldApi.getUriTemplates().isEmpty()) + return Collections.emptyList(); + + return oldApi.getUriTemplates().stream().map(templateObj -> { + ApiDefinition45.Operation op = new ApiDefinition45.Operation(); + if (templateObj instanceof Map) { + Map template = (Map) templateObj; + op.setTarget((String) template.get("uri")); + op.setVerb((String) template.get("verb")); + op.setThrottlingPolicy((String) template.get("throttlingPolicy")); + op.setAuthType((String) template.get("authType")); + op.setScopes(Collections.emptyList()); + op.setUsedProductIds(Collections.emptyList()); + + ApiDefinition45.OperationPolicies opPolicies = new ApiDefinition45.OperationPolicies(); + opPolicies.setRequest(Collections.emptyList()); + opPolicies.setResponse(Collections.emptyList()); + opPolicies.setFault(Collections.emptyList()); + op.setOperationPolicies(opPolicies); + } + return op; + }).collect(Collectors.toList()); + } } diff --git a/src/main/java/cz/trask/migration/model/ApiDefinition32.java b/src/main/java/cz/trask/migration/model/ApiDefinition32.java index ccce94f..04c3363 100644 --- a/src/main/java/cz/trask/migration/model/ApiDefinition32.java +++ b/src/main/java/cz/trask/migration/model/ApiDefinition32.java @@ -47,6 +47,11 @@ public class ApiDefinition32 { private boolean endpointAuthDigest; private String transports; private String inSequence; + private String outSequence; + private String faultSequence; + private String oldInSequence; + private String oldOutSequence; + private String oldFaultSequence; private boolean advertiseOnly; private String subscriptionAvailability; private CorsConfiguration corsConfiguration; diff --git a/src/main/java/cz/trask/migration/model/ApiDefinition45.java b/src/main/java/cz/trask/migration/model/ApiDefinition45.java index fddb7f7..ea2855e 100644 --- a/src/main/java/cz/trask/migration/model/ApiDefinition45.java +++ b/src/main/java/cz/trask/migration/model/ApiDefinition45.java @@ -1,6 +1,8 @@ package cz.trask.migration.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + import lombok.Data; import java.util.List; @@ -27,8 +29,10 @@ public class ApiDefinition45 { private boolean responseCachingEnabled; private int cacheTimeout; private boolean hasThumbnail; - private boolean isDefaultVersion; - private boolean isRevision; + @JsonProperty("isDefaultVersion") + private boolean defaultVersion; + @JsonProperty("isRevision") + private boolean revision; private int revisionId; private boolean enableSchemaValidation; private boolean enableSubscriberVerification;