Skip to content

Commit

Permalink
Refactor service contract implementation with static resource
Browse files Browse the repository at this point in the history
  • Loading branch information
TharmiganK committed Jul 15, 2024
1 parent 0d8c6a0 commit 21d208a
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@
import io.ballerina.openapi.service.mapper.model.ResourceFunction;
import io.ballerina.openapi.service.mapper.model.ResourceFunctionDeclaration;
import io.ballerina.openapi.service.mapper.model.ResourceFunctionDefinition;
import io.ballerina.openapi.service.mapper.model.ServiceContractType;
import io.ballerina.openapi.service.mapper.model.ServiceDeclaration;
import io.ballerina.openapi.service.mapper.model.ServiceNode;
import io.ballerina.openapi.service.mapper.model.ServiceObjectType;
import io.ballerina.projects.Module;
import io.ballerina.projects.Package;
import io.ballerina.projects.Project;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
Expand Down Expand Up @@ -110,16 +111,8 @@ public static List<OASResult> generateOAS3Definition(Project project, SyntaxTree
// Generating openapi specification for selected services
for (Map.Entry<String, ServiceNode> serviceNode : servicesToGenerate.entrySet()) {
String openApiName = getOpenApiFileName(syntaxTree.filePath(), serviceNode.getKey(), needJson);
OASGenerationMetaInfo.OASGenerationMetaInfoBuilder builder =
new OASGenerationMetaInfo.OASGenerationMetaInfoBuilder();
builder.setServiceNode(serviceNode.getValue())
.setSemanticModel(semanticModel)
.setOpenApiFileName(openApiName)
.setBallerinaFilePath(inputPath)
.setProject(project);
OASGenerationMetaInfo oasGenerationMetaInfo = builder.build();
OASResult oasDefinition = generateOAS(oasGenerationMetaInfo);
oasDefinition.setServiceName(openApiName);
OASResult oasDefinition = generateOasFroServiceNode(project, openApiName,
semanticModel, inputPath, serviceNode.getValue());
outputs.add(oasDefinition);
}
}
Expand All @@ -130,6 +123,21 @@ public static List<OASResult> generateOAS3Definition(Project project, SyntaxTree
return outputs;
}

public static OASResult generateOasFroServiceNode(Project project, String openApiName, SemanticModel semanticModel,
Path inputPath, ServiceNode serviceNode) {
OASGenerationMetaInfo.OASGenerationMetaInfoBuilder builder =
new OASGenerationMetaInfo.OASGenerationMetaInfoBuilder();
builder.setServiceNode(serviceNode)
.setSemanticModel(semanticModel)
.setOpenApiFileName(openApiName)
.setBallerinaFilePath(inputPath)
.setProject(project);
OASGenerationMetaInfo oasGenerationMetaInfo = builder.build();
OASResult oasDefinition = generateOAS(oasGenerationMetaInfo);
oasDefinition.setServiceName(openApiName);
return oasDefinition;
}

/**
* Filter all the end points and service nodes.
*/
Expand All @@ -154,7 +162,7 @@ private static void extractServiceNodes(String serviceName, List<String> availab
// TODO: Distinct service types should work here
if (descriptorNode.kind().equals(SyntaxKind.OBJECT_TYPE_DESC) &&
isHttpServiceContract(descriptorNode, semanticModel)) {
ServiceNode service = new ServiceObjectType((TypeDefinitionNode) node);
ServiceNode service = new ServiceContractType((TypeDefinitionNode) node);
Optional<Symbol> serviceSymbol = service.getSymbol(semanticModel);
serviceSymbol.ifPresent(symbol ->
addService(serviceName, availableService, service, servicesToGenerate, symbol));
Expand All @@ -169,7 +177,7 @@ public static Optional<ServiceNode> getServiceNode(Node node, SemanticModel sema
} else if (node instanceof TypeDefinitionNode serviceTypeNode &&
serviceTypeNode.typeDescriptor() instanceof ObjectTypeDescriptorNode serviceNode &&
isHttpServiceContract(serviceNode, semanticModel)) {
return Optional.of(new ServiceObjectType((TypeDefinitionNode) node));
return Optional.of(new ServiceContractType((TypeDefinitionNode) node));
}
return Optional.empty();
}
Expand Down Expand Up @@ -222,8 +230,11 @@ private static void addService(String serviceName, List<String> availableService
public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) {
ServiceNode serviceDefinition = oasGenerationMetaInfo.getServiceNode();
SemanticModel semanticModel = oasGenerationMetaInfo.getSemanticModel();
ModuleMemberVisitor moduleMemberVisitor = extractNodesFromProject(oasGenerationMetaInfo.getProject());
Package currentPackage = oasGenerationMetaInfo.getProject().currentPackage();
ModuleMemberVisitor moduleMemberVisitor = extractNodesFromProject(oasGenerationMetaInfo.getProject(),
semanticModel);
Set<ListenerDeclarationNode> listeners = moduleMemberVisitor.getListenerDeclarationNodes();
Set<ServiceContractType> serviceContractTypes = moduleMemberVisitor.getServiceContractTypeNodes();
String openApiFileName = oasGenerationMetaInfo.getOpenApiFileName();
Path ballerinaFilePath = oasGenerationMetaInfo.getBallerinaFilePath();
// 01.Fill the openAPI info section
Expand All @@ -240,8 +251,8 @@ public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo)
serversMapperImpl.setServers();

if (oasAvailableViaServiceContract(serviceDefinition)) {
return updateOasResultWithServiceContract((ServiceDeclaration) serviceDefinition, oasResult,
semanticModel);
return updateOasResultWithServiceContract((ServiceDeclaration) serviceDefinition, currentPackage,
oasResult, semanticModel, serviceContractTypes);
}

convertServiceToOpenAPI(serviceDefinition, serviceMapperFactory);
Expand Down Expand Up @@ -272,9 +283,11 @@ static boolean oasAvailableViaServiceContract(ServiceNode serviceNode) {
((ServiceDeclaration) serviceNode).implementsServiceContract();
}

private static OASResult updateOasResultWithServiceContract(ServiceDeclaration serviceDeclaration,
OASResult oasResult, SemanticModel semanticModel) {
Optional<OpenAPI> openAPI = serviceDeclaration.getOpenAPIFromServiceContract(semanticModel);
private static OASResult updateOasResultWithServiceContract(ServiceDeclaration serviceDeclaration, Package pkg,
OASResult oasResult, SemanticModel semanticModel,
Set<ServiceContractType> serviceContractTypes) {
Optional<OpenAPI> openAPI = serviceDeclaration.getOpenAPIFromServiceContract(pkg, semanticModel,
serviceContractTypes, oasResult.getDiagnostics());
if (openAPI.isEmpty()) {
return oasResult;
}
Expand All @@ -287,15 +300,15 @@ private static OASResult updateOasResultWithServiceContract(ServiceDeclaration s

// Copy info
Info existingOpenAPIInfo = oasResult.getOpenAPI().get().getInfo();
// Update title
existingOpenAPIInfo.setTitle(openApiFromServiceContract.getInfo().getTitle());
openApiFromServiceContract.setInfo(existingOpenAPIInfo);

// Copy servers
String basePath = extractBasePath(openApiFromServiceContract);
List<Server> existingServers = oasResult.getOpenAPI().get().getServers();
existingServers.forEach(
server -> {
server.setUrl(server.getUrl() + basePath);
}
server -> server.setUrl(server.getUrl() + basePath)
);
openApiFromServiceContract.setServers(existingServers);
oasResult.setOpenAPI(openApiFromServiceContract);
Expand All @@ -316,8 +329,8 @@ private static String extractBasePath(OpenAPI openApiFromServiceContract) {
*
* @param project - current project
*/
public static ModuleMemberVisitor extractNodesFromProject(Project project) {
ModuleMemberVisitor balNodeVisitor = new ModuleMemberVisitor();
public static ModuleMemberVisitor extractNodesFromProject(Project project, SemanticModel semanticModel) {
ModuleMemberVisitor balNodeVisitor = new ModuleMemberVisitor(semanticModel);
project.currentPackage().moduleIds().forEach(moduleId -> {
Module module = project.currentPackage().module(moduleId);
module.documentIds().forEach(documentId -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,15 @@ public enum DiagnosticMessages {
OAS_CONVERTOR_126("OAS_CONVERTOR_126", "Generated OpenAPI definition does not contain the response" +
"/request parameter information from the interceptor pipeline. Cause: %s", DiagnosticSeverity.WARNING),
OAS_CONVERTOR_127("OAS_CONVERTOR_127", "Generated OpenAPI definition does not contain the request" +
" parameter information from the interceptor pipeline. Cause: %s", DiagnosticSeverity.WARNING),;
" parameter information from the interceptor pipeline. Cause: %s", DiagnosticSeverity.WARNING),
OAS_CONVERTOR_128("OAS_CONVERTOR_128", "Generated OpenAPI definition for the service contract type: " +
"'%s' has errors", DiagnosticSeverity.ERROR),
OAS_CONVERTOR_129("OAS_CONVERTOR_129", "Failed to obtain the OpenAPI definition from the service contract" +
" type: '%s'", DiagnosticSeverity.ERROR),
OAS_CONVERTOR_130("OAS_CONVERTOR_130", "Failed to find the package to obtain the OpenAPI definition from the " +
"service contract type: '%s'", DiagnosticSeverity.ERROR),
OAS_CONVERTOR_131("OAS_CONVERTOR_131", "Failed to find the OpenAPI definition resource for the " +
"service contract type: '%s'", DiagnosticSeverity.ERROR);

private final String code;
private final String description;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,21 @@
*/
package io.ballerina.openapi.service.mapper.model;

import io.ballerina.compiler.api.SemanticModel;
import io.ballerina.compiler.syntax.tree.ClassDefinitionNode;
import io.ballerina.compiler.syntax.tree.ListenerDeclarationNode;
import io.ballerina.compiler.syntax.tree.Node;
import io.ballerina.compiler.syntax.tree.NodeVisitor;
import io.ballerina.compiler.syntax.tree.SyntaxKind;
import io.ballerina.compiler.syntax.tree.TypeDefinitionNode;
import io.ballerina.openapi.service.mapper.utils.MapperCommonUtils;

import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;

import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.isHttpServiceContract;

/**
* Visitor to get the TypeDefinitionNode and ListenerDeclarationNodes.
*
Expand All @@ -37,10 +42,22 @@ public class ModuleMemberVisitor extends NodeVisitor {
Set<TypeDefinitionNode> typeDefinitionNodes = new LinkedHashSet<>();
Set<ListenerDeclarationNode> listenerDeclarationNodes = new LinkedHashSet<>();
Set<ClassDefinitionNode> interceptorServiceClassNodes = new LinkedHashSet<>();
Set<ServiceContractType> serviceContractTypeNodes = new LinkedHashSet<>();
SemanticModel semanticModel;

public ModuleMemberVisitor(SemanticModel semanticModel) {
this.semanticModel = semanticModel;
}

@Override
public void visit(TypeDefinitionNode typeDefinitionNode) {
typeDefinitionNodes.add(typeDefinitionNode);
Node descriptorNode = typeDefinitionNode.typeDescriptor();
// TODO: Distinct service types should work here
if (descriptorNode.kind().equals(SyntaxKind.OBJECT_TYPE_DESC) &&
isHttpServiceContract(descriptorNode, semanticModel)) {
serviceContractTypeNodes.add(new ServiceContractType(typeDefinitionNode));
}
}

@Override
Expand Down Expand Up @@ -74,4 +91,8 @@ public Optional<ClassDefinitionNode> getInterceptorServiceClassNode(String typeN
}
return Optional.empty();
}

public Set<ServiceContractType> getServiceContractTypeNodes() {
return serviceContractTypeNodes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@
import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createSeparatedNodeList;
import static io.ballerina.compiler.syntax.tree.NodeFactory.createMetadataNode;

public class ServiceObjectType implements ServiceNode {
public class ServiceContractType implements ServiceNode {

private static final String DEFAULT_PATH = "/";
ObjectTypeDescriptorNode serviceObjType;
TypeDefinitionNode serviceTypeDefinition;
int serviceId;

public ServiceObjectType(TypeDefinitionNode serviceType) {
public ServiceContractType(TypeDefinitionNode serviceType) {
serviceId = serviceType.hashCode();
serviceTypeDefinition = new TypeDefinitionNode(serviceType.internalNode(), serviceType.position(),
serviceType.parent());
Expand All @@ -43,6 +43,10 @@ public ServiceObjectType(TypeDefinitionNode serviceType) {
serviceObjTypeDesc.parent());
}

public boolean matchesName(String name) {
return serviceTypeDefinition.typeName().text().equals(name);
}

public Optional<MetadataNode> metadata() {
return serviceTypeDefinition.metadata();
}
Expand Down
Loading

0 comments on commit 21d208a

Please sign in to comment.