diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ModuleMemberVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ModuleMemberVisitor.java index 714e29424..d621c24d4 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ModuleMemberVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ModuleMemberVisitor.java @@ -19,7 +19,6 @@ import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.syntax.tree.ClassDefinitionNode; -import io.ballerina.compiler.syntax.tree.ConstantDeclarationNode; import io.ballerina.compiler.syntax.tree.ListenerDeclarationNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeVisitor; @@ -45,7 +44,6 @@ public class ModuleMemberVisitor extends NodeVisitor { Set listenerDeclarationNodes = new LinkedHashSet<>(); Set interceptorServiceClassNodes = new LinkedHashSet<>(); Set serviceContractTypeNodes = new LinkedHashSet<>(); - Set constantDeclarationNodes = new LinkedHashSet<>(); SemanticModel semanticModel; public ModuleMemberVisitor(SemanticModel semanticModel) { @@ -72,11 +70,6 @@ public void visit(ClassDefinitionNode classDefinitionNode) { interceptorServiceClassNodes.add(classDefinitionNode); } - @Override - public void visit(ConstantDeclarationNode constantDeclarationNode) { - constantDeclarationNodes.add(constantDeclarationNode); - } - public Set getListenerDeclarationNodes() { return listenerDeclarationNodes; } @@ -99,16 +92,6 @@ public Optional getInterceptorServiceClassNode(String typeN return Optional.empty(); } - public Optional getConstantDeclarationNode(String constantName) { - for (ConstantDeclarationNode constantDeclarationNode : constantDeclarationNodes) { - if (MapperCommonUtils.unescapeIdentifier(constantDeclarationNode.variableName() - .text()).equals(constantName)) { - return Optional.of(constantDeclarationNode); - } - } - return Optional.empty(); - } - public Set getServiceContractTypeNodes() { return serviceContractTypeNodes; } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/AbstractParameterMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/AbstractParameterMapper.java index 537204e49..cb3f690e9 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/AbstractParameterMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/AbstractParameterMapper.java @@ -18,19 +18,19 @@ package io.ballerina.openapi.service.mapper.parameter; import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.ConstantSymbol; +import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.api.symbols.TypeSymbol; +import io.ballerina.compiler.api.values.ConstantValue; import io.ballerina.compiler.syntax.tree.DefaultableParameterNode; import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; -import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; import io.ballerina.openapi.service.mapper.model.OperationInventory; import io.ballerina.openapi.service.mapper.utils.MapperCommonUtils; import io.swagger.v3.oas.models.parameters.Parameter; import java.util.List; import java.util.Objects; - -import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getExpressionNodeForConstantDeclaration; +import java.util.Optional; /** * This {@link AbstractParameterMapper} class represents the abstract parameter mapper. @@ -59,12 +59,14 @@ public void setParameter() throws ParameterMapperException { parameterList.forEach(operationInventory::setParameter); } - static Object getDefaultValue(DefaultableParameterNode parameterNode, ModuleMemberVisitor moduleMemberVisitor) { + static Object getDefaultValue(DefaultableParameterNode parameterNode, SemanticModel semanticModel) { ExpressionNode defaultValueExpression = (ExpressionNode) parameterNode.expression(); - if (defaultValueExpression instanceof SimpleNameReferenceNode reference) { - defaultValueExpression = getExpressionNodeForConstantDeclaration( - moduleMemberVisitor, - defaultValueExpression, reference); + Optional symbol = semanticModel.symbol(defaultValueExpression); + if (symbol.isPresent() && symbol.get() instanceof ConstantSymbol constantSymbol) { + Object constValue = constantSymbol.constValue(); + if (constValue instanceof ConstantValue value) { + return value.value(); + } } if (MapperCommonUtils.isNotSimpleValueLiteralKind(defaultValueExpression.kind())) { return null; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HeaderParameterMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HeaderParameterMapper.java index 61c1b2deb..6763564f0 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HeaderParameterMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HeaderParameterMapper.java @@ -74,7 +74,7 @@ public HeaderParameterMapper(ParameterNode parameterNode, Map ap this.treatNilableAsOptional = treatNilableAsOptional; if (parameterNode instanceof DefaultableParameterNode defaultableHeaderParam) { this.defaultValue = AbstractParameterMapper.getDefaultValue(defaultableHeaderParam, - additionalData.moduleMemberVisitor()); + additionalData.semanticModel()); } this.typeMapper = typeMapper; } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/QueryParameterMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/QueryParameterMapper.java index a8dc4eb2e..6369f4090 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/QueryParameterMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/QueryParameterMapper.java @@ -70,7 +70,7 @@ public QueryParameterMapper(ParameterNode parameterNode, Map api this.typeMapper = typeMapper; if (parameterNode instanceof DefaultableParameterNode defaultableQueryParam) { this.defaultValue = AbstractParameterMapper.getDefaultValue(defaultableQueryParam, - additionalData.moduleMemberVisitor()); + additionalData.semanticModel()); } } } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/type/RecordTypeMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/type/RecordTypeMapper.java index 55dcd30e9..3ca5a124e 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/type/RecordTypeMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/type/RecordTypeMapper.java @@ -17,19 +17,22 @@ */ package io.ballerina.openapi.service.mapper.type; +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.ConstantSymbol; import io.ballerina.compiler.api.symbols.IntersectionTypeSymbol; import io.ballerina.compiler.api.symbols.RecordFieldSymbol; import io.ballerina.compiler.api.symbols.RecordTypeSymbol; +import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.api.symbols.TypeDescKind; import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; import io.ballerina.compiler.api.symbols.TypeSymbol; import io.ballerina.compiler.api.symbols.UnionTypeSymbol; +import io.ballerina.compiler.api.values.ConstantValue; import io.ballerina.compiler.syntax.tree.ExpressionNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.RecordFieldWithDefaultValueNode; import io.ballerina.compiler.syntax.tree.RecordTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; import io.ballerina.compiler.syntax.tree.TypeDefinitionNode; import io.ballerina.openapi.service.mapper.diagnostic.DiagnosticMessages; import io.ballerina.openapi.service.mapper.diagnostic.ExceptionDiagnostic; @@ -49,7 +52,6 @@ import java.util.Optional; import java.util.Set; -import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getExpressionNodeForConstantDeclaration; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getRecordFieldTypeDescription; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getTypeName; @@ -122,17 +124,12 @@ static List mapIncludedRecords(RecordTypeSymbol typeSymbol, Components c .typeDescriptor(); Map includedRecordFieldMap = includedRecordTypeSymbol.fieldDescriptors(); for (Map.Entry includedRecordField : includedRecordFieldMap.entrySet()) { - //logic comes here i guess RecordFieldSymbol recordFieldSymbol = recordFieldMap.get(includedRecordField.getKey()); RecordFieldSymbol includedRecordFieldValue = includedRecordField.getValue(); - if (recordFieldSymbol != null) { - //check for the types - if (includedRecordFieldValue.typeDescriptor().equals(recordFieldSymbol.typeDescriptor())) { - // check for the default values availability - if (!recordFieldSymbol.hasDefaultValue()) { - recordFieldMap.remove(includedRecordField.getKey()); - } - } + boolean isRemovableField = recordFieldSymbol != null && includedRecordFieldValue.typeDescriptor() + .equals(recordFieldSymbol.typeDescriptor()) && !recordFieldSymbol.hasDefaultValue(); + if (isRemovableField) { + recordFieldMap.remove(includedRecordField.getKey()); } } } @@ -160,7 +157,7 @@ public static Map mapRecordFields(Map } if (recordFieldSymbol.hasDefaultValue()) { Optional recordFieldDefaultValueOpt = getRecordFieldDefaultValue(recordName, recordFieldName, - additionalData.moduleMemberVisitor()); + additionalData.moduleMemberVisitor(), additionalData.semanticModel()); if (recordFieldDefaultValueOpt.isPresent()) { TypeMapper.setDefaultValue(recordFieldSchema, recordFieldDefaultValueOpt.get()); } else { @@ -175,18 +172,19 @@ public static Map mapRecordFields(Map } public static Optional getRecordFieldDefaultValue(String recordName, String fieldName, - ModuleMemberVisitor moduleMemberVisitor) { + ModuleMemberVisitor moduleMemberVisitor, + SemanticModel semanticModel) { Optional recordDefNodeOpt = moduleMemberVisitor.getTypeDefinitionNode(recordName); if (recordDefNodeOpt.isPresent() && recordDefNodeOpt.get().typeDescriptor() instanceof RecordTypeDescriptorNode recordDefNode) { - return getRecordFieldDefaultValue(fieldName, recordDefNode, moduleMemberVisitor); + return getRecordFieldDefaultValue(fieldName, recordDefNode, semanticModel); } return Optional.empty(); } private static Optional getRecordFieldDefaultValue(String fieldName, RecordTypeDescriptorNode recordDefNode, - ModuleMemberVisitor moduleMemberVisitor) { + SemanticModel semanticModel) { NodeList recordFields = recordDefNode.fields(); RecordFieldWithDefaultValueNode defaultValueNode = recordFields.stream() .filter(field -> field instanceof RecordFieldWithDefaultValueNode) @@ -197,10 +195,14 @@ private static Optional getRecordFieldDefaultValue(String fieldName, return Optional.empty(); } ExpressionNode defaultValueExpression = defaultValueNode.expression(); - // ConstantDeclaration - if (defaultValueExpression instanceof SimpleNameReferenceNode reference) { - defaultValueExpression = getExpressionNodeForConstantDeclaration(moduleMemberVisitor, - defaultValueExpression, reference); + Optional symbol = semanticModel.symbol(defaultValueExpression); + if (symbol.isPresent()) { + if (symbol.get() instanceof ConstantSymbol constantSymbol) { + Object constValue = constantSymbol.constValue(); + if (constValue instanceof ConstantValue value) { + return Optional.of(value.value()); + } + } } if (MapperCommonUtils.isNotSimpleValueLiteralKind(defaultValueExpression.kind())) { return Optional.empty(); diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/utils/MapperCommonUtils.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/utils/MapperCommonUtils.java index e1358b9fc..808f48354 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/utils/MapperCommonUtils.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/utils/MapperCommonUtils.java @@ -36,7 +36,6 @@ import io.ballerina.compiler.api.symbols.UnionTypeSymbol; import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.BasicLiteralNode; -import io.ballerina.compiler.syntax.tree.ConstantDeclarationNode; import io.ballerina.compiler.syntax.tree.DefaultableParameterNode; import io.ballerina.compiler.syntax.tree.DistinctTypeDescriptorNode; import io.ballerina.compiler.syntax.tree.ExpressionNode; @@ -55,7 +54,6 @@ import io.ballerina.compiler.syntax.tree.ResourcePathParameterNode; import io.ballerina.compiler.syntax.tree.SeparatedNodeList; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; -import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; import io.ballerina.compiler.syntax.tree.SpecificFieldNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.compiler.syntax.tree.TypeDefinitionNode; @@ -63,7 +61,6 @@ import io.ballerina.openapi.service.mapper.diagnostic.DiagnosticMessages; import io.ballerina.openapi.service.mapper.diagnostic.ExceptionDiagnostic; import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; -import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; import io.ballerina.openapi.service.mapper.model.OASResult; import io.ballerina.openapi.service.mapper.model.ResourceFunction; import io.ballerina.openapi.service.mapper.model.ResourceFunctionDeclaration; @@ -565,16 +562,4 @@ public static Node getTypeDescriptor(TypeDefinitionNode typeDefinitionNode) { } return node; } - - public static ExpressionNode getExpressionNodeForConstantDeclaration(ModuleMemberVisitor moduleMemberVisitor, - ExpressionNode defaultValueExpression, - SimpleNameReferenceNode reference) { - Optional constantDeclarationNode = moduleMemberVisitor - .getConstantDeclarationNode(reference.name().text()); - if (constantDeclarationNode.isPresent()) { - ConstantDeclarationNode constantNode = constantDeclarationNode.get(); - defaultValueExpression = (ExpressionNode) constantNode.initializer(); - } - return defaultValueExpression; - } } diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/RecordTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/RecordTests.java index b08d12be8..096634ffa 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/RecordTests.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/RecordTests.java @@ -125,6 +125,12 @@ public void testRestFieldInRecord() throws IOException { TestUtils.compareWithGeneratedFile(ballerinaFilePath, "record/record_rest_param.yaml"); } + @Test(description = "Test for record has included record with same fields") + public void testIncludedRecordWithSameFields() throws IOException { + Path ballerinaFilePath = RES_DIR.resolve("record/included_record.bal"); + TestUtils.compareWithGeneratedFile(ballerinaFilePath, "record/included_record.yaml"); + } + @AfterMethod public void cleanUp() { TestUtils.deleteDirectory(this.tempDir); diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/header_scenario13.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/header_scenario13.yaml index 9310ac79d..87d2d8702 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/header_scenario13.yaml +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/header_scenario13.yaml @@ -141,9 +141,9 @@ paths: default: Pod - name: header31 in: header - required: true schema: type: string + default: Pod - name: header32 in: header required: true @@ -152,6 +152,10 @@ paths: items: type: integer format: int64 + default: + - 1 + - 2 + - 3 responses: "200": description: Ok @@ -164,7 +168,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ErrorPayload' + $ref: "#/components/schemas/ErrorPayload" components: schemas: ErrorPayload: diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/parameter_annotation/annotated_query.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/parameter_annotation/annotated_query.yaml index 1955a44df..f8c1dca86 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/parameter_annotation/annotated_query.yaml +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/parameter_annotation/annotated_query.yaml @@ -33,7 +33,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ErrorPayload' + $ref: "#/components/schemas/ErrorPayload" /student9: post: operationId: postStudent9 @@ -52,13 +52,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Student' + $ref: "#/components/schemas/Student" "400": description: BadRequest content: application/json: schema: - $ref: '#/components/schemas/ErrorPayload' + $ref: "#/components/schemas/ErrorPayload" /student10: post: operationId: postStudent10 @@ -67,20 +67,20 @@ paths: in: query required: true schema: - $ref: '#/components/schemas/Status' + $ref: "#/components/schemas/Status" responses: "201": description: Created content: application/json: schema: - $ref: '#/components/schemas/Student' + $ref: "#/components/schemas/Student" "400": description: BadRequest content: application/json: schema: - $ref: '#/components/schemas/ErrorPayload' + $ref: "#/components/schemas/ErrorPayload" /student11: post: operationId: postStudent11 @@ -89,7 +89,7 @@ paths: in: query schema: allOf: - - $ref: '#/components/schemas/Status' + - $ref: "#/components/schemas/Status" default: ACTIVE responses: "201": @@ -103,7 +103,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ErrorPayload' + $ref: "#/components/schemas/ErrorPayload" /student12: post: operationId: postStudent12 @@ -125,7 +125,30 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ErrorPayload' + $ref: "#/components/schemas/ErrorPayload" + /student13: + post: + operationId: postStudent13 + parameters: + - name: kind + in: query + schema: + allOf: + - $ref: "#/components/schemas/ResourceKind" + default: Service + responses: + "201": + description: Created + content: + application/json: + schema: + type: object + "400": + description: BadRequest + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorPayload" components: schemas: ErrorPayload: @@ -151,6 +174,11 @@ components: type: string method: type: string + ResourceKind: + type: string + enum: + - Service + - Pod Status: type: string enum: diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/record/included_record.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/record/included_record.yaml new file mode 100644 index 000000000..d7805374f --- /dev/null +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/record/included_record.yaml @@ -0,0 +1,133 @@ +openapi: 3.0.1 +info: + title: PayloadV + version: 0.0.0 +servers: + - url: "{server}:{port}/payloadV" + variables: + server: + default: http://localhost + port: + default: "7080" +paths: + /Pods: + get: + operationId: getPods + responses: + "200": + description: Ok + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Pod" + /Services: + get: + operationId: getServices + responses: + "200": + description: Ok + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Service" +components: + schemas: + Metadata: + required: + - name + type: object + properties: + name: + type: string + displayName: + type: string + description: + type: string + Pod: + type: object + allOf: + - $ref: "#/components/schemas/Resource" + - required: + - spec + type: object + properties: + kind: + allOf: + - $ref: "#/components/schemas/ResourceKind" + default: Pod + spec: + $ref: "#/components/schemas/PodSpec" + PodSpec: + required: + - nodeName + type: object + properties: + nodeName: + type: string + Resource: + type: object + allOf: + - $ref: "#/components/schemas/ResourceBase" + - required: + - spec + type: object + properties: + spec: + type: object + properties: {} + status: + $ref: "#/components/schemas/Status" + ResourceBase: + required: + - group + - kind + - metadata + - version + type: object + properties: + group: + type: string + version: + type: string + kind: + $ref: "#/components/schemas/ResourceKind" + metadata: + $ref: "#/components/schemas/Metadata" + ResourceKind: + type: string + enum: + - Service + - Pod + Service: + type: object + allOf: + - $ref: "#/components/schemas/Resource" + - required: + - spec + type: object + properties: + kind: + allOf: + - $ref: "#/components/schemas/ResourceKind" + default: Service + spec: + $ref: "#/components/schemas/ServiceSpec" + ServiceSpec: + required: + - clusterIP + type: object + properties: + clusterIP: + type: string + Status: + required: + - observedGeneration + type: object + properties: + observedGeneration: + type: integer + format: int64 diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/headers/header_scenario13.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/headers/header_scenario13.bal index 963f82323..0532e54f3 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/headers/header_scenario13.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/headers/header_scenario13.bal @@ -22,7 +22,7 @@ type Header2 record {| |}; type Header3 record {| - string header31; + string header31 = RESOURCE_KIND_POD; int[] header32; |}; @@ -36,7 +36,7 @@ service /payloadV on helloEp { @http:Header Record h6 = {name: "John", city: "London"}, @http:Header Header2 h7= {header21: "header1", header22: [1,2,3], header24: false, header23: []}, @http:Header string h8 = RESOURCE_KIND_POD, - @http:Header Header3 h7= {header31: RESOURCE_KIND_POD, header32: [1,2,3]},) returns string { + @http:Header Header3 h9= {header32: [1,2,3]}) returns string { return "new"; } } diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/parameter_annotation/annotated_query.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/parameter_annotation/annotated_query.bal index fe130557c..203496cf8 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/parameter_annotation/annotated_query.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/parameter_annotation/annotated_query.bal @@ -1,7 +1,11 @@ import ballerina/http; listener http:Listener ep0 = new (443, config = {host: "petstore3.swagger.io"}); + public const RESOURCE_KIND_SERVICE = "Service"; +public const RESOURCE_KIND_POD = "Pod"; + +public type ResourceKind RESOURCE_KIND_SERVICE|RESOURCE_KIND_POD; type Student record { string Name; @@ -33,4 +37,8 @@ service /payloadV on ep0 { resource function post student12(@http:Query string status = RESOURCE_KIND_SERVICE) returns json { return {Name: "john", Status: status}; } + + resource function post student13(@http:Query ResourceKind kind = RESOURCE_KIND_SERVICE) returns json { + return {Name: "john"}; + } } diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/record/included_record.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/record/included_record.bal new file mode 100644 index 000000000..22729fb7c --- /dev/null +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/record/included_record.bal @@ -0,0 +1,61 @@ +import ballerina/http; + +public const RESOURCE_KIND_SERVICE = "Service"; +public const RESOURCE_KIND_POD = "Pod"; + +public type ResourceKind RESOURCE_KIND_SERVICE|RESOURCE_KIND_POD; + +public type Metadata record { + string name; + string displayName?; + string description?; +}; + +public type Status record { + int observedGeneration; +}; + +public type ResourceBase record { + string group; + string version; + ResourceKind kind; + Metadata metadata; +}; + +public type Resource record { + *ResourceBase; + record {} spec; + Status status?; +}; + +public type ServiceSpec record { + string clusterIP; +}; + +public type Service record { + *Resource; + ResourceKind kind = RESOURCE_KIND_SERVICE; + ServiceSpec spec; + Status status?; +}; + +public type PodSpec record { + string nodeName; +}; + +public type Pod record { + *Resource; + ResourceKind kind = RESOURCE_KIND_POD; + PodSpec spec; + Status status?; +}; + +service /payloadV on new http:Listener(7080) { + resource function get Pods() returns Pod[] { + return []; + } + + resource function get Services() returns Service[] { + return []; + } +} diff --git a/openapi-integration-tests/src/test/java/io/ballerina/openapi/cmd/BallerinaToOpenAPITests.java b/openapi-integration-tests/src/test/java/io/ballerina/openapi/cmd/BallerinaToOpenAPITests.java index 83082d548..239f569b5 100644 --- a/openapi-integration-tests/src/test/java/io/ballerina/openapi/cmd/BallerinaToOpenAPITests.java +++ b/openapi-integration-tests/src/test/java/io/ballerina/openapi/cmd/BallerinaToOpenAPITests.java @@ -178,6 +178,30 @@ public void openAPIGenWithBalExt() throws IOException, InterruptedException { "project_openapi_bal_ext/result_1.yaml", true); } + @Test(description = "Map included record type which has same fields") + public void includedRecordTest() throws IOException, InterruptedException { + // 1. generate OAS + executeCommand("project_openapi_with_included_test/service_file.bal", + "payloadV_openapi.yaml", + "project_openapi_with_included_test/result.yaml", false); + // 2. generate type using the OAS which generated point 1 + generateTypes(); + } + + private void generateTypes() throws IOException, InterruptedException { + List buildArgs = new LinkedList<>(); + buildArgs.add("-i"); + buildArgs.add(String.valueOf(TEST_RESOURCE.resolve("payloadV_openapi.yaml"))); + buildArgs.add("-o"); + buildArgs.add(tmpDir.toString()); + boolean successful = TestUtil.executeOpenAPI(DISTRIBUTION_FILE_NAME, TEST_RESOURCE, buildArgs); + Assert.assertTrue(Files.exists(Paths.get(tmpDir.toString()).resolve("types.bal"))); + String generatedOpenAPI = getStringFromGivenBalFile(Paths.get(tmpDir.toString()).resolve("types.bal")); + String expectedYaml = getStringFromGivenBalFile(TEST_RESOURCE.resolve( + "resources/expected_types.bal")); + Assert.assertEquals(expectedYaml, generatedOpenAPI); + } + @AfterClass public void cleanUp() throws IOException { TestUtil.cleanDistribution(); diff --git a/openapi-integration-tests/src/test/resources/ballerina_sources/project_openapi_bal_ext/result_1.yaml b/openapi-integration-tests/src/test/resources/ballerina_sources/project_openapi_bal_ext/result_1.yaml index 0dafce369..996094380 100644 --- a/openapi-integration-tests/src/test/resources/ballerina_sources/project_openapi_bal_ext/result_1.yaml +++ b/openapi-integration-tests/src/test/resources/ballerina_sources/project_openapi_bal_ext/result_1.yaml @@ -57,7 +57,7 @@ components: orgName: ballerina pkgName: time moduleName: time - version: 2.4.0 + version: 2.5.0 modulePrefix: time name: Date DateFields: @@ -81,7 +81,7 @@ components: orgName: ballerina pkgName: time moduleName: time - version: 2.4.0 + version: 2.5.0 modulePrefix: time name: DateFields OptionalTimeOfDayFields: @@ -100,7 +100,7 @@ components: orgName: ballerina pkgName: time moduleName: time - version: 2.4.0 + version: 2.5.0 modulePrefix: time name: OptionalTimeOfDayFields Seconds: @@ -111,7 +111,7 @@ components: orgName: ballerina pkgName: time moduleName: time - version: 2.4.0 + version: 2.5.0 modulePrefix: time name: Seconds Student: @@ -165,6 +165,6 @@ components: orgName: ballerina pkgName: time moduleName: time - version: 2.4.0 + version: 2.5.0 modulePrefix: time name: ZoneOffset diff --git a/openapi-integration-tests/src/test/resources/ballerina_sources/project_openapi_with_included_test/Ballerina.toml b/openapi-integration-tests/src/test/resources/ballerina_sources/project_openapi_with_included_test/Ballerina.toml new file mode 100644 index 000000000..d3cc51d4b --- /dev/null +++ b/openapi-integration-tests/src/test/resources/ballerina_sources/project_openapi_with_included_test/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org= "ballerina" +name= "openapi" +version= "2.0.0" diff --git a/openapi-integration-tests/src/test/resources/ballerina_sources/project_openapi_with_included_test/result.yaml b/openapi-integration-tests/src/test/resources/ballerina_sources/project_openapi_with_included_test/result.yaml new file mode 100644 index 000000000..18dfb316e --- /dev/null +++ b/openapi-integration-tests/src/test/resources/ballerina_sources/project_openapi_with_included_test/result.yaml @@ -0,0 +1,133 @@ +openapi: 3.0.1 +info: + title: PayloadV + version: 2.0.0 +servers: + - url: "{server}:{port}/payloadV" + variables: + server: + default: http://localhost + port: + default: "7080" +paths: + /Pods: + get: + operationId: getPods + responses: + "200": + description: Ok + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pod' + /Services: + get: + operationId: getServices + responses: + "200": + description: Ok + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Service' +components: + schemas: + Metadata: + required: + - name + type: object + properties: + name: + type: string + displayName: + type: string + description: + type: string + Pod: + type: object + allOf: + - $ref: '#/components/schemas/Resource' + - required: + - spec + type: object + properties: + kind: + allOf: + - $ref: '#/components/schemas/ResourceKind' + default: Pod + spec: + $ref: '#/components/schemas/PodSpec' + PodSpec: + required: + - nodeName + type: object + properties: + nodeName: + type: string + Resource: + type: object + allOf: + - $ref: '#/components/schemas/ResourceBase' + - required: + - spec + type: object + properties: + spec: + type: object + properties: {} + status: + $ref: '#/components/schemas/Status' + ResourceBase: + required: + - group + - kind + - metadata + - version + type: object + properties: + group: + type: string + version: + type: string + kind: + $ref: '#/components/schemas/ResourceKind' + metadata: + $ref: '#/components/schemas/Metadata' + ResourceKind: + type: string + enum: + - Service + - Pod + Service: + type: object + allOf: + - $ref: '#/components/schemas/Resource' + - required: + - spec + type: object + properties: + kind: + allOf: + - $ref: '#/components/schemas/ResourceKind' + default: Service + spec: + $ref: '#/components/schemas/ServiceSpec' + ServiceSpec: + required: + - clusterIP + type: object + properties: + clusterIP: + type: string + Status: + required: + - observedGeneration + type: object + properties: + observedGeneration: + type: integer + format: int64 diff --git a/openapi-integration-tests/src/test/resources/ballerina_sources/project_openapi_with_included_test/service_file.bal b/openapi-integration-tests/src/test/resources/ballerina_sources/project_openapi_with_included_test/service_file.bal new file mode 100644 index 000000000..22729fb7c --- /dev/null +++ b/openapi-integration-tests/src/test/resources/ballerina_sources/project_openapi_with_included_test/service_file.bal @@ -0,0 +1,61 @@ +import ballerina/http; + +public const RESOURCE_KIND_SERVICE = "Service"; +public const RESOURCE_KIND_POD = "Pod"; + +public type ResourceKind RESOURCE_KIND_SERVICE|RESOURCE_KIND_POD; + +public type Metadata record { + string name; + string displayName?; + string description?; +}; + +public type Status record { + int observedGeneration; +}; + +public type ResourceBase record { + string group; + string version; + ResourceKind kind; + Metadata metadata; +}; + +public type Resource record { + *ResourceBase; + record {} spec; + Status status?; +}; + +public type ServiceSpec record { + string clusterIP; +}; + +public type Service record { + *Resource; + ResourceKind kind = RESOURCE_KIND_SERVICE; + ServiceSpec spec; + Status status?; +}; + +public type PodSpec record { + string nodeName; +}; + +public type Pod record { + *Resource; + ResourceKind kind = RESOURCE_KIND_POD; + PodSpec spec; + Status status?; +}; + +service /payloadV on new http:Listener(7080) { + resource function get Pods() returns Pod[] { + return []; + } + + resource function get Services() returns Service[] { + return []; + } +} diff --git a/openapi-integration-tests/src/test/resources/ballerina_sources/resources/expected_types.bal b/openapi-integration-tests/src/test/resources/ballerina_sources/resources/expected_types.bal new file mode 100644 index 000000000..d7dd22840 --- /dev/null +++ b/openapi-integration-tests/src/test/resources/ballerina_sources/resources/expected_types.bal @@ -0,0 +1,105 @@ +// AUTO-GENERATED FILE. +// This file is auto-generated by the Ballerina OpenAPI tool. + +import ballerina/http; + +public type Status record { + int observedGeneration; +}; + +public type Pod record { + *Resource; + ResourceKind kind = "Pod"; + PodSpec spec; +}; + +public type PodSpec record { + string nodeName; +}; + +# Provides settings related to HTTP/1.x protocol. +public type ClientHttp1Settings record {| + # Specifies whether to reuse a connection for multiple requests + http:KeepAlive keepAlive = http:KEEPALIVE_AUTO; + # The chunking behaviour of the request + http:Chunking chunking = http:CHUNKING_AUTO; + # Proxy server related options + ProxyConfig proxy?; +|}; + +public type ResourceBase record { + string group; + string version; + ResourceKind kind; + Metadata metadata; +}; + +public type Metadata record { + string name; + string displayName?; + string description?; +}; + +public type Resource record { + *ResourceBase; + record {} spec; + Status status?; +}; + +public type ServiceSpec record { + string clusterIP; +}; + +public type Service record { + *Resource; + ResourceKind kind = "Service"; + ServiceSpec spec; +}; + +# Proxy server configurations to be used with the HTTP client endpoint. +public type ProxyConfig record {| + # Host name of the proxy server + string host = ""; + # Proxy server port + int port = 0; + # Proxy server username + string userName = ""; + # Proxy server password + @display {label: "", kind: "password"} + string password = ""; +|}; + +public type ResourceKind "Service"|"Pod"; + +# Provides a set of configurations for controlling the behaviours when communicating with a remote HTTP endpoint. +@display {label: "Connection Config"} +public type ConnectionConfig record {| + # The HTTP version understood by the client + http:HttpVersion httpVersion = http:HTTP_2_0; + # Configurations related to HTTP/1.x protocol + ClientHttp1Settings http1Settings?; + # Configurations related to HTTP/2 protocol + http:ClientHttp2Settings http2Settings?; + # The maximum time to wait (in seconds) for a response before closing the connection + decimal timeout = 60; + # The choice of setting `forwarded`/`x-forwarded` header + string forwarded = "disable"; + # Configurations associated with request pooling + http:PoolConfiguration poolConfig?; + # HTTP caching related configurations + http:CacheConfig cache?; + # Specifies the way of handling compression (`accept-encoding`) header + http:Compression compression = http:COMPRESSION_AUTO; + # Configurations associated with the behaviour of the Circuit Breaker + http:CircuitBreakerConfig circuitBreaker?; + # Configurations associated with retrying + http:RetryConfig retryConfig?; + # Configurations associated with inbound response size limits + http:ResponseLimitConfigs responseLimits?; + # SSL/TLS-related options + http:ClientSecureSocket secureSocket?; + # Proxy server related options + http:ProxyConfig proxy?; + # Enables the inbound payload validation functionality which provided by the constraint package. Enabled by default + boolean validation = true; +|};