diff --git a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/diagnostic/DiagnosticMessages.java b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/diagnostic/DiagnosticMessages.java index 4979795fe..ec3e30903 100644 --- a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/diagnostic/DiagnosticMessages.java +++ b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/diagnostic/DiagnosticMessages.java @@ -61,12 +61,12 @@ public enum DiagnosticMessages { DiagnosticSeverity.WARNING), OAS_CONVERTOR_114("OAS_CONVERTOR_114", "Generated OpenAPI definition does not contain information " + "for Ballerina type '%s'. ", DiagnosticSeverity.WARNING), - OAS_CONVERTOR_115("OAS_CONVERTOR_115", "Failed to parser the Number value due to: %s ", - DiagnosticSeverity.ERROR), - "for Ballerina type '%s'. ", DiagnosticSeverity.WARNING), -//todo resolve conflicts OAS_CONVERTOR_115("OAS_CONVERTOR_115", "Given Ballerina file does not contain any HTTP service.", + DiagnosticSeverity.ERROR), + OAS_CONVERTOR_116("OAS_CONVERTOR_116", "Failed to parser the Number value due to: %s ", DiagnosticSeverity.ERROR); +//todo resolve conflicts + ; private final String code; private final String description; diff --git a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/ModuleMemberVisitor.java b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/ModuleMemberVisitor.java index 252325a26..21e103b1a 100644 --- a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/ModuleMemberVisitor.java +++ b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/ModuleMemberVisitor.java @@ -22,8 +22,7 @@ import io.ballerina.compiler.syntax.tree.NodeVisitor; import io.ballerina.compiler.syntax.tree.TypeDefinitionNode; -import java.util.LinkedList; -import java.util.List; +import java.util.LinkedHashSet; /** * Visitor to get the TypeDefinitionNode and ListenerDeclarationNodes. @@ -32,9 +31,10 @@ */ public class ModuleMemberVisitor extends NodeVisitor { - LinkedList typeDefinitionNodes = new LinkedList<>(); - LinkedList listenerDeclarationNodes = new LinkedList<>(); - + LinkedHashSet typeDefinitionNodes = new LinkedHashSet<>(); + LinkedHashSet listenerDeclarationNodes = new LinkedHashSet<>(); + LinkedHashSet allTypeDefinitionNodes = new LinkedHashSet<>(); + LinkedHashSet allListenerDeclarationNodes = new LinkedHashSet<>(); @Override public void visit(TypeDefinitionNode typeDefinitionNode) { typeDefinitionNodes.add(typeDefinitionNode); @@ -45,11 +45,27 @@ public void visit(ListenerDeclarationNode listenerDeclarationNode) { listenerDeclarationNodes.add(listenerDeclarationNode); } - public List getTypeDefinitionNodes() { + public LinkedHashSet getTypeDefinitionNodes() { return typeDefinitionNodes; } - public List getListenerDeclarationNodes() { + public LinkedHashSet getListenerDeclarationNodes() { return listenerDeclarationNodes; } + + public LinkedHashSet getAllTypeDefinitionNodes() { + return allTypeDefinitionNodes; + } + + public LinkedHashSet getAllListenerDeclarationNodes() { + return allListenerDeclarationNodes; + } + + public void setAllListenerDeclarationNodes(LinkedHashSet allListenerDeclarationNodes) { + this.allListenerDeclarationNodes = allListenerDeclarationNodes; + } + + public void setAllTypeDefinitionNodes(LinkedHashSet typeDefinitionNodes) { + this.allTypeDefinitionNodes = typeDefinitionNodes; + } } diff --git a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIComponentMapper.java b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIComponentMapper.java index 32c5e155a..8bae3aec5 100644 --- a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIComponentMapper.java +++ b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIComponentMapper.java @@ -38,14 +38,15 @@ import io.ballerina.compiler.api.symbols.UnionTypeSymbol; import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.ExpressionNode; +import io.ballerina.compiler.syntax.tree.IntersectionTypeDescriptorNode; import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; import io.ballerina.compiler.syntax.tree.MappingFieldNode; import io.ballerina.compiler.syntax.tree.MetadataNode; -import io.ballerina.compiler.syntax.tree.ModulePartNode; +import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeList; -import io.ballerina.compiler.syntax.tree.NonTerminalNode; import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; import io.ballerina.compiler.syntax.tree.RecordFieldNode; +import io.ballerina.compiler.syntax.tree.RecordTypeDescriptorNode; import io.ballerina.compiler.syntax.tree.SpecificFieldNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.compiler.syntax.tree.TypeDefinitionNode; @@ -70,6 +71,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -89,14 +91,14 @@ public class OpenAPIComponentMapper { private final Components components; private final List diagnostics; - private final NonTerminalNode rootNode; private final HashSet visitedTypeDefinitionNames = new HashSet<>(); + private final LinkedHashSet typeDefinitionNodes; - public OpenAPIComponentMapper(Components components, NonTerminalNode rootNode) { + public OpenAPIComponentMapper(Components components, ModuleMemberVisitor moduleMemberVisitor) { this.components = components; - this.rootNode = rootNode; this.diagnostics = new ArrayList<>(); + this.typeDefinitionNodes = moduleMemberVisitor.getTypeDefinitionNodes(); } public List getDiagnostics() { @@ -129,17 +131,18 @@ public void createComponentSchema(Map schema, TypeSymbol typeSym //Access module part node for finding the node to given typeSymbol. //TODO: this works for module reference. - ModulePartNode modulePartNode = rootNode.syntaxTree().rootNode(); - NonTerminalNode nonTerminalNode = modulePartNode.findNode(typeSymbol.getLocation().get().textRange()); ConstraintAnnotation.ConstraintAnnotationBuilder constraintBuilder = new ConstraintAnnotation.ConstraintAnnotationBuilder(); - if (nonTerminalNode instanceof TypeDefinitionNode) { - TypeDefinitionNode node = (TypeDefinitionNode) nonTerminalNode; + ((TypeReferenceTypeSymbol) typeSymbol).definition().getName().ifPresent(name -> { + for (TypeDefinitionNode typeDefinitionNode : typeDefinitionNodes) { + if (typeDefinitionNode.typeName().text().equals(name)) { + if (typeDefinitionNode.metadata().isPresent()) { + extractedConstraintAnnotation(typeDefinitionNode.metadata().get(), constraintBuilder); + } + } - if (node.metadata().isPresent()) { - extractedConstraintAnnotation(node.metadata().get(), constraintBuilder); } - } + }); ConstraintAnnotation constraintAnnot = constraintBuilder.build(); switch (type.typeKind()) { @@ -366,18 +369,39 @@ private ObjectSchema generateObjectSchemaFromRecordFields(Map sc List required = new ArrayList<>(); componentSchema.setDescription(apiDocs.get(componentName)); Map schemaProperties = new LinkedHashMap<>(); + RecordTypeDescriptorNode record = null; + for (TypeDefinitionNode typeDefinitionNode : typeDefinitionNodes) { + if (typeDefinitionNode.typeName().text().equals(componentName)) { + if (typeDefinitionNode.typeDescriptor().kind().equals(SyntaxKind.RECORD_TYPE_DESC)) { + record = (RecordTypeDescriptorNode) typeDefinitionNode.typeDescriptor(); + } else if (typeDefinitionNode.typeDescriptor().kind().equals(SyntaxKind.INTERSECTION_TYPE_DESC)) { + IntersectionTypeDescriptorNode intersecNode = + (IntersectionTypeDescriptorNode) typeDefinitionNode.typeDescriptor(); + Node leftTypeDesc = intersecNode.leftTypeDesc(); + Node rightTypeDesc = intersecNode.rightTypeDesc(); + if (leftTypeDesc.kind().equals(SyntaxKind.RECORD_TYPE_DESC)) { + record = (RecordTypeDescriptorNode) leftTypeDesc; + } + if (rightTypeDesc.kind().equals(SyntaxKind.RECORD_TYPE_DESC)) { + record = (RecordTypeDescriptorNode) rightTypeDesc; + } + } + } + } + for (Map.Entry field : rfields.entrySet()) { ConstraintAnnotation.ConstraintAnnotationBuilder constraintBuilder = new ConstraintAnnotation.ConstraintAnnotationBuilder(); - - if (!(rootNode instanceof QualifiedNameReferenceNode)) { - ModulePartNode modulePartNode = rootNode.syntaxTree().rootNode(); - NonTerminalNode node = modulePartNode.findNode(field.getValue().getLocation().get().textRange()); - if (node instanceof RecordFieldNode) { - RecordFieldNode fieldNode = (RecordFieldNode) modulePartNode. - findNode(field.getValue().getLocation().get().textRange()); - Optional metadata = fieldNode.metadata(); - metadata.ifPresent(metadataNode -> extractedConstraintAnnotation(metadataNode, constraintBuilder)); + if (record != null) { + for (Node node : record.fields()) { + if (node instanceof RecordFieldNode) { + RecordFieldNode fieldNode = (RecordFieldNode) node; + if (fieldNode.fieldName().toString().equals(field.getKey())) { + Optional metadata = fieldNode.metadata(); + metadata.ifPresent(metadataNode -> extractedConstraintAnnotation(metadataNode, + constraintBuilder)); + } + } } } diff --git a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIHeaderMapper.java b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIHeaderMapper.java index 70466d441..054859d71 100644 --- a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIHeaderMapper.java +++ b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIHeaderMapper.java @@ -59,11 +59,14 @@ public class OpenAPIHeaderMapper { private final Components components; private final SemanticModel semanticModel; private final Map apidocs; + private final ModuleMemberVisitor moduleMemberVisitor; - public OpenAPIHeaderMapper(Components components, SemanticModel semanticModel, Map apidocs) { + public OpenAPIHeaderMapper(Components components, SemanticModel semanticModel, Map apidocs, + ModuleMemberVisitor moduleMemberVisitor) { this.apidocs = apidocs; this.components = components; this.semanticModel = semanticModel; + this.moduleMemberVisitor = moduleMemberVisitor; } /** @@ -86,7 +89,7 @@ public List setHeaderParameter(RequiredParameterNode headerParam) { if (headerDetailNode.kind() == SyntaxKind.SIMPLE_NAME_REFERENCE) { SimpleNameReferenceNode refNode = (SimpleNameReferenceNode) headerDetailNode; - headerTypeSchema = handleReference(semanticModel, components, refNode); + headerTypeSchema = handleReference(semanticModel, components, refNode, moduleMemberVisitor); } else { headerTypeSchema = ConverterCommonUtils.getOpenApiSchema(getHeaderType(headerParam)); } @@ -126,7 +129,7 @@ public List setHeaderParameter(DefaultableParameterNode headerParam) Schema headerTypeSchema; if (headerParam.typeName().kind() == SyntaxKind.SIMPLE_NAME_REFERENCE) { SimpleNameReferenceNode refNode = (SimpleNameReferenceNode) headerParam.typeName(); - headerTypeSchema = handleReference(semanticModel, components, refNode); + headerTypeSchema = handleReference(semanticModel, components, refNode, moduleMemberVisitor); } else { headerTypeSchema = ConverterCommonUtils.getOpenApiSchema(getHeaderType(headerParam)); } @@ -184,7 +187,7 @@ private void completeHeaderParameter(List parameters, String headerNa Schema itemSchema; if (kind == SyntaxKind.SIMPLE_NAME_REFERENCE) { SimpleNameReferenceNode refNode = (SimpleNameReferenceNode) arrayNode.memberTypeDesc(); - itemSchema = handleReference(semanticModel, components, refNode); + itemSchema = handleReference(semanticModel, components, refNode, moduleMemberVisitor); } else { itemSchema = ConverterCommonUtils.getOpenApiSchema(kind); } diff --git a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIParameterMapper.java b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIParameterMapper.java index 6b137ea9b..0c3169429 100644 --- a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIParameterMapper.java +++ b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIParameterMapper.java @@ -58,6 +58,7 @@ import static io.ballerina.openapi.converter.Constants.WILD_CARD_CONTENT_KEY; import static io.ballerina.openapi.converter.Constants.WILD_CARD_SUMMARY; import static io.ballerina.openapi.converter.utils.ConverterCommonUtils.extractCustomMediaType; +import static io.ballerina.openapi.converter.utils.ConverterCommonUtils.unescapeIdentifier; /** * OpenAPIParameterMapper provides functionality for converting ballerina parameter to OAS parameter model. @@ -69,6 +70,7 @@ public class OpenAPIParameterMapper { private final List errors = new ArrayList<>(); private final Components components; private final SemanticModel semanticModel; + private final ModuleMemberVisitor moduleMemberVisitor; public List getErrors() { return errors; @@ -76,13 +78,15 @@ public List getErrors() { public OpenAPIParameterMapper(FunctionDefinitionNode functionDefinitionNode, OperationAdaptor operationAdaptor, Map apidocs, - Components components, SemanticModel semanticModel) { + Components components, SemanticModel semanticModel, + ModuleMemberVisitor moduleMemberVisitor) { this.functionDefinitionNode = functionDefinitionNode; this.operationAdaptor = operationAdaptor; this.apidocs = apidocs; this.components = components; this.semanticModel = semanticModel; + this.moduleMemberVisitor = moduleMemberVisitor; } @@ -100,7 +104,7 @@ public void getResourceInputs(Components components, SemanticModel semanticModel SeparatedNodeList parameterList = functionSignature.parameters(); for (ParameterNode parameterNode : parameterList) { OpenAPIQueryParameterMapper queryParameterMapper = new OpenAPIQueryParameterMapper(apidocs, components, - semanticModel); + semanticModel, moduleMemberVisitor); if (parameterNode.kind() == SyntaxKind.REQUIRED_PARAM) { RequiredParameterNode requiredParameterNode = (RequiredParameterNode) parameterNode; // Handle query parameter @@ -159,11 +163,14 @@ private void createPathParameters(List parameters, NodeList pat ResourcePathParameterNode pathParam = (ResourcePathParameterNode) param; if (pathParam.typeDescriptor().kind() == SyntaxKind.SIMPLE_NAME_REFERENCE) { SimpleNameReferenceNode queryNode = (SimpleNameReferenceNode) pathParam.typeDescriptor(); - OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, pathParam); + OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, + moduleMemberVisitor); + +// OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, pathParam); TypeSymbol typeSymbol = (TypeSymbol) semanticModel.symbol(queryNode).orElseThrow(); componentMapper.createComponentSchema(components.getSchemas(), typeSymbol); Schema schema = new Schema(); - schema.set$ref(ConverterCommonUtils.unescapeIdentifier(queryNode.name().text().trim())); + schema.set$ref(unescapeIdentifier(queryNode.name().text().trim())); pathParameterOAS.setSchema(schema); } else { pathParameterOAS.schema(ConverterCommonUtils.getOpenApiSchema( @@ -195,12 +202,13 @@ private void handleAnnotationParameters(Components components, for (AnnotationNode annotation: annotations) { if ((annotation.annotReference().toString()).trim().equals(Constants.HTTP_HEADER)) { // Handle headers. - OpenAPIHeaderMapper openAPIHeaderMapper = new OpenAPIHeaderMapper(components, semanticModel, apidocs); + OpenAPIHeaderMapper openAPIHeaderMapper = new OpenAPIHeaderMapper(components, semanticModel, apidocs, + moduleMemberVisitor); parameters.addAll(openAPIHeaderMapper.setHeaderParameter(requiredParameterNode)); } else if ((annotation.annotReference().toString()).trim().equals(Constants.HTTP_QUERY)) { // Handle query parameter. OpenAPIQueryParameterMapper openAPIQueryParameterMapper = new OpenAPIQueryParameterMapper(apidocs, - components, semanticModel); + components, semanticModel, moduleMemberVisitor); parameters.add(openAPIQueryParameterMapper.createQueryParameter(requiredParameterNode)); } else if ((annotation.annotReference().toString()).trim().equals(Constants.HTTP_PAYLOAD) && (!Constants.GET.toLowerCase(Locale.ENGLISH).equalsIgnoreCase( @@ -210,8 +218,9 @@ private void handleAnnotationParameters(Components components, Optional customMediaType = extractCustomMediaType(functionDefinitionNode); OpenAPIRequestBodyMapper openAPIRequestBodyMapper = customMediaType.map( value -> new OpenAPIRequestBodyMapper(components, - operationAdaptor, semanticModel, value)).orElse(new OpenAPIRequestBodyMapper(components, - operationAdaptor, semanticModel)); + operationAdaptor, semanticModel, value, moduleMemberVisitor)).orElse( + new OpenAPIRequestBodyMapper(components, + operationAdaptor, semanticModel, moduleMemberVisitor)); openAPIRequestBodyMapper.handlePayloadAnnotation(requiredParameterNode, schema, annotation, apidocs); errors.addAll(openAPIRequestBodyMapper.getDiagnostics()); } else if ((annotation.annotReference().toString()).trim().equals(Constants.HTTP_PAYLOAD) && @@ -233,12 +242,13 @@ private List handleDefaultableAnnotationParameters(DefaultableParamet for (AnnotationNode annotation: annotations) { if ((annotation.annotReference().toString()).trim().equals(Constants.HTTP_HEADER)) { // Handle headers. - OpenAPIHeaderMapper openAPIHeaderMapper = new OpenAPIHeaderMapper(components, semanticModel, apidocs); + OpenAPIHeaderMapper openAPIHeaderMapper = new OpenAPIHeaderMapper(components, semanticModel, apidocs, + moduleMemberVisitor); parameters = openAPIHeaderMapper.setHeaderParameter(defaultableParameterNode); } else if ((annotation.annotReference().toString()).trim().equals(Constants.HTTP_QUERY)) { // Handle query parameter. OpenAPIQueryParameterMapper openAPIQueryParameterMapper = new OpenAPIQueryParameterMapper(apidocs, - components, semanticModel); + components, semanticModel, moduleMemberVisitor); parameters.add(openAPIQueryParameterMapper.createQueryParameter(defaultableParameterNode)); } } diff --git a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIQueryParameterMapper.java b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIQueryParameterMapper.java index 39275a4d9..bf0a53aa1 100644 --- a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIQueryParameterMapper.java +++ b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIQueryParameterMapper.java @@ -54,10 +54,10 @@ import static io.ballerina.compiler.syntax.tree.SyntaxKind.SIMPLE_NAME_REFERENCE; import static io.ballerina.compiler.syntax.tree.SyntaxKind.STRING_LITERAL; import static io.ballerina.openapi.converter.utils.ConverterCommonUtils.getAnnotationNodesFromServiceNode; +import static io.ballerina.openapi.converter.utils.ConverterCommonUtils.getOpenApiSchema; import static io.ballerina.openapi.converter.utils.ConverterCommonUtils.handleReference; import static io.ballerina.openapi.converter.utils.ConverterCommonUtils.unescapeIdentifier; - /** * This class processes mapping query parameters in between Ballerina and OAS. * @@ -67,14 +67,16 @@ public class OpenAPIQueryParameterMapper { private final Components components; private final SemanticModel semanticModel; private final Map apidocs; + private final ModuleMemberVisitor moduleMemberVisitor; private final SyntaxKind[] validExpressionKind = {STRING_LITERAL, NUMERIC_LITERAL, BOOLEAN_LITERAL, LIST_CONSTRUCTOR, NIL_LITERAL, MAPPING_CONSTRUCTOR}; public OpenAPIQueryParameterMapper(Map apidocs, Components components, - SemanticModel semanticModel) { + SemanticModel semanticModel, ModuleMemberVisitor moduleMemberVisitor) { this.apidocs = apidocs; this.components = components; this.semanticModel = semanticModel; + this.moduleMemberVisitor = moduleMemberVisitor; } /** @@ -87,7 +89,7 @@ public Parameter createQueryParameter(RequiredParameterNode queryParam) { if (queryParam.typeName() instanceof BuiltinSimpleNameReferenceNode && isQuery) { QueryParameter queryParameter = new QueryParameter(); queryParameter.setName(unescapeIdentifier(queryParamName)); - Schema openApiSchema = ConverterCommonUtils.getOpenApiSchema(queryParam.typeName().toString().trim()); + Schema openApiSchema = getOpenApiSchema(queryParam.typeName().toString().trim()); queryParameter.setSchema(openApiSchema); queryParameter.setRequired(true); if (!apidocs.isEmpty() && queryParam.paramName().isPresent() && apidocs.containsKey(queryParamName)) { @@ -115,7 +117,9 @@ public Parameter createQueryParameter(RequiredParameterNode queryParam) { QueryParameter queryParameter = new QueryParameter(); queryParameter.setName(unescapeIdentifier(queryParamName)); SimpleNameReferenceNode queryNode = (SimpleNameReferenceNode) queryParam.typeName(); - OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, queryNode); +// OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, queryNode); + OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, moduleMemberVisitor); + TypeSymbol typeSymbol = (TypeSymbol) semanticModel.symbol(queryNode).orElseThrow(); componentMapper.createComponentSchema(components.getSchemas(), typeSymbol); Schema schema = new Schema<>(); @@ -129,7 +133,7 @@ public Parameter createQueryParameter(RequiredParameterNode queryParam) { } else if (queryParam.typeName().kind() == SIMPLE_NAME_REFERENCE) { QueryParameter queryParameter = new QueryParameter(); Schema refSchema = handleReference(semanticModel, components, (SimpleNameReferenceNode) - queryParam.typeName()); + queryParam.typeName(), moduleMemberVisitor); queryParameter.setSchema(refSchema); queryParameter.setRequired(true); if (!apidocs.isEmpty() && apidocs.containsKey(queryParamName)) { @@ -157,7 +161,7 @@ public Parameter createQueryParameter(DefaultableParameterNode defaultableQueryP QueryParameter queryParameter = new QueryParameter(); if (defaultableQueryParam.typeName() instanceof BuiltinSimpleNameReferenceNode && isQuery) { queryParameter.setName(unescapeIdentifier(queryParamName)); - Schema openApiSchema = ConverterCommonUtils.getOpenApiSchema( + Schema openApiSchema = getOpenApiSchema( defaultableQueryParam.typeName().toString().trim()); queryParameter.setSchema(openApiSchema); if (!apidocs.isEmpty() && defaultableQueryParam.paramName().isPresent() && @@ -176,7 +180,7 @@ public Parameter createQueryParameter(DefaultableParameterNode defaultableQueryP } else if (defaultableQueryParam.typeName().kind() == SIMPLE_NAME_REFERENCE) { queryParameter.setName(unescapeIdentifier(queryParamName)); Schema refSchema = handleReference(semanticModel, components, - (SimpleNameReferenceNode) defaultableQueryParam.typeName()); + (SimpleNameReferenceNode) defaultableQueryParam.typeName(), moduleMemberVisitor); queryParameter.setSchema(refSchema); queryParameter.setRequired(true); if (!apidocs.isEmpty() && apidocs.containsKey(queryParamName)) { @@ -224,13 +228,13 @@ private QueryParameter handleArrayTypeQueryParameter(String queryParamName, Arra TypeDescriptorNode itemTypeNode = arrayNode.memberTypeDesc(); Schema itemSchema; if (arrayNode.memberTypeDesc().kind() == OPTIONAL_TYPE_DESC) { - itemSchema = ConverterCommonUtils.getOpenApiSchema( + itemSchema = getOpenApiSchema( ((OptionalTypeDescriptorNode) itemTypeNode).typeDescriptor().toString().trim()); itemSchema.setNullable(true); } else if (arrayNode.memberTypeDesc().kind() == SIMPLE_NAME_REFERENCE) { itemSchema = getItemSchemaForReference(arrayNode); } else { - itemSchema = ConverterCommonUtils.getOpenApiSchema(itemTypeNode.toString().trim()); + itemSchema = getOpenApiSchema(itemTypeNode.toString().trim()); } arraySchema.setItems(itemSchema); queryParameter.schema(arraySchema); @@ -243,7 +247,7 @@ private QueryParameter handleArrayTypeQueryParameter(String queryParamName, Arra private Schema getItemSchemaForReference(ArrayTypeDescriptorNode arrayNode) { SimpleNameReferenceNode record = (SimpleNameReferenceNode) arrayNode.memberTypeDesc(); - return handleReference(semanticModel, components, record); + return handleReference(semanticModel, components, record, moduleMemberVisitor); } /** @@ -267,7 +271,7 @@ private QueryParameter setOptionalQueryParameter(String queryParamName, Optional if (arrayNode.memberTypeDesc().kind() == SIMPLE_NAME_REFERENCE) { itemSchema = getItemSchemaForReference(arrayNode); } else { - itemSchema = ConverterCommonUtils.getOpenApiSchema(itemTypeNode.toString().trim()); + itemSchema = getOpenApiSchema(itemTypeNode.toString().trim()); } arraySchema.setItems(itemSchema); queryParameter.schema(arraySchema); @@ -286,7 +290,8 @@ private QueryParameter setOptionalQueryParameter(String queryParamName, Optional } return queryParameter; } else if (node.kind() == SIMPLE_NAME_REFERENCE) { - Schema refSchema = handleReference(semanticModel, components, (SimpleNameReferenceNode) node); + Schema refSchema = handleReference(semanticModel, components, (SimpleNameReferenceNode) node, + moduleMemberVisitor); queryParameter.setSchema(refSchema); if (isOptional.equals(Constants.FALSE)) { queryParameter.setRequired(true); @@ -296,7 +301,7 @@ private QueryParameter setOptionalQueryParameter(String queryParamName, Optional } return queryParameter; } else { - Schema openApiSchema = ConverterCommonUtils.getOpenApiSchema(node.toString().trim()); + Schema openApiSchema = getOpenApiSchema(node.toString().trim()); openApiSchema.setNullable(true); queryParameter.setSchema(openApiSchema); if (!apidocs.isEmpty() && apidocs.containsKey(queryParamName)) { diff --git a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIRequestBodyMapper.java b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIRequestBodyMapper.java index 201e23879..b2abe68e5 100644 --- a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIRequestBodyMapper.java +++ b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIRequestBodyMapper.java @@ -26,7 +26,6 @@ import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; import io.ballerina.compiler.syntax.tree.MappingFieldNode; import io.ballerina.compiler.syntax.tree.Node; -import io.ballerina.compiler.syntax.tree.NonTerminalNode; import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; import io.ballerina.compiler.syntax.tree.RequiredParameterNode; import io.ballerina.compiler.syntax.tree.SeparatedNodeList; @@ -73,7 +72,7 @@ public class OpenAPIRequestBodyMapper { private final SemanticModel semanticModel; private final String customMediaType; private final List diagnostics; - private NonTerminalNode rootNode; + private final ModuleMemberVisitor moduleMemberVisitor; /** * This constructor uses to create OpenAPIRequestBodyMapper instance when customMedia type enable. @@ -84,12 +83,14 @@ public class OpenAPIRequestBodyMapper { * @param customMediaType - custom media type */ public OpenAPIRequestBodyMapper(Components components, OperationAdaptor operationAdaptor, - SemanticModel semanticModel, String customMediaType) { + SemanticModel semanticModel, String customMediaType, + ModuleMemberVisitor moduleMemberVisitor) { this.components = components; this.operationAdaptor = operationAdaptor; this.semanticModel = semanticModel; this.customMediaType = customMediaType; this.diagnostics = new ArrayList<>(); + this.moduleMemberVisitor = moduleMemberVisitor; } /** @@ -100,8 +101,8 @@ public OpenAPIRequestBodyMapper(Components components, OperationAdaptor operatio * @param semanticModel - Semantic model for given ballerina service */ public OpenAPIRequestBodyMapper(Components components, OperationAdaptor operationAdaptor, - SemanticModel semanticModel) { - this(components, operationAdaptor, semanticModel, null); + SemanticModel semanticModel, ModuleMemberVisitor moduleMemberVisitor) { + this(components, operationAdaptor, semanticModel, null, moduleMemberVisitor); } public List getDiagnostics() { @@ -117,7 +118,7 @@ public List getDiagnostics() { */ public void handlePayloadAnnotation(RequiredParameterNode payloadNode, Map schema, AnnotationNode annotation, Map apiDocs) { - rootNode = payloadNode; +// rootNode = payloadNode; if ((annotation.annotReference().toString()).trim().equals(Constants.HTTP_PAYLOAD)) { // Creating request body - required. RequestBody bodyParameter = new RequestBody(); @@ -230,7 +231,8 @@ private void handleArrayTypePayload(Map schema, ArrayTypeDescrip //handle record for components SimpleNameReferenceNode referenceNode = (SimpleNameReferenceNode) typeDescriptorNode; TypeSymbol typeSymbol = getReferenceTypeSymbol(semanticModel.symbol(referenceNode)); - OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, referenceNode); +// OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, referenceNode); + OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, moduleMemberVisitor); componentMapper.createComponentSchema(schema, typeSymbol); diagnostics.addAll(componentMapper.getDiagnostics()); Schema itemSchema = new Schema(); @@ -285,7 +287,8 @@ private void createRequestBody(RequestBody bodyParameter, RequiredParameterNode private void handleReferencePayload(TypeSymbol typeSymbol, String recordName, Map schema, String mediaType, RequestBody bodyParameter) { //handle record for components - OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, rootNode); +// OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, rootNode); + OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, moduleMemberVisitor); componentMapper.createComponentSchema(schema, typeSymbol); diagnostics.addAll(componentMapper.getDiagnostics()); io.swagger.v3.oas.models.media.MediaType media = new io.swagger.v3.oas.models.media.MediaType(); diff --git a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIResourceMapper.java b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIResourceMapper.java index ae33ba95a..8153cb89a 100644 --- a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIResourceMapper.java +++ b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIResourceMapper.java @@ -58,6 +58,7 @@ */ public class OpenAPIResourceMapper { private final SemanticModel semanticModel; + private final ModuleMemberVisitor moduleMemberVisitor; private final Paths pathObject = new Paths(); private final Components components = new Components(); private final List errors; @@ -69,9 +70,10 @@ public List getErrors() { /** * Initializes a resource parser for openApi. */ - OpenAPIResourceMapper(SemanticModel semanticModel) { + OpenAPIResourceMapper(SemanticModel semanticModel, ModuleMemberVisitor moduleMemberVisitor) { this.semanticModel = semanticModel; this.errors = new ArrayList<>(); + this.moduleMemberVisitor = moduleMemberVisitor; } public Components getComponents() { @@ -211,7 +213,7 @@ private Optional convertResourceToOperation(FunctionDefinition Map apiDocs = listAPIDocumentations(resource, op); //Add path parameters if in path and query parameters OpenAPIParameterMapper openAPIParameterMapper = new OpenAPIParameterMapper(resource, op, apiDocs, components, - semanticModel); + semanticModel, moduleMemberVisitor); openAPIParameterMapper.getResourceInputs(components, semanticModel); if (openAPIParameterMapper.getErrors().size() > 1 || (openAPIParameterMapper.getErrors().size() == 1 && !openAPIParameterMapper.getErrors().get(0).getCode().equals("OAS_CONVERTOR_113"))) { @@ -221,7 +223,7 @@ private Optional convertResourceToOperation(FunctionDefinition errors.addAll(openAPIParameterMapper.getErrors()); OpenAPIResponseMapper openAPIResponseMapper = new OpenAPIResponseMapper(semanticModel, components, - resource.location()); + resource.location(), moduleMemberVisitor); openAPIResponseMapper.getResourceOutput(resource, op); if (!openAPIResponseMapper.getErrors().isEmpty()) { errors.addAll(openAPIResponseMapper.getErrors()); diff --git a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIResponseMapper.java b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIResponseMapper.java index 9fdc895e4..858992408 100644 --- a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIResponseMapper.java +++ b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIResponseMapper.java @@ -152,15 +152,18 @@ public class OpenAPIResponseMapper { private final List errors = new ArrayList<>(); private final Location location; private String httpMethod; + private ModuleMemberVisitor moduleMemberVisitor; public List getErrors() { return errors; } - public OpenAPIResponseMapper(SemanticModel semanticModel, Components components, Location location) { + public OpenAPIResponseMapper(SemanticModel semanticModel, Components components, Location location, + ModuleMemberVisitor moduleMemberVisitor) { this.semanticModel = semanticModel; this.components = components; this.location = location; + this.moduleMemberVisitor = moduleMemberVisitor; } /** @@ -585,8 +588,9 @@ private Optional handleQualifiedNameType(ApiResponses apiResponses if (typeSymbol.typeKind() == TypeDescKind.RECORD) { ApiResponses responses = handleRecordTypeSymbol(qNode.identifier().text().trim(), components.getSchemas(), customMediaPrefix, typeRef, - new OpenAPIComponentMapper(components, qNode), + new OpenAPIComponentMapper(components, moduleMemberVisitor), headers); + //todo: qNode apiResponses.putAll(responses); return Optional.of(apiResponses); } @@ -882,7 +886,8 @@ private void handleReferenceResponse(OperationAdaptor operationAdaptor, SimpleNa Optional symbol = semanticModel.symbol(referenceNode); TypeSymbol typeSymbol = (TypeSymbol) symbol.orElseThrow(); //handle record for components - OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, referenceNode); +// OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, referenceNode); + OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, moduleMemberVisitor); String mediaTypeString; // Check typeInclusion is related to the http status code if (referenceNode.parent().kind().equals(ARRAY_TYPE_DESC)) { diff --git a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIServiceMapper.java b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIServiceMapper.java index a7fc0d9aa..a7d7cbf62 100644 --- a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIServiceMapper.java +++ b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIServiceMapper.java @@ -39,6 +39,7 @@ */ public class OpenAPIServiceMapper { private final SemanticModel semanticModel; + private final ModuleMemberVisitor moduleMemberVisitor; private final List errors = new ArrayList<>(); public List getErrors() { @@ -48,9 +49,10 @@ public List getErrors() { /** * Initializes a service parser for OpenApi. */ - public OpenAPIServiceMapper(SemanticModel semanticModel) { + public OpenAPIServiceMapper(SemanticModel semanticModel, ModuleMemberVisitor moduleMemberVisitor) { // Default object mapper is JSON mapper available in openApi utils. this.semanticModel = semanticModel; + this.moduleMemberVisitor = moduleMemberVisitor; } /** @@ -69,7 +71,7 @@ public OpenAPI convertServiceToOpenAPI(ServiceDeclarationNode service, OpenAPI o resource.add((FunctionDefinitionNode) function); } } - OpenAPIResourceMapper resourceMapper = new OpenAPIResourceMapper(this.semanticModel); + OpenAPIResourceMapper resourceMapper = new OpenAPIResourceMapper(this.semanticModel, this.moduleMemberVisitor); openapi.setPaths(resourceMapper.getPaths(resource)); openapi.setComponents(resourceMapper.getComponents()); errors.addAll(resourceMapper.getErrors()); diff --git a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/utils/ConverterCommonUtils.java b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/utils/ConverterCommonUtils.java index c9f9b032f..3e5a74836 100644 --- a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/utils/ConverterCommonUtils.java +++ b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/utils/ConverterCommonUtils.java @@ -52,6 +52,7 @@ import io.ballerina.openapi.converter.diagnostic.ExceptionDiagnostic; import io.ballerina.openapi.converter.diagnostic.OpenAPIConverterDiagnostic; import io.ballerina.openapi.converter.model.OASResult; +import io.ballerina.openapi.converter.service.ModuleMemberVisitor; import io.ballerina.openapi.converter.service.OpenAPIComponentMapper; import io.ballerina.runtime.api.utils.IdentifierUtils; import io.ballerina.tools.diagnostics.Diagnostic; @@ -527,13 +528,13 @@ public static String unescapeIdentifier(String parameterName) { } public static Schema handleReference(SemanticModel semanticModel, Components components, - SimpleNameReferenceNode record) { + SimpleNameReferenceNode record, ModuleMemberVisitor moduleMemberVisitor) { Schema refSchema = new Schema<>(); // Creating request body - required. Optional symbol = semanticModel.symbol(record); if (symbol.isPresent() && symbol.get() instanceof TypeSymbol) { String recordName = record.name().toString().trim(); - OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components); + OpenAPIComponentMapper componentMapper = new OpenAPIComponentMapper(components, moduleMemberVisitor); componentMapper.createComponentSchema(components.getSchemas(), (TypeSymbol) symbol.get()); refSchema.set$ref(ConverterCommonUtils.unescapeIdentifier(recordName)); } diff --git a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/utils/ServiceToOpenAPIConverterUtils.java b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/utils/ServiceToOpenAPIConverterUtils.java index 47bbf5768..77c9a0bf9 100644 --- a/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/utils/ServiceToOpenAPIConverterUtils.java +++ b/openapi-bal-service/src/main/java/io/ballerina/openapi/converter/utils/ServiceToOpenAPIConverterUtils.java @@ -203,7 +203,8 @@ private static void extractServiceNodes(String serviceName, List availab */ public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) { ServiceDeclarationNode serviceDefinition = oasGenerationMetaInfo.getServiceDeclarationNode(); - LinkedHashSet listeners = collectListeners(oasGenerationMetaInfo.getProject());; + ModuleMemberVisitor moduleMemberVisitor = extractNodesFromProject(oasGenerationMetaInfo.getProject()); + LinkedHashSet listeners = moduleMemberVisitor.getListenerDeclarationNodes(); SemanticModel semanticModel = oasGenerationMetaInfo.getSemanticModel(); String openApiFileName = oasGenerationMetaInfo.getOpenApiFileName(); Path ballerinaFilePath = oasGenerationMetaInfo.getBallerinaFilePath(); @@ -214,7 +215,8 @@ public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) OpenAPI openapi = oasResult.getOpenAPI().get(); if (openapi.getPaths() == null) { // Take base path of service - OpenAPIServiceMapper openAPIServiceMapper = new OpenAPIServiceMapper(semanticModel); + OpenAPIServiceMapper openAPIServiceMapper = new OpenAPIServiceMapper(semanticModel, + moduleMemberVisitor); // 02. Filter and set the ServerURLs according to endpoints. Complete the server section in OAS openapi = OpenAPIEndpointMapper.ENDPOINT_MAPPER.getServers(openapi, listeners, serviceDefinition); // 03. Filter path and component sections in OAS. @@ -489,17 +491,15 @@ private static OASResult resolveContractPath(List di * * @param project - current project */ - public static LinkedHashSet collectListeners(Project project) { + public static ModuleMemberVisitor extractNodesFromProject(Project project) { ModuleMemberVisitor balNodeVisitor = new ModuleMemberVisitor(); - LinkedHashSet listeners = new LinkedHashSet<>(); project.currentPackage().moduleIds().forEach(moduleId -> { Module module = project.currentPackage().module(moduleId); module.documentIds().forEach(documentId -> { SyntaxTree syntaxTreeDoc = module.document(documentId).syntaxTree(); syntaxTreeDoc.rootNode().accept(balNodeVisitor); - listeners.addAll(balNodeVisitor.getListenerDeclarationNodes()); }); }); - return listeners; + return balNodeVisitor; } } diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/ModuleReferenceTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/ModuleReferenceTests.java index ede75ddcc..91b299a12 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/ModuleReferenceTests.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/ModuleReferenceTests.java @@ -73,7 +73,7 @@ public void testRecordReferenceWithReadOnly() throws IOException { TestUtils.compareWithGeneratedFile(ballerinaFilePath, "readonly.yaml"); } - @Test (enabled = false) + @Test () public void testListenersInSeparateModule() throws IOException { Path ballerinaFilePath = RES_DIR.resolve("listeners_in_separate_module.bal"); String osName = System.getProperty("os.name"); @@ -82,7 +82,7 @@ public void testListenersInSeparateModule() throws IOException { TestUtils.compareWithGeneratedFile(ballerinaFilePath, yamlFile); } - @Test (enabled = false) + @Test () public void testListenersInSeparateFiles() throws IOException { Path ballerinaFilePath = RES_DIR.resolve("listeners_in_separate_file.bal"); String osName = System.getProperty("os.name"); diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/arrayTypeResponse.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/arrayTypeResponse.yaml index fbbb9956a..f0df4278f 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/arrayTypeResponse.yaml +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/arrayTypeResponse.yaml @@ -51,6 +51,7 @@ components: id: type: string name: + maxLength: 14 type: string description: type: string diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/constraint/array.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/constraint/array.yaml index b9b20e03b..25987bcad 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/constraint/array.yaml +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/constraint/array.yaml @@ -20,22 +20,44 @@ paths: $ref: '#/components/schemas/Person' responses: "500": - description: Found unexpected output + description: Internal server error content: - text/plain: + application/json: schema: - type: string + $ref: '#/components/schemas/ErrorPayload' components: schemas: PersonDetailsItemsString: minLength: 7 type: string + ErrorPayload: + type: object + properties: + reason: + type: string + description: Reason phrase + path: + type: string + description: Request path + method: + type: string + description: Method type of the request + message: + type: string + description: Error message + timestamp: + type: string + description: Timestamp of the error + status: + type: integer + description: Relevant HTTP status code + format: int32 PersonFeeItemsNumber: maximum: 445.4 type: number format: float PersonLimitItemsInteger: - maximum: 67 + maximum: 67.0 type: integer format: int32 Hobby: diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/constraint/record_field.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/constraint/record_field.yaml index 25ffb4b6e..64786f551 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/constraint/record_field.yaml +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/constraint/record_field.yaml @@ -20,13 +20,35 @@ paths: $ref: '#/components/schemas/Person' responses: "500": - description: Found unexpected output + description: Internal server error content: - text/plain: + application/json: schema: - type: string + $ref: '#/components/schemas/ErrorPayload' components: schemas: + ErrorPayload: + type: object + properties: + reason: + type: string + description: Reason phrase + path: + type: string + description: Request path + method: + type: string + description: Method type of the request + message: + type: string + description: Error message + timestamp: + type: string + description: Timestamp of the error + status: + type: integer + description: Relevant HTTP status code + format: int32 Address: minLength: 5 type: string