Skip to content

Commit

Permalink
Merge branch 'master' into oas-gen-svc-type
Browse files Browse the repository at this point in the history
  • Loading branch information
TharmiganK authored Jun 27, 2024
2 parents 18224a9 + 17cefe7 commit 27c6004
Show file tree
Hide file tree
Showing 25 changed files with 808 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -330,14 +330,31 @@ public String toString() {
public static final String FALSE = "false";
public static final String SLASH = "/";
public static final String HYPHEN = "-";

//`@openapi:ServiceInfo` annotation constants
public static final String CONTRACT = "contract";
public static final String VERSION = "version";
public static final String TITLE = "title";
public static final String EMAIL = "email";
public static final String DESCRIPTION = "description";
public static final String CONTACT_NAME = "contactName";
public static final String CONTACT_URL = "contactURL";
public static final String LICENSE_NAME = "licenseName";
public static final String LICENSE_URL = "licenseURL";
public static final String TERMS_OF_SERVICE = "termsOfService";
public static final String OPENAPI_ANNOTATION = "openapi:ServiceInfo";

//File extensions
public static final String YAML_EXTENSION = ".yaml";
public static final String JSON_EXTENSION = ".json";
public static final String YML_EXTENSION = ".yml";
public static final String UNDERSCORE = "_";

//openapi:ResourceInFo annotation
public static final String OPENAPI_RESOURCE_INFO = "openapi:ResourceInfo";
public static final String TAGS = "tags";
public static final String SUMMARY = "summary";
public static final String EXAMPLES = "examples";
public static final String OPERATION_ID = "operationId";
public static final String RESPONSE_ATTRIBUTE = "response";
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
import io.ballerina.openapi.service.mapper.utils.MapperCommonUtils;
import io.ballerina.tools.diagnostics.Location;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;

import java.io.File;
import java.io.IOException;
Expand All @@ -51,10 +53,17 @@
import java.util.Locale;
import java.util.Optional;

import static io.ballerina.openapi.service.mapper.Constants.CONTACT_NAME;
import static io.ballerina.openapi.service.mapper.Constants.CONTACT_URL;
import static io.ballerina.openapi.service.mapper.Constants.CONTRACT;
import static io.ballerina.openapi.service.mapper.Constants.DESCRIPTION;
import static io.ballerina.openapi.service.mapper.Constants.EMAIL;
import static io.ballerina.openapi.service.mapper.Constants.LICENSE_NAME;
import static io.ballerina.openapi.service.mapper.Constants.LICENSE_URL;
import static io.ballerina.openapi.service.mapper.Constants.OPENAPI_ANNOTATION;
import static io.ballerina.openapi.service.mapper.Constants.SLASH;
import static io.ballerina.openapi.service.mapper.Constants.SPECIAL_CHAR_REGEX;
import static io.ballerina.openapi.service.mapper.Constants.TERMS_OF_SERVICE;
import static io.ballerina.openapi.service.mapper.Constants.TITLE;
import static io.ballerina.openapi.service.mapper.Constants.VERSION;

Expand Down Expand Up @@ -202,31 +211,66 @@ private static String normalizeTitle(String title) {
private static OASResult parseServiceInfoAnnotationAttachmentDetails(List<OpenAPIMapperDiagnostic> diagnostics,
AnnotationNode annotation,
Path ballerinaFilePath) {

Location location = annotation.location();
OpenAPI openAPI = new OpenAPI();
Optional<MappingConstructorExpressionNode> content = annotation.annotValue();
// If contract path there
if (content.isPresent()) {
SeparatedNodeList<MappingFieldNode> fields = content.get().fields();
if (!fields.isEmpty()) {
OpenAPIInfo openAPIInfo = updateOpenAPIInfoModel(fields);
// If in case ballerina file path is getting null, then openAPI specification will be generated for
// given services.
if (openAPIInfo.getContractPath().isPresent() && ballerinaFilePath != null) {
return updateExistingContractOpenAPI(diagnostics, location, openAPIInfo, ballerinaFilePath);
} else if (openAPIInfo.getTitle().isPresent() && openAPIInfo.getVersion().isPresent()) {
openAPI.setInfo(new Info().version(openAPIInfo.getVersion().get()).title(normalizeTitle
(openAPIInfo.getTitle().get())));
} else if (openAPIInfo.getVersion().isPresent()) {
openAPI.setInfo(new Info().version(openAPIInfo.getVersion().get()));
} else if (openAPIInfo.getTitle().isPresent()) {
openAPI.setInfo(new Info().title(normalizeTitle(openAPIInfo.getTitle().get())));
}
}
Optional<MappingConstructorExpressionNode> svcInfoAnnotationValue = annotation.annotValue();

if (svcInfoAnnotationValue.isEmpty()) {
return new OASResult(openAPI, diagnostics);
}

SeparatedNodeList<MappingFieldNode> fields = svcInfoAnnotationValue.get().fields();
if (fields.isEmpty()) {
return new OASResult(openAPI, diagnostics);
}

OpenAPIInfo openAPIInfo = updateOpenAPIInfoModel(fields);

// Check for contract path and existing Ballerina file path
if (openAPIInfo.getContractPath().isPresent() && ballerinaFilePath != null) {
return updateExistingContractOpenAPI(diagnostics, location, openAPIInfo, ballerinaFilePath);
}
populateOASInfo(openAPI, openAPIInfo);
return new OASResult(openAPI, diagnostics);
}

private static void populateOASInfo(OpenAPI openAPI, OpenAPIInfo openAPIInfo) {
// Populate OpenAPI Info object
Info info = openAPI.getInfo() == null ? new Info() : openAPI.getInfo();
openAPIInfo.getTitle().ifPresent(title -> info.setTitle(normalizeTitle(title)));
openAPIInfo.getVersion().ifPresent(info::setVersion);
openAPIInfo.getEmail().ifPresent(email -> {
Contact contact = (info.getContact() != null) ? info.getContact() : new Contact();
contact.setEmail(email);
info.setContact(contact);
});
openAPIInfo.getContactName().ifPresent(name -> {
Contact contact = (info.getContact() != null) ? info.getContact() : new Contact();
contact.setName(name);
info.setContact(contact);
});
openAPIInfo.getContactURL().ifPresent(url -> {
Contact contact = (info.getContact() != null) ? info.getContact() : new Contact();
contact.setUrl(url);
info.setContact(contact);
});
openAPIInfo.getLicenseURL().ifPresent(url -> {
License license = (info.getLicense() != null) ? info.getLicense() : new License();
license.setUrl(url);
info.setLicense(license);
});
openAPIInfo.getLicenseName().ifPresent(name -> {
License license = (info.getLicense() != null) ? info.getLicense() : new License();
license.setName(name);
info.setLicense(license);
});
openAPIInfo.getTermsOfService().ifPresent(info::setTermsOfService);
openAPIInfo.getDescription().ifPresent(info::setDescription);
openAPI.setInfo(info);
}


private static OASResult updateExistingContractOpenAPI(List<OpenAPIMapperDiagnostic> diagnostics,
Location location, OpenAPIInfo openAPIInfo,
Path ballerinaFilePath) {
Expand All @@ -237,21 +281,9 @@ private static OASResult updateExistingContractOpenAPI(List<OpenAPIMapperDiagnos
return oasResult;
}
OpenAPI openAPI = contract.get();
if (openAPIInfo.getVersion().isPresent() && openAPIInfo.getTitle().isPresent()) {
// read the openapi
openAPI.getInfo().setVersion(openAPIInfo.getVersion().get());
openAPI.getInfo().setTitle(openAPIInfo.getTitle().get());
diagnostics.addAll(oasResult.getDiagnostics());
return new OASResult(openAPI, oasResult.getDiagnostics());
} else if (openAPIInfo.getTitle().isPresent()) {
openAPI.getInfo().setTitle(openAPIInfo.getTitle().get());
return new OASResult(openAPI, oasResult.getDiagnostics());
} else if (openAPIInfo.getVersion().isPresent()) {
openAPI.getInfo().setVersion(openAPIInfo.getVersion().get());
return new OASResult(openAPI, oasResult.getDiagnostics());
} else {
return oasResult;
}
diagnostics.addAll(oasResult.getDiagnostics());
populateOASInfo(openAPI, openAPIInfo);
return new OASResult(openAPI, diagnostics);
}

private static OpenAPIInfo updateOpenAPIInfoModel(SeparatedNodeList<MappingFieldNode> fields) {
Expand All @@ -264,19 +296,21 @@ private static OpenAPIInfo updateOpenAPIInfoModel(SeparatedNodeList<MappingField
ExpressionNode expressionNode = value.get();
if (!expressionNode.toString().trim().isBlank()) {
fieldValue = expressionNode.toString().trim().replaceAll("\"", "");
if (!fieldValue.isBlank()) {
switch (fieldName) {
case CONTRACT:
infoBuilder.contractPath(fieldValue);
break;
case TITLE:
infoBuilder.title(fieldValue);
break;
case VERSION:
infoBuilder.version(fieldValue);
break;
default:
break;
if (fieldValue.isBlank()) {
continue;
}
switch (fieldName) {
case CONTRACT -> infoBuilder.contractPath(fieldValue);
case TITLE -> infoBuilder.title(fieldValue);
case VERSION -> infoBuilder.version(fieldValue);
case EMAIL -> infoBuilder.email(fieldValue);
case DESCRIPTION -> infoBuilder.description(fieldValue);
case CONTACT_NAME -> infoBuilder.contactName(fieldValue);
case CONTACT_URL -> infoBuilder.contactURL(fieldValue);
case TERMS_OF_SERVICE -> infoBuilder.termsOfService(fieldValue);
case LICENSE_NAME -> infoBuilder.licenseName(fieldValue);
case LICENSE_URL -> infoBuilder.licenseURL(fieldValue);
default -> {
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import io.ballerina.openapi.service.mapper.interceptor.model.RequestParameterInfo;
import io.ballerina.openapi.service.mapper.interceptor.model.ResponseInfo;
import io.ballerina.openapi.service.mapper.interceptor.pipeline.InterceptorPipeline;
import io.ballerina.openapi.service.mapper.metainfo.MetaInfoMapper;
import io.ballerina.openapi.service.mapper.metainfo.MetaInfoMapperImpl;
import io.ballerina.openapi.service.mapper.model.AdditionalData;
import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor;
import io.ballerina.openapi.service.mapper.model.OperationInventory;
Expand Down Expand Up @@ -82,6 +84,7 @@ public class ServiceMapperFactory {
private final ConstraintMapper constraintMapper;
private final HateoasMapper hateoasMapper;
private final InterceptorPipeline interceptorPipeline;
private final MetaInfoMapper metaInfoMapper;

public ServiceMapperFactory(OpenAPI openAPI, SemanticModel semanticModel, ModuleMemberVisitor moduleMemberVisitor,
List<OpenAPIMapperDiagnostic> diagnostics, ServiceNode serviceDefinition) {
Expand All @@ -96,6 +99,7 @@ public ServiceMapperFactory(OpenAPI openAPI, SemanticModel semanticModel, Module
this.typeMapper = new TypeMapperImpl(getComponents(openAPI), additionalData);
this.constraintMapper = new ConstraintMapperImpl(openAPI, moduleMemberVisitor, diagnostics);
this.hateoasMapper = new HateoasMapperImpl();
this.metaInfoMapper = new MetaInfoMapperImpl();
}

public ServersMapper getServersMapper(Set<ListenerDeclarationNode> endpoints, ServiceNode serviceNode) {
Expand Down Expand Up @@ -166,6 +170,10 @@ public HateoasMapper getHateoasMapper() {
return hateoasMapper;
}

public MetaInfoMapper getMetaInfoMapper() {
return metaInfoMapper;
}

private Components getComponents(OpenAPI openAPI) {
Components components = openAPI.getComponents();
if (Objects.isNull(components)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import io.ballerina.openapi.service.mapper.diagnostic.ExceptionDiagnostic;
import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic;
import io.ballerina.openapi.service.mapper.hateoas.HateoasMapper;
import io.ballerina.openapi.service.mapper.metainfo.MetaInfoMapper;
import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor;
import io.ballerina.openapi.service.mapper.model.OASGenerationMetaInfo;
import io.ballerina.openapi.service.mapper.model.OASResult;
Expand Down Expand Up @@ -250,6 +251,9 @@ public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo)
HateoasMapper hateoasMapper = serviceMapperFactory.getHateoasMapper();
hateoasMapper.setOpenApiLinks(serviceDefinition, openapi);

MetaInfoMapper metaInfoMapper = serviceMapperFactory.getMetaInfoMapper();
metaInfoMapper.setResourceMetaData(openapi, serviceDefinition);

if (openapi.getComponents().getSchemas().isEmpty()) {
openapi.setComponents(null);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. 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 io.ballerina.openapi.service.mapper.metainfo;

import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode;
import io.swagger.v3.oas.models.OpenAPI;

/**
* Interface for Meta information mapper.
*
* @since 2.0.1
*/
public interface MetaInfoMapper {
void setResourceMetaData(OpenAPI openAPI, ServiceDeclarationNode serviceNode);
}
Loading

0 comments on commit 27c6004

Please sign in to comment.