Java tutorial
/* * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.carbon.apimgt.migration; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.wso2.carbon.apimgt.api.APIManagementException; import org.wso2.carbon.apimgt.api.model.API; import org.wso2.carbon.apimgt.api.model.APIIdentifier; import org.wso2.carbon.apimgt.api.model.URITemplate; import org.wso2.carbon.apimgt.impl.APIConstants; import org.wso2.carbon.apimgt.impl.dao.ApiMgtDAO; import org.wso2.carbon.apimgt.migration.internal.ServiceHolder; import org.wso2.carbon.apimgt.migration.util.Constants; import org.wso2.carbon.apimgt.migration.util.ResourceUtil; import org.wso2.carbon.base.MultitenantConstants; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.governance.api.common.dataobjects.GovernanceArtifact; import org.wso2.carbon.governance.api.exception.GovernanceException; import org.wso2.carbon.governance.api.generic.GenericArtifactManager; import org.wso2.carbon.governance.api.generic.dataobjects.GenericArtifact; import org.wso2.carbon.governance.api.util.GovernanceUtils; import org.wso2.carbon.registry.core.ActionConstants; import org.wso2.carbon.registry.core.Registry; import org.wso2.carbon.registry.core.Resource; import org.wso2.carbon.registry.core.exceptions.RegistryException; import org.wso2.carbon.registry.core.session.UserRegistry; import org.wso2.carbon.user.api.Tenant; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.core.tenant.TenantManager; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * Class responsible to migrate swagger v1.2 documents to swagger v2.0 document and set it to a new swagger v2.0 location. * Swagger v2.0 doc is generated according to the <a href="https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md">Swagger 2 Specification</a> */ @SuppressWarnings("unchecked") public class Swagger19Migration { private static final Log log = LogFactory.getLog(Swagger19Migration.class); /** * Migrates the APIs to new version * * @throws UserStoreException * @throws InterruptedException */ public void migrate() throws UserStoreException, InterruptedException { log.info("Swagger migration for AM 1.9 started"); TenantManager tenantManager = ServiceHolder.getRealmService().getTenantManager(); Tenant[] tenantsArray = tenantManager.getAllTenants(); log.debug("Tenant array loaded successfully"); // Add super tenant to the tenant array Tenant[] allTenantsArray = Arrays.copyOf(tenantsArray, tenantsArray.length + 1); org.wso2.carbon.user.core.tenant.Tenant superTenant = new org.wso2.carbon.user.core.tenant.Tenant(); superTenant.setId(MultitenantConstants.SUPER_TENANT_ID); superTenant.setDomain(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME); allTenantsArray[allTenantsArray.length - 1] = superTenant; if (log.isDebugEnabled()) { log.debug("Super tenant added to the tenant array"); } for (Tenant tenant : allTenantsArray) { log.info("Swagger migration for tenant " + tenant.getDomain() + "[" + tenant.getId() + "]" + " "); try { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenant.getDomain()); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenant.getId()); String adminName = ServiceHolder.getRealmService().getTenantUserRealm(tenant.getId()) .getRealmConfiguration().getAdminUserName(); ServiceHolder.getTenantRegLoader().loadTenantRegistry(tenant.getId()); Registry registry = ServiceHolder.getRegistryService().getGovernanceUserRegistry(adminName, tenant.getId()); GenericArtifactManager manager = new GenericArtifactManager(registry, "api"); GovernanceUtils.loadGovernanceArtifacts((UserRegistry) registry); GenericArtifact[] artifacts = manager.getAllGenericArtifacts(); for (GenericArtifact artifact : artifacts) { API api; try { api = getAPI(artifact, registry); APIIdentifier apiIdentifier = api.getId(); String apiName = apiIdentifier.getApiName(); String apiVersion = apiIdentifier.getVersion(); String apiProviderName = apiIdentifier.getProviderName(); String swagger12location = ResourceUtil.getSwagger12ResourceLocation(apiName, apiVersion, apiProviderName); if (!registry.resourceExists(swagger12location)) { log.error("Swagger Resource migration has not happen yet for " + apiName + "-" + apiVersion + "-" + apiProviderName + ". Please run -D" + Constants.VERSION_1_6 + " first"); } else { log.info("Creating swagger v2.0 resource for : " + apiName + "-" + apiVersion + "-" + apiProviderName); //get swagger v2 doc String swagger2doc = getSwagger2docUsingSwagger12RegistryResources(registry, swagger12location); //create location in registry and add this String swagger2location = ResourceUtil.getSwagger2ResourceLocation(apiName, apiVersion, apiProviderName); Resource docContent = registry.newResource(); docContent.setContent(swagger2doc); docContent.setMediaType("application/json"); registry.put(swagger2location, docContent); //Find the visible roles of to set to the resource String visibleRolesList = api.getVisibleRoles(); String[] visibleRoles = new String[0]; if (visibleRolesList != null) { visibleRoles = visibleRolesList.split(","); } //Currently set to ANONYMOUS_ROLE, need to set to visible roles ServiceHolder.getRealmService().getTenantUserRealm(tenant.getId()) .getAuthorizationManager().authorizeRole(APIConstants.ANONYMOUS_ROLE, "_system/governance" + swagger2location, ActionConstants.GET); log.info("Created swagger v2.0 resource for : " + apiName + "-" + apiVersion + "-" + apiProviderName); } } catch (APIManagementException e) { log.error("APIManagementException while migrating api in " + tenant.getDomain(), e); } catch (RegistryException e) { log.error("RegistryException while getting api resource for " + tenant.getDomain(), e); } catch (ParseException e) { log.error("Error while parsing json resource for " + tenant.getDomain(), e); } catch (Exception e) { log.error(e.getMessage(), e); } } } catch (RegistryException e) { log.error("RegistryException while getting artifacts for " + tenant.getDomain(), e); } catch (Exception e) { log.error(e.getMessage(), e); } finally { PrivilegedCarbonContext.endTenantFlow(); } } log.debug("Migration done for all the tenants"); } public static API getAPI(GovernanceArtifact artifact, Registry registry) throws APIManagementException { API api; try { String providerName = artifact.getAttribute(APIConstants.API_OVERVIEW_PROVIDER); String apiName = artifact.getAttribute(APIConstants.API_OVERVIEW_NAME); String apiVersion = artifact.getAttribute(APIConstants.API_OVERVIEW_VERSION); APIIdentifier apiId = new APIIdentifier(providerName, apiName, apiVersion); api = new API(apiId); if (log.isDebugEnabled()) { log.debug("API read from registry successfully"); } api.setUrl(artifact.getAttribute(APIConstants.API_OVERVIEW_ENDPOINT_URL)); api.setSandboxUrl(artifact.getAttribute(APIConstants.API_OVERVIEW_SANDBOX_URL)); api.setVisibility(artifact.getAttribute(APIConstants.API_OVERVIEW_VISIBILITY)); api.setVisibleRoles(artifact.getAttribute(APIConstants.API_OVERVIEW_VISIBLE_ROLES)); api.setContext(artifact.getAttribute(APIConstants.API_OVERVIEW_CONTEXT)); api.setDescription(artifact.getAttribute(APIConstants.API_OVERVIEW_DESCRIPTION)); ArrayList<URITemplate> urlPatternsList = ApiMgtDAO.getAllURITemplates(api.getContext(), api.getId().getVersion()); Set<URITemplate> uriTemplates = new HashSet<URITemplate>(urlPatternsList); for (URITemplate uriTemplate : uriTemplates) { uriTemplate.setResourceURI(api.getUrl()); uriTemplate.setResourceSandboxURI(api.getSandboxUrl()); } api.setUriTemplates(uriTemplates); if (log.isDebugEnabled()) { log.debug("API updated with other attributes"); } } catch (GovernanceException e) { String errorMsg = "Failed to get API from artifact. "; throw new APIManagementException(errorMsg, e); } return api; } /** * Generates swagger v2 doc using swagger 1.2 doc * * @param registry governance registry * @param swagger12location the location of swagger 1.2 doc * @return JSON string of swagger v2 doc * @throws MalformedURLException * @throws ParseException * @throws RegistryException */ private String getSwagger2docUsingSwagger12RegistryResources(Registry registry, String swagger12location) throws MalformedURLException, ParseException, RegistryException { JSONParser parser = new JSONParser(); String swagger12BasePath = null; Resource swaggerRes = registry.get(swagger12location + APIConstants.API_DOC_1_2_RESOURCE_NAME); JSONObject swagger12doc = (JSONObject) parser.parse(new String((byte[]) swaggerRes.getContent())); Map<String, JSONArray> apiDefPaths = new HashMap<String, JSONArray>(); Resource swagger12Res = registry.get(swagger12location); //get all the resources inside the 1.2 resource location String[] apiDefinitions = (String[]) swagger12Res.getContent(); //get each resource in the 1.2 folder except the api-doc resource for (String apiDefinition : apiDefinitions) { String resourceName = apiDefinition.substring(apiDefinition.lastIndexOf("/")); //skip if api-doc file if (resourceName.equals(APIConstants.API_DOC_1_2_RESOURCE_NAME)) { continue; } Resource resource = registry.get(apiDefinition); JSONObject apiDef = (JSONObject) parser.parse(new String((byte[]) resource.getContent())); //get the base path. this is same for all api definitions. swagger12BasePath = (String) apiDef.get("basePath"); JSONArray apiArray = (JSONArray) apiDef.get("apis"); for (Object anApiArray : apiArray) { JSONObject api = (JSONObject) anApiArray; String path = (String) api.get("path"); JSONArray operations = (JSONArray) api.get("operations"); //set the operations object inside each api definitions resource and set it in a map against its resource path apiDefPaths.put(path, operations); } } JSONObject swagger2Doc = generateSwagger2Document(swagger12doc, apiDefPaths, swagger12BasePath); return swagger2Doc.toJSONString(); } /** * Generate Swagger v2.0 document using Swagger v1.2 resources * * @param swagger12doc Old Swagger Document * @param apiDefPaths Paths in API definition * @param swagger12BasePath Location of swagger v1.2 document * @return Swagger v2.0 document as a JSON object * @throws ParseException * @throws MalformedURLException */ private static JSONObject generateSwagger2Document(JSONObject swagger12doc, Map<String, JSONArray> apiDefPaths, String swagger12BasePath) throws ParseException, MalformedURLException { //create swagger 2.0 doc JSONObject swagger20doc = new JSONObject(); //set swagger version swagger20doc.put("swagger", "2.0"); //set the info object JSONObject info = generateInfoObject(swagger12doc); //update info object swagger20doc.put("info", info); //set the paths object JSONObject pathObj = generatePathsObj(apiDefPaths); swagger20doc.put("paths", pathObj); URL url = new URL(swagger12BasePath); swagger20doc.put("host", url.getHost()); swagger20doc.put("basePath", url.getPath()); JSONArray schemes = new JSONArray(); schemes.add(url.getProtocol()); swagger20doc.put("schemes", schemes); //securityDefinitions if (swagger12doc.containsKey("authorizations")) { JSONObject securityDefinitions = generateSecurityDefinitionsObject(swagger12doc); swagger20doc.put("securityDefinitions", securityDefinitions); } return swagger20doc; } /** * Generate swagger v2 security definition object * See <a href="https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#securityDefinitionsObject">Swagger v2 definition object</a> * * @param swagger12doc Old Swagger Document * @return security definition object * @throws ParseException */ private static JSONObject generateSecurityDefinitionsObject(JSONObject swagger12doc) throws ParseException { JSONParser parser = new JSONParser(); JSONObject securityDefinitionObject = new JSONObject(); JSONObject securitySchemeObject = (JSONObject) parser.parse(Constants.DEFAULT_SECURITY_SCHEME); JSONObject authorizations = (JSONObject) swagger12doc.get("authorizations"); Set authTypes = authorizations.keySet(); for (Object obj : authTypes) { JSONObject authObj = (JSONObject) authorizations.get(obj.toString()); if (authObj.containsKey("scopes")) { //Put it to custom WSO2 scopes securitySchemeObject.put("x-wso2-scopes", authObj.get("scopes")); } securityDefinitionObject.put(obj.toString(), securitySchemeObject); } return securityDefinitionObject; } /** * generate swagger v2 info object using swagger 1.2 doc. * See <a href="https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#infoObject">Swagger v2 info object</a> * * @param swagger12doc Old Swagger Document * @return swagger v2 infoObject * @throws ParseException */ private static JSONObject generateInfoObject(JSONObject swagger12doc) throws ParseException { JSONObject infoObj = (JSONObject) swagger12doc.get("info"); JSONParser parser = new JSONParser(); JSONObject swagger2InfoObj = (JSONObject) parser.parse(Constants.DEFAULT_INFO); //set the required parameters first String title = (String) infoObj.get("title"); String version = (String) swagger12doc.get("apiVersion"); swagger2InfoObj.put("title", title); swagger2InfoObj.put("version", version); if (infoObj.containsKey("description")) { swagger2InfoObj.put("description", infoObj.get("description")); } if (infoObj.containsKey("termsOfServiceUrl")) { swagger2InfoObj.put("termsOfService", infoObj.get("termsOfServiceUrl")); } //contact object if (infoObj.containsKey("contact")) { JSONObject contactsObj = new JSONObject(); String contact = (String) infoObj.get("contact"); if (contact.contains("http")) { contactsObj.put("url", contact); } else if (contact.contains("@")) { contactsObj.put("email", contact); } else { contactsObj.put("name", contact); } swagger2InfoObj.put("contact", contactsObj); } //licence object JSONObject licenseObj = new JSONObject(); if (infoObj.containsKey("license")) { licenseObj.put("name", infoObj.get("license")); } if (infoObj.containsKey("licenseUrl")) { licenseObj.put("url", infoObj.get("licenseUrl")); } if (!licenseObj.isEmpty()) { swagger2InfoObj.put("license", licenseObj); } return swagger2InfoObj; } /** * Generate Swagger v2 paths object from swagger v1.2 document * See <a href="https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#paths-object">Swagger v2 paths object</a> * * @param apiDefinitionPaths API definition paths * @return swagger v2 paths object * @throws ParseException */ private static JSONObject generatePathsObj(Map<String, JSONArray> apiDefinitionPaths) throws ParseException { JSONObject pathsObj = new JSONObject(); JSONParser jsonParser = new JSONParser(); for (Map.Entry<String, JSONArray> entry : apiDefinitionPaths.entrySet()) { String key = entry.getKey(); JSONArray operations = entry.getValue(); JSONObject pathItemObj = new JSONObject(); for (Object operation : operations) { JSONObject operationObject = (JSONObject) operation; String method = (String) operationObject.get("method"); JSONArray swagger2ParamObjects = (JSONArray) operationObject.get("parameters"); JSONObject swagger2OperationsObj = new JSONObject(); JSONArray newParameters = new JSONArray(); for (Object swagger2ParamObj : swagger2ParamObjects) { JSONObject oldParam = (JSONObject) swagger2ParamObj; JSONObject paramObj = new JSONObject(); paramObj.put("name", oldParam.get("name")); paramObj.put("in", oldParam.get("paramType")); paramObj.put("required", oldParam.get("required")); if (paramObj.containsKey("description")) { paramObj.put("description", oldParam.get("description")); } else { paramObj.put("description", ""); } newParameters.add(paramObj); } //generate the Operation object (https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#operationObject) swagger2OperationsObj.put("operationId", operationObject.get("nickname")); //setting operation level params swagger2OperationsObj.put("parameters", newParameters); if (operationObject.containsKey("notes")) { swagger2OperationsObj.put("description", operationObject.get("notes")); } if (operationObject.containsKey("summary")) { swagger2OperationsObj.put("summary", operationObject.get("summary")); } //set pathItem object for the resource(https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#pathItemObject) pathItemObj.put(method.toLowerCase(), swagger2OperationsObj); //set the responseObject (https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#responsesObject) JSONObject responseObject = null; if (operationObject.containsKey("responseMessages")) { responseObject = new JSONObject(); JSONArray responseMessages = (JSONArray) operationObject.get("responseMessages"); for (Object responseMessage : responseMessages) { JSONObject errorObj = (JSONObject) responseMessage; responseObject.put(errorObj.get("code"), errorObj.get("message")); } } if (responseObject == null) { //set a default response message since this is required field responseObject = (JSONObject) jsonParser.parse(Constants.DEFAULT_RESPONSE); } swagger2OperationsObj.put("responses", responseObject); } pathsObj.put(key, pathItemObj); } return pathsObj; } }