From 7324acb8d37f4af7a4e032dbd96a0facfbd205cc Mon Sep 17 00:00:00 2001 From: Sasini Sandamali Date: Tue, 25 Apr 2023 08:15:47 +0000 Subject: [PATCH] Add API doc while publishing APIs (#79) Co-authored-by: Sasini_Sandamali Co-authored-by: Pahansith Gunathilake Reviewed-on: https://repository.entgra.net/community/device-mgt-core/pulls/79 Co-authored-by: Sasini Sandamali Co-committed-by: Sasini Sandamali --- .../apimgt/webapp/publisher/APIConfig.java | 43 +++++- .../publisher/APIPublisherServiceImpl.java | 47 ++++++- .../webapp/publisher/APIPublisherUtil.java | 35 ++++- .../config/APIResourceConfiguration.java | 129 +++++++++++------- .../lifecycle/util/AnnotationProcessor.java | 88 ++++++------ 5 files changed, 239 insertions(+), 103 deletions(-) diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIConfig.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIConfig.java index b3b880b251..dd3efdeac8 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIConfig.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIConfig.java @@ -28,17 +28,16 @@ import java.util.Set; /** * This bean class carries the properties used by some API that needs to be published within the underlying * API-Management infrastructure. - * * A sample API configuration accepted by this particular bean class would look like what's shown below. * e.g. * * - * enrollment - * admin - * /enrol - * 1.0.0 - * http://localhost:9763/ - * http,https + * enrollment + * admin + * /enrol + * 1.0.0 + * http://localhost:9763/ + * http,https * */ @XmlRootElement(name = "API") @@ -47,6 +46,9 @@ public class APIConfig { private String name; private String owner; private String context; + private String apiDocumentationName; + private String apiDocumentationSummary; + private String apiDocumentationSourceFile; private String endpoint; private String version; private String policy; @@ -82,6 +84,33 @@ public class APIConfig { this.name = name; } + @XmlElement(name = "ApiDocumentationName", required = false) + public String getApiDocumentationName() { + return apiDocumentationName; + } + + public void setApiDocumentationName(String apiDocumentationName) { + this.apiDocumentationName = apiDocumentationName; + } + + @XmlElement(name = "ApiDocumentationSummary", required = false) + public String getApiDocumentationSummary() { + return apiDocumentationSummary; + } + + public void setApiDocumentationSummary(String apiDocumentationSummary) { + this.apiDocumentationSummary = apiDocumentationSummary; + } + + @XmlElement(name = "ApiDocumentationSourceFile", required = false) + public String getApiDocumentationSourceFile() { + return apiDocumentationSourceFile; + } + + public void setApiDocumentationSourceFile(String apiDocumentationSourceFile) { + this.apiDocumentationSourceFile = apiDocumentationSourceFile; + } + @XmlElement(name = "Owner", required = true) public String getOwner() { return owner; diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java index 5135e439bd..0dcc92e638 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java @@ -21,6 +21,8 @@ package org.wso2.carbon.apimgt.webapp.publisher; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.apimgt.api.model.Documentation; +import org.wso2.carbon.apimgt.api.model.DocumentationType; import org.wso2.carbon.apimgt.api.APIManagementException; import org.wso2.carbon.apimgt.api.APIProvider; import org.wso2.carbon.apimgt.api.FaultGatewaysException; @@ -66,18 +68,19 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Date; /** * This class represents the concrete implementation of the APIPublisherService that corresponds to providing all * API publishing related operations. */ public class APIPublisherServiceImpl implements APIPublisherService { + public static final APIManagerFactory API_MANAGER_FACTORY = APIManagerFactory.getInstance(); private static final String UNLIMITED_TIER = "Unlimited"; private static final String WS_UNLIMITED_TIER = "AsyncUnlimited"; private static final String API_PUBLISH_ENVIRONMENT = "Default"; private static final String CREATED_STATUS = "CREATED"; private static final String PUBLISH_ACTION = "Publish"; - public static final APIManagerFactory API_MANAGER_FACTORY = APIManagerFactory.getInstance(); private static final Log log = LogFactory.getLog(APIPublisherServiceImpl.class); @Override @@ -293,7 +296,45 @@ public class APIPublisherServiceImpl implements APIPublisherService { } } } - } catch (FaultGatewaysException | APIManagementException e) { + if (apiConfig.getApiDocumentationSourceFile() != null) { + API api = getAPI(apiConfig, true); + + String fileName = + CarbonUtils.getCarbonHome() + File.separator + "repository" + + File.separator + "resources" + File.separator + "api-docs" + File.separator + + apiConfig.getApiDocumentationSourceFile(); + + BufferedReader br = new BufferedReader(new FileReader(fileName)); + StringBuilder stringBuilder = new StringBuilder(); + String line = null; + String ls = System.lineSeparator(); + while ((line = br.readLine()) != null) { + stringBuilder.append(line); + stringBuilder.append(ls); + } + stringBuilder.deleteCharAt(stringBuilder.length() - 1); + br.close(); + String docContent = stringBuilder.toString(); + + Documentation apiDocumentation = new Documentation(DocumentationType.HOWTO, apiConfig.getApiDocumentationName()); + apiDocumentation.setVisibility(Documentation.DocumentVisibility.API_LEVEL); + apiDocumentation.setSourceType(Documentation.DocumentSourceType.MARKDOWN); + apiDocumentation.setCreatedDate(new Date()); + apiDocumentation.setLastUpdated(new Date()); + apiDocumentation.setSummary(apiConfig.getApiDocumentationSummary()); + apiDocumentation.setOtherTypeName(null); + + try { + //Including below code lines inside the try block because 'getDocumentation' method returns an APIManagementException exception when it doesn't have any existing doc + Documentation existingDoc = apiProvider.getDocumentation(api.getId(), DocumentationType.HOWTO, apiConfig.getApiDocumentationName()); + apiProvider.removeDocumentation(api.getId(), existingDoc.getId(), null); + } catch (APIManagementException e) { + log.info("There is no any existing api documentation."); + } + apiProvider.addDocumentation(api.getId(), apiDocumentation); + apiProvider.addDocumentationContent(api, apiConfig.getApiDocumentationName(), docContent); + } + } catch (FaultGatewaysException | APIManagementException | IOException e) { String msg = "Error occurred while publishing api"; log.error(msg, e); throw new APIManagerPublisherException(e); @@ -327,7 +368,7 @@ public class APIPublisherServiceImpl implements APIPublisherService { try { String fileName = CarbonUtils.getCarbonConfigDirPath() + File.separator + "etc" - + File.separator + tenantDomain + ".csv"; + + File.separator + tenantDomain + ".csv"; if (Files.exists(Paths.get(fileName))) { BufferedReader br = new BufferedReader(new FileReader(fileName)); int lineNumber = 0; diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherUtil.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherUtil.java index b7ad837ed8..2b6a86fb06 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherUtil.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherUtil.java @@ -64,7 +64,7 @@ public class APIPublisherUtil { } public static String getWsServerBaseUrl() { - return getServerBaseUrl().replace("https","wss"); + return getServerBaseUrl().replace("https", "wss"); } public static String getApiEndpointUrl(String context) { @@ -104,7 +104,32 @@ public class APIPublisherUtil { } apiConfig.setVersion(version); + String apiDocumentationName = apiDef.getApiDocumentationName(); + if (apiDocumentationName == null || apiDocumentationName.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("'API Documentation not set in @SwaggerDefinition Annotation'"); + } + } else { + apiConfig.setApiDocumentationName(apiDef.getApiDocumentationName()); + } + String apiDocumentationSummary = apiDef.getApiDocumentationSummary(); + if (apiDocumentationSummary == null || apiDocumentationSummary.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("'API Documentation summary not set in @SwaggerDefinition Annotation'"); + } + } else { + apiConfig.setApiDocumentationSummary(apiDef.getApiDocumentationSummary()); + } + + String apiDocumentationSourceFile = apiDef.getApiDocumentationSourceFile(); + if (apiDocumentationSourceFile == null || apiDocumentationSourceFile.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("'API Documentation source file not set in @SwaggerDefinition Annotation'"); + } + } else { + apiConfig.setApiDocumentationSourceFile(apiDef.getApiDocumentationSourceFile()); + } String context = apiDef.getContext(); if (context == null || context.isEmpty()) { if (log.isDebugEnabled()) { @@ -302,20 +327,20 @@ public class APIPublisherUtil { public static void setResourceAuthTypes(ServletContext servletContext, APIConfig apiConfig) { List resourcesList = null; String nonSecuredResources = servletContext.getInitParameter(NON_SECURED_RESOURCES); - if(null != nonSecuredResources){ + if (null != nonSecuredResources) { resourcesList = Arrays.asList(nonSecuredResources.split(",")); } Set templates = apiConfig.getUriTemplates(); - if(null != resourcesList) { + if (null != resourcesList) { for (ApiUriTemplate template : templates) { String fullPaath = ""; if (!template.getUriTemplate().equals(AnnotationProcessor.WILD_CARD)) { fullPaath = apiConfig.getContext() + template.getUriTemplate(); } - else{ + else { fullPaath = apiConfig.getContext(); } - for(String context : resourcesList) { + for (String context : resourcesList) { if (context.trim().equals(fullPaath)) { template.setAuthType(AUTH_TYPE_NON_SECURED); } diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/APIResourceConfiguration.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/APIResourceConfiguration.java index 49b66e6db9..1fb2ab7880 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/APIResourceConfiguration.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/APIResourceConfiguration.java @@ -25,60 +25,92 @@ import java.util.List; @XmlRootElement(name = "ResourceConfiguration") public class APIResourceConfiguration { - private String name; - private String context; - private String version; - private List resources; - private String[] tags; + private String name; + private String context; + private String apiDocumentationName; + private String apiDocumentationSummary; + private String apiDocumentationSourceFile; + private String version; + private List resources; + private String[] tags; private String endpointType; private String inSequenceName; private String inSequenceConfig; private String asyncApiDefinition; public List getResources() { - return resources; - } - - @XmlElement(name = "Resources", required = true) - public void setResources(List resources) { - this.resources = resources; - } - - public String getContext() { - return context; - } - - @XmlElement(name = "Context", required = true) - public void setContext(String context) { - this.context = context; - } - - public String getName() { - return name; - } - - @XmlElement(name = "Name") - public void setName(String name) { - this.name = name; - } - - public String getVersion() { - return version; - } - - @XmlElement(name = "Version") - public void setVersion(String version) { - this.version = version; - } - - public String[] getTags() { - return tags; - } - - @XmlElement(name = "Tags") - public void setTags(String[] tags) { - this.tags = tags; - } + return resources; + } + + @XmlElement(name = "Resources", required = true) + public void setResources(List resources) { + this.resources = resources; + } + + public String getContext() { + return context; + } + + @XmlElement(name = "Context", required = true) + public void setContext(String context) { + this.context = context; + } + + public String getName() { + return name; + } + + @XmlElement(name = "Name") + public void setName(String name) { + this.name = name; + } + + public String getApiDocumentationName() { + return apiDocumentationName; + } + + @XmlElement(name = "ApiDocumentation") + + + public void setApiDocumentationName(String apiDocumentationName) { + this.apiDocumentationName = apiDocumentationName; + } + + public String getApiDocumentationSummary() { + return apiDocumentationSummary; + } + + @XmlElement(name = "ApiDocumentationSummary") + public void setApiDocumentationSummary(String apiDocumentationSummary) { + this.apiDocumentationSummary = apiDocumentationSummary; + } + + public String getApiDocumentationSourceFile() { + return apiDocumentationSourceFile; + } + + @XmlElement(name = "ApiDocumentationSourceFile") + public void setApiDocumentationSourceFile(String apiDocumentationSourceFile) { + this.apiDocumentationSourceFile = apiDocumentationSourceFile; + } + + public String getVersion() { + return version; + } + + @XmlElement(name = "Version") + public void setVersion(String version) { + this.version = version; + } + + public String[] getTags() { + return tags; + } + + @XmlElement(name = "Tags") + public void setTags(String[] tags) { + this.tags = tags; + } public String getEndpointType() { return endpointType; @@ -110,6 +142,7 @@ public class APIResourceConfiguration { public String getAsyncApiDefinition() { return asyncApiDefinition; } + @XmlElement(name = "asyncApiDefinition") public void setAsyncApiDefinition(String asyncApiDefinition) { this.asyncApiDefinition = asyncApiDefinition; diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationProcessor.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationProcessor.java index 6774aa29bd..f41c07967a 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationProcessor.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationProcessor.java @@ -53,7 +53,6 @@ import java.util.*; public class AnnotationProcessor { private static final Log log = LogFactory.getLog(AnnotationProcessor.class); - private static final String AUTH_TYPE = "Application & Application User"; private static final String STRING_ARR = "string_arr"; private static final String STRING = "string"; @@ -61,7 +60,6 @@ public class AnnotationProcessor { private static final String PACKAGE_ORG_CODEHAUS = "org.codehaus"; private static final String PACKAGE_ORG_SPRINGFRAMEWORK = "org.springframework"; public static final String WILD_CARD = "/*"; - private static final String SWAGGER_ANNOTATIONS_INFO = "info"; private static final String SWAGGER_ANNOTATIONS_TAGS = "tags"; private static final String SWAGGER_ANNOTATIONS_EXTENSIONS = "extensions"; @@ -73,6 +71,9 @@ public class AnnotationProcessor { private static final String SWAGGER_ANNOTATIONS_PROPERTIES_ROLES = "roles"; private static final String SWAGGER_ANNOTATIONS_PROPERTIES_VERSION = "version"; private static final String SWAGGER_ANNOTATIONS_PROPERTIES_CONTEXT = "context"; + private static final String SWAGGER_ANNOTATIONS_PROPERTIES_API_DOCUMENTATION_NAME = "apiDocumentationName"; + private static final String SWAGGER_ANNOTATIONS_PROPERTIES_API_DOCUMENTATION_SUMMARY = "apiDocumentationSummary"; + private static final String SWAGGER_ANNOTATIONS_PROPERTIES_API_DOCUMENTATION_SOURCE_FILE = "apiDocumentationSourceFile"; private static final String SWAGGER_ANNOTATIONS_PROPERTIES_ENDPOINT_TYPE = "endpointType"; private static final String SWAGGER_ANNOTATIONS_PROPERTIES_IN_SEQUENCE_NAME = "inSequenceName"; private static final String SWAGGER_ANNOTATIONS_PROPERTIES_IN_SEQUENCE_CONFIG = "inSequenceConfig"; @@ -112,26 +113,25 @@ public class AnnotationProcessor { pathClazz = (Class) classLoader.loadClass(Path.class.getName()); consumesClass = (Class) classLoader.loadClass(Consumes.class.getName()); producesClass = (Class) classLoader.loadClass(Produces.class.getName()); - apiClazz= (Class)classLoader.loadClass((SwaggerDefinition.class.getName())); - infoClass = (Class)classLoader + apiClazz = (Class) classLoader.loadClass((SwaggerDefinition.class.getName())); + infoClass = (Class) classLoader .loadClass((io.swagger.annotations.Info.class.getName())); - tagClass = (Class)classLoader + tagClass = (Class) classLoader .loadClass((io.swagger.annotations.Tag.class.getName())); - extensionClass = (Class)classLoader + extensionClass = (Class) classLoader .loadClass((io.swagger.annotations.Extension.class.getName())); - extensionPropertyClass = (Class)classLoader + extensionPropertyClass = (Class) classLoader .loadClass(io.swagger.annotations.ExtensionProperty.class.getName()); scopeClass = (Class) classLoader .loadClass(org.wso2.carbon.apimgt.annotations.api.Scope.class.getName()); scopesClass = (Class) classLoader .loadClass(org.wso2.carbon.apimgt.annotations.api.Scopes.class.getName()); - apiOperation = (Class)classLoader + apiOperation = (Class) classLoader .loadClass((ApiOperation.class.getName())); } catch (ClassNotFoundException e) { log.error("An error has occurred while loading classes ", e); } } - public Set scanStandardContext(String className) throws IOException { ExtendedAnnotationDB db = new ExtendedAnnotationDB(); db.addIgnoredPackages(PACKAGE_ORG_APACHE); @@ -166,7 +166,7 @@ public class AnnotationProcessor { if (Scopes != null) { apiScopes = processAPIScopes(Scopes); } - if(apiResourceConfig != null){ + if (apiResourceConfig != null) { String rootContext = servletContext.getContextPath(); pathClazzMethods = pathClazz.getMethods(); Annotation rootContectAnno = clazz.getAnnotation(pathClazz); @@ -199,21 +199,21 @@ public class AnnotationProcessor { " This API will not be published."; log.error(msg, e1); } catch (RuntimeException e) { - log.error("Unexpected error has been occurred while publishing "+ className - +"hence, this API will not be published."); + log.error("Unexpected error has been occurred while publishing " + className + + "hence, this API will not be published."); throw new RuntimeException(e); } return apiResourceConfig; } }); - if(apiResourceConfiguration !=null) + if (apiResourceConfiguration != null) apiResourceConfigs.add(apiResourceConfiguration); } } return apiResourceConfigs; } - private Map processAPIScopes(Annotation annotation) throws Throwable { + private Map processAPIScopes(Annotation annotation) throws Throwable { Map scopes = new HashMap<>(); InvocationHandler methodHandler = Proxy.getInvocationHandler(annotation); @@ -225,7 +225,7 @@ public class AnnotationProcessor { StringBuilder aggregatedPermissions; String roles[]; StringBuilder aggregatedRoles; - for(int i=0; i