From 0a9443187460a7fc53c06dbeefa5b9c8f0144e3f Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Wed, 3 Jan 2024 15:10:24 +0530 Subject: [PATCH 01/86] Initial commit --- .../service/mapper/hateoas/HateoasMapper.java | 40 +++++++ .../mapper/model/ServiceNodeVisitor.java | 104 ++++++++++++++++++ .../mapper/parameter/ResponseMapper.java | 9 ++ .../generators/openapi/HateoasTests.java | 34 ++++++ .../hateoas/hateoas_automatic_linking.yaml | 87 +++++++++++++++ .../hateoas/hateoas_automatic_linking.bal | 40 +++++++ openapi-cli/src/test/resources/testng.xml | 1 + 7 files changed, 315 insertions(+) create mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java create mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java create mode 100644 openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java create mode 100644 openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_automatic_linking.yaml create mode 100644 openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java new file mode 100644 index 000000000..6ab7d9d4d --- /dev/null +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.openapi.service.mapper.hateoas; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; +import io.ballerina.openapi.service.mapper.model.ServiceNodeVisitor; +import io.swagger.v3.oas.models.OpenAPI; + +import java.util.List; + +public class HateoasMapper { + + private final SemanticModel semanticModel; + private final ServiceNodeVisitor serviceNodeVisitor; + + public HateoasMapper(SemanticModel semanticModel, ServiceNodeVisitor serviceNodeVisitor) { + this.semanticModel = semanticModel; + this.serviceNodeVisitor = serviceNodeVisitor; + } + +// public void mapHateoasLinks() { +// +// } +} diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java new file mode 100644 index 000000000..e61e56b31 --- /dev/null +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.service.mapper.model; + +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.NodeVisitor; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.MetadataNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.compiler.syntax.tree.Node; +//import io.swagger.v3.oas.models.links.Link; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Visitor to get the ServiceDeclarationNode. + * + */ +public class ServiceNodeVisitor extends NodeVisitor { + + public Map>> resourceMap = new HashMap<>(); +// private String resourceName; +// private String method; +// private String operationId; + + @Override + public void visit(ServiceDeclarationNode serviceDeclarationNode) { + for (Node child : serviceDeclarationNode.children()) { + if (SyntaxKind.RESOURCE_ACCESSOR_DEFINITION.equals(child.kind())) { + child.accept(this); + } + } + System.out.println(resourceMap); + } + + @Override + public void visit(FunctionDefinitionNode functionDefinitionNode) { + for (Node child : functionDefinitionNode.children()) { + if (SyntaxKind.METADATA.equals(child.kind())) { + child.accept(this); + } + } + } + + @Override + public void visit(MetadataNode metadataNode) { + metadataNode.children().get(0).accept(this); + } + + @Override + public void visit(AnnotationNode annotationNode) { + for (Node child : annotationNode.children()) { + if (SyntaxKind.MAPPING_CONSTRUCTOR.equals(child.kind())) { + child.accept(this); + } + } + } + + @Override + public void visit(MappingConstructorExpressionNode mappingConstructorExpressionNode) { + for (Node child : mappingConstructorExpressionNode.children()) { + if (SyntaxKind.SPECIFIC_FIELD.equals(child.kind())) { + child.accept(this); + } + } + } + + @Override + public void visit(SpecificFieldNode specificFieldNode) { + if (SyntaxKind.STRING_LITERAL.equals(specificFieldNode.children().get(2).kind())) { +// resourceName = specificFieldNode.children().get(2).toString().replace("\"",""); + } else { + specificFieldNode.children().get(2).accept(this); + } + } + + @Override + public void visit(ListConstructorExpressionNode listConstructorExpressionNode) { +// resourceLink = listConstructorExpressionNode.children().get(1); +// resourceMap.put(resourceName, resourceLink); + } +} diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java index ddb9645f1..4410352a5 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java @@ -35,8 +35,10 @@ import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; import io.ballerina.openapi.service.mapper.AdditionalData; import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; +//import io.ballerina.openapi.service.mapper.hateoas.HateoasMapper; import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; import io.ballerina.openapi.service.mapper.model.OperationAdaptor; +//import io.ballerina.openapi.service.mapper.model.ServiceNodeVisitor; import io.ballerina.openapi.service.mapper.parameter.model.CacheConfigAnnotation; import io.ballerina.openapi.service.mapper.parameter.utils.CacheHeaderUtils; import io.ballerina.openapi.service.mapper.parameter.utils.MediaTypeUtils; @@ -46,6 +48,7 @@ import io.ballerina.openapi.service.mapper.utils.MapperCommonUtils; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.headers.Header; +//import io.swagger.v3.oas.models.links.Link; import io.swagger.v3.oas.models.media.ComposedSchema; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; @@ -210,6 +213,11 @@ private void addResponseContent(TypeSymbol returnType, ApiResponse apiResponse, } } +// private void addResponseLinks(ApiResponse apiResponse, ServiceNodeVisitor serviceNodeVisitor) { +// HateoasMapper hateoasMapper = new HateoasMapper(semanticModel, serviceNodeVisitor); +// apiResponse.setLinks(serviceNodeVisitor.resourceMap); +// } + public void addApiResponse(ApiResponse apiResponse, String statusCode) { addHeaders(apiResponse, statusCode); if (apiResponses.containsKey(statusCode)) { @@ -287,6 +295,7 @@ private void addResponseMappingForHttpResponse() { ApiResponse apiResponse = new ApiResponse(); apiResponse.setDescription("Any Response"); apiResponse.setContent(new Content().addMediaType("*/*", mediaTypeObj)); +// addResponseLinks(apiResponse, new ServiceNodeVisitor()); addApiResponse(apiResponse, "default"); } diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java new file mode 100644 index 000000000..eaa7c5848 --- /dev/null +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.openapi.generators.openapi; + +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class HateoasTests { + private static final Path RES_DIR = Paths.get("src/test/resources/ballerina-to-openapi").toAbsolutePath(); + + @Test(description = "Automatic linking between resoruce functions") + public void testHateoasAutomaticLinking() throws IOException { + Path ballerinafilePath = RES_DIR.resolve("hateoas/hateoas_automatic_linking.bal"); + TestUtils.compareWithGeneratedFile(ballerinafilePath, "hateoas/hateoas_automatic_linking.yaml"); + } +} diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_automatic_linking.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_automatic_linking.yaml new file mode 100644 index 000000000..6b441cb47 --- /dev/null +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_automatic_linking.yaml @@ -0,0 +1,87 @@ +openapi: 3.0.1 +info: + title: PayloadV + version: 0.0.0 +servers: + - url: "{server}:{port}/payloadV" + variables: + server: + default: http://localhost + port: + default: "9090" +paths: + /locations: + get: + operationId: getLocations + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Location' + links: + room: + operationId: getLocationsIdRooms + /locations/{id}/rooms: + get: + operationId: getLocationsIdRooms + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "202": + description: Accepted + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' +components: + schemas: + Location: + required: + - address + - id + - name + type: object + properties: + name: + type: string + id: + type: string + address: + type: string + ErrorPayload: + required: + - message + - method + - path + - reason + - status + - timestamp + type: object + properties: + timestamp: + type: string + status: + type: integer + format: int64 + reason: + type: string + message: + type: string + path: + type: string + method: + type: string diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal new file mode 100644 index 000000000..acd75cbff --- /dev/null +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal @@ -0,0 +1,40 @@ +// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/http; + +public type Location record { + string name; + string id; + string address; +}; + +service /payloadV on new http:Listener(9090) { + @http:ResourceConfig { + name: "Locations", + linkedTo: [ {name: "Rooms", relation: "room", method: "get"} ] + } + resource function get locations() returns Location { + return {name: "Cinnamon Lodge", id: "1001", address: "Habarana"}; + } + + @http:ResourceConfig { + name: "Rooms" + } + resource function get locations/[string id]/rooms() returns error? { + return; + } +} diff --git a/openapi-cli/src/test/resources/testng.xml b/openapi-cli/src/test/resources/testng.xml index b70c7e5b3..5273f05fb 100644 --- a/openapi-cli/src/test/resources/testng.xml +++ b/openapi-cli/src/test/resources/testng.xml @@ -47,6 +47,7 @@ under the License. + From 1e4b3e2cbd3fca80b3f363a2de35fd801ebb4fca Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Wed, 3 Jan 2024 15:30:38 +0530 Subject: [PATCH 02/86] Fix checkstyle errors --- .../openapi/service/mapper/hateoas/HateoasMapper.java | 4 ---- .../openapi/service/mapper/model/ServiceNodeVisitor.java | 1 - .../openapi/service/mapper/parameter/ResponseMapper.java | 3 --- 3 files changed, 8 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java index 6ab7d9d4d..7a8cbcdde 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java @@ -18,11 +18,7 @@ package io.ballerina.openapi.service.mapper.hateoas; import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; import io.ballerina.openapi.service.mapper.model.ServiceNodeVisitor; -import io.swagger.v3.oas.models.OpenAPI; - -import java.util.List; public class HateoasMapper { diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java index e61e56b31..29f514b84 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java @@ -28,7 +28,6 @@ import io.ballerina.compiler.syntax.tree.MetadataNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.compiler.syntax.tree.Node; -//import io.swagger.v3.oas.models.links.Link; import java.util.HashMap; import java.util.List; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java index 4410352a5..f19375151 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java @@ -35,10 +35,8 @@ import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; import io.ballerina.openapi.service.mapper.AdditionalData; import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; -//import io.ballerina.openapi.service.mapper.hateoas.HateoasMapper; import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; import io.ballerina.openapi.service.mapper.model.OperationAdaptor; -//import io.ballerina.openapi.service.mapper.model.ServiceNodeVisitor; import io.ballerina.openapi.service.mapper.parameter.model.CacheConfigAnnotation; import io.ballerina.openapi.service.mapper.parameter.utils.CacheHeaderUtils; import io.ballerina.openapi.service.mapper.parameter.utils.MediaTypeUtils; @@ -48,7 +46,6 @@ import io.ballerina.openapi.service.mapper.utils.MapperCommonUtils; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.headers.Header; -//import io.swagger.v3.oas.models.links.Link; import io.swagger.v3.oas.models.media.ComposedSchema; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; From 804da50dcbffae95f0f4907bab3f3f47534e3e7f Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Wed, 3 Jan 2024 15:37:58 +0530 Subject: [PATCH 03/86] Fix checkstyle errors --- .../mapper/model/ServiceNodeVisitor.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java index 29f514b84..ab7f2bac5 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java @@ -18,20 +18,20 @@ package io.ballerina.openapi.service.mapper.model; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode; import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.MetadataNode; +import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeVisitor; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; -import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.SpecificFieldNode; -import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode; -import io.ballerina.compiler.syntax.tree.MetadataNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; -import io.ballerina.compiler.syntax.tree.Node; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; /** * Visitor to get the ServiceDeclarationNode. @@ -51,7 +51,7 @@ public void visit(ServiceDeclarationNode serviceDeclarationNode) { child.accept(this); } } - System.out.println(resourceMap); +// System.out.println(resourceMap); } @Override From e0403dc079acf152e8a94b162c5fee2ba8882316 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Wed, 3 Jan 2024 16:38:57 +0530 Subject: [PATCH 04/86] Fix checkstyle violations --- .../service/mapper/hateoas/HateoasMapper.java | 36 ------------------- .../mapper/model/ServiceNodeVisitor.java | 19 +++++----- 2 files changed, 9 insertions(+), 46 deletions(-) delete mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java deleted file mode 100644 index 7a8cbcdde..000000000 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.openapi.service.mapper.hateoas; - -import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.openapi.service.mapper.model.ServiceNodeVisitor; - -public class HateoasMapper { - - private final SemanticModel semanticModel; - private final ServiceNodeVisitor serviceNodeVisitor; - - public HateoasMapper(SemanticModel semanticModel, ServiceNodeVisitor serviceNodeVisitor) { - this.semanticModel = semanticModel; - this.serviceNodeVisitor = serviceNodeVisitor; - } - -// public void mapHateoasLinks() { -// -// } -} diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java index 29f514b84..60adaecaf 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java @@ -18,20 +18,20 @@ package io.ballerina.openapi.service.mapper.model; +import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode; import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.MetadataNode; +import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeVisitor; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; -import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.SpecificFieldNode; -import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode; -import io.ballerina.compiler.syntax.tree.MetadataNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; -import io.ballerina.compiler.syntax.tree.Node; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; +// +//import java.util.HashMap; +//import java.util.List; +//import java.util.Map; /** * Visitor to get the ServiceDeclarationNode. @@ -39,7 +39,7 @@ */ public class ServiceNodeVisitor extends NodeVisitor { - public Map>> resourceMap = new HashMap<>(); +// public Map>> resourceMap = new HashMap<>(); // private String resourceName; // private String method; // private String operationId; @@ -51,7 +51,6 @@ public void visit(ServiceDeclarationNode serviceDeclarationNode) { child.accept(this); } } - System.out.println(resourceMap); } @Override From 5a4839096165f5cb96638111d0f96ceba8f15bc8 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Thu, 4 Jan 2024 11:14:46 +0530 Subject: [PATCH 05/86] Implement hateoas meta-info sharing logic --- .../service/mapper/hateoas/ContextHolder.java | 57 +++++++++++++++++++ .../service/mapper/hateoas/Resource.java | 37 ++++++++++++ .../service/mapper/hateoas/Service.java | 49 ++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java create mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java create mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java new file mode 100644 index 000000000..36626369f --- /dev/null +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.service.mapper.hateoas; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +public final class ContextHolder { + private static ContextHolder instance; + + private final List hateoasServices; + + private ContextHolder() { + this.hateoasServices = new ArrayList<>(); + } + + public static ContextHolder getHateoasContextHolder() { + synchronized (ContextHolder.class) { + if (Objects.isNull(instance)) { + instance = new ContextHolder(); + } + } + return instance; + } + + public Optional getHateoasResource(int serviceId, String resourceName, String resourceMethod) { + return this.hateoasServices.stream() + .filter(svc -> svc.getServiceId() == serviceId) + .findFirst() + .flatMap(svc -> svc.getHateoasResourceMapping().entrySet().stream() + .filter(resources -> resourceName.equals(resources.getKey())) + .findFirst() + ).flatMap(hateoasResourceMapping -> hateoasResourceMapping.getValue().stream() + .filter(resource -> resourceMethod.equals(resource.getResourceMethod())) + .findFirst() + ); + + } +} diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java new file mode 100644 index 000000000..dd9a7e659 --- /dev/null +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.service.mapper.hateoas; + +public class Resource { + private final String resourceMethod; + private final String operationId; + + public Resource(String resourceName, String resourceMethod, String operationId) { + this.resourceMethod = resourceMethod; + this.operationId = operationId; + } + + public String getResourceMethod() { + return resourceMethod; + } + + public String getOperationId() { + return operationId; + } +} diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java new file mode 100644 index 000000000..460e78324 --- /dev/null +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.service.mapper.hateoas; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Service { + private final int serviceId; + private final Map> hateoasResourceMapping = new HashMap<>(); + + public Service(int serviceId) { + this.serviceId = serviceId; + } + + public void addResource(String resourceName, Resource resource) { + if (hateoasResourceMapping.containsKey(resourceName)) { + hateoasResourceMapping.get(resourceName).add(resource); + return; + } + hateoasResourceMapping.put(resourceName, Arrays.asList(resource)); + } + + public int getServiceId() { + return serviceId; + } + + public Map> getHateoasResourceMapping() { + return hateoasResourceMapping; + } +} From e06bebbbb068501ff7fdb3fa64c111aa23bae80e Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Thu, 4 Jan 2024 15:06:21 +0530 Subject: [PATCH 06/86] Remove unwanted node-visitor --- .../mapper/model/ServiceNodeVisitor.java | 102 ------------------ 1 file changed, 102 deletions(-) delete mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java deleted file mode 100644 index 60adaecaf..000000000 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/model/ServiceNodeVisitor.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.ballerina.openapi.service.mapper.model; - -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode; -import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; -import io.ballerina.compiler.syntax.tree.MetadataNode; -import io.ballerina.compiler.syntax.tree.Node; -import io.ballerina.compiler.syntax.tree.NodeVisitor; -import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; -import io.ballerina.compiler.syntax.tree.SpecificFieldNode; -import io.ballerina.compiler.syntax.tree.SyntaxKind; -// -//import java.util.HashMap; -//import java.util.List; -//import java.util.Map; - -/** - * Visitor to get the ServiceDeclarationNode. - * - */ -public class ServiceNodeVisitor extends NodeVisitor { - -// public Map>> resourceMap = new HashMap<>(); -// private String resourceName; -// private String method; -// private String operationId; - - @Override - public void visit(ServiceDeclarationNode serviceDeclarationNode) { - for (Node child : serviceDeclarationNode.children()) { - if (SyntaxKind.RESOURCE_ACCESSOR_DEFINITION.equals(child.kind())) { - child.accept(this); - } - } - } - - @Override - public void visit(FunctionDefinitionNode functionDefinitionNode) { - for (Node child : functionDefinitionNode.children()) { - if (SyntaxKind.METADATA.equals(child.kind())) { - child.accept(this); - } - } - } - - @Override - public void visit(MetadataNode metadataNode) { - metadataNode.children().get(0).accept(this); - } - - @Override - public void visit(AnnotationNode annotationNode) { - for (Node child : annotationNode.children()) { - if (SyntaxKind.MAPPING_CONSTRUCTOR.equals(child.kind())) { - child.accept(this); - } - } - } - - @Override - public void visit(MappingConstructorExpressionNode mappingConstructorExpressionNode) { - for (Node child : mappingConstructorExpressionNode.children()) { - if (SyntaxKind.SPECIFIC_FIELD.equals(child.kind())) { - child.accept(this); - } - } - } - - @Override - public void visit(SpecificFieldNode specificFieldNode) { - if (SyntaxKind.STRING_LITERAL.equals(specificFieldNode.children().get(2).kind())) { -// resourceName = specificFieldNode.children().get(2).toString().replace("\"",""); - } else { - specificFieldNode.children().get(2).accept(this); - } - } - - @Override - public void visit(ListConstructorExpressionNode listConstructorExpressionNode) { -// resourceLink = listConstructorExpressionNode.children().get(1); -// resourceMap.put(resourceName, resourceLink); - } -} From 7f0688f58b12ea6d36fdbf02d79ae06efce673ac Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Thu, 4 Jan 2024 15:06:57 +0530 Subject: [PATCH 07/86] Update node-visiting logic to extract hateoas-metadata --- .../hateoas/HateoasMetadataVisitor.java | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java new file mode 100644 index 000000000..c529be1a1 --- /dev/null +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.service.mapper.hateoas; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.ServiceDeclarationSymbol; +import io.ballerina.compiler.api.symbols.Symbol; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.MetadataNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.NodeVisitor; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.openapi.service.mapper.utils.MapperCommonUtils; + +import java.util.Optional; + +import static io.ballerina.openapi.service.mapper.hateoas.ContextHolder.getHateoasContextHolder; + +public class HateoasMetadataVisitor extends NodeVisitor { + private final SemanticModel semanticModel; + + public HateoasMetadataVisitor(SemanticModel semanticModel) { + this.semanticModel = semanticModel; + } + + @Override + public void visit(ServiceDeclarationNode serviceNode) { + boolean isHttpService = MapperCommonUtils.isHttpService(serviceNode, semanticModel); + if (!isHttpService) { + return; + } + Optional serviceDeclarationOpt = semanticModel.symbol(serviceNode); + if (serviceDeclarationOpt.isEmpty()) { + return; + } + ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceDeclarationOpt.get(); + int serviceId = serviceSymbol.hashCode(); + for (Node child : serviceNode.children()) { + if (SyntaxKind.RESOURCE_ACCESSOR_DEFINITION.equals(child.kind())) { + FunctionDefinitionNode resourceFunction = (FunctionDefinitionNode) child; + String resourceMethod = resourceFunction.functionName().text(); + String operationId = generateOperationId(resourceFunction); + Optional resourceName = getResourceConfigAnnotation(resourceFunction) + .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, "name")); + if (resourceName.isEmpty()) { + return; + } + Resource hateoasResource = new Resource(resourceMethod, operationId); + getHateoasContextHolder().updateHateoasResource(serviceId, resourceName.get(), hateoasResource); + } + } + } + + private String generateOperationId(FunctionDefinitionNode resourceFunction) { + String relativePath = MapperCommonUtils.generateRelativePath(resourceFunction); + String cleanResourcePath = MapperCommonUtils.unescapeIdentifier(relativePath); + String resName = (resourceFunction.functionName().text() + "_" + + cleanResourcePath).replaceAll("\\{///\\}", "_"); + + if (cleanResourcePath.equals("/")) { + resName = resourceFunction.functionName().text(); + } + return MapperCommonUtils.getOperationId(resName); + } + + private Optional getResourceConfigAnnotation(FunctionDefinitionNode resourceFunction) { + Optional metadata = resourceFunction.metadata(); + if (metadata.isEmpty()) { + return Optional.empty(); + } + MetadataNode metaData = metadata.get(); + NodeList annotations = metaData.annotations(); + return annotations.stream() + .filter(ann -> "http:ResourceConfig".equals(ann.annotReference().toString().trim())) + .findFirst(); + } + + private Optional getValueForAnnotationFields(AnnotationNode resourceConfigAnnotation, String fieldName) { + return resourceConfigAnnotation + .annotValue() + .map(MappingConstructorExpressionNode::fields) + .flatMap(fields -> + fields.stream() + .filter(fld -> fld instanceof SpecificFieldNode) + .map(fld -> (SpecificFieldNode) fld) + .filter(fld -> fieldName.equals(fld.fieldName().toString().trim())) + .findFirst() + ).flatMap(SpecificFieldNode::valueExpr) + .map(en -> en.toString().trim()); + } +} From 80cb40628135728fd0619e6449e0e3d86f1b7b1a Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Thu, 4 Jan 2024 15:07:46 +0530 Subject: [PATCH 08/86] Update shared hateoas metadata updating logic --- .../service/mapper/hateoas/ContextHolder.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java index 36626369f..7b026d4f6 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java @@ -41,6 +41,20 @@ public static ContextHolder getHateoasContextHolder() { return instance; } + public void updateHateoasResource(int serviceId, String resourceName, Resource resource) { + Optional hateoasService = this.hateoasServices.stream() + .filter(svc -> svc.getServiceId() == serviceId) + .findFirst(); + if (hateoasService.isEmpty()) { + Service service = new Service(serviceId); + service.addResource(resourceName, resource); + this.hateoasServices.add(service); + return; + } + Service service = hateoasService.get(); + service.addResource(resourceName, resource); + } + public Optional getHateoasResource(int serviceId, String resourceName, String resourceMethod) { return this.hateoasServices.stream() .filter(svc -> svc.getServiceId() == serviceId) @@ -49,7 +63,7 @@ public Optional getHateoasResource(int serviceId, String resourceName, .filter(resources -> resourceName.equals(resources.getKey())) .findFirst() ).flatMap(hateoasResourceMapping -> hateoasResourceMapping.getValue().stream() - .filter(resource -> resourceMethod.equals(resource.getResourceMethod())) + .filter(resource -> resourceMethod.equals(resource.resourceMethod())) .findFirst() ); From 95b4d66c13392fee8aca8a23b05938ad82560594 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Thu, 4 Jan 2024 15:08:11 +0530 Subject: [PATCH 09/86] Mark hateoas resource as a record --- .../service/mapper/hateoas/Resource.java | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java index dd9a7e659..18c237a37 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java @@ -18,20 +18,5 @@ package io.ballerina.openapi.service.mapper.hateoas; -public class Resource { - private final String resourceMethod; - private final String operationId; - - public Resource(String resourceName, String resourceMethod, String operationId) { - this.resourceMethod = resourceMethod; - this.operationId = operationId; - } - - public String getResourceMethod() { - return resourceMethod; - } - - public String getOperationId() { - return operationId; - } +public record Resource(String resourceMethod, String operationId) { } From f3a2fb5f11eaf6d27f2632d9a54705ffa6e9af3c Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Thu, 4 Jan 2024 15:08:42 +0530 Subject: [PATCH 10/86] Refactor common utility functions and their usage --- .../service/mapper/OpenAPIResourceMapper.java | 31 +++----------- .../mapper/utils/MapperCommonUtils.java | 40 ++++++++++--------- 2 files changed, 27 insertions(+), 44 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java index 23ac06686..461ca4096 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java @@ -101,7 +101,8 @@ public void addMapping() { * @param httpMethods Sibling methods related to operation. */ private void getResourcePath(FunctionDefinitionNode resource, List httpMethods) { - String path = MapperCommonUtils.unescapeIdentifier(generateRelativePath(resource)); + String relativePath = MapperCommonUtils.generateRelativePath(resource); + String cleanResourcePath = MapperCommonUtils.unescapeIdentifier(relativePath); Operation operation; for (String httpMethod : httpMethods) { //Iterate through http methods and fill path map. @@ -113,10 +114,10 @@ private void getResourcePath(FunctionDefinitionNode resource, List httpM errors.add(error); } else { Optional operationAdaptor = convertResourceToOperation(resource, httpMethod, - path); + cleanResourcePath); if (operationAdaptor.isPresent()) { operation = operationAdaptor.get().getOperation(); - generatePathItem(httpMethod, pathObject, operation, path); + generatePathItem(httpMethod, pathObject, operation, cleanResourcePath); } else { break; } @@ -281,13 +282,13 @@ private List getHttpMethods(FunctionDefinitionNode resource) { ServiceDeclarationNode parentNode = (ServiceDeclarationNode) resource.parent(); NodeList siblings = parentNode.members(); httpMethods.add(resource.functionName().text()); - String relativePath = generateRelativePath(resource); + String relativePath = MapperCommonUtils.generateRelativePath(resource); for (Node function: siblings) { SyntaxKind kind = function.kind(); if (kind.equals(SyntaxKind.RESOURCE_ACCESSOR_DEFINITION)) { FunctionDefinitionNode sibling = (FunctionDefinitionNode) function; //need to build relative path - String siblingRelativePath = generateRelativePath(sibling); + String siblingRelativePath = MapperCommonUtils.generateRelativePath(sibling); if (relativePath.equals(siblingRelativePath)) { httpMethods.add(sibling.functionName().text()); } @@ -295,24 +296,4 @@ private List getHttpMethods(FunctionDefinitionNode resource) { } return new ArrayList<>(httpMethods); } - - private String generateRelativePath(FunctionDefinitionNode resource) { - - StringBuilder relativePath = new StringBuilder(); - relativePath.append("/"); - if (!resource.relativeResourcePath().isEmpty()) { - for (Node node: resource.relativeResourcePath()) { - if (node instanceof ResourcePathParameterNode pathNode) { - relativePath.append("{"); - relativePath.append(pathNode.paramName().get()); - relativePath.append("}"); - } else if ((resource.relativeResourcePath().size() == 1) && (node.toString().trim().equals("."))) { - return relativePath.toString(); - } else { - relativePath.append(node.toString().trim()); - } - } - } - return relativePath.toString(); - } } 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 70214075c..e76649589 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 @@ -34,25 +34,7 @@ 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.syntax.tree.AbstractNodeFactory; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.BasicLiteralNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode; -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.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.RequiredParameterNode; -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.*; import io.ballerina.openapi.service.mapper.Constants; import io.ballerina.openapi.service.mapper.diagnostic.DiagnosticMessages; import io.ballerina.openapi.service.mapper.diagnostic.ExceptionDiagnostic; @@ -220,6 +202,26 @@ public static Schema getOpenApiSchema(SyntaxKind type) { return schema; } + public static String generateRelativePath(FunctionDefinitionNode resourceFunction) { + StringBuilder relativePath = new StringBuilder(); + relativePath.append("/"); + if (!resourceFunction.relativeResourcePath().isEmpty()) { + for (Node node: resourceFunction.relativeResourcePath()) { + if (node instanceof ResourcePathParameterNode pathNode) { + relativePath.append("{"); + relativePath.append(pathNode.paramName().get()); + relativePath.append("}"); + } else if ((resourceFunction.relativeResourcePath().size() == 1) + && (node.toString().trim().equals("."))) { + return relativePath.toString(); + } else { + relativePath.append(node.toString().trim()); + } + } + } + return relativePath.toString(); + } + /** * Generate operationId by removing special characters. * From 527b921b629ac53cb1c21996e57f3b2ace98ceee Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Thu, 4 Jan 2024 15:23:08 +0530 Subject: [PATCH 11/86] Fix checkstyle violations --- .../service/mapper/OpenAPIResourceMapper.java | 1 - .../mapper/utils/MapperCommonUtils.java | 21 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java index 461ca4096..a83c7bf5f 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java @@ -26,7 +26,6 @@ import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeList; -import io.ballerina.compiler.syntax.tree.ResourcePathParameterNode; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.openapi.service.mapper.diagnostic.DiagnosticMessages; 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 e76649589..bea027c6d 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 @@ -34,7 +34,26 @@ 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.syntax.tree.*; +import io.ballerina.compiler.syntax.tree.AbstractNodeFactory; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.BasicLiteralNode; +import io.ballerina.compiler.syntax.tree.ExpressionNode; +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode; +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.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.RequiredParameterNode; +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.openapi.service.mapper.Constants; import io.ballerina.openapi.service.mapper.diagnostic.DiagnosticMessages; import io.ballerina.openapi.service.mapper.diagnostic.ExceptionDiagnostic; From 96d2951c07a369bb10336c4ab4bc7c4c52b7b1fa Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Thu, 4 Jan 2024 16:46:28 +0530 Subject: [PATCH 12/86] Add hateoas mapper --- .../mapper/parameter/HateoasMapper.java | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java new file mode 100644 index 000000000..16b4ba119 --- /dev/null +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.service.mapper.parameter; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.ServiceDeclarationSymbol; +import io.ballerina.compiler.api.symbols.Symbol; +import io.ballerina.compiler.syntax.tree.*; +import io.ballerina.openapi.service.mapper.parameter.model.HateoasLink; + +import java.util.List; +import java.util.Optional; + +public class HateoasMapper { + private int serviceId; + public HateoasMapper(int serviceId) { + this.serviceId = serviceId; + } + + private int getServiceId(SemanticModel semanticModel, ServiceDeclarationNode serviceNode) { + Optional serviceDeclarationOpt = semanticModel.symbol(serviceNode); + ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceDeclarationOpt.get(); + serviceId = serviceSymbol.hashCode(); + return serviceId; + } + + public void mapHateoasLink(int serviceId, FunctionDefinitionNode resourceFunction) { + Optional linkedTo = getResourceConfigAnnotation(resourceFunction) + .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, "linkedTo")); + if (linkedTo.isEmpty()) { + return; + } + List links = getLinks(linkedTo.get()); + } + + // todo: implement this properly + private List getLinks(String linkedTo) { + return List.of(); + } + + private Optional getResourceConfigAnnotation(FunctionDefinitionNode resourceFunction) { + Optional metadata = resourceFunction.metadata(); + if (metadata.isEmpty()) { + return Optional.empty(); + } + MetadataNode metaData = metadata.get(); + NodeList annotations = metaData.annotations(); + return annotations.stream() + .filter(ann -> "http:ResourceConfig".equals(ann.annotReference().toString().trim())) + .findFirst(); + } + + private Optional getValueForAnnotationFields(AnnotationNode resourceConfigAnnotation, String fieldName) { + return resourceConfigAnnotation + .annotValue() + .map(MappingConstructorExpressionNode::fields) + .flatMap(fields -> + fields.stream() + .filter(fld -> fld instanceof SpecificFieldNode) + .map(fld -> (SpecificFieldNode) fld) + .filter(fld -> fieldName.equals(fld.fieldName().toString().trim())) + .findFirst() + ).flatMap(SpecificFieldNode::valueExpr) + .map(en -> en.toString().trim()); + } +} From fbedc3745942a95a91e505b44a6c2735df7b4855 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Thu, 4 Jan 2024 16:49:44 +0530 Subject: [PATCH 13/86] Implementing hateoas link object --- .../mapper/parameter/model/HateoasLink.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/model/HateoasLink.java diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/model/HateoasLink.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/model/HateoasLink.java new file mode 100644 index 000000000..d1805d358 --- /dev/null +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/model/HateoasLink.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.service.mapper.parameter.model; + +public class HateoasLink { + private String resourceName; + private String rel; + private String resourceMethod; + + public String getResourceName() { + return resourceName; + } + + public void setResourceName(String resourceName) { + this.resourceName = resourceName; + } + + public String getRel() { + return rel; + } + + public void setRel(String rel) { + this.rel = rel; + } + + public String getResourceMethod() { + return resourceMethod; + } + + public void setResourceMethod(String resourceMethod) { + this.resourceMethod = resourceMethod; + } +} From a3a826dc3cf22772ee190ba63878a6c7ee6b8afd Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Thu, 4 Jan 2024 17:20:47 +0530 Subject: [PATCH 14/86] Fix checkstyle violations --- .../openapi/service/mapper/parameter/HateoasMapper.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index 16b4ba119..4993b419f 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -21,7 +21,13 @@ import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.api.symbols.ServiceDeclarationSymbol; import io.ballerina.compiler.api.symbols.Symbol; -import io.ballerina.compiler.syntax.tree.*; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.MetadataNode; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.openapi.service.mapper.parameter.model.HateoasLink; import java.util.List; From 4dca3fe789fe566a09eff841d79f3c33e319a3f5 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Fri, 5 Jan 2024 11:12:44 +0530 Subject: [PATCH 15/86] Improve hateoas mapper --- .../service/mapper/hateoas/ContextHolder.java | 3 +- .../mapper/parameter/HateoasMapper.java | 73 ++++++++++++++----- .../mapper/parameter/ResponseMapper.java | 15 ++-- 3 files changed, 64 insertions(+), 27 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java index 7b026d4f6..569e888ab 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java @@ -28,7 +28,7 @@ public final class ContextHolder { private final List hateoasServices; - private ContextHolder() { + public ContextHolder() { this.hateoasServices = new ArrayList<>(); } @@ -66,6 +66,5 @@ public Optional getHateoasResource(int serviceId, String resourceName, .filter(resource -> resourceMethod.equals(resource.resourceMethod())) .findFirst() ); - } } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index 4993b419f..abe0f5096 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -18,46 +18,79 @@ package io.ballerina.openapi.service.mapper.parameter; -import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.compiler.api.symbols.ServiceDeclarationSymbol; -import io.ballerina.compiler.api.symbols.Symbol; +import java.util.Map; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Optional; +import java.util.HashMap; + +import io.swagger.v3.oas.models.links.Link; +import io.swagger.v3.oas.models.responses.ApiResponse; import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.compiler.syntax.tree.MetadataNode; -import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import io.ballerina.compiler.syntax.tree.SpecificFieldNode; import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.openapi.service.mapper.hateoas.ContextHolder; +import io.ballerina.openapi.service.mapper.hateoas.Resource; import io.ballerina.openapi.service.mapper.parameter.model.HateoasLink; -import java.util.List; -import java.util.Optional; - public class HateoasMapper { - private int serviceId; - public HateoasMapper(int serviceId) { - this.serviceId = serviceId; - } + Map hateoasLinks; + public HateoasMapper() { - private int getServiceId(SemanticModel semanticModel, ServiceDeclarationNode serviceNode) { - Optional serviceDeclarationOpt = semanticModel.symbol(serviceNode); - ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceDeclarationOpt.get(); - serviceId = serviceSymbol.hashCode(); - return serviceId; } - public void mapHateoasLink(int serviceId, FunctionDefinitionNode resourceFunction) { + public Map mapHateoasLinks(int serviceId, FunctionDefinitionNode resourceFunction, + ApiResponse apiResponse) { Optional linkedTo = getResourceConfigAnnotation(resourceFunction) .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, "linkedTo")); if (linkedTo.isEmpty()) { - return; + return Collections.emptyMap(); } List links = getLinks(linkedTo.get()); + ContextHolder contextHolder = new ContextHolder(); + Link swaggerLink = new Link(); + for (HateoasLink link : links) { + Optional resource = contextHolder.getHateoasResource(serviceId, link.getResourceName(), + link.getResourceMethod()); + if (resource.isPresent()) { + String operationId = resource.get().operationId(); + swaggerLink.setOperationId(operationId); + hateoasLinks.put(link.getRel(), swaggerLink); + } + } + return hateoasLinks; } - // todo: implement this properly private List getLinks(String linkedTo) { - return List.of(); + List links = new ArrayList<>(); + String[] linkArray = linkedTo.replaceAll("[\\[\\]]", "").split(",\\s*"); + for (String linkString : linkArray) { + HateoasLink link = parseHateoasLink(linkString); + links.add(link); + } + return links; + } + + public static HateoasLink parseHateoasLink(String input) { + HateoasLink hateoasLink = new HateoasLink(); + HashMap keyValueMap = new HashMap<>(); + String[] keyValuePairs = input.replaceAll("[{}]", "").split(",\\s*"); + for (String pair : keyValuePairs) { + String[] parts = pair.split(":\\s*"); + if (parts.length == 2) { + String key = parts[0].trim(); + String value = parts[1].replaceAll("\"", "").trim(); + keyValueMap.put(key, value); + } + } + hateoasLink.setResourceName(keyValueMap.get("name")); + hateoasLink.setRel(keyValueMap.get("rel")); + hateoasLink.setResourceMethod(keyValueMap.get("method")); + return hateoasLink; } private Optional getResourceConfigAnnotation(FunctionDefinitionNode resourceFunction) { diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java index f19375151..41a48c085 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java @@ -28,11 +28,13 @@ 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.symbols.ServiceDeclarationSymbol; import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import io.ballerina.openapi.service.mapper.AdditionalData; import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; @@ -210,10 +212,13 @@ private void addResponseContent(TypeSymbol returnType, ApiResponse apiResponse, } } -// private void addResponseLinks(ApiResponse apiResponse, ServiceNodeVisitor serviceNodeVisitor) { -// HateoasMapper hateoasMapper = new HateoasMapper(semanticModel, serviceNodeVisitor); -// apiResponse.setLinks(serviceNodeVisitor.resourceMap); -// } + private void addResponseLinks(ApiResponse apiResponse, ServiceDeclarationNode serviceNode) { + Optional serviceDeclarationOpt = semanticModel.symbol(serviceNode); + ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceDeclarationOpt.get(); + int serviceId = serviceSymbol.hashCode(); + HateoasMapper hateoasMapper = new HateoasMapper(); +// apiResponse.setLinks(); + } public void addApiResponse(ApiResponse apiResponse, String statusCode) { addHeaders(apiResponse, statusCode); @@ -292,7 +297,7 @@ private void addResponseMappingForHttpResponse() { ApiResponse apiResponse = new ApiResponse(); apiResponse.setDescription("Any Response"); apiResponse.setContent(new Content().addMediaType("*/*", mediaTypeObj)); -// addResponseLinks(apiResponse, new ServiceNodeVisitor()); +// addResponseLinks(apiResponse,); addApiResponse(apiResponse, "default"); } From 82c3cd7a993080763f5ee705147f83475ef55945 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 5 Jan 2024 13:52:17 +0530 Subject: [PATCH 16/86] Refactor HateoasLink to SwaggerLink mapping-logic --- .../openapi/service/mapper/parameter/HateoasMapper.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index abe0f5096..935354dd0 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -38,10 +38,6 @@ import io.ballerina.openapi.service.mapper.parameter.model.HateoasLink; public class HateoasMapper { - Map hateoasLinks; - public HateoasMapper() { - - } public Map mapHateoasLinks(int serviceId, FunctionDefinitionNode resourceFunction, ApiResponse apiResponse) { @@ -52,11 +48,12 @@ public Map mapHateoasLinks(int serviceId, FunctionDefinitionNode r } List links = getLinks(linkedTo.get()); ContextHolder contextHolder = new ContextHolder(); - Link swaggerLink = new Link(); + Map hateoasLinks = new HashMap<>(); for (HateoasLink link : links) { Optional resource = contextHolder.getHateoasResource(serviceId, link.getResourceName(), link.getResourceMethod()); if (resource.isPresent()) { + Link swaggerLink = new Link(); String operationId = resource.get().operationId(); swaggerLink.setOperationId(operationId); hateoasLinks.put(link.getRel(), swaggerLink); From 4dfc46793fc5b3bb20f1613e77bc7d5e9d50c2d8 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 5 Jan 2024 13:56:46 +0530 Subject: [PATCH 17/86] Refactor method name --- .../openapi/service/mapper/parameter/HateoasMapper.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index 935354dd0..c4d83612a 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -26,7 +26,6 @@ import java.util.HashMap; import io.swagger.v3.oas.models.links.Link; -import io.swagger.v3.oas.models.responses.ApiResponse; import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.compiler.syntax.tree.MetadataNode; @@ -39,8 +38,8 @@ public class HateoasMapper { - public Map mapHateoasLinks(int serviceId, FunctionDefinitionNode resourceFunction, - ApiResponse apiResponse) { + public Map mapHateoasLinksToSwaggerLinks(int serviceId, + FunctionDefinitionNode resourceFunction) { Optional linkedTo = getResourceConfigAnnotation(resourceFunction) .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, "linkedTo")); if (linkedTo.isEmpty()) { From 4b57fec7ef88857dacd4881c2a5da3a5872de045 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Fri, 5 Jan 2024 15:41:01 +0530 Subject: [PATCH 18/86] Resolve merge conflicts in response mapper --- .../service/mapper/parameter/ResponseMapper.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java index 41a48c085..ddb9645f1 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java @@ -28,13 +28,11 @@ 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.symbols.ServiceDeclarationSymbol; import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import io.ballerina.openapi.service.mapper.AdditionalData; import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; @@ -212,14 +210,6 @@ private void addResponseContent(TypeSymbol returnType, ApiResponse apiResponse, } } - private void addResponseLinks(ApiResponse apiResponse, ServiceDeclarationNode serviceNode) { - Optional serviceDeclarationOpt = semanticModel.symbol(serviceNode); - ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceDeclarationOpt.get(); - int serviceId = serviceSymbol.hashCode(); - HateoasMapper hateoasMapper = new HateoasMapper(); -// apiResponse.setLinks(); - } - public void addApiResponse(ApiResponse apiResponse, String statusCode) { addHeaders(apiResponse, statusCode); if (apiResponses.containsKey(statusCode)) { @@ -297,7 +287,6 @@ private void addResponseMappingForHttpResponse() { ApiResponse apiResponse = new ApiResponse(); apiResponse.setDescription("Any Response"); apiResponse.setContent(new Content().addMediaType("*/*", mediaTypeObj)); -// addResponseLinks(apiResponse,); addApiResponse(apiResponse, "default"); } From affe803e5c58de56aad3244462c8920c2e3074ab Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 5 Jan 2024 16:44:53 +0530 Subject: [PATCH 19/86] Refactor common utilities --- .../hateoas/HateoasMetadataVisitor.java | 34 ++----------------- .../mapper/parameter/HateoasMapper.java | 34 ++----------------- .../mapper/utils/MapperCommonUtils.java | 27 +++++++++++++++ 3 files changed, 32 insertions(+), 63 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java index c529be1a1..bd1e0a9df 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java @@ -21,21 +21,18 @@ import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.api.symbols.ServiceDeclarationSymbol; import io.ballerina.compiler.api.symbols.Symbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; -import io.ballerina.compiler.syntax.tree.MetadataNode; import io.ballerina.compiler.syntax.tree.Node; -import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.NodeVisitor; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; -import io.ballerina.compiler.syntax.tree.SpecificFieldNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.openapi.service.mapper.utils.MapperCommonUtils; import java.util.Optional; import static io.ballerina.openapi.service.mapper.hateoas.ContextHolder.getHateoasContextHolder; +import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; +import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getValueForAnnotationFields; public class HateoasMetadataVisitor extends NodeVisitor { private final SemanticModel semanticModel; @@ -77,36 +74,9 @@ private String generateOperationId(FunctionDefinitionNode resourceFunction) { String cleanResourcePath = MapperCommonUtils.unescapeIdentifier(relativePath); String resName = (resourceFunction.functionName().text() + "_" + cleanResourcePath).replaceAll("\\{///\\}", "_"); - if (cleanResourcePath.equals("/")) { resName = resourceFunction.functionName().text(); } return MapperCommonUtils.getOperationId(resName); } - - private Optional getResourceConfigAnnotation(FunctionDefinitionNode resourceFunction) { - Optional metadata = resourceFunction.metadata(); - if (metadata.isEmpty()) { - return Optional.empty(); - } - MetadataNode metaData = metadata.get(); - NodeList annotations = metaData.annotations(); - return annotations.stream() - .filter(ann -> "http:ResourceConfig".equals(ann.annotReference().toString().trim())) - .findFirst(); - } - - private Optional getValueForAnnotationFields(AnnotationNode resourceConfigAnnotation, String fieldName) { - return resourceConfigAnnotation - .annotValue() - .map(MappingConstructorExpressionNode::fields) - .flatMap(fields -> - fields.stream() - .filter(fld -> fld instanceof SpecificFieldNode) - .map(fld -> (SpecificFieldNode) fld) - .filter(fld -> fieldName.equals(fld.fieldName().toString().trim())) - .findFirst() - ).flatMap(SpecificFieldNode::valueExpr) - .map(en -> en.toString().trim()); - } } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index c4d83612a..691d780e6 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -26,16 +26,14 @@ import java.util.HashMap; import io.swagger.v3.oas.models.links.Link; -import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.MetadataNode; -import io.ballerina.compiler.syntax.tree.SpecificFieldNode; -import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.openapi.service.mapper.hateoas.ContextHolder; import io.ballerina.openapi.service.mapper.hateoas.Resource; import io.ballerina.openapi.service.mapper.parameter.model.HateoasLink; +import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; +import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getValueForAnnotationFields; + public class HateoasMapper { public Map mapHateoasLinksToSwaggerLinks(int serviceId, @@ -88,30 +86,4 @@ public static HateoasLink parseHateoasLink(String input) { hateoasLink.setResourceMethod(keyValueMap.get("method")); return hateoasLink; } - - private Optional getResourceConfigAnnotation(FunctionDefinitionNode resourceFunction) { - Optional metadata = resourceFunction.metadata(); - if (metadata.isEmpty()) { - return Optional.empty(); - } - MetadataNode metaData = metadata.get(); - NodeList annotations = metaData.annotations(); - return annotations.stream() - .filter(ann -> "http:ResourceConfig".equals(ann.annotReference().toString().trim())) - .findFirst(); - } - - private Optional getValueForAnnotationFields(AnnotationNode resourceConfigAnnotation, String fieldName) { - return resourceConfigAnnotation - .annotValue() - .map(MappingConstructorExpressionNode::fields) - .flatMap(fields -> - fields.stream() - .filter(fld -> fld instanceof SpecificFieldNode) - .map(fld -> (SpecificFieldNode) fld) - .filter(fld -> fieldName.equals(fld.fieldName().toString().trim())) - .findFirst() - ).flatMap(SpecificFieldNode::valueExpr) - .map(en -> en.toString().trim()); - } } 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 bea027c6d..c7119a55e 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 @@ -641,4 +641,31 @@ public static Map getComponentsSchema(OpenAPI openAPI) { } return schemas; } + + public static Optional getResourceConfigAnnotation(FunctionDefinitionNode resourceFunction) { + Optional metadata = resourceFunction.metadata(); + if (metadata.isEmpty()) { + return Optional.empty(); + } + MetadataNode metaData = metadata.get(); + NodeList annotations = metaData.annotations(); + return annotations.stream() + .filter(ann -> "http:ResourceConfig".equals(ann.annotReference().toString().trim())) + .findFirst(); + } + + public static Optional getValueForAnnotationFields(AnnotationNode resourceConfigAnnotation, + String fieldName) { + return resourceConfigAnnotation + .annotValue() + .map(MappingConstructorExpressionNode::fields) + .flatMap(fields -> + fields.stream() + .filter(fld -> fld instanceof SpecificFieldNode) + .map(fld -> (SpecificFieldNode) fld) + .filter(fld -> fieldName.equals(fld.fieldName().toString().trim())) + .findFirst() + ).flatMap(SpecificFieldNode::valueExpr) + .map(en -> en.toString().trim()); + } } From 2594e9906dee0c6845187d5d361d06b2dabdd0fc Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 5 Jan 2024 16:50:07 +0530 Subject: [PATCH 20/86] Add logic to incorporate hateoas-metadata visitor execution --- .../service/mapper/ServiceToOpenAPIMapper.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index 7bea627e2..b3b799fb8 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -40,6 +40,7 @@ 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.hateoas.HateoasMetadataVisitor; import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; import io.ballerina.openapi.service.mapper.model.OASGenerationMetaInfo; import io.ballerina.openapi.service.mapper.model.OASResult; @@ -203,6 +204,7 @@ private static void extractServiceNodes(String serviceName, List availab public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) { ServiceDeclarationNode serviceDefinition = oasGenerationMetaInfo.getServiceDeclarationNode(); ModuleMemberVisitor moduleMemberVisitor = extractNodesFromProject(oasGenerationMetaInfo.getProject()); + extractHateoasLinkMetadata(oasGenerationMetaInfo.getProject()); Set listeners = moduleMemberVisitor.getListenerDeclarationNodes(); SemanticModel semanticModel = oasGenerationMetaInfo.getSemanticModel(); String openApiFileName = oasGenerationMetaInfo.getOpenApiFileName(); @@ -501,4 +503,16 @@ public static ModuleMemberVisitor extractNodesFromProject(Project project) { }); return balNodeVisitor; } + + public static void extractHateoasLinkMetadata(Project project) { + project.currentPackage().moduleIds().forEach(moduleId -> { + Module module = project.currentPackage().module(moduleId); + SemanticModel semanticModel = project.currentPackage().getCompilation().getSemanticModel(moduleId); + HateoasMetadataVisitor hateoasMetadataVisitor = new HateoasMetadataVisitor(semanticModel); + module.documentIds().forEach(documentId -> { + SyntaxTree syntaxTreeDoc = module.document(documentId).syntaxTree(); + syntaxTreeDoc.rootNode().accept(hateoasMetadataVisitor); + }); + }); + } } From 36358f3ff575f042f5197857a76fd8df050eacd9 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Fri, 5 Jan 2024 17:13:53 +0530 Subject: [PATCH 21/86] Add links to Api response --- .../service/mapper/OpenAPIResourceMapper.java | 28 +++++++++++++++---- .../service/mapper/OpenAPIServiceMapper.java | 2 +- .../mapper/parameter/HateoasMapper.java | 4 +-- .../mapper/parameter/model/HateoasLink.java | 8 +++--- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java index a83c7bf5f..192732388 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java @@ -22,6 +22,7 @@ import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.api.symbols.Documentable; import io.ballerina.compiler.api.symbols.Documentation; +import io.ballerina.compiler.api.symbols.ServiceDeclarationSymbol; import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.compiler.syntax.tree.Node; @@ -33,6 +34,7 @@ import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; import io.ballerina.openapi.service.mapper.model.OperationAdaptor; +import io.ballerina.openapi.service.mapper.parameter.HateoasMapper; import io.ballerina.openapi.service.mapper.parameter.ResponseMapper; import io.ballerina.openapi.service.mapper.type.TypeMapper; import io.ballerina.openapi.service.mapper.utils.MapperCommonUtils; @@ -42,6 +44,8 @@ import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.links.Link; +import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import java.util.ArrayList; @@ -70,6 +74,7 @@ public class OpenAPIResourceMapper { private final TypeMapper typeMapper; private final OpenAPI openAPI; private final List resources; + private final HateoasMapper hateoasMapper; /** * Initializes a resource parser for openApi. @@ -83,12 +88,13 @@ public class OpenAPIResourceMapper { this.errors = errors; this.moduleMemberVisitor = moduleMemberVisitor; this.typeMapper = typeMapper; + this.hateoasMapper = new HateoasMapper(); } - public void addMapping() { + public void addMapping(ServiceDeclarationNode service) { for (FunctionDefinitionNode resource : resources) { List methods = this.getHttpMethods(resource); - getResourcePath(resource, methods); + getResourcePath(resource, methods, service); } openAPI.setPaths(pathObject); } @@ -99,7 +105,7 @@ public void addMapping() { * @param resource The ballerina resource. * @param httpMethods Sibling methods related to operation. */ - private void getResourcePath(FunctionDefinitionNode resource, List httpMethods) { + private void getResourcePath(FunctionDefinitionNode resource, List httpMethods, ServiceDeclarationNode service) { String relativePath = MapperCommonUtils.generateRelativePath(resource); String cleanResourcePath = MapperCommonUtils.unescapeIdentifier(relativePath); Operation operation; @@ -113,7 +119,7 @@ private void getResourcePath(FunctionDefinitionNode resource, List httpM errors.add(error); } else { Optional operationAdaptor = convertResourceToOperation(resource, httpMethod, - cleanResourcePath); + cleanResourcePath, service); if (operationAdaptor.isPresent()) { operation = operationAdaptor.get().getOperation(); generatePathItem(httpMethod, pathObject, operation, cleanResourcePath); @@ -195,7 +201,8 @@ private void generatePathItem(String httpMethod, Paths path, Operation operation * @return Operation Adaptor object of given resource */ private Optional convertResourceToOperation(FunctionDefinitionNode resource, String httpMethod, - String generateRelativePath) { + String generateRelativePath, + ServiceDeclarationNode service) { OperationAdaptor op = new OperationAdaptor(); op.setHttpOperation(httpMethod); op.setPath(generateRelativePath); @@ -231,6 +238,17 @@ private Optional convertResourceToOperation(FunctionDefinition ResponseMapper responseMapper = new ResponseMapper(semanticModel, openAPI, resource, op, errors, moduleMemberVisitor); ApiResponses apiResponses = responseMapper.getApiResponses(); + Optional serviceDeclarationOpt = semanticModel.symbol(service); + ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceDeclarationOpt.get(); + int serviceId = serviceSymbol.hashCode(); + Map swaggerLinks = this.hateoasMapper.mapHateoasLinksToSwaggerLinks(serviceId, resource); + for (Map.Entry entry : apiResponses.entrySet()) { + int statusCode = Integer.parseInt(entry.getKey()); + if (statusCode >= 200 && statusCode < 300) { + entry.getValue().getLinks().putAll(swaggerLinks); + System.out.println("chipi chipi chapa chapa"); + } + } op.getOperation().setResponses(apiResponses); return Optional.of(op); } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIServiceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIServiceMapper.java index 0540d60c7..fdf99fe0d 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIServiceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIServiceMapper.java @@ -77,7 +77,7 @@ public OpenAPI convertServiceToOpenAPI(ServiceDeclarationNode service, OpenAPI o TypeMapper typeMapper = new TypeMapper(openapi, semanticModel, moduleMemberVisitor, errors); OpenAPIResourceMapper resourceMapper = new OpenAPIResourceMapper(openapi, resources, semanticModel, moduleMemberVisitor, errors, typeMapper); - resourceMapper.addMapping(); + resourceMapper.addMapping(service); ConstraintMapper constraintMapper = new ConstraintMapper(openapi, moduleMemberVisitor, errors); constraintMapper.addMapping(); return openapi; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index 691d780e6..c6c803404 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -61,7 +61,7 @@ public Map mapHateoasLinksToSwaggerLinks(int serviceId, private List getLinks(String linkedTo) { List links = new ArrayList<>(); - String[] linkArray = linkedTo.replaceAll("[\\[\\]]", "").split(",\\s*"); + String[] linkArray = linkedTo.replaceAll("[\\[\\]]", "").split("},\\\\s*"); for (String linkString : linkArray) { HateoasLink link = parseHateoasLink(linkString); links.add(link); @@ -82,7 +82,7 @@ public static HateoasLink parseHateoasLink(String input) { } } hateoasLink.setResourceName(keyValueMap.get("name")); - hateoasLink.setRel(keyValueMap.get("rel")); + hateoasLink.setRel(keyValueMap.get("relation")); hateoasLink.setResourceMethod(keyValueMap.get("method")); return hateoasLink; } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/model/HateoasLink.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/model/HateoasLink.java index d1805d358..f5d114f30 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/model/HateoasLink.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/model/HateoasLink.java @@ -20,7 +20,7 @@ public class HateoasLink { private String resourceName; - private String rel; + private String relation; private String resourceMethod; public String getResourceName() { @@ -32,11 +32,11 @@ public void setResourceName(String resourceName) { } public String getRel() { - return rel; + return relation; } - public void setRel(String rel) { - this.rel = rel; + public void setRel(String relation) { + this.relation = relation; } public String getResourceMethod() { From 01f814e3a74573b2cad7fb68e04a49090d3390c3 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 5 Jan 2024 17:38:42 +0530 Subject: [PATCH 22/86] Fix checkstyle violations --- .../openapi/service/mapper/OpenAPIResourceMapper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java index 192732388..988d943ce 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java @@ -105,7 +105,8 @@ public void addMapping(ServiceDeclarationNode service) { * @param resource The ballerina resource. * @param httpMethods Sibling methods related to operation. */ - private void getResourcePath(FunctionDefinitionNode resource, List httpMethods, ServiceDeclarationNode service) { + private void getResourcePath(FunctionDefinitionNode resource, List httpMethods, + ServiceDeclarationNode service) { String relativePath = MapperCommonUtils.generateRelativePath(resource); String cleanResourcePath = MapperCommonUtils.unescapeIdentifier(relativePath); Operation operation; @@ -246,7 +247,6 @@ private Optional convertResourceToOperation(FunctionDefinition int statusCode = Integer.parseInt(entry.getKey()); if (statusCode >= 200 && statusCode < 300) { entry.getValue().getLinks().putAll(swaggerLinks); - System.out.println("chipi chipi chapa chapa"); } } op.getOperation().setResponses(apiResponses); From 4a4ab1473ccb4033d76211c3f24002050301d761 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 5 Jan 2024 17:39:08 +0530 Subject: [PATCH 23/86] Refactor shared-context usage logic --- .../mapper/parameter/HateoasMapper.java | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index c6c803404..dca8fb98f 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -18,19 +18,19 @@ package io.ballerina.openapi.service.mapper.parameter; -import java.util.Map; -import java.util.List; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Optional; -import java.util.HashMap; - -import io.swagger.v3.oas.models.links.Link; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.openapi.service.mapper.hateoas.ContextHolder; import io.ballerina.openapi.service.mapper.hateoas.Resource; import io.ballerina.openapi.service.mapper.parameter.model.HateoasLink; +import io.swagger.v3.oas.models.links.Link; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import static io.ballerina.openapi.service.mapper.hateoas.ContextHolder.getHateoasContextHolder; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getValueForAnnotationFields; @@ -44,11 +44,10 @@ public Map mapHateoasLinksToSwaggerLinks(int serviceId, return Collections.emptyMap(); } List links = getLinks(linkedTo.get()); - ContextHolder contextHolder = new ContextHolder(); Map hateoasLinks = new HashMap<>(); for (HateoasLink link : links) { - Optional resource = contextHolder.getHateoasResource(serviceId, link.getResourceName(), - link.getResourceMethod()); + Optional resource = getHateoasContextHolder() + .getHateoasResource(serviceId, link.getResourceName(), link.getResourceMethod()); if (resource.isPresent()) { Link swaggerLink = new Link(); String operationId = resource.get().operationId(); From e00b24c8a27639ac6146c99413efd9fadd30c62f Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 5 Jan 2024 18:01:04 +0530 Subject: [PATCH 24/86] Remove unwanted quotations from hateoas resource-name --- .../openapi/service/mapper/hateoas/HateoasMetadataVisitor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java index bd1e0a9df..decec0921 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java @@ -63,8 +63,9 @@ public void visit(ServiceDeclarationNode serviceNode) { if (resourceName.isEmpty()) { return; } + String cleanedResourceName = resourceName.get().replaceAll("\"", ""); Resource hateoasResource = new Resource(resourceMethod, operationId); - getHateoasContextHolder().updateHateoasResource(serviceId, resourceName.get(), hateoasResource); + getHateoasContextHolder().updateHateoasResource(serviceId, cleanedResourceName, hateoasResource); } } } From 1294e6a6b388daa19a9bb92025bb80fb293d908c Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 5 Jan 2024 18:01:32 +0530 Subject: [PATCH 25/86] Fix swagger-links adding logic --- .../openapi/service/mapper/OpenAPIResourceMapper.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java index 988d943ce..ffeef0dcd 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java @@ -243,10 +243,12 @@ private Optional convertResourceToOperation(FunctionDefinition ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceDeclarationOpt.get(); int serviceId = serviceSymbol.hashCode(); Map swaggerLinks = this.hateoasMapper.mapHateoasLinksToSwaggerLinks(serviceId, resource); - for (Map.Entry entry : apiResponses.entrySet()) { - int statusCode = Integer.parseInt(entry.getKey()); - if (statusCode >= 200 && statusCode < 300) { - entry.getValue().getLinks().putAll(swaggerLinks); + if (!swaggerLinks.isEmpty()) { + for (Map.Entry entry : apiResponses.entrySet()) { + int statusCode = Integer.parseInt(entry.getKey()); + if (statusCode >= 200 && statusCode < 300) { + entry.getValue().setLinks(swaggerLinks); + } } } op.getOperation().setResponses(apiResponses); From 466a545bb063a7217e264f36527005e748c7cce7 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Fri, 5 Jan 2024 18:09:10 +0530 Subject: [PATCH 26/86] Add swagger links to Api response --- .../ballerina/openapi/service/mapper/OpenAPIResourceMapper.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java index ffeef0dcd..35463ecae 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java @@ -248,6 +248,8 @@ private Optional convertResourceToOperation(FunctionDefinition int statusCode = Integer.parseInt(entry.getKey()); if (statusCode >= 200 && statusCode < 300) { entry.getValue().setLinks(swaggerLinks); + ApiResponse apiResponse = new ApiResponse(); + apiResponse.setLinks(swaggerLinks); } } } From 08f04e3c375546a469d8f5c454d76c44d83b5d34 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Sun, 7 Jan 2024 23:28:38 +0530 Subject: [PATCH 27/86] Improve setting swagger links in Api response --- .../openapi/service/mapper/OpenAPIResourceMapper.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java index 35463ecae..b74711f3f 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java @@ -32,6 +32,7 @@ import io.ballerina.openapi.service.mapper.diagnostic.DiagnosticMessages; import io.ballerina.openapi.service.mapper.diagnostic.IncompatibleResourceDiagnostic; import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; +import io.ballerina.openapi.service.mapper.hateoas.Resource; import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; import io.ballerina.openapi.service.mapper.model.OperationAdaptor; import io.ballerina.openapi.service.mapper.parameter.HateoasMapper; @@ -239,6 +240,13 @@ private Optional convertResourceToOperation(FunctionDefinition ResponseMapper responseMapper = new ResponseMapper(semanticModel, openAPI, resource, op, errors, moduleMemberVisitor); ApiResponses apiResponses = responseMapper.getApiResponses(); + setSwaggerLinksInApiResponse(semanticModel, service, apiResponses, resource); + op.getOperation().setResponses(apiResponses); + return Optional.of(op); + } + + private void setSwaggerLinksInApiResponse(SemanticModel semanticModel, ServiceDeclarationNode service, ApiResponses apiResponses, + FunctionDefinitionNode resource) { Optional serviceDeclarationOpt = semanticModel.symbol(service); ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceDeclarationOpt.get(); int serviceId = serviceSymbol.hashCode(); @@ -253,8 +261,6 @@ private Optional convertResourceToOperation(FunctionDefinition } } } - op.getOperation().setResponses(apiResponses); - return Optional.of(op); } private boolean checkRestParamInResourcePath(OpenAPIParameterMapper openAPIParameterMapper) { From 0ff1631ef7f1c2ce24bd504db5d5a98391d81dfd Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Sun, 7 Jan 2024 23:31:53 +0530 Subject: [PATCH 28/86] Add tests --- .../generators/openapi/HateoasTests.java | 8 +- .../hateoas/hateoas_multiple_links.yaml | 113 ++++++++++++++++++ .../hateoas/hateoas_multiple_links.bal | 48 ++++++++ 3 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_multiple_links.yaml create mode 100644 openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java index eaa7c5848..ae8c28c07 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java @@ -26,9 +26,15 @@ public class HateoasTests { private static final Path RES_DIR = Paths.get("src/test/resources/ballerina-to-openapi").toAbsolutePath(); - @Test(description = "Automatic linking between resoruce functions") + @Test(description = "Automatic linking between resource functions") public void testHateoasAutomaticLinking() throws IOException { Path ballerinafilePath = RES_DIR.resolve("hateoas/hateoas_automatic_linking.bal"); TestUtils.compareWithGeneratedFile(ballerinafilePath, "hateoas/hateoas_automatic_linking.yaml"); } + + @Test(description = "Multiple reference links to a resource") + public void testHateoasMultipleLinks() throws IOException { + Path ballerinafilePath = RES_DIR.resolve("hateoas/hateoas_multiple_links.bal"); + TestUtils.compareWithGeneratedFile(ballerinafilePath, "hateoas/hateoas_multiple_links.yaml"); + } } diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_multiple_links.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_multiple_links.yaml new file mode 100644 index 000000000..a85dcfcb1 --- /dev/null +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_multiple_links.yaml @@ -0,0 +1,113 @@ +openapi: 3.0.1 +info: + title: PayloadV + version: 0.0.0 +servers: + - url: "{server}:{port}/payloadV" + variables: + server: + default: http://localhost + port: + default: "9090" +paths: + /locations: + get: + operationId: getLocations + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Location' + links: + room: + operationId: getLocationsIdRooms + payment: + operationId: getLocationsIdPayments + /locations/{id}/rooms: + get: + operationId: getLocationsIdRooms + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "202": + description: Accepted + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + /locations/{id}/payments: + get: + operationId: getLocationsIdPayments + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "202": + description: Accepted + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' +components: + schemas: + Location: + required: + - address + - id + - name + type: object + properties: + name: + type: string + id: + type: string + address: + type: string + ErrorPayload: + required: + - message + - method + - path + - reason + - status + - timestamp + type: object + properties: + timestamp: + type: string + status: + type: integer + format: int64 + reason: + type: string + message: + type: string + path: + type: string + method: + type: string diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal new file mode 100644 index 000000000..2becb1ece --- /dev/null +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal @@ -0,0 +1,48 @@ +// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/http; + +public type Location record { + string name; + string id; + string address; +}; + +service /payloadV on new http:Listener(9090) { + @http:ResourceConfig { + name: "Locations", + linkedTo: [ {name: "Rooms", relation: "room", method: "get"}, + {name: "Payments", relation: "payment", method: "get"}] + } + resource function get locations() returns Location { + return {name: "Cinnamon Lodge", id: "1001", address: "Habarana"}; + } + + @http:ResourceConfig { + name: "Rooms" + } + resource function get locations/[string id]/rooms() returns error? { + return; + } + + @http:ResourceConfig { + name: "Payments" + } + resource function get locations/[string id]/payments() returns error? { + return; + } +} From 576af53052729bf682a928b2cd34aed0272a2ea5 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Mon, 8 Jan 2024 10:15:34 +0530 Subject: [PATCH 29/86] Fix failing tests and checkstyle violations --- .../openapi/service/mapper/OpenAPIResourceMapper.java | 7 ++----- .../openapi/service/mapper/parameter/HateoasMapper.java | 2 +- .../expected_gen/hateoas/hateoas_multiple_links.yaml | 4 ++-- .../hateoas/hateoas_multiple_links.bal | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java index b74711f3f..e0734fe49 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIResourceMapper.java @@ -32,7 +32,6 @@ import io.ballerina.openapi.service.mapper.diagnostic.DiagnosticMessages; import io.ballerina.openapi.service.mapper.diagnostic.IncompatibleResourceDiagnostic; import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; -import io.ballerina.openapi.service.mapper.hateoas.Resource; import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; import io.ballerina.openapi.service.mapper.model.OperationAdaptor; import io.ballerina.openapi.service.mapper.parameter.HateoasMapper; @@ -245,8 +244,8 @@ private Optional convertResourceToOperation(FunctionDefinition return Optional.of(op); } - private void setSwaggerLinksInApiResponse(SemanticModel semanticModel, ServiceDeclarationNode service, ApiResponses apiResponses, - FunctionDefinitionNode resource) { + private void setSwaggerLinksInApiResponse(SemanticModel semanticModel, ServiceDeclarationNode service, + ApiResponses apiResponses, FunctionDefinitionNode resource) { Optional serviceDeclarationOpt = semanticModel.symbol(service); ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceDeclarationOpt.get(); int serviceId = serviceSymbol.hashCode(); @@ -256,8 +255,6 @@ private void setSwaggerLinksInApiResponse(SemanticModel semanticModel, ServiceDe int statusCode = Integer.parseInt(entry.getKey()); if (statusCode >= 200 && statusCode < 300) { entry.getValue().setLinks(swaggerLinks); - ApiResponse apiResponse = new ApiResponse(); - apiResponse.setLinks(swaggerLinks); } } } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index dca8fb98f..d43134600 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -60,7 +60,7 @@ public Map mapHateoasLinksToSwaggerLinks(int serviceId, private List getLinks(String linkedTo) { List links = new ArrayList<>(); - String[] linkArray = linkedTo.replaceAll("[\\[\\]]", "").split("},\\\\s*"); + String[] linkArray = linkedTo.replaceAll("[\\[\\]]", "").split("\\},\\s*"); for (String linkString : linkArray) { HateoasLink link = parseHateoasLink(linkString); links.add(link); diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_multiple_links.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_multiple_links.yaml index a85dcfcb1..e441734ee 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_multiple_links.yaml +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_multiple_links.yaml @@ -21,10 +21,10 @@ paths: schema: $ref: '#/components/schemas/Location' links: - room: - operationId: getLocationsIdRooms payment: operationId: getLocationsIdPayments + room: + operationId: getLocationsIdRooms /locations/{id}/rooms: get: operationId: getLocationsIdRooms diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal index 2becb1ece..f1cd84571 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal @@ -26,7 +26,7 @@ service /payloadV on new http:Listener(9090) { @http:ResourceConfig { name: "Locations", linkedTo: [ {name: "Rooms", relation: "room", method: "get"}, - {name: "Payments", relation: "payment", method: "get"}] + {name: "Payments", relation: "payment", method: "get"}] } resource function get locations() returns Location { return {name: "Cinnamon Lodge", id: "1001", address: "Habarana"}; From f5fc72bfb0f2844f2a4a1f361e8c26ebaf444970 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Mon, 8 Jan 2024 14:25:35 +0530 Subject: [PATCH 30/86] Add test cases --- .../mapper/parameter/HateoasMapper.java | 6 +- .../generators/openapi/HateoasTests.java | 6 ++ .../hateoas/hateoas_self_rel.yaml | 87 +++++++++++++++++++ .../hateoas/hateoas_self_rel.bal | 40 +++++++++ 4 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_self_rel.yaml create mode 100644 openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index d43134600..d5f57329f 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -81,7 +81,11 @@ public static HateoasLink parseHateoasLink(String input) { } } hateoasLink.setResourceName(keyValueMap.get("name")); - hateoasLink.setRel(keyValueMap.get("relation")); + if (keyValueMap.get("relation") == null) { + hateoasLink.setRel("self"); + } else { + hateoasLink.setRel(keyValueMap.get("relation")); + } hateoasLink.setResourceMethod(keyValueMap.get("method")); return hateoasLink; } diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java index ae8c28c07..4cb5bb14c 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java @@ -37,4 +37,10 @@ public void testHateoasMultipleLinks() throws IOException { Path ballerinafilePath = RES_DIR.resolve("hateoas/hateoas_multiple_links.bal"); TestUtils.compareWithGeneratedFile(ballerinafilePath, "hateoas/hateoas_multiple_links.yaml"); } + + @Test(description = "Self relation to a resource (default relation value)") + public void testHateoasSelfRelation() throws IOException { + Path ballerinaFilePath = RES_DIR.resolve("hateoas/hateoas_self_rel.bal"); + TestUtils.compareWithGeneratedFile(ballerinaFilePath, "hateoas/hateoas_self_rel.yaml"); + } } diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_self_rel.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_self_rel.yaml new file mode 100644 index 000000000..1c5db6aa6 --- /dev/null +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_self_rel.yaml @@ -0,0 +1,87 @@ +openapi: 3.0.1 +info: + title: PayloadV + version: 0.0.0 +servers: + - url: "{server}:{port}/payloadV" + variables: + server: + default: http://localhost + port: + default: "9090" +paths: + /locations: + get: + operationId: getLocations + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Location' + links: + self: + operationId: getLocationsIdRooms + /locations/{id}/rooms: + get: + operationId: getLocationsIdRooms + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "202": + description: Accepted + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' +components: + schemas: + Location: + required: + - address + - id + - name + type: object + properties: + name: + type: string + id: + type: string + address: + type: string + ErrorPayload: + required: + - message + - method + - path + - reason + - status + - timestamp + type: object + properties: + timestamp: + type: string + status: + type: integer + format: int64 + reason: + type: string + message: + type: string + path: + type: string + method: + type: string diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal new file mode 100644 index 000000000..08e0a11e2 --- /dev/null +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal @@ -0,0 +1,40 @@ +// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/http; + +public type Location record { + string name; + string id; + string address; +}; + +service /payloadV on new http:Listener(9090) { + @http:ResourceConfig { + name: "Locations", + linkedTo: [ {name: "Rooms", method: "get"} ] + } + resource function get locations() returns Location { + return {name: "Cinnamon Lodge", id: "1001", address: "Habarana"}; + } + + @http:ResourceConfig { + name: "Rooms" + } + resource function get locations/[string id]/rooms() returns error? { + return; + } +} From dae619888f7bc569667d2f5a2c878378f439e6fa Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Tue, 9 Jan 2024 09:05:09 +0530 Subject: [PATCH 31/86] Define the get type name interface --- .../openapi/service/mapper/ServiceToOpenAPIMapper.java | 3 ++- .../service/mapper/constraint/ConstraintMapper.java | 8 ++++++-- .../service/mapper/model/ModuleMemberVisitor.java | 10 ++++++++-- .../openapi/service/mapper/type/RecordTypeMapper.java | 7 ++++--- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index 737deb023..10374e881 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -202,7 +202,8 @@ public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) // 03. Filter path and component sections in OAS. // Generate openApi string for the mentioned service name. convertServiceToOpenAPI(serviceDefinition, openapi, semanticModel, moduleMemberVisitor, diagnostics); - ConstraintMapper constraintMapper = new ConstraintMapper(openapi, moduleMemberVisitor, diagnostics); + ConstraintMapper constraintMapper = new ConstraintMapper(openapi, moduleMemberVisitor, semanticModel, + diagnostics); constraintMapper.addMapping(); return new OASResult(openapi, diagnostics); } else { diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapper.java index 94b193842..444a293f2 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapper.java @@ -1,5 +1,6 @@ package io.ballerina.openapi.service.mapper.constraint; +import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.ExpressionNode; import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; @@ -44,12 +45,14 @@ public class ConstraintMapper { private final OpenAPI openAPI; private final ModuleMemberVisitor moduleMemberVisitor; + private final SemanticModel semanticModel; private final List diagnostics; - public ConstraintMapper(OpenAPI openAPI, ModuleMemberVisitor moduleMemberVisitor, + public ConstraintMapper(OpenAPI openAPI, ModuleMemberVisitor moduleMemberVisitor, SemanticModel semanticModel, List diagnostics) { this.openAPI = openAPI; this.moduleMemberVisitor = moduleMemberVisitor; + this.semanticModel = semanticModel; this.diagnostics = diagnostics; } @@ -60,7 +63,8 @@ public void addMapping() { } Map schemas = components.getSchemas(); for (Map.Entry schemaEntry : schemas.entrySet()) { - TypeDefinitionNode typeDefinitionNode = moduleMemberVisitor.getTypeDefinitionNode(schemaEntry.getKey()); + TypeDefinitionNode typeDefinitionNode = moduleMemberVisitor.getTypeDefinitionNode(schemaEntry.getKey(), + semanticModel); if (Objects.isNull(typeDefinitionNode)) { continue; } 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 c518cf940..df1e4db09 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 @@ -18,12 +18,16 @@ package io.ballerina.openapi.service.mapper.model; +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.Symbol; +import io.ballerina.compiler.api.symbols.TypeSymbol; import io.ballerina.compiler.syntax.tree.ListenerDeclarationNode; import io.ballerina.compiler.syntax.tree.NodeVisitor; 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; /** @@ -50,9 +54,11 @@ public Set getListenerDeclarationNodes() { return listenerDeclarationNodes; } - public TypeDefinitionNode getTypeDefinitionNode(String typeName) { + public TypeDefinitionNode getTypeDefinitionNode(String typeName, SemanticModel semanticModel) { for (TypeDefinitionNode typeDefinitionNode : typeDefinitionNodes) { - if (MapperCommonUtils.unescapeIdentifier(typeDefinitionNode.typeName().text()).equals(typeName)) { + Optional symbol = semanticModel.symbol(typeDefinitionNode); + if (symbol.isPresent() && symbol.get() instanceof TypeSymbol typeSymbol && + MapperCommonUtils.getTypeName(typeSymbol).equals(typeName)) { return typeDefinitionNode; } } 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 026df8511..9ffa19c24 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 @@ -16,6 +16,7 @@ package io.ballerina.openapi.service.mapper.type; +import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.api.symbols.IntersectionTypeSymbol; import io.ballerina.compiler.api.symbols.RecordFieldSymbol; import io.ballerina.compiler.api.symbols.RecordTypeSymbol; @@ -139,7 +140,7 @@ public static Map mapRecordFields(Map } if (recordFieldSymbol.hasDefaultValue()) { Object recordFieldDefaultValue = getRecordFieldDefaultValue(recordName, recordFieldName, - additionalData.moduleMemberVisitor()); + additionalData.semanticModel(), additionalData.moduleMemberVisitor()); if (Objects.nonNull(recordFieldDefaultValue)) { TypeMapper.setDefaultValue(recordFieldSchema, recordFieldDefaultValue); } else { @@ -154,9 +155,9 @@ public static Map mapRecordFields(Map return properties; } - public static Object getRecordFieldDefaultValue(String recordName, String fieldName, + public static Object getRecordFieldDefaultValue(String recordName, String fieldName, SemanticModel semanticModel, ModuleMemberVisitor moduleMemberVisitor) { - TypeDefinitionNode recordDefNode = moduleMemberVisitor.getTypeDefinitionNode(recordName); + TypeDefinitionNode recordDefNode = moduleMemberVisitor.getTypeDefinitionNode(recordName, semanticModel); if (Objects.isNull(recordDefNode) || !(recordDefNode.typeDescriptor() instanceof RecordTypeDescriptorNode)) { return null; } From dfd8b90677feb6d14d064fc589d4a904e815af8d Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 9 Jan 2024 10:25:07 +0530 Subject: [PATCH 32/86] Improve default self relation mapping --- .../openapi/service/mapper/parameter/HateoasMapper.java | 2 +- .../expected_gen/hateoas/hateoas_self_rel.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index d5f57329f..a01b5c862 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -82,7 +82,7 @@ public static HateoasLink parseHateoasLink(String input) { } hateoasLink.setResourceName(keyValueMap.get("name")); if (keyValueMap.get("relation") == null) { - hateoasLink.setRel("self"); + hateoasLink.setRel("_self"); } else { hateoasLink.setRel(keyValueMap.get("relation")); } diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_self_rel.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_self_rel.yaml index 1c5db6aa6..7f04fe77d 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_self_rel.yaml +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/hateoas_self_rel.yaml @@ -21,7 +21,7 @@ paths: schema: $ref: '#/components/schemas/Location' links: - self: + _self: operationId: getLocationsIdRooms /locations/{id}/rooms: get: From fe2858287e8dedb0cd39e571791acf1466b0b8e7 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 9 Jan 2024 12:36:08 +0530 Subject: [PATCH 33/86] Add snowpeak example to the tests --- .../openapi/service/mapper/Constants.java | 1 + .../mapper/parameter/HateoasMapper.java | 3 +- .../generators/openapi/HateoasTests.java | 6 + .../hateoas/snowpeak_hateoas.yaml | 210 ++++++++++++++++++ .../hateoas/snowpeak_hateoas.bal | 92 ++++++++ 5 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/snowpeak_hateoas.yaml create mode 100644 openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/Constants.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/Constants.java index eb7257986..4c388191a 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/Constants.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/Constants.java @@ -61,6 +61,7 @@ public final class Constants { public static final String OPTIONS = "OPTIONS"; public static final String HEAD = "HEAD"; public static final String OPENAPI_SUFFIX = "_openapi"; + public static final String OPENAPI_LINK_DEFAULT_REL = "_self"; public static final String SERVER = "server"; public static final String PORT = "port"; public static final String APPLICATION_PREFIX = "application/"; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index a01b5c862..bd0971ed8 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.Optional; +import static io.ballerina.openapi.service.mapper.Constants.OPENAPI_LINK_DEFAULT_REL; import static io.ballerina.openapi.service.mapper.hateoas.ContextHolder.getHateoasContextHolder; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getValueForAnnotationFields; @@ -82,7 +83,7 @@ public static HateoasLink parseHateoasLink(String input) { } hateoasLink.setResourceName(keyValueMap.get("name")); if (keyValueMap.get("relation") == null) { - hateoasLink.setRel("_self"); + hateoasLink.setRel(OPENAPI_LINK_DEFAULT_REL); } else { hateoasLink.setRel(keyValueMap.get("relation")); } diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java index 4cb5bb14c..c2662db49 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java @@ -43,4 +43,10 @@ public void testHateoasSelfRelation() throws IOException { Path ballerinaFilePath = RES_DIR.resolve("hateoas/hateoas_self_rel.bal"); TestUtils.compareWithGeneratedFile(ballerinaFilePath, "hateoas/hateoas_self_rel.yaml"); } + + @Test(description = "Hateoas with snowpeak example") + public void testHateoasSnowpeakExample() throws IOException { + Path ballerinaFilePath = RES_DIR.resolve("hateoas/snowpeak_hateoas.bal"); + TestUtils.compareWithGeneratedFile(ballerinaFilePath, "hateoas/snowpeak_hateoas.yaml"); + } } diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/snowpeak_hateoas.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/snowpeak_hateoas.yaml new file mode 100644 index 000000000..102af7a71 --- /dev/null +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/snowpeak_hateoas.yaml @@ -0,0 +1,210 @@ +openapi: 3.0.1 +info: + title: PayloadV + version: 0.0.0 +servers: + - url: "{server}:{port}/payloadV" + variables: + server: + default: http://localhost + port: + default: "9090" +paths: + /locations: + get: + operationId: getLocations + responses: + "200": + description: Ok + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Location' + links: + room: + operationId: getLocationsIdRooms + /locations/{id}/rooms: + get: + operationId: getLocationsIdRooms + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: startDate + in: query + required: true + schema: + type: string + - name: endDate + in: query + required: true + schema: + type: string + responses: + "200": + description: Ok + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Room' + links: + reservation: + operationId: postReservations + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + /reservations: + post: + operationId: postReservations + responses: + "201": + description: Created + content: + text/plain: + schema: + type: string + links: + cancel: + operationId: deleteReservationsId + edit: + operationId: putReservationsId + payment: + operationId: postReservationsId + /reservations/{id}: + put: + operationId: putReservationsId + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: Ok + content: + text/plain: + schema: + type: string + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + post: + operationId: postReservationsId + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "201": + description: Created + content: + text/plain: + schema: + type: string + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + delete: + operationId: deleteReservationsId + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: Ok + content: + text/plain: + schema: + type: string + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' +components: + schemas: + Location: + required: + - address + - id + - name + type: object + properties: + name: + type: string + id: + type: string + address: + type: string + Room: + required: + - category + - count + - currency + - id + - price + - status + - wifi + type: object + properties: + id: + type: string + category: + type: string + wifi: + type: boolean + status: + type: string + currency: + type: string + price: + type: integer + format: int64 + count: + type: integer + format: int64 + ErrorPayload: + required: + - message + - method + - path + - reason + - status + - timestamp + type: object + properties: + timestamp: + type: string + status: + type: integer + format: int64 + reason: + type: string + message: + type: string + path: + type: string + method: + type: string diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal new file mode 100644 index 000000000..02d83d001 --- /dev/null +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal @@ -0,0 +1,92 @@ +// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/http; + +public type Location record { + string name; + string id; + string address; +}; + +public type Room record { + string id; + string category; + boolean wifi; + string status; + string currency; + int price; + int count; +}; + +service /payloadV on new http:Listener(9090) { + @http:ResourceConfig { + name: "Locations", + linkedTo: [ {name: "Rooms", relation: "room", method: "get"}] + } + resource function get locations() returns Location[] { + return [{name: "Alps", id: "l1000", address: "NC 29384, some place, switzerland"}, + {name: "Pilatus", id: "l2000", address: "NC 29444, some place, switzerland"}]; + } + + @http:ResourceConfig { + name: "Rooms", + linkedTo: [{name: "Reservations", relation: "reservation", method: "post"}] + } + resource function get locations/[string id]/rooms(string startDate, string endDate) returns Room[] { + return [{ + "id": "r1000", + "category": "DELUXE", + "capacity": 5, + "wifi": true, + "status": "AVAILABLE", + "currency": "USD", + "price": 200, + "count": 3 + }]; + } + + @http:ResourceConfig { + name: "Reservations", + linkedTo: [ {name: "DeleteReservation", relation: "cancel", method: "delete"}, + {name: "UpdateReservation", relation: "edit", method: "put"}, + {name: "PayReservation", relation: "payment", method: "post"}] + } + resource function post reservations() returns string { + return "Room Reserved!"; + } + + @http:ResourceConfig { + name: "DeleteReservation" + } + resource function delete reservations/[string id]() returns string { + return "Reservation Deleted!"; + } + + @http:ResourceConfig { + name: "UpdateReservation" + } + resource function put reservations/[string id]() returns string { + return "Reservation Updated!"; + } + + @http:ResourceConfig { + name: "PayReservation" + } + resource function post reservations/[string id]() returns string { + return "Payment Successful!"; + } +} From 5ea199cbc319d8ba0abc0f5bdd2fe5dc30f7cd14 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 9 Jan 2024 21:13:03 +0530 Subject: [PATCH 34/86] Revert "Define the get type name interface" This reverts commit dae619888f7bc569667d2f5a2c878378f439e6fa. --- .../openapi/service/mapper/ServiceToOpenAPIMapper.java | 3 +-- .../service/mapper/constraint/ConstraintMapper.java | 8 ++------ .../service/mapper/model/ModuleMemberVisitor.java | 10 ++-------- .../openapi/service/mapper/type/RecordTypeMapper.java | 7 +++---- 4 files changed, 8 insertions(+), 20 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index 10374e881..737deb023 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -202,8 +202,7 @@ public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) // 03. Filter path and component sections in OAS. // Generate openApi string for the mentioned service name. convertServiceToOpenAPI(serviceDefinition, openapi, semanticModel, moduleMemberVisitor, diagnostics); - ConstraintMapper constraintMapper = new ConstraintMapper(openapi, moduleMemberVisitor, semanticModel, - diagnostics); + ConstraintMapper constraintMapper = new ConstraintMapper(openapi, moduleMemberVisitor, diagnostics); constraintMapper.addMapping(); return new OASResult(openapi, diagnostics); } else { diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapper.java index 444a293f2..94b193842 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/ConstraintMapper.java @@ -1,6 +1,5 @@ package io.ballerina.openapi.service.mapper.constraint; -import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.ExpressionNode; import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; @@ -45,14 +44,12 @@ public class ConstraintMapper { private final OpenAPI openAPI; private final ModuleMemberVisitor moduleMemberVisitor; - private final SemanticModel semanticModel; private final List diagnostics; - public ConstraintMapper(OpenAPI openAPI, ModuleMemberVisitor moduleMemberVisitor, SemanticModel semanticModel, + public ConstraintMapper(OpenAPI openAPI, ModuleMemberVisitor moduleMemberVisitor, List diagnostics) { this.openAPI = openAPI; this.moduleMemberVisitor = moduleMemberVisitor; - this.semanticModel = semanticModel; this.diagnostics = diagnostics; } @@ -63,8 +60,7 @@ public void addMapping() { } Map schemas = components.getSchemas(); for (Map.Entry schemaEntry : schemas.entrySet()) { - TypeDefinitionNode typeDefinitionNode = moduleMemberVisitor.getTypeDefinitionNode(schemaEntry.getKey(), - semanticModel); + TypeDefinitionNode typeDefinitionNode = moduleMemberVisitor.getTypeDefinitionNode(schemaEntry.getKey()); if (Objects.isNull(typeDefinitionNode)) { continue; } 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 df1e4db09..c518cf940 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 @@ -18,16 +18,12 @@ package io.ballerina.openapi.service.mapper.model; -import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.compiler.api.symbols.Symbol; -import io.ballerina.compiler.api.symbols.TypeSymbol; import io.ballerina.compiler.syntax.tree.ListenerDeclarationNode; import io.ballerina.compiler.syntax.tree.NodeVisitor; 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; /** @@ -54,11 +50,9 @@ public Set getListenerDeclarationNodes() { return listenerDeclarationNodes; } - public TypeDefinitionNode getTypeDefinitionNode(String typeName, SemanticModel semanticModel) { + public TypeDefinitionNode getTypeDefinitionNode(String typeName) { for (TypeDefinitionNode typeDefinitionNode : typeDefinitionNodes) { - Optional symbol = semanticModel.symbol(typeDefinitionNode); - if (symbol.isPresent() && symbol.get() instanceof TypeSymbol typeSymbol && - MapperCommonUtils.getTypeName(typeSymbol).equals(typeName)) { + if (MapperCommonUtils.unescapeIdentifier(typeDefinitionNode.typeName().text()).equals(typeName)) { return typeDefinitionNode; } } 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 9ffa19c24..026df8511 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 @@ -16,7 +16,6 @@ package io.ballerina.openapi.service.mapper.type; -import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.api.symbols.IntersectionTypeSymbol; import io.ballerina.compiler.api.symbols.RecordFieldSymbol; import io.ballerina.compiler.api.symbols.RecordTypeSymbol; @@ -140,7 +139,7 @@ public static Map mapRecordFields(Map } if (recordFieldSymbol.hasDefaultValue()) { Object recordFieldDefaultValue = getRecordFieldDefaultValue(recordName, recordFieldName, - additionalData.semanticModel(), additionalData.moduleMemberVisitor()); + additionalData.moduleMemberVisitor()); if (Objects.nonNull(recordFieldDefaultValue)) { TypeMapper.setDefaultValue(recordFieldSchema, recordFieldDefaultValue); } else { @@ -155,9 +154,9 @@ public static Map mapRecordFields(Map return properties; } - public static Object getRecordFieldDefaultValue(String recordName, String fieldName, SemanticModel semanticModel, + public static Object getRecordFieldDefaultValue(String recordName, String fieldName, ModuleMemberVisitor moduleMemberVisitor) { - TypeDefinitionNode recordDefNode = moduleMemberVisitor.getTypeDefinitionNode(recordName, semanticModel); + TypeDefinitionNode recordDefNode = moduleMemberVisitor.getTypeDefinitionNode(recordName); if (Objects.isNull(recordDefNode) || !(recordDefNode.typeDescriptor() instanceof RecordTypeDescriptorNode)) { return null; } From afc589d1a416a68bda5e1d0c02dfaa26837b0699 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 9 Jan 2024 21:38:31 +0530 Subject: [PATCH 35/86] Update license headers --- .../ballerina/openapi/service/mapper/hateoas/ContextHolder.java | 2 +- .../openapi/service/mapper/hateoas/HateoasMetadataVisitor.java | 2 +- .../io/ballerina/openapi/service/mapper/hateoas/Resource.java | 2 +- .../io/ballerina/openapi/service/mapper/hateoas/Service.java | 2 +- .../openapi/service/mapper/parameter/HateoasMapper.java | 2 +- .../io/ballerina/openapi/generators/openapi/HateoasTests.java | 2 +- .../ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal | 2 +- .../ballerina-to-openapi/hateoas/hateoas_multiple_links.bal | 2 +- .../resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal | 2 +- .../resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java index 569e888ab..fad7ad402 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java index decec0921..7dd4ffff0 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java index 18c237a37..07215c9d0 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java index 460e78324..dfd968829 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index bd0971ed8..1db2dfdc2 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java index c2662db49..985cfea8f 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal index acd75cbff..1609648bd 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal index f1cd84571..42c65ae2b 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal index 08e0a11e2..60dc804fd 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal index 02d83d001..1ad07caaf 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except From a47f83b4ad3d2368e8f18b5bc8a12cd06cfd92ff Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Wed, 10 Jan 2024 10:07:48 +0530 Subject: [PATCH 36/86] Update license header --- .../openapi/service/mapper/parameter/model/HateoasLink.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/model/HateoasLink.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/model/HateoasLink.java index f5d114f30..f80faf4da 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/model/HateoasLink.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/model/HateoasLink.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except From e3e2682044e8d3cd2577ce5657a48a9c1e9005ab Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 12 Jan 2024 09:46:02 +0530 Subject: [PATCH 37/86] Refactor naming conventions --- ...{ContextHolder.java => HateoasContextHolder.java} | 12 ++++++------ .../mapper/hateoas/HateoasMetadataVisitor.java | 2 +- .../service/mapper/parameter/HateoasMapper.java | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) rename ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/{ContextHolder.java => HateoasContextHolder.java} (88%) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java similarity index 88% rename from ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java rename to ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java index fad7ad402..2e4ddcf73 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/ContextHolder.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java @@ -23,19 +23,19 @@ import java.util.Objects; import java.util.Optional; -public final class ContextHolder { - private static ContextHolder instance; +public final class HateoasContextHolder { + private static HateoasContextHolder instance; private final List hateoasServices; - public ContextHolder() { + public HateoasContextHolder() { this.hateoasServices = new ArrayList<>(); } - public static ContextHolder getHateoasContextHolder() { - synchronized (ContextHolder.class) { + public static HateoasContextHolder getHateoasContextHolder() { + synchronized (HateoasContextHolder.class) { if (Objects.isNull(instance)) { - instance = new ContextHolder(); + instance = new HateoasContextHolder(); } } return instance; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java index 7dd4ffff0..4b4c227b1 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java @@ -30,7 +30,7 @@ import java.util.Optional; -import static io.ballerina.openapi.service.mapper.hateoas.ContextHolder.getHateoasContextHolder; +import static io.ballerina.openapi.service.mapper.hateoas.HateoasContextHolder.getHateoasContextHolder; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getValueForAnnotationFields; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index 1db2dfdc2..f9a15cd37 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -31,7 +31,7 @@ import java.util.Optional; import static io.ballerina.openapi.service.mapper.Constants.OPENAPI_LINK_DEFAULT_REL; -import static io.ballerina.openapi.service.mapper.hateoas.ContextHolder.getHateoasContextHolder; +import static io.ballerina.openapi.service.mapper.hateoas.HateoasContextHolder.getHateoasContextHolder; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getValueForAnnotationFields; From 06bf733da7ee76f5d6875e2aa5e168517ddd9d77 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Mon, 15 Jan 2024 12:14:58 +0530 Subject: [PATCH 38/86] Add hateoas support proposal to the docs --- .../proposals/hateoas_support.md | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 docs/ballerina-to-oas/proposals/hateoas_support.md diff --git a/docs/ballerina-to-oas/proposals/hateoas_support.md b/docs/ballerina-to-oas/proposals/hateoas_support.md new file mode 100644 index 000000000..4ae7487e0 --- /dev/null +++ b/docs/ballerina-to-oas/proposals/hateoas_support.md @@ -0,0 +1,164 @@ +# Proposal: Map Ballerina HATEOAS Links to OpenAPI Specification + +_Owners_: [@SachinAkash01](https://github.com/SachinAkash01) +_Reviewers_: [@lnash94](https://github.com/lnash94) [@TharmiganK](https://github.com/TharmiganK) +_Created_: 2023/10/25 +_Updated_: 2023/10/31 +_Issue_: [#4788](https://github.com/ballerina-platform/ballerina-library/issues/4788) + +## Summary +This proposal is to introduce the capabilities to support HATEOAS (Hypermedia As The Engine Of Application State) links +in the generated OpenAPI specification of the Ballerina OpenAPI tool. Note that in the rest of the proposal HATEOAS is +referred to as Hypermedia constraint. + +## Goals +- Enhance the Ballerina OpenAPI tool by adding support for Hypermedia constraints in the generated OpenAPI specification. + +## Motivation +OpenAPI is a widely used standard for documenting APIs. It allows developers to specify the structure and data types +expected in API requests and responses. + +Hypermedia constraints are one of the key principles in REST though it is often ignored. This principle emphasizes the +interconnection of resources, offering API users an experience similar to navigating the web. The existing tool for +generating OpenAPI specification from Ballerina code lacks support for including these Hypermedia constraints in the +OpenAPI specification. This means that when developers use Hypermedia constraints in their Ballerina code, this +information is not properly reflected in the OpenAPI specification. + +Overall, improving the OpenAPI tool to support Hypermedia constraints of the Ballerina programming language in the +generated OpenAPI specification is a worthwhile investment that will improve the overall developer experience. + +## Description +There are two ways of generating links between the resources in Ballerina, manually defining links between resources and +automatically generating links between resources. The feature implementation focuses on integrating Ballerina +Hypermedia constraints into generated OpenAPI specifications to accurately document the API. In this proposal we will +be addressing the automatic linking between the resources using `ResourceConfig` annotation in Ballerina. + +Currently in manual linking approach, we are generating the response component schema by including `*http:Links`. +There will be no changes to this behaviour of generating the OpenAPI specification. + +**Sample Ballerina Code (Manually defining links between resources):** +```ballerina +import ballerina/http; + +public type Link record { + string rel?; + string href; + string types?; + string methods?; +}; + +public type Location record {| + *http:Links; + string name; + string id; + string address; +|}; + +service /snowpeak on new http:Listener(9090) { + resource function get locations() returns Location { + return { + name: "Alps", + id: "l100", + address: "Switzerland", + _links: { + room: { + href: "/snowpeak/locations/{id}/rooms", + methods: ["GET"] + } + } + }; + } +} +``` + +**OpenAPI Specification (response component schema including the `*http:Links`):** +```yaml +Location: + - required: + - address + - id + - name + type: object + properties: + name: + type: string + description: Name of the location + id: + type: string + description: Unique identification + address: + type: string + description: Address of the location + links: + type: array + items: + type: object + properties: + rel: + type: string + default: "room" + href: + type: string + default: "/snowpeak/locations/{id}/rooms" + method: + type: string + default: "GET" +``` + +Following is the proposed way of reflecting Ballerina Hypermedia constraints using `ResourceConfig` annotation into +OpenAPI specification. + +**Sample Ballerina Code:** +```ballerina +service /snowpeak on new http:Listener(port) { + + @http:ResourceConfig { + name: "Locations", + linkedTo: [ {name: "Rooms", relation: "room", method: "get"} ] + } + resource function get locations() returns @http:Cache rep:Locations|rep:SnowpeakInternalError { + // some logic + } + + @http:ResourceConfig { + name: "Rooms" + } + resource function get locations/[string id]/rooms(string startDate, string endDate) + returns rep:Rooms|rep:SnowpeakInternalError { + // some logic + } +} +``` + +**Generated OpenAPI Specification:** +```yaml +paths: + /locations: + get: + operationId: getLocations + responses: + "200": + description: ok + content: + application/json: + schema: + $ref: '#/components/schemas/Location' + links: + room: + operationId: getLocationsIdRooms + /locations/{id}/rooms: + get: + operationId: getLocationsIdRooms + parameters: +``` + +> [!NOTE] +> Since OpenAPI doesn't support HATEOAS directly, there is no proper and direct way to reflect resource name +`@http:ResourceConfig { name: "Locations"}` in OpenAPI specification. This might be a problem when we generate +Ballerina service stub from OAS. + +## Testing +- Unit Testing: Evaluate the individual components and core logic of the Ballerina OpenAPI tool, focusing on functions, +methods, and modules to ensure correctness and Hypermedia constraint handling. +- Integration Testing: Assess the interaction and collaborations between various modules and components of the tool, +verifying the seamless integration of Hypermedia constraints into the OpenAPI specification. From 493cc64e330c6d188f0f8f3ce5a597031e182da2 Mon Sep 17 00:00:00 2001 From: Sachin Akash Date: Tue, 16 Jan 2024 11:11:59 +0530 Subject: [PATCH 39/86] Remove links from owners and reviewers --- docs/ballerina-to-oas/proposals/hateoas_support.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ballerina-to-oas/proposals/hateoas_support.md b/docs/ballerina-to-oas/proposals/hateoas_support.md index 4ae7487e0..25ac94a0e 100644 --- a/docs/ballerina-to-oas/proposals/hateoas_support.md +++ b/docs/ballerina-to-oas/proposals/hateoas_support.md @@ -1,7 +1,7 @@ # Proposal: Map Ballerina HATEOAS Links to OpenAPI Specification -_Owners_: [@SachinAkash01](https://github.com/SachinAkash01) -_Reviewers_: [@lnash94](https://github.com/lnash94) [@TharmiganK](https://github.com/TharmiganK) +_Owners_: @SachinAkash01 +_Reviewers_: @lnash94 @TharmiganK _Created_: 2023/10/25 _Updated_: 2023/10/31 _Issue_: [#4788](https://github.com/ballerina-platform/ballerina-library/issues/4788) From 4586bcc3f9c3aba21363ba29217073e92c647eff Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 16 Jan 2024 14:51:38 +0530 Subject: [PATCH 40/86] Fix checkstyle errors --- .../openapi/service/mapper/OpenAPIServiceMapper.java | 0 .../openapi/service/mapper/ResourceMapper.java | 11 ++++++----- .../service/mapper/ServiceToOpenAPIMapper.java | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIServiceMapper.java diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIServiceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/OpenAPIServiceMapper.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java index cf48ed41a..a8cf4d6f4 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java @@ -97,8 +97,8 @@ public void addMapping(SemanticModel semanticModel, ServiceDeclarationNode servi openAPI.setPaths(pathObject); } - private void generateResourceMapping(SemanticModel semanticModel, FunctionDefinitionNode resource, Components components, - ServiceDeclarationNode service) { + private void generateResourceMapping(SemanticModel semanticModel, FunctionDefinitionNode resource, + Components components, ServiceDeclarationNode service) { String relativePath = MapperCommonUtils.generateRelativePath(resource); String cleanResourcePath = MapperCommonUtils.unescapeIdentifier(relativePath); String httpMethod = resource.functionName().toString().trim(); @@ -108,8 +108,8 @@ private void generateResourceMapping(SemanticModel semanticModel, FunctionDefini resource.location()); additionalData.diagnostics().add(error); } else { - Optional operationDTO = convertResourceToOperation(semanticModel, resource, httpMethod, components, - cleanResourcePath, service); + Optional operationDTO = convertResourceToOperation(semanticModel, resource, httpMethod, + components, cleanResourcePath, service); if (operationDTO.isPresent()) { Operation operation = operationDTO.get().getOperation(); generatePathItem(httpMethod, pathObject, operation, cleanResourcePath); @@ -185,7 +185,8 @@ private void generatePathItem(String httpMethod, Paths path, Operation operation * * @return Operation Adaptor object of given resource */ - private Optional convertResourceToOperation(SemanticModel semanticModel, FunctionDefinitionNode resource, String httpMethod, + private Optional convertResourceToOperation(SemanticModel semanticModel, + FunctionDefinitionNode resource, String httpMethod, Components components, String generateRelativePath, ServiceDeclarationNode service) { OperationDTO op = new OperationDTO(); diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index 2f7e30bd5..f70eed6bb 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -36,10 +36,10 @@ import io.ballerina.openapi.service.mapper.diagnostic.ExceptionDiagnostic; import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; import io.ballerina.openapi.service.mapper.model.AdditionalData; -import io.ballerina.openapi.service.mapper.hateoas.HateoasMetadataVisitor; import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; import io.ballerina.openapi.service.mapper.model.OASGenerationMetaInfo; import io.ballerina.openapi.service.mapper.model.OASResult; +import io.ballerina.openapi.service.mapper.hateoas.HateoasMetadataVisitor; import io.ballerina.projects.Module; import io.ballerina.projects.Project; import io.swagger.v3.oas.models.OpenAPI; From 6e51648986b720abbde5a15ae8003f6c75318bd2 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 16 Jan 2024 15:08:15 +0530 Subject: [PATCH 41/86] Fix checkstyle violations --- .../mapper/ServiceToOpenAPIMapper.java | 2 +- .../mapper/parameter/ResponseMapper.java | 20 +++++++++---------- .../mapper/utils/MapperCommonUtils.java | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index f70eed6bb..1ab6b3ad7 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -35,11 +35,11 @@ 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.hateoas.HateoasMetadataVisitor; import io.ballerina.openapi.service.mapper.model.AdditionalData; import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; import io.ballerina.openapi.service.mapper.model.OASGenerationMetaInfo; import io.ballerina.openapi.service.mapper.model.OASResult; -import io.ballerina.openapi.service.mapper.hateoas.HateoasMetadataVisitor; import io.ballerina.projects.Module; import io.ballerina.projects.Project; import io.swagger.v3.oas.models.OpenAPI; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java index 37e7ebfa0..0efa215df 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java @@ -17,19 +17,19 @@ package io.ballerina.openapi.service.mapper.parameter; import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.compiler.api.symbols.Symbol; -import io.ballerina.compiler.api.symbols.TypeSymbol; -import io.ballerina.compiler.api.symbols.ResourceMethodSymbol; -import io.ballerina.compiler.api.symbols.UnionTypeSymbol; -import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; -import io.ballerina.compiler.api.symbols.IntersectionTypeSymbol; -import io.ballerina.compiler.api.symbols.TypeDefinitionSymbol; -import io.ballerina.compiler.api.symbols.RecordTypeSymbol; -import io.ballerina.compiler.api.symbols.RecordFieldSymbol; import io.ballerina.compiler.api.symbols.ClassSymbol; -import io.ballerina.compiler.api.symbols.TypeDescKind; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; +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.ResourceMethodSymbol; +import io.ballerina.compiler.api.symbols.Symbol; +import io.ballerina.compiler.api.symbols.TypeDefinitionSymbol; +import io.ballerina.compiler.api.symbols.TypeDescKind; +import io.ballerina.compiler.api.symbols.TypeSymbol; +import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; +import io.ballerina.compiler.api.symbols.UnionTypeSymbol; import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.NodeList; 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 fd2bb0b0e..309a17061 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 @@ -37,15 +37,15 @@ import io.ballerina.compiler.syntax.tree.AnnotationNode; import io.ballerina.compiler.syntax.tree.BasicLiteralNode; import io.ballerina.compiler.syntax.tree.ExpressionNode; +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode; 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.Node; import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; import io.ballerina.compiler.syntax.tree.ResourcePathParameterNode; -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.MetadataNode; import io.ballerina.compiler.syntax.tree.SeparatedNodeList; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import io.ballerina.compiler.syntax.tree.SpecificFieldNode; From adbcf0bf1207d13e3b33d747d47d3a1ad897f198 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 16 Jan 2024 15:26:58 +0530 Subject: [PATCH 42/86] Resolve checkstyle violations --- .../openapi/service/mapper/parameter/ResponseMapper.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java index 0efa215df..d8ca2f0f8 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/ResponseMapper.java @@ -18,8 +18,6 @@ import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.api.symbols.ClassSymbol; -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; import io.ballerina.compiler.api.symbols.IntersectionTypeSymbol; import io.ballerina.compiler.api.symbols.RecordFieldSymbol; import io.ballerina.compiler.api.symbols.RecordTypeSymbol; @@ -27,12 +25,14 @@ import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.api.symbols.TypeDefinitionSymbol; import io.ballerina.compiler.api.symbols.TypeDescKind; -import io.ballerina.compiler.api.symbols.TypeSymbol; 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.syntax.tree.ReturnTypeDescriptorNode; import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; import io.ballerina.openapi.service.mapper.model.AdditionalData; import io.ballerina.openapi.service.mapper.model.OperationDTO; import io.ballerina.openapi.service.mapper.parameter.model.CacheConfigAnnotation; From f6e213aa2070623ced91614124698ddfbffa83cf Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 16 Jan 2024 21:01:42 +0530 Subject: [PATCH 43/86] Resolve merge conflicts --- .../service/mapper/ResourceMapper.java | 30 +++--------- .../mapper/ResourceMapperInterface.java | 5 +- .../mapper/ServiceToOpenAPIMapper.java | 2 +- .../mapper/parameter/HateoasMapper.java | 2 +- .../mapper/response/ResponseMapper.java | 4 ++ .../response/ResponseMapperInterface.java | 3 ++ .../mapper/response/model/HateoasLink.java | 49 +++++++++++++++++++ .../openapi/generators/openapi/TestUtils.java | 1 + .../hateoas/snowpeak_hateoas.yaml | 25 +++++----- 9 files changed, 83 insertions(+), 38 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java index 552d304a4..b442a5a12 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java @@ -64,6 +64,7 @@ public class ResourceMapper implements ResourceMapperInterface { private final Paths pathObject = new Paths(); private final AdditionalData additionalData; private final OpenAPI openAPI; + private final SemanticModel semanticModel; private final List resources; private final boolean treatNilableAsOptional; private final HateoasMapper hateoasMapper; @@ -74,6 +75,7 @@ public class ResourceMapper implements ResourceMapperInterface { ResourceMapper(OpenAPI openAPI, List resources, SemanticModel semanticModel, AdditionalData additionalData, boolean treatNilableAsOptional) { this.openAPI = openAPI; + this.semanticModel = semanticModel; this.resources = resources; this.additionalData = additionalData; this.treatNilableAsOptional = treatNilableAsOptional; @@ -109,8 +111,8 @@ private void generateResourceMapping(SemanticModel semanticModel, FunctionDefini resource.location()); additionalData.diagnostics().add(error); } else { - Optional operationBuilder = convertResourceToOperation(resource, httpMethod, cleanResourcePath, - components); + Optional operationBuilder = convertResourceToOperation(resource, httpMethod, + cleanResourcePath, components, service); if (operationBuilder.isPresent()) { Operation operation = operationBuilder.get().getOperation(); generatePathItem(httpMethod, pathObject, operation, cleanResourcePath); @@ -187,7 +189,8 @@ private void generatePathItem(String httpMethod, Paths path, Operation operation * @return Operation Adaptor object of given resource */ private Optional convertResourceToOperation(FunctionDefinitionNode resource, String httpMethod, - String generateRelativePath, Components components) { + String generateRelativePath, Components components, + ServiceDeclarationNode service) { OperationBuilder operationBuilder = new OperationBuilder(); operationBuilder.setHttpOperation(httpMethod); operationBuilder.setPath(generateRelativePath); @@ -220,6 +223,7 @@ private Optional convertResourceToOperation(FunctionDefinition ResponseMapperInterface responseMapper = new ResponseMapper(resource, operationBuilder, components, additionalData); + setSwaggerLinksInApiResponse(semanticModel, service, responseMapper.getApiResponses(), resource); responseMapper.setApiResponses(); return Optional.of(operationBuilder); } @@ -265,24 +269,4 @@ private Map listAPIDocumentations(FunctionDefinitionNode resourc } return apiDocs; } - - private String generateRelativePath(FunctionDefinitionNode resource) { - - StringBuilder relativePath = new StringBuilder(); - relativePath.append("/"); - if (!resource.relativeResourcePath().isEmpty()) { - for (Node node: resource.relativeResourcePath()) { - if (node instanceof ResourcePathParameterNode pathNode) { - relativePath.append("{"); - relativePath.append(pathNode.paramName().get()); - relativePath.append("}"); - } else if ((resource.relativeResourcePath().size() == 1) && (node.toString().trim().equals("."))) { - return relativePath.toString(); - } else { - relativePath.append(node.toString().trim()); - } - } - } - return relativePath.toString(); - } } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperInterface.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperInterface.java index c1e196c01..eb6ae48dc 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperInterface.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperInterface.java @@ -17,7 +17,10 @@ */ package io.ballerina.openapi.service.mapper; +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; + public interface ResourceMapperInterface { - void addMapping(); + void addMapping(SemanticModel semanticModel, ServiceDeclarationNode serviceNode); } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index cfdec7728..3479e26f4 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -246,7 +246,7 @@ private static void convertServiceToOpenAPI(ServiceDeclarationNode serviceNode, } } AdditionalData additionalData = new AdditionalData(semanticModel, moduleMemberVisitor, diagnostics); - ResourceMapperInterface resourceMapper = new ResourceMapper(openAPI, resources, additionalData, + ResourceMapperInterface resourceMapper = new ResourceMapper(openAPI, resources, semanticModel, additionalData, isTreatNilableAsOptionalParameter(serviceNode)); resourceMapper.addMapping(semanticModel, serviceNode); } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index f9a15cd37..67e10d0f4 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -20,7 +20,7 @@ import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.openapi.service.mapper.hateoas.Resource; -import io.ballerina.openapi.service.mapper.parameter.model.HateoasLink; +import io.ballerina.openapi.service.mapper.response.model.HateoasLink; import io.swagger.v3.oas.models.links.Link; import java.util.ArrayList; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java index ffd10a28e..290d1e03b 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java @@ -105,6 +105,10 @@ public void setApiResponses() { operationBuilder.setApiResponses(apiResponses); } + public ApiResponses getApiResponses() { + return apiResponses; + } + private TypeSymbol getReturnTypeSymbol(FunctionDefinitionNode resourceNode) { Optional symbol = semanticModel.symbol(resourceNode); if (symbol.isEmpty() || !(symbol.get() instanceof ResourceMethodSymbol resourceMethodSymbol)) { diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperInterface.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperInterface.java index 32f4716f0..8bb2db4e0 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperInterface.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperInterface.java @@ -17,7 +17,10 @@ */ package io.ballerina.openapi.service.mapper.response; +import io.swagger.v3.oas.models.responses.ApiResponses; + public interface ResponseMapperInterface { void setApiResponses(); + ApiResponses getApiResponses(); } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/model/HateoasLink.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/model/HateoasLink.java index e69de29bb..0496c1a10 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/model/HateoasLink.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/model/HateoasLink.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.service.mapper.response.model; + +public class HateoasLink { + private String resourceName; + private String relation; + private String resourceMethod; + + public String getResourceName() { + return resourceName; + } + + public void setResourceName(String resourceName) { + this.resourceName = resourceName; + } + + public String getRel() { + return relation; + } + + public void setRel(String relation) { + this.relation = relation; + } + + public String getResourceMethod() { + return resourceMethod; + } + + public void setResourceMethod(String resourceMethod) { + this.resourceMethod = resourceMethod; + } +} diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/TestUtils.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/TestUtils.java index bc70f1472..21bfb2812 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/TestUtils.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/TestUtils.java @@ -70,6 +70,7 @@ public static List compareWithGeneratedFile(OASContract ballerinaFilePath, tempDir, null, false); if (Files.exists(tempDir.resolve("payloadV_openapi.yaml"))) { String generatedYaml = getStringFromGivenBalFile(tempDir, "payloadV_openapi.yaml"); + System.out.println(generatedYaml); generatedYaml = (generatedYaml.trim()).replaceAll("\\s+", ""); expectedYamlContent = (expectedYamlContent.trim()).replaceAll("\\s+", ""); Assert.assertTrue(generatedYaml.contains(expectedYamlContent)); diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/snowpeak_hateoas.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/snowpeak_hateoas.yaml index b48b87b1a..463fdadb1 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/snowpeak_hateoas.yaml +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/hateoas/snowpeak_hateoas.yaml @@ -168,6 +168,19 @@ components: type: string method: type: string + Location: + required: + - address + - id + - name + type: object + properties: + name: + type: string + id: + type: string + address: + type: string Room: required: - category @@ -195,15 +208,3 @@ components: count: type: integer format: int64 - Location: - required: - - address - - id - - name - type: object - properties: - name: - type: string - id: - type: string - address: From 00d5d2142c562e6c6fcb73bf1c24e47ac91c652a Mon Sep 17 00:00:00 2001 From: Sachin Akash Date: Tue, 16 Jan 2024 21:02:47 +0530 Subject: [PATCH 44/86] Delete docs/ballerina-to-oas/proposals/constraint-support.md --- .../proposals/constraint-support.md | 352 ------------------ 1 file changed, 352 deletions(-) delete mode 100644 docs/ballerina-to-oas/proposals/constraint-support.md diff --git a/docs/ballerina-to-oas/proposals/constraint-support.md b/docs/ballerina-to-oas/proposals/constraint-support.md deleted file mode 100644 index 6b3f2da72..000000000 --- a/docs/ballerina-to-oas/proposals/constraint-support.md +++ /dev/null @@ -1,352 +0,0 @@ -# Proposal: Map Ballerina constraints to OpenAPI Specification - -_Owners_: @SachinAkash01 -_Reviewers_: @lnash94 @TharmiganK -_Created_: 2023/09/08 -_Updated_: 2023/09/21 -_Issue_: [#4788](https://github.com/ballerina-platform/ballerina-library/issues/4788) - -## Summary -Implement the capabilities to support constraint validations in the generated OpenAPI specification of the Ballerina -OpenAPI tool. - -## Goals -- Enhance the Ballerina OpenAPI tool by adding support for constraint validations in the generated OpenAPI -specification. - -## Motivation -OpenAPI is a widely used standard for documenting APIs. It allows developers to specify the structure and data types -expected in API requests and responses. One of its valuable features is built-in schema validation, which ensures that -the data exchanged between different software components conforms to the defined structure and types. - -Constraints are the OpenAPI schema validations equivalent in the Ballerina programming language. The existing tool for -generating OpenAPI specification from Ballerina code lacks support for including these constraints in the OpenAPI -specification. This means that when developers use constraints in their Ballerina code, this information is not properly -reflected in the OpenAPI specification. - -Overall, improving the OpenAPI tool to support Ballerina constraints in the generated OpenAPI specification is a -worthwhile investment that will improve the overall developer experience. - -## Description -The feature implementation focuses on integrating Ballerina constraints into generated OpenAPI specifications to -accurately document the API. Following is the proposed list of Ballerina constraint types and the corresponding -OpenAPI schema validations. - -**1. OpenAPI data type: integer (formats: int32, int64) : (ballerina mapping): int** - - - -
- -| Ballerina Mapping | Keywords | -|-------------------------------------------------| -- | -| `@constraint:Int { minValue: }` | minimum | -| `@constraint:Int { maxValue: }` | maximum | -| `@constraint:Int { minValueExclusive: }` | exclusiveMinimum | -| `@constraint:Int { maxValueExclusive: }` | exclusiveMaximum | -
- - - -1.1. Sample Ballerina Code: -```ballerina -import ballerina/http; -import ballerina/constraint; - -@constraint:Int{ - minValue: 5, - maxValue: 20 -} -public type Age int; - -type Person record{ - Age age?; -}; - -service / on new http:Listener(9090){ - resource function post newPerson(Person body) returns error?{ - return; - } -} -``` - -1.2. Generated OpenAPI Specification: -```yaml -components: - schemas: - Age: - minimum: 5 - maximum: 20 - type: integer - format: int32 - Person: - type: object - properties: - age: - $ref: '#/components/schemas/Age' -``` - -**2. OpenAPI data type: number (format: float) : (ballerina mapping): float** - - - -
- -| Ballerina Mapping | Keywords | -|---------------------------------------------------| -- | -| `@constraint:Float { minValue: }` | minimum | -| `@constraint:Float { maxValue: }` | maximum | -| `@constraint:Float { minValueExclusive: }` | exclusiveMinimum | -| `@constraint:Float { maxValueExclusive: }` | exclusiveMaximum | -
- - - -2.1. Ballerina Sample Code: -```ballerina -import ballerina/http; -import ballerina/constraint; - -@constraint:Float{ - maxValue: 1000.5 -} -public type Salary float; - -type Person record{ - Salary salary?; -}; - -service / on new http:Listener(9090){ - resource function post newPerson(Person body) returns error?{ - return; - } -} -``` - -2.2. Generated OpenAPI Specification: -```yaml -components: - schemas: - Salary: - maximum: 1000.5 - type: number - format: float - Person: - type: object - properties: - salary: - $ref: '#/components/schemas/Salary' -``` - -**3. OpenAPI data type: number (format: decimal) : (ballerina mapping): decimal** - - - -
- -| Ballerina Mapping | Keywords | -|----------------------------------------------------| -- | -| `@constraint:Number { minValue: }` | minimum | -| `@constraint:Number { maxValue: }` | maximum | -| `@constraint:Number { minValueExclusive: }` | exclusiveMinimum | -| `@constraint:Number { maxValueExclusive: }` | exclusiveMaximum | -
- - - -3.1. Sample Ballerina Code: -```ballerina -import ballerina/http; -import ballerina/constraint; - -@constraint:Number{ - maxValueExclusive: 5.1 -} -public type Rating decimal; - -type Hotel record{ - Rating rate?; -}; - -service / on new http:Listener(9090){ - resource function post newHotel(Hotel body) returns error?{ - return; - } -} -``` - -3.2. Generated OpenAPI Specification: -```yaml -components: - schemas: - Rating: - maximum: 5.1 - exclusiveMaximum: true - type: number - format: decimal - Hotel: - type: object - properties: - rate: - $ref: '#/components/schemas/Rating' -``` - -**4. OpenAPI data type: string : (ballerina mapping): string** - - - -
- -| Ballerina Mapping | Keywords | -|--------------------------------------------| -- | -| `@constraint:String { minLength: }` | minLength | -| `@constraint:String { maxLength: }` | maxLength | -| `@constraint:String { length: }` | minLength: ``, maxLength: `` | -| `@constraint:String { pattern: }` | pattern | -
- - - -4.1.1. Sample Ballerina Code (minLength, maxLength, pattern): -```ballerina -import ballerina/http; -import ballerina/constraint; - -@constraint:String{ - minLength: 5, - maxLength: 20, - pattern: re `^[a-zA-Z0-9_]+$` -} -public type Username string; - -type Person record{ - Username username?; -}; - -service / on new http:Listener(9090){ - resource function post newUser(Person body) returns error?{ - return; - } -} -``` - -4.1.2. Generated OpenAPI Specification: -```yaml -components: - schemas: - Username: - minLength: 3 - maxLength: 20 - pattern: "^[a-zA-Z0-9_]+$" - type: string - Person: - type: object - properties: - username: - $ref: '#/components/schemas/Username' -``` - -4.2.1. Sample Ballerina Code (length): -```ballerina -import ballerina/http; -import ballerina/constraint; - -@constraint:String{ - length: 5 -} -public type ID string; - -type Employee record{ - ID id; -}; - -service / on new http:Listener(9090){ - resource function post newEmployee(Employee body) returns error?{ - return; - } -} -``` - -4.2.2. Generated OpenAPI Specification: -```yaml -components: - schemas: - ID: - minLength: 5 - maxLength: 5 - type: string - Employee: - type: object - properties: - id: - $ref: '#/components/schemas/ID' -``` - -**5. OpenAPI data type: array : (ballerina mapping): array** - - - -
- -| Ballerina Mapping | Keywords | -|---------------------------------------------| -- | -| `@constraint:Array { minLength: }` | minItems | -| `@constraint:Array { maxLength: }` | maxItems | -| `@constraint:Array { length: }` | minItems: ``, maxItems: `` | - -
- - - -5.1. Sample Ballerina Code: -```ballerina -import ballerina/http; -import ballerina/constraint; - -@constraint:String{ - maxLength: 23 -} -public type HobbyItemsString string; - -@constraint:Array{ - minLength: 2, - maxLength: 5 -} -public type Hobby HobbyItemsString[]; - -public type person record{ - Hobby hobby?; -}; - -service / on new http:Listener(9090){ - resource function post hobby(Person body) returns error?{ - return; - } -} -``` - -5.2. Generated OpenAPI Specification: -```yaml -components: - schemas: - HobbyItemsString: - maxLength: 23 - type: string - Hobby: - minLength: 2 - maxLength: 5 - type: array - items: - $ref: '#/components/schemas/HobbyItemsString' - Person: - type: object - properties: - hobby: - $ref: '#/components/schemas/Hobby' -``` - -## Testing -- **Unit Testing:** evaluate the individual components and core logic of the Ballerina OpenAPI tool, focusing on -functions, methods, and modules to ensure correctness and constraint handling. -- **Integration Testing:** assess the interactions and collaborations between various modules and components of the tool, -verifying the seamless integration of data validation constraints and rule generation into the OpenAPI specification. From b34c246ee6c7ec8c50c22f2f5a7e82563bfe0e51 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 16 Jan 2024 21:05:55 +0530 Subject: [PATCH 45/86] Resolve checkstyle violations --- .../java/io/ballerina/openapi/generators/openapi/TestUtils.java | 1 - 1 file changed, 1 deletion(-) diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/TestUtils.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/TestUtils.java index 21bfb2812..bc70f1472 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/TestUtils.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/TestUtils.java @@ -70,7 +70,6 @@ public static List compareWithGeneratedFile(OASContract ballerinaFilePath, tempDir, null, false); if (Files.exists(tempDir.resolve("payloadV_openapi.yaml"))) { String generatedYaml = getStringFromGivenBalFile(tempDir, "payloadV_openapi.yaml"); - System.out.println(generatedYaml); generatedYaml = (generatedYaml.trim()).replaceAll("\\s+", ""); expectedYamlContent = (expectedYamlContent.trim()).replaceAll("\\s+", ""); Assert.assertTrue(generatedYaml.contains(expectedYamlContent)); From 99ffc68adad541617100a00bd7ebbc81c5d86bf4 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Wed, 17 Jan 2024 11:38:28 +0530 Subject: [PATCH 46/86] Fix formats --- .../openapi/service/mapper/parameter/HateoasMapper.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index 67e10d0f4..5ba1cdafe 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -82,11 +82,7 @@ public static HateoasLink parseHateoasLink(String input) { } } hateoasLink.setResourceName(keyValueMap.get("name")); - if (keyValueMap.get("relation") == null) { - hateoasLink.setRel(OPENAPI_LINK_DEFAULT_REL); - } else { - hateoasLink.setRel(keyValueMap.get("relation")); - } + hateoasLink.setRel(keyValueMap.getOrDefault("relation", OPENAPI_LINK_DEFAULT_REL)); hateoasLink.setResourceMethod(keyValueMap.get("method")); return hateoasLink; } From b6fc12b6c45ee7274a6d32df5552243478295d71 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Wed, 17 Jan 2024 11:46:01 +0530 Subject: [PATCH 47/86] Refactor hypermedia support --- .../service/mapper/ResourceMapper.java | 30 +++++++++++-------- .../mapper/ResourceMapperInterface.java | 2 +- .../mapper/ServiceToOpenAPIMapper.java | 15 ++++++---- .../mapper/hateoas/HateoasContextHolder.java | 11 +++---- .../hateoas/HateoasMetadataVisitor.java | 7 +++-- .../service/mapper/hateoas/Service.java | 8 ++++- .../mapper/parameter/HateoasMapper.java | 4 +-- 7 files changed, 47 insertions(+), 30 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java index b442a5a12..b4727741a 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java @@ -64,7 +64,7 @@ public class ResourceMapper implements ResourceMapperInterface { private final Paths pathObject = new Paths(); private final AdditionalData additionalData; private final OpenAPI openAPI; - private final SemanticModel semanticModel; + private final String packageId; private final List resources; private final boolean treatNilableAsOptional; private final HateoasMapper hateoasMapper; @@ -72,17 +72,17 @@ public class ResourceMapper implements ResourceMapperInterface { /** * Initializes a resource parser for openApi. */ - ResourceMapper(OpenAPI openAPI, List resources, SemanticModel semanticModel, + ResourceMapper(OpenAPI openAPI, List resources, String packageId, AdditionalData additionalData, boolean treatNilableAsOptional) { this.openAPI = openAPI; - this.semanticModel = semanticModel; + this.packageId = packageId; this.resources = resources; this.additionalData = additionalData; this.treatNilableAsOptional = treatNilableAsOptional; this.hateoasMapper = new HateoasMapper(); } - public void addMapping(SemanticModel semanticModel, ServiceDeclarationNode service) { + public void addMapping(String packageId, SemanticModel semanticModel, ServiceDeclarationNode service) { Components components = openAPI.getComponents(); if (components == null) { components = new Components(); @@ -92,7 +92,7 @@ public void addMapping(SemanticModel semanticModel, ServiceDeclarationNode servi components.setSchemas(new TreeMap<>()); } for (FunctionDefinitionNode resource : resources) { - generateResourceMapping(semanticModel, resource, components, service); + generateResourceMapping(packageId, semanticModel, resource, components, service); } if (components.getSchemas().isEmpty()) { openAPI.setComponents(null); @@ -100,7 +100,7 @@ public void addMapping(SemanticModel semanticModel, ServiceDeclarationNode servi openAPI.setPaths(pathObject); } - private void generateResourceMapping(SemanticModel semanticModel, FunctionDefinitionNode resource, + private void generateResourceMapping(String packageId, SemanticModel semanticModel, FunctionDefinitionNode resource, Components components, ServiceDeclarationNode service) { String relativePath = MapperCommonUtils.generateRelativePath(resource); String cleanResourcePath = MapperCommonUtils.unescapeIdentifier(relativePath); @@ -111,8 +111,8 @@ private void generateResourceMapping(SemanticModel semanticModel, FunctionDefini resource.location()); additionalData.diagnostics().add(error); } else { - Optional operationBuilder = convertResourceToOperation(resource, httpMethod, - cleanResourcePath, components, service); + Optional operationBuilder = convertResourceToOperation(packageId, semanticModel, resource, + httpMethod, cleanResourcePath, components, service); if (operationBuilder.isPresent()) { Operation operation = operationBuilder.get().getOperation(); generatePathItem(httpMethod, pathObject, operation, cleanResourcePath); @@ -188,7 +188,8 @@ private void generatePathItem(String httpMethod, Paths path, Operation operation * * @return Operation Adaptor object of given resource */ - private Optional convertResourceToOperation(FunctionDefinitionNode resource, String httpMethod, + private Optional convertResourceToOperation(String packageId, SemanticModel semanticModel, + FunctionDefinitionNode resource, String httpMethod, String generateRelativePath, Components components, ServiceDeclarationNode service) { OperationBuilder operationBuilder = new OperationBuilder(); @@ -223,17 +224,20 @@ private Optional convertResourceToOperation(FunctionDefinition ResponseMapperInterface responseMapper = new ResponseMapper(resource, operationBuilder, components, additionalData); - setSwaggerLinksInApiResponse(semanticModel, service, responseMapper.getApiResponses(), resource); + + setSwaggerLinksInApiResponse(packageId, semanticModel, service, responseMapper.getApiResponses(), resource); responseMapper.setApiResponses(); return Optional.of(operationBuilder); } - private void setSwaggerLinksInApiResponse(SemanticModel semanticModel, ServiceDeclarationNode service, - ApiResponses apiResponses, FunctionDefinitionNode resource) { + private void setSwaggerLinksInApiResponse(String packageId, SemanticModel semanticModel, + ServiceDeclarationNode service, ApiResponses apiResponses, + FunctionDefinitionNode resource) { Optional serviceDeclarationOpt = semanticModel.symbol(service); ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceDeclarationOpt.get(); int serviceId = serviceSymbol.hashCode(); - Map swaggerLinks = this.hateoasMapper.mapHateoasLinksToSwaggerLinks(serviceId, resource); + Map swaggerLinks = this.hateoasMapper.mapHateoasLinksToSwaggerLinks(packageId, serviceId, + resource); if (!swaggerLinks.isEmpty()) { for (Map.Entry entry : apiResponses.entrySet()) { int statusCode = Integer.parseInt(entry.getKey()); diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperInterface.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperInterface.java index eb6ae48dc..a7a81d25f 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperInterface.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperInterface.java @@ -22,5 +22,5 @@ public interface ResourceMapperInterface { - void addMapping(SemanticModel semanticModel, ServiceDeclarationNode serviceNode); + void addMapping(String packageId, SemanticModel semanticModel, ServiceDeclarationNode serviceNode); } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index 3479e26f4..1a8cd03e7 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -40,8 +40,9 @@ import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; import io.ballerina.openapi.service.mapper.model.OASGenerationMetaInfo; import io.ballerina.openapi.service.mapper.model.OASResult; +import io.ballerina.projects.*; import io.ballerina.projects.Module; -import io.ballerina.projects.Project; +import io.ballerina.projects.Package; import io.swagger.v3.oas.models.OpenAPI; import java.nio.file.Path; @@ -204,7 +205,8 @@ public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) serversMapper.setServers(); // 03. Filter path and component sections in OAS. // Generate openApi string for the mentioned service name. - convertServiceToOpenAPI(serviceDefinition, openapi, semanticModel, moduleMemberVisitor, diagnostics); + String packageId = oasGenerationMetaInfo.getProject().currentPackage().packageId().id().toString(); + convertServiceToOpenAPI(packageId, serviceDefinition, openapi, semanticModel, moduleMemberVisitor, diagnostics); ConstraintMapperInterface constraintMapper = new ConstraintMapper(openapi, moduleMemberVisitor, diagnostics); constraintMapper.addMapping(); @@ -234,7 +236,7 @@ public static ModuleMemberVisitor extractNodesFromProject(Project project) { return balNodeVisitor; } - private static void convertServiceToOpenAPI(ServiceDeclarationNode serviceNode, OpenAPI openAPI, + private static void convertServiceToOpenAPI(String packageId, ServiceDeclarationNode serviceNode, OpenAPI openAPI, SemanticModel semanticModel, ModuleMemberVisitor moduleMemberVisitor, List diagnostics) { NodeList functions = serviceNode.members(); @@ -246,9 +248,9 @@ private static void convertServiceToOpenAPI(ServiceDeclarationNode serviceNode, } } AdditionalData additionalData = new AdditionalData(semanticModel, moduleMemberVisitor, diagnostics); - ResourceMapperInterface resourceMapper = new ResourceMapper(openAPI, resources, semanticModel, additionalData, + ResourceMapperInterface resourceMapper = new ResourceMapper(openAPI, resources, packageId, additionalData, isTreatNilableAsOptionalParameter(serviceNode)); - resourceMapper.addMapping(semanticModel, serviceNode); + resourceMapper.addMapping(packageId, semanticModel, serviceNode); } private static boolean isTreatNilableAsOptionalParameter(ServiceDeclarationNode serviceNode) { @@ -270,10 +272,11 @@ private static boolean isTreatNilableAsOptionalParameter(ServiceDeclarationNode } public static void extractHateoasLinkMetadata(Project project) { + String packageId = project.currentPackage().packageId().id().toString(); project.currentPackage().moduleIds().forEach(moduleId -> { Module module = project.currentPackage().module(moduleId); SemanticModel semanticModel = project.currentPackage().getCompilation().getSemanticModel(moduleId); - HateoasMetadataVisitor hateoasMetadataVisitor = new HateoasMetadataVisitor(semanticModel); + HateoasMetadataVisitor hateoasMetadataVisitor = new HateoasMetadataVisitor(packageId, semanticModel); module.documentIds().forEach(documentId -> { SyntaxTree syntaxTreeDoc = module.document(documentId).syntaxTree(); syntaxTreeDoc.rootNode().accept(hateoasMetadataVisitor); diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java index 2e4ddcf73..dcab6039c 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java @@ -41,12 +41,12 @@ public static HateoasContextHolder getHateoasContextHolder() { return instance; } - public void updateHateoasResource(int serviceId, String resourceName, Resource resource) { + public void updateHateoasResource(String packageId, int serviceId, String resourceName, Resource resource) { Optional hateoasService = this.hateoasServices.stream() - .filter(svc -> svc.getServiceId() == serviceId) + .filter(svc -> packageId.equals(svc.getPackageId()) && svc.getServiceId() == serviceId) .findFirst(); if (hateoasService.isEmpty()) { - Service service = new Service(serviceId); + Service service = new Service(packageId, serviceId); service.addResource(resourceName, resource); this.hateoasServices.add(service); return; @@ -55,9 +55,10 @@ public void updateHateoasResource(int serviceId, String resourceName, Resource r service.addResource(resourceName, resource); } - public Optional getHateoasResource(int serviceId, String resourceName, String resourceMethod) { + public Optional getHateoasResource(String packageId, int serviceId, String resourceName, + String resourceMethod) { return this.hateoasServices.stream() - .filter(svc -> svc.getServiceId() == serviceId) + .filter(svc -> svc.getPackageId().equals(packageId) && svc.getServiceId() == serviceId) .findFirst() .flatMap(svc -> svc.getHateoasResourceMapping().entrySet().stream() .filter(resources -> resourceName.equals(resources.getKey())) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java index 4b4c227b1..4a7182156 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java @@ -35,9 +35,11 @@ import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getValueForAnnotationFields; public class HateoasMetadataVisitor extends NodeVisitor { + private final String packageId; private final SemanticModel semanticModel; - public HateoasMetadataVisitor(SemanticModel semanticModel) { + public HateoasMetadataVisitor(String packageId, SemanticModel semanticModel) { + this.packageId = packageId; this.semanticModel = semanticModel; } @@ -65,7 +67,8 @@ public void visit(ServiceDeclarationNode serviceNode) { } String cleanedResourceName = resourceName.get().replaceAll("\"", ""); Resource hateoasResource = new Resource(resourceMethod, operationId); - getHateoasContextHolder().updateHateoasResource(serviceId, cleanedResourceName, hateoasResource); + getHateoasContextHolder().updateHateoasResource( + packageId, serviceId, cleanedResourceName, hateoasResource); } } } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java index dfd968829..3a875f117 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java @@ -24,10 +24,12 @@ import java.util.Map; public class Service { + private final String packageId; private final int serviceId; private final Map> hateoasResourceMapping = new HashMap<>(); - public Service(int serviceId) { + public Service(String packageId, int serviceId) { + this.packageId = packageId; this.serviceId = serviceId; } @@ -39,6 +41,10 @@ public void addResource(String resourceName, Resource resource) { hateoasResourceMapping.put(resourceName, Arrays.asList(resource)); } + public String getPackageId() { + return packageId; + } + public int getServiceId() { return serviceId; } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index 5ba1cdafe..35673418b 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -37,7 +37,7 @@ public class HateoasMapper { - public Map mapHateoasLinksToSwaggerLinks(int serviceId, + public Map mapHateoasLinksToSwaggerLinks(String packageId, int serviceId, FunctionDefinitionNode resourceFunction) { Optional linkedTo = getResourceConfigAnnotation(resourceFunction) .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, "linkedTo")); @@ -48,7 +48,7 @@ public Map mapHateoasLinksToSwaggerLinks(int serviceId, Map hateoasLinks = new HashMap<>(); for (HateoasLink link : links) { Optional resource = getHateoasContextHolder() - .getHateoasResource(serviceId, link.getResourceName(), link.getResourceMethod()); + .getHateoasResource(packageId, serviceId, link.getResourceName(), link.getResourceMethod()); if (resource.isPresent()) { Link swaggerLink = new Link(); String operationId = resource.get().operationId(); From b9c9f0f46d11c27fd250e73c10d4557ee59e5bca Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Wed, 17 Jan 2024 11:52:06 +0530 Subject: [PATCH 48/86] Fix import issues --- .../openapi/service/mapper/ServiceToOpenAPIMapper.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index 1a8cd03e7..21a81c986 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -40,9 +40,8 @@ import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; import io.ballerina.openapi.service.mapper.model.OASGenerationMetaInfo; import io.ballerina.openapi.service.mapper.model.OASResult; -import io.ballerina.projects.*; import io.ballerina.projects.Module; -import io.ballerina.projects.Package; +import io.ballerina.projects.Project; import io.swagger.v3.oas.models.OpenAPI; import java.nio.file.Path; From 35157a094d2cc4cc1680118871374e7ab189c689 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Wed, 17 Jan 2024 13:47:57 +0530 Subject: [PATCH 49/86] Refactor the usage of semantic-model and package-id --- .../io/ballerina/openapi/service/mapper/ResourceMapper.java | 6 ++---- .../openapi/service/mapper/ServiceToOpenAPIMapper.java | 5 +++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java index b4727741a..76d178669 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java @@ -64,7 +64,6 @@ public class ResourceMapper implements ResourceMapperInterface { private final Paths pathObject = new Paths(); private final AdditionalData additionalData; private final OpenAPI openAPI; - private final String packageId; private final List resources; private final boolean treatNilableAsOptional; private final HateoasMapper hateoasMapper; @@ -72,10 +71,9 @@ public class ResourceMapper implements ResourceMapperInterface { /** * Initializes a resource parser for openApi. */ - ResourceMapper(OpenAPI openAPI, List resources, String packageId, - AdditionalData additionalData, boolean treatNilableAsOptional) { + ResourceMapper(OpenAPI openAPI, List resources, AdditionalData additionalData, + boolean treatNilableAsOptional) { this.openAPI = openAPI; - this.packageId = packageId; this.resources = resources; this.additionalData = additionalData; this.treatNilableAsOptional = treatNilableAsOptional; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index 21a81c986..922bf19c1 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -205,7 +205,8 @@ public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) // 03. Filter path and component sections in OAS. // Generate openApi string for the mentioned service name. String packageId = oasGenerationMetaInfo.getProject().currentPackage().packageId().id().toString(); - convertServiceToOpenAPI(packageId, serviceDefinition, openapi, semanticModel, moduleMemberVisitor, diagnostics); + convertServiceToOpenAPI( + packageId, serviceDefinition, openapi, semanticModel, moduleMemberVisitor, diagnostics); ConstraintMapperInterface constraintMapper = new ConstraintMapper(openapi, moduleMemberVisitor, diagnostics); constraintMapper.addMapping(); @@ -247,7 +248,7 @@ private static void convertServiceToOpenAPI(String packageId, ServiceDeclaration } } AdditionalData additionalData = new AdditionalData(semanticModel, moduleMemberVisitor, diagnostics); - ResourceMapperInterface resourceMapper = new ResourceMapper(openAPI, resources, packageId, additionalData, + ResourceMapperInterface resourceMapper = new ResourceMapper(openAPI, resources, additionalData, isTreatNilableAsOptionalParameter(serviceNode)); resourceMapper.addMapping(packageId, semanticModel, serviceNode); } From 0420bd893fb76780549d36394f208ed69d229662 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Thu, 18 Jan 2024 02:05:45 +0530 Subject: [PATCH 50/86] Resolve merge conflicts --- .../service/mapper/ResourceMapper.java | 5 +- .../service/mapper/ResourceMapperImpl.java | 46 +++++++++++++++---- .../mapper/ResourceMapperInterface.java | 0 .../mapper/response/ResponseMapper.java | 3 ++ .../mapper/response/ResponseMapperImpl.java | 4 ++ .../response/ResponseMapperInterface.java | 0 6 files changed, 49 insertions(+), 9 deletions(-) delete mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperInterface.java delete mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperInterface.java diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java index 13b8b35af..e622b3cab 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java @@ -17,7 +17,10 @@ */ package io.ballerina.openapi.service.mapper; +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; + public interface ResourceMapper { - void setOperation(); + void setOperation(String packageId, SemanticModel semanticModel, ServiceDeclarationNode service); } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java index 35d3af93c..c192bc199 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java @@ -17,17 +17,21 @@ */ package io.ballerina.openapi.service.mapper; +import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.api.symbols.Documentable; import io.ballerina.compiler.api.symbols.Documentation; +import io.ballerina.compiler.api.symbols.ServiceDeclarationSymbol; import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.ResourcePathParameterNode; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import io.ballerina.openapi.service.mapper.diagnostic.DiagnosticMessages; import io.ballerina.openapi.service.mapper.diagnostic.IncompatibleResourceDiagnostic; import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; import io.ballerina.openapi.service.mapper.model.AdditionalData; import io.ballerina.openapi.service.mapper.model.OperationInventory; +import io.ballerina.openapi.service.mapper.parameter.HateoasMapper; import io.ballerina.openapi.service.mapper.parameter.ParameterMapper; import io.ballerina.openapi.service.mapper.parameter.ParameterMapperImpl; import io.ballerina.openapi.service.mapper.response.ResponseMapper; @@ -39,6 +43,9 @@ import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.links.Link; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; import java.util.HashMap; import java.util.List; @@ -61,6 +68,7 @@ public class ResourceMapperImpl implements ResourceMapper { private final OpenAPI openAPI; private final List resources; private final boolean treatNilableAsOptional; + private final HateoasMapper hateoasMapper; /** * Initializes a resource parser for openApi. @@ -71,9 +79,10 @@ public class ResourceMapperImpl implements ResourceMapper { this.resources = resources; this.additionalData = additionalData; this.treatNilableAsOptional = treatNilableAsOptional; + this.hateoasMapper = new HateoasMapper(); } - public void setOperation() { + public void setOperation(String packageId, SemanticModel semanticModel, ServiceDeclarationNode service) { Components components = openAPI.getComponents(); if (components == null) { components = new Components(); @@ -83,7 +92,7 @@ public void setOperation() { components.setSchemas(new TreeMap<>()); } for (FunctionDefinitionNode resource : resources) { - addResourceMapping(resource, components); + addResourceMapping(packageId, semanticModel, resource, components, service); } if (components.getSchemas().isEmpty()) { openAPI.setComponents(null); @@ -91,7 +100,8 @@ public void setOperation() { openAPI.setPaths(pathObject); } - private void addResourceMapping(FunctionDefinitionNode resource, Components components) { + private void addResourceMapping(String packageId, SemanticModel semanticModel, FunctionDefinitionNode resource, + Components components, ServiceDeclarationNode service) { String path = MapperCommonUtils.unescapeIdentifier(generateRelativePath(resource)); String httpMethod = resource.functionName().toString().trim(); if (httpMethod.equals(String.format("'%s", DEFAULT)) || httpMethod.equals(DEFAULT)) { @@ -100,8 +110,8 @@ private void addResourceMapping(FunctionDefinitionNode resource, Components comp resource.location()); additionalData.diagnostics().add(error); } else { - convertResourceToOperation(resource, httpMethod, path, components).ifPresent( - operation -> addPathItem(httpMethod, pathObject, operation.getOperation(), path)); + convertResourceToOperation(packageId, semanticModel, resource, httpMethod, path, components, service) + .ifPresent(operation -> addPathItem(httpMethod, pathObject, operation.getOperation(), path)); } } @@ -173,9 +183,10 @@ private void addPathItem(String httpMethod, Paths path, Operation operation, Str * * @return Operation Adaptor object of given resource */ - private Optional convertResourceToOperation(FunctionDefinitionNode resource, String httpMethod, - String generateRelativePath, - Components components) { + private Optional convertResourceToOperation(String packageId, SemanticModel semanticModel, + FunctionDefinitionNode resource, String httpMethod, + String generateRelativePath, Components components, + ServiceDeclarationNode service) { OperationInventory operationInventory = new OperationInventory(); operationInventory.setHttpOperation(httpMethod); operationInventory.setPath(generateRelativePath); @@ -208,10 +219,29 @@ private Optional convertResourceToOperation(FunctionDefiniti ResponseMapper responseMapper = new ResponseMapperImpl(resource, operationInventory, components, additionalData); + setSwaggerLinksInApiResponse(packageId, semanticModel, service, responseMapper.getApiResponses(), resource); responseMapper.setApiResponses(); return Optional.of(operationInventory); } + private void setSwaggerLinksInApiResponse(String packageId, SemanticModel semanticModel, + ServiceDeclarationNode service, ApiResponses apiResponses, + FunctionDefinitionNode resource) { + Optional serviceDeclarationOpt = semanticModel.symbol(service); + ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceDeclarationOpt.get(); + int serviceId = serviceSymbol.hashCode(); + Map swaggerLinks = this.hateoasMapper.mapHateoasLinksToSwaggerLinks(packageId, serviceId, + resource); + if (!swaggerLinks.isEmpty()) { + for (Map.Entry entry : apiResponses.entrySet()) { + int statusCode = Integer.parseInt(entry.getKey()); + if (statusCode >= 200 && statusCode < 300) { + entry.getValue().setLinks(swaggerLinks); + } + } + } + } + /** * Filter the API documentations from resource function node. */ diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperInterface.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperInterface.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java index 2d75430a8..117f56944 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java @@ -17,7 +17,10 @@ */ package io.ballerina.openapi.service.mapper.response; +import io.swagger.v3.oas.models.responses.ApiResponses; + public interface ResponseMapper { void setApiResponses(); + ApiResponses getApiResponses(); } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperImpl.java index 2245bf314..116200ba6 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperImpl.java @@ -103,6 +103,10 @@ public void setApiResponses() { operationInventory.setApiResponses(apiResponses); } + public ApiResponses getApiResponses() { + return apiResponses; + } + private TypeSymbol getReturnTypeSymbol(FunctionDefinitionNode resourceNode) { Optional symbol = semanticModel.symbol(resourceNode); if (symbol.isEmpty() || !(symbol.get() instanceof ResourceMethodSymbol resourceMethodSymbol)) { diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperInterface.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperInterface.java deleted file mode 100644 index e69de29bb..000000000 From d8b853331e6a40af90277ddbf82addde30279239 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Thu, 18 Jan 2024 09:01:23 +0530 Subject: [PATCH 51/86] Revert "Merge pull request #12 from ballerina-platform/master" This reverts commit fc71806a9261822f61dc5ad7aca730bce6e2cf6a, reversing changes made to 0420bd893fb76780549d36394f208ed69d229662. --- .../openapi/cmd/BallerinaCodeGenerator.java | 2 +- .../openapi/cmd/OpenApiGenServiceCmdTest.java | 32 -------- .../expected_gen/without-data-binding.bal | 11 --- .../test/resources/withoutDataBinding.yaml | 76 ------------------- .../openapi/core/GeneratorUtils.java | 47 ++---------- .../client/BallerinaClientGenerator.java | 3 +- .../service/BallerinaServiceGenerator.java | 5 +- 7 files changed, 9 insertions(+), 167 deletions(-) delete mode 100644 openapi-cli/src/test/resources/expected_gen/without-data-binding.bal delete mode 100644 openapi-cli/src/test/resources/withoutDataBinding.yaml diff --git a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BallerinaCodeGenerator.java b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BallerinaCodeGenerator.java index 5b83fe25a..297277b12 100644 --- a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BallerinaCodeGenerator.java +++ b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BallerinaCodeGenerator.java @@ -441,7 +441,7 @@ public List generateBallerinaService(Path openAPI, String serviceNam openAPIDef, nullable, preGeneratedTypeDefNodes); String schemaContent = Formatter.format( ballerinaSchemaGenerator.generateSyntaxTree()).toSourceCode(); - if (!schemaContent.isBlank() && !generateWithoutDataBinding) { + if (!schemaContent.isBlank()) { sourceFiles.add(new GenSrcFile(GenSrcFile.GenFileType.GEN_SRC, srcPackage, TYPE_FILE_NAME, (licenseHeader.isBlank() ? DEFAULT_FILE_HEADER : licenseHeader) + schemaContent)); } diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/cmd/OpenApiGenServiceCmdTest.java b/openapi-cli/src/test/java/io/ballerina/openapi/cmd/OpenApiGenServiceCmdTest.java index c9d68c09f..c8765cfab 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/cmd/OpenApiGenServiceCmdTest.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/cmd/OpenApiGenServiceCmdTest.java @@ -17,10 +17,8 @@ */ package io.ballerina.openapi.cmd; -import io.ballerina.cli.launcher.BLauncherException; import org.testng.Assert; import org.testng.annotations.Test; -import picocli.CommandLine; import java.io.IOException; import java.nio.file.Files; @@ -113,34 +111,4 @@ public void testOneOfSchemaGen() throws IOException { Assert.fail("Service generation for OneOf Schema type failed."); } } - - @Test(description = "Test for --without-data-binding flag") - public void testWithoutDataBinding() throws IOException { - Path yamlPath = resourceDir.resolve(Paths.get("withoutDataBinding.yaml")); - String[] args = {"--input", yamlPath.toString(), "--without-data-binding", "-o", - this.tmpDir.toString(), "--mode", "service"}; - OpenApiCmd cmd = new OpenApiCmd(printStream, this.tmpDir); - new CommandLine(cmd).parseArgs(args); - String output = ""; - try { - cmd.execute(); - } catch (BLauncherException e) { - output = e.getDetailedMessages().get(0); - } - - Path expectedServiceFile = resourceDir.resolve(Paths.get("expected_gen", - "without-data-binding.bal")); - Stream expectedServiceLines = Files.lines(expectedServiceFile); - String expectedService = expectedServiceLines.collect(Collectors.joining("\n")); - expectedServiceLines.close(); - Assert.assertFalse(Files.exists(this.tmpDir.resolve("types.bal"))); - if (Files.exists(this.tmpDir.resolve("withoutdatabinding_service.bal"))) { - String generatedService = getStringFromFile(this.tmpDir.resolve("withoutdatabinding_service.bal")); - Assert.assertEquals(replaceWhiteSpace(generatedService), replaceWhiteSpace(expectedService), - "Expected content and actual generated content is mismatched for: " + yamlPath); - deleteGeneratedFiles("without-data-binding-service.bal"); - } else { - Assert.fail("Service generation for low level service is failed."); - } - } } diff --git a/openapi-cli/src/test/resources/expected_gen/without-data-binding.bal b/openapi-cli/src/test/resources/expected_gen/without-data-binding.bal deleted file mode 100644 index bf56b7a0a..000000000 --- a/openapi-cli/src/test/resources/expected_gen/without-data-binding.bal +++ /dev/null @@ -1,11 +0,0 @@ -// AUTO-GENERATED FILE. -// This file is auto-generated by the Ballerina OpenAPI tool. - -import ballerina/http; - -listener http:Listener ep0 = new (9090, config = {host: "localhost"}); - -service /v1 on ep0 { - resource function get coupons/[string couponCode]/[int id]/[string limits](http:Caller caller, http:Request request) returns error? { - } -} diff --git a/openapi-cli/src/test/resources/withoutDataBinding.yaml b/openapi-cli/src/test/resources/withoutDataBinding.yaml deleted file mode 100644 index 029d20c3a..000000000 --- a/openapi-cli/src/test/resources/withoutDataBinding.yaml +++ /dev/null @@ -1,76 +0,0 @@ -openapi: 3.0.1 -info: - title: V1 - version: 0.1.0 -servers: - - url: "{server}:{port}/v1" - variables: - server: - default: http://localhost - port: - default: "9090" -paths: - /coupons/{couponCode}/{id}/{limits}: - get: - operationId: getCoupon - parameters: - - $ref: '#/components/parameters/ngwCouponCodePathParam' - - $ref: '#/components/parameters/ngwCouponCodePathParam02' - - $ref: '#/components/parameters/ngwCouponCodePathParam03' - responses: - '200': - description: Successful operation, coupon was found by requested code - content: - application/json: - schema: - $ref: '#/components/schemas/Cat' -components: - parameters: - ngwCouponCodePathParam: - in: path - name: couponCode - required: true - schema: - $ref: '#/components/schemas/Coupon' - ngwCouponCodePathParam02: - in: path - name: id - required: true - schema: - $ref: '#/components/schemas/Id' - ngwCouponCodePathParam03: - in: path - name: limits - required: true - schema: - type: array - items: - type: integer - schemas: - Coupon: - type: string - Id: - $ref: '#/components/schemas/AddressNo' - AddressNo: - type: integer - Pet: - type: object - properties: - name: - type: string - tag: - type: string - required: - - name - - tag - Cat: - type: object - properties: - name: - type: string - parent: - $ref: '#/components/schemas/Pet' - petType: - $ref: '#/components/schemas/Cat' - required: - - name diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/GeneratorUtils.java b/openapi-core/src/main/java/io/ballerina/openapi/core/GeneratorUtils.java index 923a5676e..dc0facb62 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/GeneratorUtils.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/GeneratorUtils.java @@ -221,8 +221,7 @@ public static QualifiedNameReferenceNode getQualifiedNameReferenceNode(String mo * @return - node lists * @throws BallerinaOpenApiException */ - public static List getRelativeResourcePath(String path, Operation operation, List resourceFunctionDocs, - Components components, boolean isWithoutDataBinding) + public static List getRelativeResourcePath(String path, Operation operation, List resourceFunctionDocs) throws BallerinaOpenApiException { List functionRelativeResourcePath = new ArrayList<>(); @@ -242,7 +241,7 @@ public static List getRelativeResourcePath(String path, Operation operatio */ if (operation.getParameters() != null) { extractPathParameterDetails(operation, functionRelativeResourcePath, pathNode, - pathParam, resourceFunctionDocs, components, isWithoutDataBinding); + pathParam, resourceFunctionDocs); } } else if (!pathNode.isBlank()) { IdentifierToken idToken = createIdentifierToken(escapeIdentifier(pathNode.trim())); @@ -262,9 +261,7 @@ public static List getRelativeResourcePath(String path, Operation operatio } private static void extractPathParameterDetails(Operation operation, List functionRelativeResourcePath, - String pathNode, String pathParam, - List resourceFunctionDocs, - Components components, boolean isWithoutDataBinding) + String pathNode, String pathParam, List resourceFunctionDocs) throws BallerinaOpenApiException { // check whether path parameter segment has special character String[] split = pathNode.split(CLOSE_CURLY_BRACE, 2); @@ -284,10 +281,9 @@ private static void extractPathParameterDetails(Operation operation, List && parameter.getIn().equals("path")) { String paramType; if (parameter.getSchema().get$ref() != null) { - paramType = resolveReferenceType(parameter.getSchema(), components, isWithoutDataBinding, - pathParam); + paramType = getValidName(extractReferenceType(parameter.getSchema().get$ref()), true); } else { - paramType = getPathParameterType(parameter.getSchema(), pathParam); + paramType = convertOpenAPITypeToBallerina(parameter.getSchema()); if (paramType.endsWith(NILLABLE)) { throw new BallerinaOpenApiException("Path parameter value cannot be null."); } @@ -1106,37 +1102,4 @@ public static boolean isIntegerSchema(Schema fieldSchema) { public static boolean isNumberSchema(Schema fieldSchema) { return Objects.equals(GeneratorUtils.getOpenAPIType(fieldSchema), NUMBER); } - - public static String resolveReferenceType(Schema schema, Components components, boolean isWithoutDataBinding, - String pathParam) throws BallerinaOpenApiException { - String type = GeneratorUtils.extractReferenceType(schema.get$ref()); - - if (isWithoutDataBinding) { - Schema referencedSchema = components.getSchemas().get(getValidName(type, true)); - if (referencedSchema != null) { - if (referencedSchema.get$ref() != null) { - type = resolveReferenceType(referencedSchema, components, isWithoutDataBinding, pathParam); - } else { - type = getPathParameterType(referencedSchema, pathParam); - } - } - } else { - type = getValidName(type, true); - } - return type; - } - - private static String getPathParameterType(Schema typeSchema, String pathParam) - throws BallerinaOpenApiException { - String type; - if (!(isStringSchema(typeSchema) || isNumberSchema(typeSchema) || isBooleanSchema(typeSchema) - || isIntegerSchema(typeSchema))) { - type = STRING; - LOGGER.warn("unsupported path parameter type found in the parameter `" + pathParam + "`. hence the " + - "parameter type is set to string."); - } else { - type = GeneratorUtils.convertOpenAPITypeToBallerina(typeSchema); - } - return type; - } } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGenerator.java index 47aa8b535..f34a83f4d 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGenerator.java @@ -580,8 +580,7 @@ private FunctionDefinitionNode getClientMethodFunctionDefinitionNode(List relativeResourcePath = resourceMode ? - createNodeList(GeneratorUtils.getRelativeResourcePath(path, operation.getValue(), - null, openAPI.getComponents(), false)) : + createNodeList(GeneratorUtils.getRelativeResourcePath(path, operation.getValue(), null)) : createEmptyNodeList(); return createFunctionDefinitionNode(null, metadataNode, qualifierList, functionKeyWord, functionName, relativeResourcePath, diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/service/BallerinaServiceGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/service/BallerinaServiceGenerator.java index c3393a31c..3de6ec406 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/service/BallerinaServiceGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/service/BallerinaServiceGenerator.java @@ -247,8 +247,7 @@ private List applyFiltersForOperations(Filter filter, String path, filterOperations.contains(operation.getValue().getOperationId().trim()))) { // getRelative resource path List functionRelativeResourcePath = GeneratorUtils.getRelativeResourcePath(path, - operation.getValue(), resourceFunctionDocs, openAPI.getComponents(), - generateWithoutDataBinding); + operation.getValue(), resourceFunctionDocs); // function call FunctionDefinitionNode functionDefinitionNode = generateWithoutDataBinding ? @@ -262,7 +261,7 @@ private List applyFiltersForOperations(Filter filter, String path, } else { // getRelative resource path List relativeResourcePath = GeneratorUtils.getRelativeResourcePath(path, operation.getValue(), - resourceFunctionDocs, openAPI.getComponents(), generateWithoutDataBinding); + resourceFunctionDocs); // function call FunctionDefinitionNode resourceFunction = generateWithoutDataBinding ? generateGenericResourceFunctions(operation, From e7a3e28e13071951741e3ccaa8635a1975f0a008 Mon Sep 17 00:00:00 2001 From: Sachin Akash Date: Thu, 18 Jan 2024 15:05:01 +0530 Subject: [PATCH 52/86] Update license header Co-authored-by: Sumudu Nissanka --- .../openapi/service/mapper/hateoas/HateoasContextHolder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java index dcab6039c..a365fe75f 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at From af850bb93a0c3004845931f2806c21a817ff4ec7 Mon Sep 17 00:00:00 2001 From: Sachin Akash Date: Thu, 18 Jan 2024 15:05:10 +0530 Subject: [PATCH 53/86] Update ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java Co-authored-by: Sumudu Nissanka --- .../openapi/service/mapper/hateoas/HateoasMetadataVisitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java index 4a7182156..bfc5e8cd4 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at From 3c5e534ece5b090dbadae786f06f6d80a4dda6dc Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Fri, 19 Jan 2024 10:50:05 +0530 Subject: [PATCH 54/86] Fix failing tests --- .../openapi/service/mapper/parameter/HateoasMapper.java | 2 +- .../ballerina/openapi/service/mapper/response/HateoasLink.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java index 35673418b..c6af1fd59 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java @@ -20,7 +20,7 @@ import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.openapi.service.mapper.hateoas.Resource; -import io.ballerina.openapi.service.mapper.response.model.HateoasLink; +import io.ballerina.openapi.service.mapper.response.HateoasLink; import io.swagger.v3.oas.models.links.Link; import java.util.ArrayList; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java index 0496c1a10..75d2070b6 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java @@ -16,7 +16,7 @@ * under the License. */ -package io.ballerina.openapi.service.mapper.response.model; +package io.ballerina.openapi.service.mapper.response; public class HateoasLink { private String resourceName; From 9925e9bfaf45dc1772dfb8d87d7ef9bd3390107c Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Fri, 19 Jan 2024 13:01:17 +0530 Subject: [PATCH 55/86] Add test cases for fixed issues --- .../openapi/ModuleReferenceTests.java | 7 +- .../generators/openapi/RecordTests.java | 6 + ...e_annotation.bal => common_type_tests.bal} | 6 + .../parameter_annotation/annotated_query.yaml | 57 ++++ .../record/record_rest_param.yaml | 271 ++++++++++++++++++ .../response/cache_config_02.yaml | 21 ++ .../expected_gen/response/common_types.yaml | 140 +++++++++ .../expected_gen/response/readonly.yaml | 39 +++ .../response/response_annotation.yaml | 35 --- .../parameter_annotation/annotated_query.bal | 15 +- .../record/record_rest_param.bal | 61 ++++ .../response/configuration_rs02.bal | 3 + .../response/readonly.bal | 18 ++ 13 files changed, 639 insertions(+), 40 deletions(-) rename openapi-cli/src/test/resources/ballerina-to-openapi/ballerina-project/service/{response_annotation.bal => common_type_tests.bal} (72%) create mode 100644 openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/record/record_rest_param.yaml create mode 100644 openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/common_types.yaml delete mode 100644 openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/response_annotation.yaml create mode 100644 openapi-cli/src/test/resources/ballerina-to-openapi/record/record_rest_param.bal 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 a7245fc91..252651723 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 @@ -60,11 +60,10 @@ public void testResponse05() throws IOException { TestUtils.compareWithGeneratedFile(ballerinaFilePath, "arrayTypeResponse.yaml"); } - @Test(description = "Response has payload annotation with media type defined in a separate module.", - enabled = false) + @Test(description = "Response has payload annotation with media type defined in a separate module.") public void testResponse06() throws IOException { - Path ballerinaFilePath = RES_DIR.resolve("response_annotation.bal"); - TestUtils.compareWithGeneratedFile(ballerinaFilePath, "response/response_annotation.yaml"); + Path ballerinaFilePath = RES_DIR.resolve("common_type_tests.bal"); + TestUtils.compareWithGeneratedFile(ballerinaFilePath, "response/common_types.yaml"); } @Test 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 90ceb50cf..b08d12be8 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 @@ -119,6 +119,12 @@ public void testInterdependenceRecordWithUnionType() throws IOException { TestUtils.compareWithGeneratedFile(ballerinaFilePath, "record/union_records_with_interdependency.yaml"); } + @Test(description = "Test for rest field in the record") + public void testRestFieldInRecord() throws IOException { + Path ballerinaFilePath = RES_DIR.resolve("record/record_rest_param.bal"); + TestUtils.compareWithGeneratedFile(ballerinaFilePath, "record/record_rest_param.yaml"); + } + @AfterMethod public void cleanUp() { TestUtils.deleteDirectory(this.tempDir); diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/ballerina-project/service/response_annotation.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/ballerina-project/service/common_type_tests.bal similarity index 72% rename from openapi-cli/src/test/resources/ballerina-to-openapi/ballerina-project/service/response_annotation.bal rename to openapi-cli/src/test/resources/ballerina-to-openapi/ballerina-project/service/common_type_tests.bal index 7faf424b1..d9546f3af 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/ballerina-project/service/response_annotation.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/ballerina-project/service/common_type_tests.bal @@ -16,4 +16,10 @@ service /payloadV on helloEp { }; return user; } + + resource function get expense(@http:Query ty:Currency currency) returns int? { + } + + resource function get bill(ty:Product p) { + } } 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 01eca5a28..2da9c3cc2 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 @@ -46,6 +46,51 @@ paths: type: object default: Name: John + responses: + "201": + description: Created + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + /student10: + post: + operationId: postStudent10 + parameters: + - name: status + in: query + required: true + schema: + $ref: '#/components/schemas/Status' + responses: + "201": + description: Created + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + /student11: + post: + operationId: postStudent11 + parameters: + - name: status + in: query + schema: + allOf: + - $ref: '#/components/schemas/Status' + default: ACTIVE responses: "201": description: Created @@ -84,3 +129,15 @@ components: type: string method: type: string + Status: + type: string + enum: + - INACTIVE + - ACTIVE + Student: + required: + - Name + type: object + properties: + Name: + type: string diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/record/record_rest_param.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/record/record_rest_param.yaml new file mode 100644 index 000000000..124f1e62b --- /dev/null +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/record/record_rest_param.yaml @@ -0,0 +1,271 @@ +openapi: 3.0.1 +info: + title: PayloadV + version: 0.0.0 +servers: + - url: "{server}:{port}/payloadV" + variables: + server: + default: http://localhost + port: + default: "9090" +paths: + /path1: + get: + operationId: getPath1 + parameters: + - name: user + in: query + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/User' + responses: + "202": + description: Accepted + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + /path2: + get: + operationId: getPath2 + parameters: + - name: name + in: header + required: true + schema: + type: string + - name: id + in: header + required: true + schema: + type: integer + format: int64 + responses: + "202": + description: Accepted + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + /path3: + post: + operationId: postPath3 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserRestString' + required: true + responses: + "202": + description: Accepted + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + /path4: + post: + operationId: postPath4 + parameters: + - name: user1 + in: query + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UserRestMap' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserClosed' + required: true + responses: + "202": + description: Accepted + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + /path5: + post: + operationId: postPath5 + parameters: + - name: name + in: header + required: true + schema: + type: string + - name: id + in: header + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserRestProperty' + required: true + responses: + "202": + description: Accepted + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + /path6: + post: + operationId: postPath6 + parameters: + - name: user2 + in: query + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UserClosed' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserRestMapProperty' + required: true + responses: + "202": + description: Accepted + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' +components: + schemas: + ErrorPayload: + required: + - message + - method + - path + - reason + - status + - timestamp + type: object + properties: + timestamp: + type: string + status: + type: integer + format: int64 + reason: + type: string + message: + type: string + path: + type: string + method: + type: string + Property: + required: + - name + - value + type: object + properties: + name: + type: string + value: + type: string + additionalProperties: false + User: + required: + - id + - name + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + UserClosed: + required: + - id + - name + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + additionalProperties: false + UserRestMap: + required: + - id + - name + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + additionalProperties: + type: object + additionalProperties: + type: string + UserRestMapProperty: + required: + - id + - name + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + additionalProperties: + type: object + additionalProperties: + $ref: '#/components/schemas/Property' + UserRestProperty: + required: + - id + - name + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + additionalProperties: + $ref: '#/components/schemas/Property' + UserRestString: + required: + - id + - name + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + additionalProperties: + type: string diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/cache_config_02.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/cache_config_02.yaml index 88462cd69..57f8a75c9 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/cache_config_02.yaml +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/cache_config_02.yaml @@ -50,3 +50,24 @@ paths: text/plain: schema: type: string + /cachingBackEnd04: + get: + operationId: getCachingbackend04 + responses: + "200": + description: Ok + headers: + Last-Modified: + schema: + type: string + Cache-Control: + schema: + type: string + default: "must-revalidate,private,max-age=3600" + ETag: + schema: + type: string + content: + text/plain: + schema: + type: string diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/common_types.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/common_types.yaml new file mode 100644 index 000000000..971cdf24a --- /dev/null +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/common_types.yaml @@ -0,0 +1,140 @@ +openapi: 3.0.1 +info: + title: PayloadV + version: 0.1.0 +servers: + - url: "{server}:{port}/payloadV" + variables: + server: + default: http://localhost + port: + default: "9090" +paths: + /pet01: + get: + operationId: getPet01 + responses: + "200": + description: Ok + content: + text/html: + schema: + $ref: '#/components/schemas/User' + /expense: + get: + operationId: getExpense + parameters: + - name: currency + in: query + required: true + schema: + $ref: '#/components/schemas/Currency' + responses: + "200": + description: Ok + content: + application/json: + schema: + type: integer + format: int64 + "202": + description: Accepted + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' + /bill: + get: + operationId: getBill + parameters: + - name: p + in: query + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Product' + responses: + "202": + description: Accepted + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPayload' +components: + schemas: + Currency: + type: string + enum: + - GBP + - SGD + - LKR + - USD + ErrorPayload: + required: + - message + - method + - path + - reason + - status + - timestamp + type: object + properties: + timestamp: + type: string + status: + type: integer + format: int64 + reason: + type: string + message: + type: string + path: + type: string + method: + type: string + Price: + required: + - amount + - currency + type: object + properties: + currency: + $ref: '#/components/schemas/Currency' + amount: + type: number + format: float + additionalProperties: false + Product: + required: + - description + - name + - price + type: object + properties: + id: + type: string + name: + maxLength: 14 + type: string + description: + type: string + price: + $ref: '#/components/schemas/Price' + additionalProperties: false + description: Represents a product + User: + required: + - id + - name + type: object + properties: + id: + type: integer + format: int64 + name: + type: string diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/readonly.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/readonly.yaml index fb2df283e..d3235c80a 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/readonly.yaml +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/readonly.yaml @@ -262,6 +262,45 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorPayload' + /res12: + get: + operationId: getRes12 + responses: + "201": + description: Created + "500": + description: InternalServerError + "409": + description: Conflict + /res13: + get: + operationId: getRes13 + responses: + "200": + description: Ok + content: + application/json: + schema: + oneOf: + - type: integer + format: int64 + - type: object + - $ref: '#/components/schemas/Pet' + - required: + - id + - name + type: object + properties: + id: + type: string + name: + type: string + additionalProperties: false + text/plain: + schema: + oneOf: + - type: string + - type: string components: schemas: ErrorPayload: diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/response_annotation.yaml b/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/response_annotation.yaml deleted file mode 100644 index 5bc47acd1..000000000 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/expected_gen/response/response_annotation.yaml +++ /dev/null @@ -1,35 +0,0 @@ -openapi: 3.0.1 -info: - title: PayloadV - version: 0.1.0 -servers: - - url: "{server}:{port}/payloadV" - variables: - server: - default: http://localhost - port: - default: "9090" -paths: - /pet01: - get: - operationId: getPet01 - responses: - "200": - description: Ok - content: - text/html: - schema: - $ref: '#/components/schemas/User' -components: - schemas: - User: - required: - - id - - name - type: object - properties: - id: - type: integer - format: int64 - name: - type: string 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 abd65147c..8851a1252 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 @@ -6,13 +6,26 @@ type Student record { string Name; }; +enum Status { + ACTIVE, + INACTIVE +}; + service /payloadV on ep0 { resource function post student8(@http:Query map student) returns json { return {Name: "john"}; } - resource function post student9(@http:Query map students = {"Name" : "John"}) returns json { + resource function post student9(@http:Query map students = {"Name" : "John"}) returns Student { return {Name: "john"}; } + + resource function post student10(@http:Query Status status) returns Student { + return {Name: "john", "Status": status}; + } + + resource function post student11(@http:Query Status status = "ACTIVE") returns json { + return {Name: "john", Status: status}; + } } diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/record/record_rest_param.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/record/record_rest_param.bal new file mode 100644 index 000000000..3f2c2c11d --- /dev/null +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/record/record_rest_param.bal @@ -0,0 +1,61 @@ +import ballerina/http; + +type User record { + int id; + string name; +}; + +type UserClosed record {| + int id; + string name; +|}; + +type UserRestString record {| + int id; + string name; + string...; +|}; + +type UserRestMap record {| + int id; + string name; + map...; +|}; + +type Property record {| + string name; + string value; +|}; + +type UserRestProperty record {| + int id; + string name; + Property...; +|}; + +type UserRestMapProperty record {| + int id; + string name; + map...; +|}; + +service /payloadV on new http:Listener(9090) { + + resource function get path1(User user) { + } + + resource function get path2(@http:Header UserClosed user) { + } + + resource function post path3(UserRestString user) { + } + + resource function post path4(@http:Query UserRestMap user1, UserClosed user2) { + } + + resource function post path5(UserRestProperty user1, @http:Header UserClosed user2) { + } + + resource function post path6(@http:Payload UserRestMapProperty user1, UserClosed user2) { + } +} diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/response/configuration_rs02.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/response/configuration_rs02.bal index cb38b3ce2..04fbf93cf 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/response/configuration_rs02.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/response/configuration_rs02.bal @@ -15,5 +15,8 @@ service /payloadV on helloEp { resource function get cachingBackEnd03() returns @http:Cache{setETag : false, mustRevalidate:false} string { return "Hello, World!!";} + //With private and without private fields + resource function get cachingBackEnd04() returns @http:Cache{isPrivate: true} string { + return "Hello, World!!";} } diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/response/readonly.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/response/readonly.bal index 690f1fcc3..3b24ac81d 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/response/readonly.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/response/readonly.bal @@ -86,4 +86,22 @@ service /payloadV on new http:Listener(9090) { name: "Kitty" }; } + + resource function get res12() + returns http:InternalServerError & readonly|http:Created & readonly|http:Conflict & readonly { + return http:CREATED; + } + + resource function get res13() returns + readonly & (string|int) | + readonly & string|int | + readonly & map | + string | + readonly & Pet | + readonly & record {| + readonly string id; + string name; + |} { + return "Hello World"; + } } From 4a5324b3327470995135ac03b61552688724723a Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Fri, 19 Jan 2024 13:17:23 +0530 Subject: [PATCH 56/86] Add the spec for service to OAS --- docs/ballerina-to-oas/spec/spec.md | 1452 ++++++++++++++++++++++++++++ 1 file changed, 1452 insertions(+) create mode 100644 docs/ballerina-to-oas/spec/spec.md diff --git a/docs/ballerina-to-oas/spec/spec.md b/docs/ballerina-to-oas/spec/spec.md new file mode 100644 index 000000000..ff3153c5c --- /dev/null +++ b/docs/ballerina-to-oas/spec/spec.md @@ -0,0 +1,1452 @@ +# OpenAPI tool specification: Ballerina to OpenAPI + +Owners: @shafreenAnfar @TharmiganK @lnash94 +Reviewers: @shafreenAnfar @TharmiganK @lnash94 @dilanSachi +Created: 2024/01/10 +Updated: 2024/01/10 +Edition: Swan Lake + +The Ballerina to OpenAPI tool can generate an OpenAPI specification for a Ballerina service. This specification describes the Ballerina service to OpenAPI mapping. + +## Table of contents + +- [OpenAPI tool specification: Ballerina to OpenAPI](#openapi-tool-specification-ballerina-to-openapi) + - [Table of contents](#table-of-contents) + - [Section 1: The `info` section](#section-1-the-info-section) + - [Title of the API](#title-of-the-api) + - [Version of the API](#version-of-the-api) + - [The `ServiceInfo` annotation](#the-serviceinfo-annotation) + - [Section 2: The `servers` section](#section-2-the-servers-section) + - [Section 3: The `paths` section](#section-3-the-paths-section) + - [Path](#path) + - [Operation](#operation) + - [The `operationId` field](#the-operationid-field) + - [The `responses` field](#the-responses-field) + - [The status code](#the-status-code) + - [The media type](#the-media-type) + - [The return type schema](#the-return-type-schema) + - [The headers](#the-headers) + - [The cache headers](#the-cache-headers) + - [The headers from status code response](#the-headers-from-the-status-code-response) + - [The `requestBody` field](#the-requestbody-field) + - [The request body accessed using the `http:Request` object](#the-httprequest-object) + - [The request body accessed using data binding](#the-payload-data-binding) + - [The `parameters` field](#the-parameters-field) + - [The path parameter](#the-path-parameter) + - [The query parameter](#the-query-parameter) + - [The header parameter](#the-header-parameter) + - [Section 4: The `components` section](#section-4-the-components-section) + - [The Ballerina type to schema mapping](#the-ballerina-type-to-schema-mapping) + - [Ballerina basic types](#ballerina-basic-types) + - [Ballerina structural types](#ballerina-structural-types) + - [Ballerina constraints mapping to type schema](#ballerina-constraints-mapping-to-type-schema) + - [Integer constraints](#integer-constraints) + - [Float constraints](#float-constraints) + - [Number constraints](#number-constraints) + - [String constraints](#string-constraints) + - [Array constraints](#array-constraints) + - [Date constraints](#date-constraints) + +## Section 1: The `info` section + +The [`info` section](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#infoObject) provides metadata about the API. + +The Ballerina OpenAPI tool populates the following fields of the `info` section: + +- `title`: The title of the API +- `version`: The version of the API + +```yml +info: +title: Title of the API +version: 1.0.0 +``` + +### Title of the API + +The title of the API is derived from the Ballerina service base path. + +| Base path | Title of the API | +|--------------|---------------------------------------------| +| empty or `/` | The name of the file containing the service | +| `/api` | `Api` | +| `/api/v1` | `Api V1` | + +### Version of the API + +The version of the API is derived from the Ballerina package version. If the file is not in a package, the version is set to `1.0.0`. + +### The `ServiceInfo` annotation + +The `ServiceInfo` annotation can be used to override the title and version of the API. + +```ballerina +@openapi:ServiceInfo { + title: "My API", + 'version: "2.0.0" +} +service /api/v1 on new http:Listener(9090) { + // ... +} +``` + +```yml +info: +title: My API +version: 2.0.0 +``` + +## Section 2: The `servers` section + +The `servers` section specifies the API servers and corresponding base URLs. This section has a list of [`server`](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-object) objects. + +The Ballerina OpenAPI tool populates the `url` field of the `server` objects in this section. The `url` field is derived from the Ballerina service object and the listener configuration. The `url` field is constructed using the following information: + +| Field | Description | +|-------------|-----------------------------------------------------------------------------------------------------------------------------------------| +| `scheme` | The `scheme` field derived from the listener definition | +| `server` | The `host` field of the listener configuration. If the `host` field is not specified, defaults to `localhost` | +| `port` | The `port` field of the listener configuration. If the `port` field is not specified, defaults to `80` for `http` and `443` for `https` | +| `base-path` | The base path retrieved from the Ballerina service object | + +```yml +servers: +- url: "://{server}:{port}/" + variables: + server: + default: + port: + default: +``` + +A Ballerina service can be attached to multiple listeners. + +```ballerina +listener http:Listener listenerEp1 = new (9090); +listener http:Listener listenerEp2 = new (9091, config = {host: "mocksvc.io"}); + +service /basepath on listenerEp1, listenerEp2 { + + // ... +} +``` + +For the above service, the following `servers` section is generated. + +```yml +servers: +- url: "{server}:{port}/basepath" +variables: + server: + enum: + - mocksvc.io + - http://localhost + default: http://localhost + port: + enum: + - "9091" + - "9090" + default: "9090" +``` + +## Section 3: The `paths` section + +The `paths` section defines the available API endpoints(paths) and the HTTP methods supported by them(operations). + +### Path + +A `path` object is defined by a unique path and may contain one or more operations. + +The `path` name is derived from the path of the Ballerina resource function. + +```ballerina +service /basepath on new http:Listener(9090) { + + resource function get path { + // ... + } +} +``` + +```yml +paths: +/path: + # ... +``` + +If the resource has a dot resource path, then the path name is defined as a slash. + +```ballerina +service /basepath on new http:Listener(9090) { + + resource function get . { + // ... + } +} +``` + +```yml +paths: +/: + # ... +``` + +When the resource function has a path parameter, the path name is derived from the path of the Ballerina resource function with the path parameter in curly braces. + +```ballerina +service /basepath on new http:Listener(9090) { + + resource function get path/[string param] { + // ... + } +} +``` + +```yml +paths: +/path/{param}: + # ... +``` + +### Operation + +For each `path` object, operations define the accessible HTTP methods. + +The Ballerina OpenAPI tool supports the following HTTP methods: `get`, `post`, `put`, `patch`, `delete`, `head`, `options`, and `trace`. + +The Ballerina default resource is not supported. + +```ballerina +service /payloadV on new http:Listener(9090) { + + resource function default path () { + // ... + } +} +``` + +```console +WARNING Generated OpenAPI definition does not contain details for the `default` resource method in the Ballerina service. +``` + +An operation is defined by the following fields: + +- `operationId` +- `responses` +- `requestBody` +- `parameters` + +#### The `operationId` field + +The operation object will contain a unique operation ID. This operation ID is derived from the Ballerina resource function method and path. + +Example: +| Ballerina resource function | Operation ID | +| -------------------------------------------- | -------------- | +| `resource function get path` | getPath | +| `resource function post path1/path2` | postPath1Path2 | +| `resource function post path/[string param]` | postPathParam | + +#### The `responses` field + +The `responses` field contains the expected responses for the Ballerina resource function described by the operation object. This is a required field. + +This follows the [OpenAPI specification](https://swagger.io/docs/specification/describing-responses/). + +```yml +responses: + : + description: + content: + : + schema: + headers: + : + + + # Definition of all error statuses + default: +``` + +The Ballerina HTTP resource can return one of the following types: + +1. `anydata` +2. `http:StatusCodeResponse` +3. `http:Response` +4. `error` + +##### The status code + +The status code is inferred from the Ballerina resource function return type and the description is populated according to the status code. + +If the return type is an `http:StatusCodeResponse`, then the status code is derived from the type of the `http:StatusCodeResponse`. Otherwise, the default status code will be used depending on the HTTP method and return type. + +| HTTP Method | Default status code for success response | +|-------------|------------------------------------------| +| `post` | `201` | +| other | `200` | + +| Return Type | Status code | +|------------------------------|---------------------------------------------------| +| `nil` | `202` | +| `error` | `500` | +| `anydata` (other than `nil`) | default success status code using the HTTP method | +| `http:StatusCodeResponse` | derived from the type of the `StatusCodeResponse` | +| `http:Response` | `default` | + +##### The media type + +The media type is inferred from the effective type of the Ballerina resource function return type. + +| Effective Type | Inferred media type | +|----------------|----------------------------| +| `byte[]` | `application/octet-stream` | +| `string` | `text/plain` | +| `xml` | `application/xml` | +| `Other` | `application/json` | + +This default media type can be overridden using the `@http:Payload` annotation in the return type. + +```ballerina +service /api on new http:Listener(9090) { + + resource function get users/[string userId]/uploads/[string docId]() returns @http:Payload {mediaType: "text/csv"} string { + // ... + } +} +``` + +Additionally, if the service defines a media type subtype prefix via `@http:ServiceConfig` annotation then the media type is prefixed with the subtype. + +```ballerina +@http:ServiceConfig { + mediaTypeSubtypePrefix: "vnd.example" +} +service /api on new http:Listener(9090) { + + resource function get users/[string userId]() returns User { + // ... + } +} +`````` + +##### The return type schema + +The return type schema is derived as follows for each of these return types: + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Response TypeMapping
anydata (other than nil) + +```yml +responses: + : + description: + content: + : + schema: + headers: + : +``` + +
nil + +```yml +responses: + "202": + description: "Accepted" +``` + +
http:Response + +```yml +responses: + default: + description: "Any Response" + content: + '*/*': + schema: + description: "Any type of entity body" + headers: + : +``` + +
http:StatusCodeResponse + +```yml +responses: + : + description: + content: + : + schema: + headers: + : +``` + +
+ +> **Note:** +> +> - Here the types `anydata` and `http:StatusCodeResponse` can generate multiple response types if the body type contains a union type that has different effective media types. +> - If the return type is a union type, then the return type is split into multiple types which have a single effective media type. Each of these types will be considered as a separate response. +> - If the return type is a union of `anydata` and `http:StatusCodeResponse`, the union of `anydata` and the `body` of the `http:StatusCodeResponse` will be considered as the return type only if the `http:StatusCodeResponse` produces the same status code as the `anydata` type. + +##### The headers + +###### The cache headers + +The HTTP cache headers are derived from the `@http:CacheConfig` annotation in the return type. The following HTTP cache headers are populated according to the configuration. + +- `Cache-Control` +- `Last-Modified` +- `ETag` + +The default value for `Cache-Control` is populated with the directives according to the cache configuration. + +```ballerina +service / on new http:Listener(9090) { + + resource function get albums/[string title]() returns @http:Cache {maxAge: 15} Album { + // ... + } +} +``` + +```yml +headers: + Last-Modified: + schema: + type: string + Cache-Control: + schema: + type: string + default: "must-revalidate,public,max-age=15" + ETag: + schema: + type: string +``` + +**Note:** Since caching is only allowed for successful responses, the cache headers are only populated for successful responses. + +###### The headers from the status code response + +The `http:StatusCodeResponse` type can contain headers. If the headers are specified as a record type then the headers are derived from the fields of that record type. + +```ballerina +type OrgHeaders record {| + string xOrg = "ballerina"; + string[] xApiKeys; + string xVersion?; +|}; + +type AlbumCreated record {| + *http:Created; + OrgHeaders headers; + Album body; +|}; + +service / on new http:Listener(9090) { + resource function get albums/[string title]() returns AlbumCreated { + // ... + } +} +``` + +```yml +headers: + xOrg: + schema: + type: string + default: ballerina + xApiKeys: + schema: + type: array + items: + type: string + xVersion: + schema: + type: string +``` + +#### The `requestBody` field + +The `requestBody` field describes the request body content for the Ballerina resource function described by the operation object. This is an optional field and is only populated if the Ballerina resource function accesses the request payload. + +```yml +requestBody: + description: + required: + content: + : + schema: +``` + +This field is skipped for the `GET` method. + +```ballerina +service /api on new http:Listener(9090) { + + resource function get path(http:Request req) { + // ... + } +} +``` + +```console +WARNING Generated OpenAPI definition does not contain `http:Request` body information of the `GET` method, as it's not supported by the OpenAPI specification. +``` + +Ballerina resource functions can access the request body using the `http:Request` object or can be directly bound to an `anydata` type (using the `@http:Payload` annotation - The annotation is optional when the payload is bound to a structural type). + +##### The `http:Request` object + +```ballerina +service /api on new http:Listener(9090) { + + resource function post path(http:Request req) { + // ... + } +} +``` + +```yml +requestBody: + content: + '*/*': + schema: + description: Any type of entity body +``` + +##### The payload data binding + +```ballerina +service /api on new http:Listener(9090) { + + resource function post path(User user) { + // ... + } +} +``` + +```yml +requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/User" +``` + +Here the `media-type` is inferred from the payload bound type, and the logic is the same as the return type media type inference. + +If the payload bound type includes a `nil` type, then the `required` field is set to `false`. + +```ballerina +service /api on new http:Listener(9090) { + + resource function post path(User? user) { + // ... + } +} +``` + +```yml +requestBody: + required: false + content: + application/json: + schema: + $ref: "#/components/schemas/User" +``` + +> **Note:** If the resource function accesses the payload using the `http:Request` object and also binds the payload to a type, then the `requestBody` field is populated with the bound type. + +#### The `parameters` field + +The `parameters` field describes the parameters that can be used in the Ballerina resource function described by the operation object. This is an optional field. + +This field includes the following parameters: + +- Path parameters +- Query parameters +- Header parameters + +##### The path parameter + +The sample schema for a path parameter is as follows: + +```yml +parameters: + - name: + in: path + description: + required: true + schema: +``` + +The path parameter name should be the same as the path parameter defined in the `path` object and path parameters are always required. + +The path parameter is derived from the path parameter type defined in the Ballerina resource path. Ballerina supports the following path parameter types: + +- `string` +- `int` +- `float` +- `decimal` +- `boolean` + +The Ballerina OpenAPI tool does not support Ballerina resource functions with the rest parameter as a path parameter. + +```ballerina +service /basepath on new http:Listener(9090) { + + resource function get path/[string... params] { + // ... + } +} +``` + +```console +WARNING Generated OpenAPI specification excludes details for operation with rest parameter in the resource `path` +``` + +##### The query parameter + +The sample schema for a query parameter is as follows: + +```yml +parameters: + - name: + in: query + description: + required: + schema: +``` + +The query parameter is derived from the query parameter type defined in the Ballerina resource function. Ballerina supports the following query parameter types: + +- `string` +- `int` +- `float` +- `decimal` +- `boolean` +- `map` +- array of the above types + +Additionally, the query parameter type can be nilable. + +The query parameter is not required in the following cases: + +- The query parameter type is nilable and the `treatNilableAsOptional` option in service configuration is set to `true` (Default is `true`). + + ```ballerina + service /api on new http:Listener(9090) { + + resource function get path(string? param) { + // ... + } + } + ``` + + ```yml + parameters: + - name: param + in: query + schema: + type: string + nullable: true + ``` + +- The query parameter has a default value + + ```ballerina + service /api on new http:Listener(9090) { + + resource function get path(string param = "default") { + // ... + } + } + ``` + + ```yml + parameters: + - name: param + in: query + schema: + type: string + default: default + ``` + +If the `treatNilableAsOptional` option in the service configuration is set to `false`, then even if the query parameter type is nilable, the query parameter is required. + +```ballerina +@http:ServiceConfig { + treatNilableAsOptional: false +} +service /api on new http:Listener(9090) { + + resource function get path(string? param) { + // ... + } +} +``` + +```yml +parameters: + - name: param + in: query + required: true + schema: + type: string + nullable: true +``` + +If the query parameter type is a `map` or `record` with `anydata` fields, then the query parameter schema is wrapped with `content` and `application/json` to indicate that the query parameter should be a JSON object which should be encoded properly. + +```ballerina +service /api on new http:Listener(9090) { + + resource function get path(@http:Query User param) { + // ... + } +} +``` + +```yml +parameters: + - name: param + in: query + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/User" +``` + +##### The header parameter + +The sample schema for a header parameter is as follows: + +```yml +parameters: + - name: + in: header + description: + required: + schema: +``` + +The header parameter is derived from the header parameter type defined in the Ballerina resource function using the `@http:Header` annotation. Ballerina supports the following header parameter types: + +- `string` +- `int` +- `float` +- `decimal` +- `boolean` +- array of the above types + +The header parameter type can be nilable. + +The header parameter is not required in the following cases: + +- The header parameter type is nilable and the `treatNilableAsOptional` option in service configuration is set to `true` (Default is `true`). + + ```ballerina + service /api on new http:Listener(9090) { + resource function get path(@http:Header string? param) { + // ... + } + } + ``` + + ```yml + parameters: + - name: param + in: header + schema: + type: string + nullable: true + ``` + +- The header parameter has a default value + + ```ballerina + service /api on new http:Listener(9090) { + + resource function get path(@http:Header string param = "default") { + // ... + } + } + ``` + + ```yml + parameters: + - name: param + in: header + schema: + type: string + default: default + ``` + +If the `treatNilableAsOptional` option in the service configuration is set to `false`, then even if the header parameter type is nilable, the header parameter is required. + +```ballerina +@http:ServiceConfig { + treatNilableAsOptional: false +} +service /api on new http:Listener(9090) { + + resource function get path(@http:Header string? param) { + // ... + } +} +``` + +```yml +parameters: + - name: param + in: header + required: true + schema: + type: string + nullable: true +``` + +Additionally, the header parameter can be a closed `record` which contains the above basic types as fields. In that case, each field of this record represents a header parameter. + +```ballerina +type OrgHeaders record {| + string xOrg = "ballerina"; + string[] xApiKeys; + string xVersion?; +|}; + +service /api on new http:Listener(9090) { + + resource function get path(@http:Header OrgHeaders headers) { + // ... + } +} +``` + +```yml +parameters: + - name: xApiKeys + in: header + required: true + schema: + type: array + items: + type: string + - name: xVersion + in: header + schema: + type: string + - name: xOrg + in: header + schema: + type: string + default: ballerina +``` + +## Section 4: The `components` section + +The `components` section defines reusable components that can be used across the API specification. This section has the type schemas generated for Ballerina types. The component name is the same as the Ballerina type name. + +### The Ballerina type to schema mapping + +#### Ballerina basic types + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Basic TypeMapping
int + +```yml +type: integer +format: int64 +``` + +
int:Signed32 + +```yml +type: integer +format: int32 +``` + +
Other int types + +```yml +type: integer +``` + +
float + +```yml +type: number +format: float +``` + +
decimal + +```yml +type: number +format: double +``` + +
boolean + +```yml +type: boolean +``` + +
string + +```yml +type: string +``` + +
json + +```yml +type: object +``` + +
xml and subtypes of xml + +```yml +type: object +``` + +
byteNo mapping
() + +```yml +nullable: true +``` + +
anydata + +```yml +{} +``` + +
error + +```yml +type: object +properties: + timestamp: + type: string + status: + type: integer + format: int64 + reason: + type: string + message: + type: string + path: + type: string + method: + type: string +additionalProperties: false +``` +(This is inferred from the error details defined in the Ballerina HTTP module) +
string:Char + +```yml +type: string +``` + +
enum + +```yml +type: string +enum: + - ... +``` + +
+ +#### Ballerina structural types + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Structural TypeMapping
array + +```yml +type: array +items: + +``` +
map + +```yml +type: object +additionalProperties: + +``` +
record{} + +```yml +type: object +allOf: + - + - type: object + properties: + + additionalProperties: + +``` +
table + +```yml +type: object +additionalProperties: + +``` + +
tuple + +```yml +type: array +properties: + oneOf: + +``` + +
union + +```yml +oneOf: + +``` + +
intersection + +```yml + +``` + +
+ +### Ballerina constraints mapping to type schema + +The Ballerina constraint package supports constraints on types. These constraints are mapped to the corresponding constraints in each type schema in the components section. + +> **Note:** The constraint values are mapped to the type schema only if the value is directly provided in the constraint. If the constraint value is a variable, then the value is not mapped to the type schema. + +#### Integer constraints + +| Ballerina Mapping | Keywords | +|-------------------------------------------------|--------------------| +| `@constraint:Int { minValue: }` | `minimum` | +| `@constraint:Int { maxValue: }` | `maximum` | +| `@constraint:Int { minValueExclusive: }` | `exclusiveMinimum` | +| `@constraint:Int { maxValueExclusive: }` | `exclusiveMaximum` | + +Example: + +```ballerina +import ballerina/http; +import ballerina/constraint; + +@constraint:Int{ + minValue: 5, + maxValue: 20 +} + +public type Age int; + +type Person record{ + Age age?; +}; + +service / on new http:Listener(9090){ + resource function post newPerson(Person body) returns error?{ + return; + } +} +``` + +```yml +components: + schemas: + Age: + minimum: 5 + maximum: 20 + type: integer + format: int32 + Person: + type: object + properties: + age: + $ref: '#/components/schemas/Age' +``` + +#### Float constraints + +| Ballerina Mapping | Keywords | +|---------------------------------------------------|--------------------| +| `@constraint:Float { minValue: }` | `minimum` | +| `@constraint:Float { maxValue: }` | `maximum` | +| `@constraint:Float { minValueExclusive: }` | `exclusiveMinimum` | +| `@constraint:Float { maxValueExclusive: }` | `exclusiveMaximum` | + +Example: + +```ballerina +import ballerina/http; +import ballerina/constraint; + +@constraint:Float{ + maxValue: 1000.5 +} + +public type Salary float; + +type Person record{ + Salary salary?; +}; + +service / on new http:Listener(9090){ + resource function post newPerson(Person body) returns error?{ + return; + } +} +``` + +```yml +components: + schemas: + Salary: + maximum: 1000.5 + type: number + format: float + Person: + type: object + properties: + salary: + $ref: '#/components/schemas/Salary' +``` + +#### Number constraints + +| Ballerina Mapping | Keywords | +|----------------------------------------------------|--------------------| +| `@constraint:Number { minValue: }` | `minimum` | +| `@constraint:Number { maxValue: }` | `maximum` | +| `@constraint:Number { minValueExclusive: }` | `exclusiveMinimum` | +| `@constraint:Number { maxValueExclusive: }` | `exclusiveMaximum` | + +Example: + +```ballerina +import ballerina/http; +import ballerina/constraint; + +@constraint:Number{ + maxValueExclusive: 5.1 +} + +public type Rating decimal; + +type Hotel record{ + Rating rate?; +}; + +service / on new http:Listener(9090){ + resource function post newHotel(Hotel body) returns error?{ + return; + } +} +``` + +```yml +components: + schemas: + Rating: + maximum: 5.1 + exclusiveMaximum: true + type: number + format: decimal + Hotel: + type: object + properties: + rate: + $ref: '#/components/schemas/Rating' +``` + +#### String constraints + +| Ballerina Mapping | Keywords | +|--------------------------------------------|------------------------------------------| +| `@constraint:String { minLength: }` | `minLength` | +| `@constraint:String { maxLength: }` | `maxLength` | +| `@constraint:String { length: }` | `minLength: , maxLength: ` | +| `@constraint:String { pattern: }` | `pattern` | + +Example 1: + +```ballerina +import ballerina/http; +import ballerina/constraint; + +@constraint:String{ + minLength: 5, + maxLength: 20, + pattern: re `^[a-zA-Z0-9_]+$` +} + +public type Username string; + +type Person record{ + Username username?; +}; + +service / on new http:Listener(9090){ + resource function post newUser(Person body) returns error?{ + return; + } +} +``` + +```yml +components: + schemas: + Username: + minLength: 3 + maxLength: 20 + pattern: "^[a-zA-Z0-9_]+$" + type: string + Person: + type: object + properties: + username: + $ref: '#/components/schemas/Username' +``` + +Example 2: + +```ballerina +import ballerina/http; +import ballerina/constraint; + +@constraint:String{ + length: 5 +} + +public type ID string; + +type Employee record{ + ID id; +}; + +service / on new http:Listener(9090){ + resource function post newEmployee(Employee body) returns error?{ + return; + } +} +``` + +```yml +components: + schemas: + ID: + minLength: 5 + maxLength: 5 + type: string + Employee: + type: object + properties: + id: + $ref: '#/components/schemas/ID' +``` + +#### Array constraints + +| Ballerina Mapping | Keywords | +|-------------------------------------------|----------------------------------------| +| `@constraint:Array { minLength: }` | `minItems` | +| `@constraint:Array { maxLength: }` | `maxItems` | +| `@constraint:Array { length: }` | `minItems: , maxItems: ` | + +Example: + +```ballerina +import ballerina/http; +import ballerina/constraint; + +@constraint:String{ + maxLength: 23 +} +public type HobbyItemsString string; + +@constraint:Array{ + minLength: 2, + maxLength: 5 +} +public type Hobby HobbyItemsString[]; + +public type person record{ + Hobby hobby?; +}; + +service / on new http:Listener(9090){ + resource function post hobby(Person body) returns error?{ + return; + } +} +``` + +```yml +components: + schemas: + HobbyItemsString: + maxLength: 23 + type: string + Hobby: + minLength: 2 + maxLength: 5 + type: array + items: + $ref: '#/components/schemas/HobbyItemsString' + Person: + type: object + properties: + hobby: + $ref: '#/components/schemas/Hobby' +``` + +#### Date constraints + +This is not yet supported in the OpenAPI tool. + +```ballerina +@constraint:Date { + option: "PAST" +} +type Date record { + int year; + int month; + int day; +}; +``` + +```console +WARNING Ballerina Date constraints might not be reflected in the OpenAPI definition +``` From dab9951693df08788a95faa2b7055ce9b49b8ba3 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 19 Jan 2024 14:28:50 +0530 Subject: [PATCH 57/86] Remove Hateoas link mapping logic from response mapper --- .../service/mapper/ResourceMapper.java | 2 +- .../service/mapper/ResourceMapperImpl.java | 29 ++++--------------- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java index 2b648e826..dec246139 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java @@ -25,5 +25,5 @@ */ public interface ResourceMapper { - void setOperation(String packageId, SemanticModel semanticModel, ServiceDeclarationNode service); + void setOperation(SemanticModel semanticModel, ServiceDeclarationNode service); } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java index 9e3954bf2..38ade9aeb 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java @@ -83,7 +83,7 @@ public class ResourceMapperImpl implements ResourceMapper { this.hateoasMapper = new HateoasMapper(); } - public void setOperation(String packageId, SemanticModel semanticModel, ServiceDeclarationNode service) { + public void setOperation(SemanticModel semanticModel, ServiceDeclarationNode service) { Components components = openAPI.getComponents(); if (components == null) { components = new Components(); @@ -93,7 +93,7 @@ public void setOperation(String packageId, SemanticModel semanticModel, ServiceD components.setSchemas(new TreeMap<>()); } for (FunctionDefinitionNode resource : resources) { - addResourceMapping(packageId, semanticModel, resource, components, service); + addResourceMapping(semanticModel, resource, components, service); } if (components.getSchemas().isEmpty()) { openAPI.setComponents(null); @@ -101,7 +101,7 @@ public void setOperation(String packageId, SemanticModel semanticModel, ServiceD openAPI.setPaths(pathObject); } - private void addResourceMapping(String packageId, SemanticModel semanticModel, FunctionDefinitionNode resource, + private void addResourceMapping(SemanticModel semanticModel, FunctionDefinitionNode resource, Components components, ServiceDeclarationNode service) { String path = MapperCommonUtils.unescapeIdentifier(generateRelativePath(resource)); String httpMethod = resource.functionName().toString().trim(); @@ -111,7 +111,7 @@ private void addResourceMapping(String packageId, SemanticModel semanticModel, F resource.location()); additionalData.diagnostics().add(error); } else { - convertResourceToOperation(packageId, semanticModel, resource, httpMethod, path, components, service) + convertResourceToOperation(semanticModel, resource, httpMethod, path, components, service) .ifPresent(operation -> addPathItem(httpMethod, pathObject, operation.getOperation(), path)); } } @@ -184,7 +184,7 @@ private void addPathItem(String httpMethod, Paths path, Operation operation, Str * * @return Operation Adaptor object of given resource */ - private Optional convertResourceToOperation(String packageId, SemanticModel semanticModel, + private Optional convertResourceToOperation(SemanticModel semanticModel, FunctionDefinitionNode resource, String httpMethod, String generateRelativePath, Components components, ServiceDeclarationNode service) { @@ -220,29 +220,10 @@ private Optional convertResourceToOperation(String packageId ResponseMapper responseMapper = new ResponseMapperImpl(resource, operationInventory, components, additionalData); - setSwaggerLinksInApiResponse(packageId, semanticModel, service, responseMapper.getApiResponses(), resource); responseMapper.setApiResponses(); return Optional.of(operationInventory); } - private void setSwaggerLinksInApiResponse(String packageId, SemanticModel semanticModel, - ServiceDeclarationNode service, ApiResponses apiResponses, - FunctionDefinitionNode resource) { - Optional serviceDeclarationOpt = semanticModel.symbol(service); - ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceDeclarationOpt.get(); - int serviceId = serviceSymbol.hashCode(); - Map swaggerLinks = this.hateoasMapper.mapHateoasLinksToSwaggerLinks(packageId, serviceId, - resource); - if (!swaggerLinks.isEmpty()) { - for (Map.Entry entry : apiResponses.entrySet()) { - int statusCode = Integer.parseInt(entry.getKey()); - if (statusCode >= 200 && statusCode < 300) { - entry.getValue().setLinks(swaggerLinks); - } - } - } - } - /** * Filter the API documentations from resource function node. */ From 63ac241d79644776e53dc2f338009c91970f6df7 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 19 Jan 2024 15:07:41 +0530 Subject: [PATCH 58/86] Add new hateoas-mapper implementation --- .../service/mapper/hateoas/HateoasMapper.java | 26 +++ .../mapper/hateoas/HateoasMapperImpl.java | 180 ++++++++++++++++++ .../mapper/parameter/HateoasMapper.java | 89 --------- 3 files changed, 206 insertions(+), 89 deletions(-) create mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java create mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java delete mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java new file mode 100644 index 000000000..2005c9f2d --- /dev/null +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.service.mapper.hateoas; + +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.swagger.v3.oas.models.OpenAPI; + +public interface HateoasMapper { + void setSwaggerLinks(ServiceDeclarationNode serviceNode, OpenAPI openAPI); +} diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java new file mode 100644 index 000000000..acffbaf6f --- /dev/null +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.service.mapper.hateoas; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.ServiceDeclarationSymbol; +import io.ballerina.compiler.api.symbols.Symbol; +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.openapi.service.mapper.Constants; +import io.ballerina.openapi.service.mapper.response.HateoasLink; +import io.ballerina.openapi.service.mapper.utils.MapperCommonUtils; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.links.Link; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; + +import static io.ballerina.openapi.service.mapper.Constants.OPENAPI_LINK_DEFAULT_REL; +import static io.ballerina.openapi.service.mapper.hateoas.HateoasContextHolder.getHateoasContextHolder; +import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.generateRelativePath; +import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; +import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getValueForAnnotationFields; + +public class HateoasMapperImpl implements HateoasMapper { + private final String packageId; + private final SemanticModel semanticModel; + + public HateoasMapperImpl(String packageId, SemanticModel semanticModel) { + this.packageId = packageId; + this.semanticModel = semanticModel; + } + + @Override + public void setSwaggerLinks(ServiceDeclarationNode serviceNode, OpenAPI openAPI) { + Optional serviceSymbolOpt = semanticModel.symbol(serviceNode); + if (serviceSymbolOpt.isEmpty()) { + return; + } + ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceSymbolOpt.get(); + int serviceId = serviceSymbol.hashCode(); + Paths paths = openAPI.getPaths(); + for (Node node: serviceNode.members()) { + if (!node.kind().equals(SyntaxKind.RESOURCE_ACCESSOR_DEFINITION)) { + continue; + } + FunctionDefinitionNode resource = (FunctionDefinitionNode) node; + Optional responses = getApiResponsesForResource(resource, paths); + if (responses.isEmpty()) { + continue; + } + setSwaggerLinksInApiResponse(serviceId, resource, responses.get()); + } + } + + private Optional getApiResponsesForResource(FunctionDefinitionNode resource, Paths paths) { + String resourcePath = MapperCommonUtils.unescapeIdentifier(generateRelativePath(resource)); + if (!paths.containsKey(resourcePath)) { + return Optional.empty(); + } + PathItem openApiResource = paths.get(resourcePath); + String httpMethod = resource.functionName().toString().trim(); + ApiResponses responses = null; + switch (httpMethod.trim().toUpperCase(Locale.ENGLISH)) { + case Constants.GET -> { + responses = openApiResource.getGet().getResponses(); + } + case Constants.PUT -> { + responses = openApiResource.getPut().getResponses(); + } + case Constants.POST -> { + responses = openApiResource.getPost().getResponses(); + } + case Constants.DELETE -> { + responses = openApiResource.getDelete().getResponses(); + } + case Constants.OPTIONS -> { + responses = openApiResource.getOptions().getResponses(); + } + case Constants.PATCH -> { + responses = openApiResource.getPatch().getResponses(); + } + case Constants.HEAD -> { + responses = openApiResource.getHead().getResponses(); + } + default -> { } + } + return Optional.ofNullable(responses); + } + + private void setSwaggerLinksInApiResponse(int serviceId, FunctionDefinitionNode resource, + ApiResponses apiResponses) { + Map swaggerLinks = mapHateoasLinksToSwaggerLinks(packageId, serviceId, resource); + if (!swaggerLinks.isEmpty()) { + for (Map.Entry entry : apiResponses.entrySet()) { + int statusCode = Integer.parseInt(entry.getKey()); + if (statusCode >= 200 && statusCode < 300) { + entry.getValue().setLinks(swaggerLinks); + } + } + } + } + + private Map mapHateoasLinksToSwaggerLinks(String packageId, int serviceId, + FunctionDefinitionNode resourceFunction) { + Optional linkedTo = getResourceConfigAnnotation(resourceFunction) + .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, "linkedTo")); + if (linkedTo.isEmpty()) { + return Collections.emptyMap(); + } + List links = getLinks(linkedTo.get()); + Map hateoasLinks = new HashMap<>(); + for (HateoasLink link : links) { + Optional resource = getHateoasContextHolder() + .getHateoasResource(packageId, serviceId, link.getResourceName(), link.getResourceMethod()); + if (resource.isPresent()) { + Link swaggerLink = new Link(); + String operationId = resource.get().operationId(); + swaggerLink.setOperationId(operationId); + hateoasLinks.put(link.getRel(), swaggerLink); + } + } + return hateoasLinks; + } + + private List getLinks(String linkedTo) { + List links = new ArrayList<>(); + String[] linkArray = linkedTo.replaceAll("[\\[\\]]", "").split("\\},\\s*"); + for (String linkString : linkArray) { + HateoasLink link = parseHateoasLink(linkString); + links.add(link); + } + return links; + } + + private HateoasLink parseHateoasLink(String input) { + HateoasLink hateoasLink = new HateoasLink(); + HashMap keyValueMap = new HashMap<>(); + String[] keyValuePairs = input.replaceAll("[{}]", "").split(",\\s*"); + for (String pair : keyValuePairs) { + String[] parts = pair.split(":\\s*"); + if (parts.length == 2) { + String key = parts[0].trim(); + String value = parts[1].replaceAll("\"", "").trim(); + keyValueMap.put(key, value); + } + } + hateoasLink.setResourceName(keyValueMap.get("name")); + hateoasLink.setRel(keyValueMap.getOrDefault("relation", OPENAPI_LINK_DEFAULT_REL)); + hateoasLink.setResourceMethod(keyValueMap.get("method")); + return hateoasLink; + } +} diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java deleted file mode 100644 index c6af1fd59..000000000 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/parameter/HateoasMapper.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.ballerina.openapi.service.mapper.parameter; - -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.openapi.service.mapper.hateoas.Resource; -import io.ballerina.openapi.service.mapper.response.HateoasLink; -import io.swagger.v3.oas.models.links.Link; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import static io.ballerina.openapi.service.mapper.Constants.OPENAPI_LINK_DEFAULT_REL; -import static io.ballerina.openapi.service.mapper.hateoas.HateoasContextHolder.getHateoasContextHolder; -import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; -import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getValueForAnnotationFields; - -public class HateoasMapper { - - public Map mapHateoasLinksToSwaggerLinks(String packageId, int serviceId, - FunctionDefinitionNode resourceFunction) { - Optional linkedTo = getResourceConfigAnnotation(resourceFunction) - .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, "linkedTo")); - if (linkedTo.isEmpty()) { - return Collections.emptyMap(); - } - List links = getLinks(linkedTo.get()); - Map hateoasLinks = new HashMap<>(); - for (HateoasLink link : links) { - Optional resource = getHateoasContextHolder() - .getHateoasResource(packageId, serviceId, link.getResourceName(), link.getResourceMethod()); - if (resource.isPresent()) { - Link swaggerLink = new Link(); - String operationId = resource.get().operationId(); - swaggerLink.setOperationId(operationId); - hateoasLinks.put(link.getRel(), swaggerLink); - } - } - return hateoasLinks; - } - - private List getLinks(String linkedTo) { - List links = new ArrayList<>(); - String[] linkArray = linkedTo.replaceAll("[\\[\\]]", "").split("\\},\\s*"); - for (String linkString : linkArray) { - HateoasLink link = parseHateoasLink(linkString); - links.add(link); - } - return links; - } - - public static HateoasLink parseHateoasLink(String input) { - HateoasLink hateoasLink = new HateoasLink(); - HashMap keyValueMap = new HashMap<>(); - String[] keyValuePairs = input.replaceAll("[{}]", "").split(",\\s*"); - for (String pair : keyValuePairs) { - String[] parts = pair.split(":\\s*"); - if (parts.length == 2) { - String key = parts[0].trim(); - String value = parts[1].replaceAll("\"", "").trim(); - keyValueMap.put(key, value); - } - } - hateoasLink.setResourceName(keyValueMap.get("name")); - hateoasLink.setRel(keyValueMap.getOrDefault("relation", OPENAPI_LINK_DEFAULT_REL)); - hateoasLink.setResourceMethod(keyValueMap.get("method")); - return hateoasLink; - } -} From 9b939c7c27722f195d38a06c07ce59d4010c35d6 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 19 Jan 2024 15:15:56 +0530 Subject: [PATCH 59/86] Add doc-comment to the hateoas-mapper interface --- .../openapi/service/mapper/hateoas/HateoasMapper.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java index 2005c9f2d..524b25968 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java @@ -21,6 +21,12 @@ import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import io.swagger.v3.oas.models.OpenAPI; +/** + * This {@link HateoasMapper} uses to set HATEOAS links into the OpenAPI context. + * + * @since 1.9.0 + */ public interface HateoasMapper { + void setSwaggerLinks(ServiceDeclarationNode serviceNode, OpenAPI openAPI); } From 1ee2599c4cf963025288418e92321a40faee88c5 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 19 Jan 2024 15:16:07 +0530 Subject: [PATCH 60/86] Refactor code-base --- .../openapi/service/mapper/ResourceMapperImpl.java | 7 ------- .../service/mapper/ServiceToOpenAPIMapper.java | 11 +++++++---- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java index 38ade9aeb..512c387e0 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java @@ -20,7 +20,6 @@ import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.api.symbols.Documentable; import io.ballerina.compiler.api.symbols.Documentation; -import io.ballerina.compiler.api.symbols.ServiceDeclarationSymbol; import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.compiler.syntax.tree.Node; @@ -31,7 +30,6 @@ import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; import io.ballerina.openapi.service.mapper.model.AdditionalData; import io.ballerina.openapi.service.mapper.model.OperationInventory; -import io.ballerina.openapi.service.mapper.parameter.HateoasMapper; import io.ballerina.openapi.service.mapper.parameter.ParameterMapper; import io.ballerina.openapi.service.mapper.parameter.ParameterMapperImpl; import io.ballerina.openapi.service.mapper.response.ResponseMapper; @@ -43,9 +41,6 @@ import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.Paths; -import io.swagger.v3.oas.models.links.Link; -import io.swagger.v3.oas.models.responses.ApiResponse; -import io.swagger.v3.oas.models.responses.ApiResponses; import java.util.HashMap; import java.util.List; @@ -69,7 +64,6 @@ public class ResourceMapperImpl implements ResourceMapper { private final OpenAPI openAPI; private final List resources; private final boolean treatNilableAsOptional; - private final HateoasMapper hateoasMapper; /** * Initializes a resource parser for openApi. @@ -80,7 +74,6 @@ public class ResourceMapperImpl implements ResourceMapper { this.resources = resources; this.additionalData = additionalData; this.treatNilableAsOptional = treatNilableAsOptional; - this.hateoasMapper = new HateoasMapper(); } public void setOperation(SemanticModel semanticModel, ServiceDeclarationNode service) { diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index 4a3ea701d..f091c7230 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -35,6 +35,8 @@ 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.hateoas.HateoasMapper; +import io.ballerina.openapi.service.mapper.hateoas.HateoasMapperImpl; import io.ballerina.openapi.service.mapper.hateoas.HateoasMetadataVisitor; import io.ballerina.openapi.service.mapper.model.AdditionalData; import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; @@ -205,11 +207,12 @@ public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) // 03. Filter path and component sections in OAS. // Generate openApi string for the mentioned service name. String packageId = oasGenerationMetaInfo.getProject().currentPackage().packageId().id().toString(); - convertServiceToOpenAPI( - packageId, serviceDefinition, openapi, semanticModel, moduleMemberVisitor, diagnostics); + convertServiceToOpenAPI(serviceDefinition, openapi, semanticModel, moduleMemberVisitor, diagnostics); ConstraintMapper constraintMapper = new ConstraintMapperImpl(openapi, moduleMemberVisitor, diagnostics); constraintMapper.setConstraints(); + HateoasMapper hateoasMapper = new HateoasMapperImpl(packageId, semanticModel); + hateoasMapper.setSwaggerLinks(serviceDefinition, openapi); return new OASResult(openapi, diagnostics); } else { return new OASResult(openapi, oasResult.getDiagnostics()); @@ -236,7 +239,7 @@ public static ModuleMemberVisitor extractNodesFromProject(Project project) { return balNodeVisitor; } - private static void convertServiceToOpenAPI(String packageId, ServiceDeclarationNode serviceNode, OpenAPI openAPI, + private static void convertServiceToOpenAPI(ServiceDeclarationNode serviceNode, OpenAPI openAPI, SemanticModel semanticModel, ModuleMemberVisitor moduleMemberVisitor, List diagnostics) { NodeList functions = serviceNode.members(); @@ -250,7 +253,7 @@ private static void convertServiceToOpenAPI(String packageId, ServiceDeclaration AdditionalData additionalData = new AdditionalData(semanticModel, moduleMemberVisitor, diagnostics); ResourceMapper resourceMapper = new ResourceMapperImpl(openAPI, resources, additionalData, isTreatNilableAsOptionalParameter(serviceNode)); - resourceMapper.setOperation(packageId, semanticModel, serviceNode); + resourceMapper.setOperation(semanticModel, serviceNode); } private static boolean isTreatNilableAsOptionalParameter(ServiceDeclarationNode serviceNode) { From 9f6645325dc172a7833e47009cf64c1936a91644 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Fri, 19 Jan 2024 15:22:04 +0530 Subject: [PATCH 61/86] Update doc-comments of the hateoas mapper --- .../openapi/service/mapper/hateoas/HateoasMapper.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java index 524b25968..4c5b4fb07 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java @@ -27,6 +27,12 @@ * @since 1.9.0 */ public interface HateoasMapper { - + + /** + * Sets HATEOAS links into the OpenAPI context. + * + * @param serviceNode Specific service declaration node + * @param openAPI Current OpenAPI context + */ void setSwaggerLinks(ServiceDeclarationNode serviceNode, OpenAPI openAPI); } From dd17f5ef4c017ef0b595c6ad75e14bbf89c6b699 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Fri, 19 Jan 2024 16:46:54 +0530 Subject: [PATCH 62/86] Remove unused function parameters --- .../openapi/service/mapper/ResourceMapperImpl.java | 14 ++++++-------- .../service/mapper/ServiceToOpenAPIMapper.java | 2 +- .../service/mapper/hateoas/HateoasMapper.java | 2 +- .../service/mapper/hateoas/HateoasMapperImpl.java | 2 +- .../service/mapper/response/ResponseMapper.java | 3 --- .../mapper/response/ResponseMapperImpl.java | 4 ---- 6 files changed, 9 insertions(+), 18 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java index 512c387e0..40fa0f198 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java @@ -86,7 +86,7 @@ public void setOperation(SemanticModel semanticModel, ServiceDeclarationNode ser components.setSchemas(new TreeMap<>()); } for (FunctionDefinitionNode resource : resources) { - addResourceMapping(semanticModel, resource, components, service); + addResourceMapping(resource, components); } if (components.getSchemas().isEmpty()) { openAPI.setComponents(null); @@ -94,8 +94,7 @@ public void setOperation(SemanticModel semanticModel, ServiceDeclarationNode ser openAPI.setPaths(pathObject); } - private void addResourceMapping(SemanticModel semanticModel, FunctionDefinitionNode resource, - Components components, ServiceDeclarationNode service) { + private void addResourceMapping(FunctionDefinitionNode resource, Components components) { String path = MapperCommonUtils.unescapeIdentifier(generateRelativePath(resource)); String httpMethod = resource.functionName().toString().trim(); if (httpMethod.equals(String.format("'%s", DEFAULT)) || httpMethod.equals(DEFAULT)) { @@ -104,7 +103,7 @@ private void addResourceMapping(SemanticModel semanticModel, FunctionDefinitionN resource.location()); additionalData.diagnostics().add(error); } else { - convertResourceToOperation(semanticModel, resource, httpMethod, path, components, service) + convertResourceToOperation(resource, httpMethod, path, components) .ifPresent(operation -> addPathItem(httpMethod, pathObject, operation.getOperation(), path)); } } @@ -177,10 +176,9 @@ private void addPathItem(String httpMethod, Paths path, Operation operation, Str * * @return Operation Adaptor object of given resource */ - private Optional convertResourceToOperation(SemanticModel semanticModel, - FunctionDefinitionNode resource, String httpMethod, - String generateRelativePath, Components components, - ServiceDeclarationNode service) { + private Optional convertResourceToOperation(FunctionDefinitionNode resource, String httpMethod, + String generateRelativePath, + Components components) { OperationInventory operationInventory = new OperationInventory(); operationInventory.setHttpOperation(httpMethod); operationInventory.setPath(generateRelativePath); diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index f091c7230..4519d145b 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -274,7 +274,7 @@ private static boolean isTreatNilableAsOptionalParameter(ServiceDeclarationNode return true; } - public static void extractHateoasLinkMetadata(Project project) { + private static void extractHateoasLinkMetadata(Project project) { String packageId = project.currentPackage().packageId().id().toString(); project.currentPackage().moduleIds().forEach(moduleId -> { Module module = project.currentPackage().module(moduleId); diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java index 4c5b4fb07..3421ec52f 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java index acffbaf6f..1572b6d83 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java index eead2b829..1673466a9 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java @@ -17,8 +17,6 @@ */ package io.ballerina.openapi.service.mapper.response; -import io.swagger.v3.oas.models.responses.ApiResponses; - /** * This {@link ResponseMapper} represents the interface for response mapper. * @@ -27,5 +25,4 @@ public interface ResponseMapper { void setApiResponses(); - ApiResponses getApiResponses(); } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperImpl.java index 3c780eed7..00977bfd8 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapperImpl.java @@ -110,10 +110,6 @@ public void setApiResponses() { operationInventory.setApiResponses(apiResponses); } - public ApiResponses getApiResponses() { - return apiResponses; - } - private TypeSymbol getReturnTypeSymbol(FunctionDefinitionNode resourceNode) { Optional symbol = semanticModel.symbol(resourceNode); if (symbol.isEmpty() || !(symbol.get() instanceof ResourceMethodSymbol resourceMethodSymbol)) { From dae465e481efc827375e7b5d04bbcad19835990a Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Mon, 22 Jan 2024 09:24:58 +0530 Subject: [PATCH 63/86] Update license headers --- .../openapi/service/mapper/hateoas/HateoasContextHolder.java | 2 +- .../openapi/service/mapper/hateoas/HateoasMetadataVisitor.java | 2 +- .../io/ballerina/openapi/service/mapper/hateoas/Resource.java | 2 +- .../io/ballerina/openapi/service/mapper/hateoas/Service.java | 2 +- .../ballerina/openapi/service/mapper/response/HateoasLink.java | 2 +- .../io/ballerina/openapi/generators/openapi/HateoasTests.java | 2 +- .../ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal | 2 +- .../ballerina-to-openapi/hateoas/hateoas_multiple_links.bal | 2 +- .../resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal | 2 +- .../resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java index a365fe75f..076800133 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java index bfc5e8cd4..bb21ab416 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java index 07215c9d0..a6f1865fd 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java index 3a875f117..b484991f9 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java index 75d2070b6..8199d463d 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java index 985cfea8f..0b4c17d15 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal index 1609648bd..cabd3937f 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal index 42c65ae2b..1c0ed1493 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal index 60dc804fd..e44368ee7 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal index 1ad07caaf..9c3831d05 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except From 7b2befaf76d899815c1659af6def29699aab2634 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Mon, 22 Jan 2024 09:30:05 +0530 Subject: [PATCH 64/86] Update license headers --- .../ballerina/openapi/service/mapper/hateoas/HateoasMapper.java | 2 +- .../openapi/service/mapper/hateoas/HateoasMapperImpl.java | 2 +- .../io/ballerina/openapi/service/mapper/hateoas/Resource.java | 2 +- .../io/ballerina/openapi/service/mapper/hateoas/Service.java | 2 +- .../ballerina/openapi/service/mapper/response/HateoasLink.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java index 3421ec52f..cf46f9fc2 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java index 1572b6d83..0de9916ba 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java index a6f1865fd..174c07eb6 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java index b484991f9..f1a5fa523 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java index 8199d463d..cf43e973c 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at From 4d2f941bc7a91034962cfdb2ced5196c174a8f21 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Mon, 22 Jan 2024 09:31:00 +0530 Subject: [PATCH 65/86] Refactor code base --- .../mapper/hateoas/HateoasMapperImpl.java | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java index acffbaf6f..301930d11 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -49,6 +49,11 @@ import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getValueForAnnotationFields; +/** + * This {@link HateoasMapperImpl} class represents the implementation of the {@link HateoasMapper}. + * + * @since 1.9.0 + */ public class HateoasMapperImpl implements HateoasMapper { private final String packageId; private final SemanticModel semanticModel; @@ -118,12 +123,13 @@ private Optional getApiResponsesForResource(FunctionDefinitionNode private void setSwaggerLinksInApiResponse(int serviceId, FunctionDefinitionNode resource, ApiResponses apiResponses) { Map swaggerLinks = mapHateoasLinksToSwaggerLinks(packageId, serviceId, resource); - if (!swaggerLinks.isEmpty()) { - for (Map.Entry entry : apiResponses.entrySet()) { - int statusCode = Integer.parseInt(entry.getKey()); - if (statusCode >= 200 && statusCode < 300) { - entry.getValue().setLinks(swaggerLinks); - } + if (swaggerLinks.isEmpty()) { + return; + } + for (Map.Entry entry : apiResponses.entrySet()) { + int statusCode = Integer.parseInt(entry.getKey()); + if (statusCode >= 200 && statusCode < 300) { + entry.getValue().setLinks(swaggerLinks); } } } @@ -140,12 +146,13 @@ private Map mapHateoasLinksToSwaggerLinks(String packageId, int se for (HateoasLink link : links) { Optional resource = getHateoasContextHolder() .getHateoasResource(packageId, serviceId, link.getResourceName(), link.getResourceMethod()); - if (resource.isPresent()) { - Link swaggerLink = new Link(); - String operationId = resource.get().operationId(); - swaggerLink.setOperationId(operationId); - hateoasLinks.put(link.getRel(), swaggerLink); + if (resource.isEmpty()) { + continue; } + Link swaggerLink = new Link(); + String operationId = resource.get().operationId(); + swaggerLink.setOperationId(operationId); + hateoasLinks.put(link.getRel(), swaggerLink); } return hateoasLinks; } @@ -166,11 +173,12 @@ private HateoasLink parseHateoasLink(String input) { String[] keyValuePairs = input.replaceAll("[{}]", "").split(",\\s*"); for (String pair : keyValuePairs) { String[] parts = pair.split(":\\s*"); - if (parts.length == 2) { - String key = parts[0].trim(); - String value = parts[1].replaceAll("\"", "").trim(); - keyValueMap.put(key, value); + if (parts.length != 2) { + continue; } + String key = parts[0].trim(); + String value = parts[1].replaceAll("\"", "").trim(); + keyValueMap.put(key, value); } hateoasLink.setResourceName(keyValueMap.get("name")); hateoasLink.setRel(keyValueMap.getOrDefault("relation", OPENAPI_LINK_DEFAULT_REL)); From a9c7016dc2598e21fc0f94c771b4cd816660a635 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Mon, 22 Jan 2024 09:31:16 +0530 Subject: [PATCH 66/86] Update licensing headers and doc-comments --- .../mapper/hateoas/HateoasContextHolder.java | 27 +++++++++++-------- .../hateoas/HateoasMetadataVisitor.java | 27 +++++++++++-------- .../service/mapper/hateoas/Resource.java | 27 +++++++++++-------- .../service/mapper/hateoas/Service.java | 27 +++++++++++-------- 4 files changed, 64 insertions(+), 44 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java index a365fe75f..a3739ef1d 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java @@ -1,19 +1,19 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package io.ballerina.openapi.service.mapper.hateoas; @@ -23,6 +23,11 @@ import java.util.Objects; import java.util.Optional; +/** + * A context-holder to share HATEOAS meta-data information. + * + * @since 1.6.0 + */ public final class HateoasContextHolder { private static HateoasContextHolder instance; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java index bfc5e8cd4..3fc2b8825 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java @@ -1,19 +1,19 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package io.ballerina.openapi.service.mapper.hateoas; @@ -34,6 +34,11 @@ import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getValueForAnnotationFields; +/** + * Visitor to retrieve the Hateoas meta-data from resource functions within a {@link ServiceDeclarationNode}. + * + * @since 1.6.0 + */ public class HateoasMetadataVisitor extends NodeVisitor { private final String packageId; private final SemanticModel semanticModel; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java index 07215c9d0..24c701fee 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java @@ -1,22 +1,27 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package io.ballerina.openapi.service.mapper.hateoas; +/** + * A context-holder to save and retrieve HATEOAS meta-data for a given resources. + * + * @since 1.6.0 + */ public record Resource(String resourceMethod, String operationId) { } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java index 3a875f117..42a82b65a 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java @@ -1,19 +1,19 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package io.ballerina.openapi.service.mapper.hateoas; @@ -23,6 +23,11 @@ import java.util.List; import java.util.Map; +/** + * A data class to hold service level HATEOAS meta-data. + * + * @since 1.6.0 + */ public class Service { private final String packageId; private final int serviceId; From e3030562d1952aad9b6fdb566d6afe5971cb3254 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Mon, 22 Jan 2024 09:35:18 +0530 Subject: [PATCH 67/86] Fix licensing headers --- .../openapi/service/mapper/hateoas/HateoasContextHolder.java | 2 +- .../openapi/service/mapper/hateoas/HateoasMapper.java | 4 ++-- .../openapi/service/mapper/hateoas/HateoasMapperImpl.java | 2 +- .../service/mapper/hateoas/HateoasMetadataVisitor.java | 2 +- .../io/ballerina/openapi/service/mapper/hateoas/Resource.java | 2 +- .../io/ballerina/openapi/service/mapper/hateoas/Service.java | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java index a3739ef1d..3f915a846 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java index 4c5b4fb07..cf46f9fc2 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java index 301930d11..471daabcf 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java index 3fc2b8825..332a42686 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java index 24c701fee..5627bad9a 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java index 42a82b65a..d39960142 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at From 814f2b9b996e061390d132ef042e6e68c7d4fed2 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Mon, 22 Jan 2024 10:02:09 +0530 Subject: [PATCH 68/86] Update Java doc --- .../io/ballerina/openapi/service/mapper/hateoas/Resource.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java index 5627bad9a..92874e5ec 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java @@ -20,7 +20,8 @@ /** * A context-holder to save and retrieve HATEOAS meta-data for a given resources. - * + * @param resourceMethod http resource for the method + * @param operationId generated operationId * @since 1.6.0 */ public record Resource(String resourceMethod, String operationId) { From d7716d20cc3868dbb681b2b7c7a44dfe08450708 Mon Sep 17 00:00:00 2001 From: Sachin Akash Date: Mon, 22 Jan 2024 11:57:26 +0530 Subject: [PATCH 69/86] Update ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java Co-authored-by: Sumudu Nissanka --- .../openapi/service/mapper/hateoas/HateoasContextHolder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java index 3f915a846..ee8b66d44 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java @@ -26,7 +26,7 @@ /** * A context-holder to share HATEOAS meta-data information. * - * @since 1.6.0 + * @since 1.9.0 */ public final class HateoasContextHolder { private static HateoasContextHolder instance; From 53d24dabf8f1782db27a55f6a60ae2a4ff404b9a Mon Sep 17 00:00:00 2001 From: Sachin Akash Date: Mon, 22 Jan 2024 13:41:56 +0530 Subject: [PATCH 70/86] Update ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java Co-authored-by: Krishnananthalingam Tharmigan <63336800+TharmiganK@users.noreply.github.com> --- .../openapi/service/mapper/ServiceToOpenAPIMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index 4519d145b..9817166e7 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -212,7 +212,7 @@ public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) diagnostics); constraintMapper.setConstraints(); HateoasMapper hateoasMapper = new HateoasMapperImpl(packageId, semanticModel); - hateoasMapper.setSwaggerLinks(serviceDefinition, openapi); + hateoasMapper.setLinks(serviceDefinition, openapi); return new OASResult(openapi, diagnostics); } else { return new OASResult(openapi, oasResult.getDiagnostics()); From 6324c6578e24d5ba975bd785c0c3c2b81a29be6a Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Mon, 22 Jan 2024 14:50:21 +0530 Subject: [PATCH 71/86] Resolve review suggestions --- .../openapi/service/mapper/Constants.java | 1 - .../openapi/service/mapper/ResourceMapper.java | 2 +- .../service/mapper/ResourceMapperImpl.java | 4 +--- .../service/mapper/ServiceToOpenAPIMapper.java | 4 ++-- .../service/mapper/hateoas/HateoasConstants.java | 7 +++++++ .../service/mapper/hateoas/HateoasMapper.java | 2 +- .../service/mapper/hateoas/HateoasMapperImpl.java | 15 ++++++++------- 7 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasConstants.java diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/Constants.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/Constants.java index 51e17e942..187d39a0c 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/Constants.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/Constants.java @@ -49,7 +49,6 @@ public final class Constants { public static final String OPTIONS = "OPTIONS"; public static final String HEAD = "HEAD"; public static final String OPENAPI_SUFFIX = "_openapi"; - public static final String OPENAPI_LINK_DEFAULT_REL = "_self"; public static final String SERVER = "server"; public static final String PORT = "port"; public static final String HTTP_REQUEST = "http:Request"; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java index dec246139..b586b898f 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java @@ -25,5 +25,5 @@ */ public interface ResourceMapper { - void setOperation(SemanticModel semanticModel, ServiceDeclarationNode service); + void setOperation(); } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java index 40fa0f198..2cc1646ab 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java @@ -17,14 +17,12 @@ */ package io.ballerina.openapi.service.mapper; -import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.api.symbols.Documentable; import io.ballerina.compiler.api.symbols.Documentation; import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.ResourcePathParameterNode; -import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import io.ballerina.openapi.service.mapper.diagnostic.DiagnosticMessages; import io.ballerina.openapi.service.mapper.diagnostic.IncompatibleResourceDiagnostic; import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; @@ -76,7 +74,7 @@ public class ResourceMapperImpl implements ResourceMapper { this.treatNilableAsOptional = treatNilableAsOptional; } - public void setOperation(SemanticModel semanticModel, ServiceDeclarationNode service) { + public void setOperation() { Components components = openAPI.getComponents(); if (components == null) { components = new Components(); diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index 9817166e7..6e9147959 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -212,7 +212,7 @@ public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) diagnostics); constraintMapper.setConstraints(); HateoasMapper hateoasMapper = new HateoasMapperImpl(packageId, semanticModel); - hateoasMapper.setLinks(serviceDefinition, openapi); + hateoasMapper.setOpenApiLinks(serviceDefinition, openapi); return new OASResult(openapi, diagnostics); } else { return new OASResult(openapi, oasResult.getDiagnostics()); @@ -253,7 +253,7 @@ private static void convertServiceToOpenAPI(ServiceDeclarationNode serviceNode, AdditionalData additionalData = new AdditionalData(semanticModel, moduleMemberVisitor, diagnostics); ResourceMapper resourceMapper = new ResourceMapperImpl(openAPI, resources, additionalData, isTreatNilableAsOptionalParameter(serviceNode)); - resourceMapper.setOperation(semanticModel, serviceNode); + resourceMapper.setOperation(); } private static boolean isTreatNilableAsOptionalParameter(ServiceDeclarationNode serviceNode) { diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasConstants.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasConstants.java new file mode 100644 index 000000000..7b36c92e1 --- /dev/null +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasConstants.java @@ -0,0 +1,7 @@ +package io.ballerina.openapi.service.mapper.hateoas; + +public class HateoasConstants { + + public static final String OPENAPI_LINK_DEFAULT_REL = "_self"; + public static final String BALLERINA_LINKEDTO_KEYWORD = "linkedTo"; +} diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java index cf46f9fc2..b97fec8f1 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java @@ -34,5 +34,5 @@ public interface HateoasMapper { * @param serviceNode Specific service declaration node * @param openAPI Current OpenAPI context */ - void setSwaggerLinks(ServiceDeclarationNode serviceNode, OpenAPI openAPI); + void setOpenApiLinks(ServiceDeclarationNode serviceNode, OpenAPI openAPI); } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java index 471daabcf..697c2db01 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java @@ -43,7 +43,8 @@ import java.util.Map; import java.util.Optional; -import static io.ballerina.openapi.service.mapper.Constants.OPENAPI_LINK_DEFAULT_REL; +import static io.ballerina.openapi.service.mapper.hateoas.HateoasConstants.BALLERINA_LINKEDTO_KEYWORD; +import static io.ballerina.openapi.service.mapper.hateoas.HateoasConstants.OPENAPI_LINK_DEFAULT_REL; import static io.ballerina.openapi.service.mapper.hateoas.HateoasContextHolder.getHateoasContextHolder; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.generateRelativePath; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; @@ -64,7 +65,7 @@ public HateoasMapperImpl(String packageId, SemanticModel semanticModel) { } @Override - public void setSwaggerLinks(ServiceDeclarationNode serviceNode, OpenAPI openAPI) { + public void setOpenApiLinks(ServiceDeclarationNode serviceNode, OpenAPI openAPI) { Optional serviceSymbolOpt = semanticModel.symbol(serviceNode); if (serviceSymbolOpt.isEmpty()) { return; @@ -81,7 +82,7 @@ public void setSwaggerLinks(ServiceDeclarationNode serviceNode, OpenAPI openAPI) if (responses.isEmpty()) { continue; } - setSwaggerLinksInApiResponse(serviceId, resource, responses.get()); + setOpenApiLinksInApiResponse(serviceId, resource, responses.get()); } } @@ -120,9 +121,9 @@ private Optional getApiResponsesForResource(FunctionDefinitionNode return Optional.ofNullable(responses); } - private void setSwaggerLinksInApiResponse(int serviceId, FunctionDefinitionNode resource, + private void setOpenApiLinksInApiResponse(int serviceId, FunctionDefinitionNode resource, ApiResponses apiResponses) { - Map swaggerLinks = mapHateoasLinksToSwaggerLinks(packageId, serviceId, resource); + Map swaggerLinks = mapHateoasLinksToOpenApiLinks(packageId, serviceId, resource); if (swaggerLinks.isEmpty()) { return; } @@ -134,10 +135,10 @@ private void setSwaggerLinksInApiResponse(int serviceId, FunctionDefinitionNode } } - private Map mapHateoasLinksToSwaggerLinks(String packageId, int serviceId, + private Map mapHateoasLinksToOpenApiLinks(String packageId, int serviceId, FunctionDefinitionNode resourceFunction) { Optional linkedTo = getResourceConfigAnnotation(resourceFunction) - .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, "linkedTo")); + .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, BALLERINA_LINKEDTO_KEYWORD)); if (linkedTo.isEmpty()) { return Collections.emptyMap(); } From 0de1125e6a4f42f0d84dc0e81a470e59a7dd622d Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Mon, 22 Jan 2024 14:59:48 +0530 Subject: [PATCH 72/86] Remove unused imports --- .../io/ballerina/openapi/service/mapper/ResourceMapper.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java index b586b898f..736c70dc0 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapper.java @@ -17,9 +17,6 @@ */ package io.ballerina.openapi.service.mapper; -import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; - /** * The {@link ResourceMapper} represents the interface for resource mapper. */ From 1461a87e5ca274fd8cc30794499c570a034529b3 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Mon, 22 Jan 2024 16:50:09 +0530 Subject: [PATCH 73/86] Refactor naming --- .../ballerina/openapi/service/mapper/ResourceMapperImpl.java | 4 ++-- .../mapper/hateoas/{HateoasConstants.java => Constants.java} | 2 +- .../openapi/service/mapper/hateoas/HateoasMapperImpl.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/{HateoasConstants.java => Constants.java} (85%) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java index 2cc1646ab..5455bbccc 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java @@ -101,8 +101,8 @@ private void addResourceMapping(FunctionDefinitionNode resource, Components comp resource.location()); additionalData.diagnostics().add(error); } else { - convertResourceToOperation(resource, httpMethod, path, components) - .ifPresent(operation -> addPathItem(httpMethod, pathObject, operation.getOperation(), path)); + convertResourceToOperation(resource, httpMethod, path, components).ifPresent( + operation -> addPathItem(httpMethod, pathObject, operation.getOperation(), path)); } } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasConstants.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Constants.java similarity index 85% rename from ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasConstants.java rename to ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Constants.java index 7b36c92e1..decccf15a 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasConstants.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Constants.java @@ -1,6 +1,6 @@ package io.ballerina.openapi.service.mapper.hateoas; -public class HateoasConstants { +public class Constants { public static final String OPENAPI_LINK_DEFAULT_REL = "_self"; public static final String BALLERINA_LINKEDTO_KEYWORD = "linkedTo"; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java index 697c2db01..9c1e41990 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java @@ -43,8 +43,8 @@ import java.util.Map; import java.util.Optional; -import static io.ballerina.openapi.service.mapper.hateoas.HateoasConstants.BALLERINA_LINKEDTO_KEYWORD; -import static io.ballerina.openapi.service.mapper.hateoas.HateoasConstants.OPENAPI_LINK_DEFAULT_REL; +import static io.ballerina.openapi.service.mapper.hateoas.Constants.BALLERINA_LINKEDTO_KEYWORD; +import static io.ballerina.openapi.service.mapper.hateoas.Constants.OPENAPI_LINK_DEFAULT_REL; import static io.ballerina.openapi.service.mapper.hateoas.HateoasContextHolder.getHateoasContextHolder; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.generateRelativePath; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; From d60e728bee2fe99adf2458dd8e5ff70143c7148c Mon Sep 17 00:00:00 2001 From: Sachin Akash Date: Tue, 23 Jan 2024 09:30:54 +0530 Subject: [PATCH 74/86] Update ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java Co-authored-by: Krishnananthalingam Tharmigan <63336800+TharmiganK@users.noreply.github.com> --- .../openapi/service/mapper/hateoas/HateoasMapperImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java index 9c1e41990..d56821c0b 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java @@ -150,7 +150,7 @@ private Map mapHateoasLinksToOpenApiLinks(String packageId, int se if (resource.isEmpty()) { continue; } - Link swaggerLink = new Link(); + Link openapiLink = new Link(); String operationId = resource.get().operationId(); swaggerLink.setOperationId(operationId); hateoasLinks.put(link.getRel(), swaggerLink); From e458073d607a52d29e51db349f677810a0e85b8a Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 23 Jan 2024 10:23:46 +0530 Subject: [PATCH 75/86] Refactor the variable names --- .../service/mapper/{response => hateoas}/HateoasLink.java | 2 +- .../openapi/service/mapper/hateoas/HateoasMapperImpl.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) rename ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/{response => hateoas}/HateoasLink.java (96%) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasLink.java similarity index 96% rename from ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java rename to ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasLink.java index cf43e973c..d33aa83c4 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/HateoasLink.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasLink.java @@ -16,7 +16,7 @@ * under the License. */ -package io.ballerina.openapi.service.mapper.response; +package io.ballerina.openapi.service.mapper.hateoas; public class HateoasLink { private String resourceName; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java index d56821c0b..438d1b91e 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java @@ -26,7 +26,6 @@ import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.openapi.service.mapper.Constants; -import io.ballerina.openapi.service.mapper.response.HateoasLink; import io.ballerina.openapi.service.mapper.utils.MapperCommonUtils; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.PathItem; @@ -152,8 +151,8 @@ private Map mapHateoasLinksToOpenApiLinks(String packageId, int se } Link openapiLink = new Link(); String operationId = resource.get().operationId(); - swaggerLink.setOperationId(operationId); - hateoasLinks.put(link.getRel(), swaggerLink); + openapiLink.setOperationId(operationId); + hateoasLinks.put(link.getRel(), openapiLink); } return hateoasLinks; } From dfa05455b7fb2180d9b9ece0f445314a25553116 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 23 Jan 2024 10:25:30 +0530 Subject: [PATCH 76/86] Refactor the method for generating operationId --- .../service/mapper/ResourceMapperImpl.java | 17 ++++---------- .../hateoas/HateoasMetadataVisitor.java | 13 +---------- .../mapper/utils/MapperCommonUtils.java | 23 ++++++++++++------- 3 files changed, 21 insertions(+), 32 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java index 5455bbccc..eaba491ec 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java @@ -174,25 +174,18 @@ private void addPathItem(String httpMethod, Paths path, Operation operation, Str * * @return Operation Adaptor object of given resource */ - private Optional convertResourceToOperation(FunctionDefinitionNode resource, String httpMethod, + private Optional convertResourceToOperation(FunctionDefinitionNode resourceFunction, String httpMethod, String generateRelativePath, Components components) { OperationInventory operationInventory = new OperationInventory(); operationInventory.setHttpOperation(httpMethod); operationInventory.setPath(generateRelativePath); - /* Set operation id */ - String resName = (resource.functionName().text() + "_" + - generateRelativePath).replaceAll("\\{///}", "_"); - - if (generateRelativePath.equals("/")) { - resName = resource.functionName().text(); - } - operationInventory.setOperationId(getOperationId(resName)); + operationInventory.setOperationId(getOperationId(resourceFunction)); // Set operation summary // Map API documentation - Map apiDocs = listAPIDocumentations(resource, operationInventory); + Map apiDocs = listAPIDocumentations(resourceFunction, operationInventory); //Add path parameters if in path and query parameters - ParameterMapper parameterMapper = new ParameterMapperImpl(resource, operationInventory, components, apiDocs, + ParameterMapper parameterMapper = new ParameterMapperImpl(resourceFunction, operationInventory, components, apiDocs, additionalData, treatNilableAsOptional); parameterMapper.setParameters(); List diagnostics = additionalData.diagnostics(); @@ -207,7 +200,7 @@ private Optional convertResourceToOperation(FunctionDefiniti } } - ResponseMapper responseMapper = new ResponseMapperImpl(resource, operationInventory, components, + ResponseMapper responseMapper = new ResponseMapperImpl(resourceFunction, operationInventory, components, additionalData); responseMapper.setApiResponses(); return Optional.of(operationInventory); diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java index 332a42686..b7d9776d3 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java @@ -64,7 +64,7 @@ public void visit(ServiceDeclarationNode serviceNode) { if (SyntaxKind.RESOURCE_ACCESSOR_DEFINITION.equals(child.kind())) { FunctionDefinitionNode resourceFunction = (FunctionDefinitionNode) child; String resourceMethod = resourceFunction.functionName().text(); - String operationId = generateOperationId(resourceFunction); + String operationId = MapperCommonUtils.getOperationId(resourceFunction); Optional resourceName = getResourceConfigAnnotation(resourceFunction) .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, "name")); if (resourceName.isEmpty()) { @@ -77,15 +77,4 @@ public void visit(ServiceDeclarationNode serviceNode) { } } } - - private String generateOperationId(FunctionDefinitionNode resourceFunction) { - String relativePath = MapperCommonUtils.generateRelativePath(resourceFunction); - String cleanResourcePath = MapperCommonUtils.unescapeIdentifier(relativePath); - String resName = (resourceFunction.functionName().text() + "_" + - cleanResourcePath).replaceAll("\\{///\\}", "_"); - if (cleanResourcePath.equals("/")) { - resName = resourceFunction.functionName().text(); - } - return MapperCommonUtils.getOperationId(resName); - } } 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 345ab790f..1b146df85 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 @@ -123,16 +123,23 @@ public static String generateRelativePath(FunctionDefinitionNode resourceFunctio /** * Generate operationId by removing special characters. * - * @param operationID input function name, record name or operation Id - * @return string with new generated name + * @param resourceFunction resource function definition + * @return string with a unique operationId */ - public static String getOperationId(String operationID) { + public static String getOperationId(FunctionDefinitionNode resourceFunction) { //For the flatten enable we need to remove first Part of valid name check // this - > !operationID.matches("\\b[a-zA-Z][a-zA-Z0-9]*\\b") && - if (operationID.matches("\\b[0-9]*\\b")) { - return operationID; + String relativePath = MapperCommonUtils.generateRelativePath(resourceFunction); + String cleanResourcePath = MapperCommonUtils.unescapeIdentifier(relativePath); + String resName = (resourceFunction.functionName().text() + "_" + + cleanResourcePath).replaceAll("\\{///\\}", "_"); + if (cleanResourcePath.equals("/")) { + resName = resourceFunction.functionName().text(); } - String[] split = operationID.split(Constants.SPECIAL_CHAR_REGEX); + if (resName.matches("\\b[0-9]*\\b")) { + return resName; + } + String[] split = resName.split(Constants.SPECIAL_CHAR_REGEX); StringBuilder validName = new StringBuilder(); for (String part : split) { if (!part.isBlank()) { @@ -143,8 +150,8 @@ public static String getOperationId(String operationID) { validName.append(part); } } - operationID = validName.toString(); - return operationID.substring(0, 1).toLowerCase(Locale.ENGLISH) + operationID.substring(1); + resName = validName.toString(); + return resName.substring(0, 1).toLowerCase(Locale.ENGLISH) + resName.substring(1); } /** From 0a979f2b3d7b557e9defad88cdf97ccb73ba0a79 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Tue, 23 Jan 2024 10:42:47 +0530 Subject: [PATCH 77/86] Fix checkstyle violations caused by line lengths --- .../openapi/service/mapper/ResourceMapperImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java index eaba491ec..529c7930a 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ResourceMapperImpl.java @@ -174,8 +174,8 @@ private void addPathItem(String httpMethod, Paths path, Operation operation, Str * * @return Operation Adaptor object of given resource */ - private Optional convertResourceToOperation(FunctionDefinitionNode resourceFunction, String httpMethod, - String generateRelativePath, + private Optional convertResourceToOperation(FunctionDefinitionNode resourceFunction, + String httpMethod, String generateRelativePath, Components components) { OperationInventory operationInventory = new OperationInventory(); operationInventory.setHttpOperation(httpMethod); @@ -185,8 +185,8 @@ private Optional convertResourceToOperation(FunctionDefiniti // Map API documentation Map apiDocs = listAPIDocumentations(resourceFunction, operationInventory); //Add path parameters if in path and query parameters - ParameterMapper parameterMapper = new ParameterMapperImpl(resourceFunction, operationInventory, components, apiDocs, - additionalData, treatNilableAsOptional); + ParameterMapper parameterMapper = new ParameterMapperImpl(resourceFunction, operationInventory, components, + apiDocs, additionalData, treatNilableAsOptional); parameterMapper.setParameters(); List diagnostics = additionalData.diagnostics(); if (diagnostics.size() > 1 || (diagnostics.size() == 1 && !diagnostics.get(0).getCode().equals( From 4ec420266e7b4600f728d29c473b50748d77e9d9 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Wed, 24 Jan 2024 13:54:56 +0530 Subject: [PATCH 78/86] Remove hateoas meta-data extraction logic from single OAS generation path --- .../openapi/service/mapper/ServiceToOpenAPIMapper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index 6e9147959..c28fe7343 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -110,6 +110,7 @@ public static List generateOAS3Definition(Project project, SyntaxTree .setOpenApiFileName(openApiName) .setBallerinaFilePath(inputPath) .setProject(project); + extractHateoasLinkMetadata(project); OASGenerationMetaInfo oasGenerationMetaInfo = builder.build(); OASResult oasDefinition = generateOAS(oasGenerationMetaInfo); oasDefinition.setServiceName(openApiName); @@ -188,7 +189,7 @@ private static void extractServiceNodes(String serviceName, List availab public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) { ServiceDeclarationNode serviceDefinition = oasGenerationMetaInfo.getServiceDeclarationNode(); ModuleMemberVisitor moduleMemberVisitor = extractNodesFromProject(oasGenerationMetaInfo.getProject()); - extractHateoasLinkMetadata(oasGenerationMetaInfo.getProject()); +// extractHateoasLinkMetadata(oasGenerationMetaInfo.getProject()); Set listeners = moduleMemberVisitor.getListenerDeclarationNodes(); SemanticModel semanticModel = oasGenerationMetaInfo.getSemanticModel(); String openApiFileName = oasGenerationMetaInfo.getOpenApiFileName(); From 2cf17b0f9198507b38c2737fe1a49bacf222e4ab Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Wed, 24 Jan 2024 14:58:12 +0530 Subject: [PATCH 79/86] Refactor hateoas-metadata visitor logic --- .../openapi/service/mapper/ServiceToOpenAPIMapper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index c28fe7343..339065077 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -100,6 +100,8 @@ public static List generateOAS3Definition(Project project, SyntaxTree null, serviceName, availableService.toString()); diagnostics.add(error); } + // Extract HATEOAS meta-data for the all the service-declarations in the current project + extractHateoasLinkMetadata(project); // Generating openapi specification for selected services for (Map.Entry serviceNode : servicesToGenerate.entrySet()) { String openApiName = getOpenApiFileName(syntaxTree.filePath(), serviceNode.getKey(), needJson); @@ -110,7 +112,6 @@ public static List generateOAS3Definition(Project project, SyntaxTree .setOpenApiFileName(openApiName) .setBallerinaFilePath(inputPath) .setProject(project); - extractHateoasLinkMetadata(project); OASGenerationMetaInfo oasGenerationMetaInfo = builder.build(); OASResult oasDefinition = generateOAS(oasGenerationMetaInfo); oasDefinition.setServiceName(openApiName); @@ -189,7 +190,6 @@ private static void extractServiceNodes(String serviceName, List availab public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) { ServiceDeclarationNode serviceDefinition = oasGenerationMetaInfo.getServiceDeclarationNode(); ModuleMemberVisitor moduleMemberVisitor = extractNodesFromProject(oasGenerationMetaInfo.getProject()); -// extractHateoasLinkMetadata(oasGenerationMetaInfo.getProject()); Set listeners = moduleMemberVisitor.getListenerDeclarationNodes(); SemanticModel semanticModel = oasGenerationMetaInfo.getSemanticModel(); String openApiFileName = oasGenerationMetaInfo.getOpenApiFileName(); From 90eb48d672af742702eb9b00245208ce99b7c871 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Wed, 31 Jan 2024 11:15:20 +0530 Subject: [PATCH 80/86] Remove the visitor usage --- .../mapper/ServiceToOpenAPIMapper.java | 3 +- .../mapper/hateoas/HateoasMapperImpl.java | 94 ++++++++++--------- 2 files changed, 52 insertions(+), 45 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index 339065077..02cb95a24 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -207,12 +207,11 @@ public static OASResult generateOAS(OASGenerationMetaInfo oasGenerationMetaInfo) serversMapperImpl.setServers(); // 03. Filter path and component sections in OAS. // Generate openApi string for the mentioned service name. - String packageId = oasGenerationMetaInfo.getProject().currentPackage().packageId().id().toString(); convertServiceToOpenAPI(serviceDefinition, openapi, semanticModel, moduleMemberVisitor, diagnostics); ConstraintMapper constraintMapper = new ConstraintMapperImpl(openapi, moduleMemberVisitor, diagnostics); constraintMapper.setConstraints(); - HateoasMapper hateoasMapper = new HateoasMapperImpl(packageId, semanticModel); + HateoasMapper hateoasMapper = new HateoasMapperImpl(); hateoasMapper.setOpenApiLinks(serviceDefinition, openapi); return new OASResult(openapi, diagnostics); } else { diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java index 438d1b91e..929faf253 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java @@ -18,9 +18,6 @@ package io.ballerina.openapi.service.mapper.hateoas; -import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.compiler.api.symbols.ServiceDeclarationSymbol; -import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; @@ -44,7 +41,6 @@ import static io.ballerina.openapi.service.mapper.hateoas.Constants.BALLERINA_LINKEDTO_KEYWORD; import static io.ballerina.openapi.service.mapper.hateoas.Constants.OPENAPI_LINK_DEFAULT_REL; -import static io.ballerina.openapi.service.mapper.hateoas.HateoasContextHolder.getHateoasContextHolder; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.generateRelativePath; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getValueForAnnotationFields; @@ -55,23 +51,11 @@ * @since 1.9.0 */ public class HateoasMapperImpl implements HateoasMapper { - private final String packageId; - private final SemanticModel semanticModel; - - public HateoasMapperImpl(String packageId, SemanticModel semanticModel) { - this.packageId = packageId; - this.semanticModel = semanticModel; - } @Override public void setOpenApiLinks(ServiceDeclarationNode serviceNode, OpenAPI openAPI) { - Optional serviceSymbolOpt = semanticModel.symbol(serviceNode); - if (serviceSymbolOpt.isEmpty()) { - return; - } - ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceSymbolOpt.get(); - int serviceId = serviceSymbol.hashCode(); Paths paths = openAPI.getPaths(); + Service hateoasService = extractHateoasMetaInfo(serviceNode); for (Node node: serviceNode.members()) { if (!node.kind().equals(SyntaxKind.RESOURCE_ACCESSOR_DEFINITION)) { continue; @@ -81,8 +65,28 @@ public void setOpenApiLinks(ServiceDeclarationNode serviceNode, OpenAPI openAPI) if (responses.isEmpty()) { continue; } - setOpenApiLinksInApiResponse(serviceId, resource, responses.get()); + setOpenApiLinksInApiResponse(hateoasService, resource, responses.get()); + } + } + + private Service extractHateoasMetaInfo(ServiceDeclarationNode serviceNode) { + Service service = new Service("ss", 0); + for (Node child : serviceNode.children()) { + if (SyntaxKind.RESOURCE_ACCESSOR_DEFINITION.equals(child.kind())) { + FunctionDefinitionNode resourceFunction = (FunctionDefinitionNode) child; + String resourceMethod = resourceFunction.functionName().text(); + String operationId = MapperCommonUtils.getOperationId(resourceFunction); + Optional resourceName = getResourceConfigAnnotation(resourceFunction) + .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, "name")); + if (resourceName.isEmpty()) { + continue; + } + String cleanedResourceName = resourceName.get().replaceAll("\"", ""); + Resource hateoasResource = new Resource(resourceMethod, operationId); + service.addResource(cleanedResourceName, hateoasResource); + } } + return service; } private Optional getApiResponsesForResource(FunctionDefinitionNode resource, Paths paths) { @@ -120,9 +124,9 @@ private Optional getApiResponsesForResource(FunctionDefinitionNode return Optional.ofNullable(responses); } - private void setOpenApiLinksInApiResponse(int serviceId, FunctionDefinitionNode resource, + private void setOpenApiLinksInApiResponse(Service hateoasService, FunctionDefinitionNode resource, ApiResponses apiResponses) { - Map swaggerLinks = mapHateoasLinksToOpenApiLinks(packageId, serviceId, resource); + Map swaggerLinks = mapHateoasLinksToOpenApiLinks(hateoasService, resource); if (swaggerLinks.isEmpty()) { return; } @@ -134,29 +138,6 @@ private void setOpenApiLinksInApiResponse(int serviceId, FunctionDefinitionNode } } - private Map mapHateoasLinksToOpenApiLinks(String packageId, int serviceId, - FunctionDefinitionNode resourceFunction) { - Optional linkedTo = getResourceConfigAnnotation(resourceFunction) - .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, BALLERINA_LINKEDTO_KEYWORD)); - if (linkedTo.isEmpty()) { - return Collections.emptyMap(); - } - List links = getLinks(linkedTo.get()); - Map hateoasLinks = new HashMap<>(); - for (HateoasLink link : links) { - Optional resource = getHateoasContextHolder() - .getHateoasResource(packageId, serviceId, link.getResourceName(), link.getResourceMethod()); - if (resource.isEmpty()) { - continue; - } - Link openapiLink = new Link(); - String operationId = resource.get().operationId(); - openapiLink.setOperationId(operationId); - hateoasLinks.put(link.getRel(), openapiLink); - } - return hateoasLinks; - } - private List getLinks(String linkedTo) { List links = new ArrayList<>(); String[] linkArray = linkedTo.replaceAll("[\\[\\]]", "").split("\\},\\s*"); @@ -185,4 +166,31 @@ private HateoasLink parseHateoasLink(String input) { hateoasLink.setResourceMethod(keyValueMap.get("method")); return hateoasLink; } + + private Map mapHateoasLinksToOpenApiLinks(Service hateoasService, + FunctionDefinitionNode resourceFunction) { + Optional linkedTo = getResourceConfigAnnotation(resourceFunction) + .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, BALLERINA_LINKEDTO_KEYWORD)); + if (linkedTo.isEmpty()) { + return Collections.emptyMap(); + } + List links = getLinks(linkedTo.get()); + Map hateoasLinks = new HashMap<>(); + for (HateoasLink link : links) { + Optional resource = hateoasService.getHateoasResourceMapping().entrySet().stream() + .filter(resources -> link.getResourceName().equals(resources.getKey())) + .findFirst() + .flatMap(hateoasResourceMapping -> hateoasResourceMapping.getValue().stream() + .filter(hateoasRes -> link.getResourceMethod().equals(hateoasRes.resourceMethod())) + .findFirst()); + if (resource.isEmpty()) { + continue; + } + Link openapiLink = new Link(); + String operationId = resource.get().operationId(); + openapiLink.setOperationId(operationId); + hateoasLinks.put(link.getRel(), openapiLink); + } + return hateoasLinks; + } } From 4c8bdc3e56d26fd80f0fd8fce7c334516cfaa6c7 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Wed, 31 Jan 2024 11:23:10 +0530 Subject: [PATCH 81/86] Refactor the code base --- .../mapper/ServiceToOpenAPIMapper.java | 16 ---- .../mapper/hateoas/HateoasContextHolder.java | 76 ------------------ .../mapper/hateoas/HateoasMapperImpl.java | 2 +- .../hateoas/HateoasMetadataVisitor.java | 80 ------------------- .../service/mapper/hateoas/Service.java | 15 ---- 5 files changed, 1 insertion(+), 188 deletions(-) delete mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java delete mode 100644 ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java index 02cb95a24..855203618 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/ServiceToOpenAPIMapper.java @@ -37,7 +37,6 @@ import io.ballerina.openapi.service.mapper.diagnostic.OpenAPIMapperDiagnostic; import io.ballerina.openapi.service.mapper.hateoas.HateoasMapper; import io.ballerina.openapi.service.mapper.hateoas.HateoasMapperImpl; -import io.ballerina.openapi.service.mapper.hateoas.HateoasMetadataVisitor; import io.ballerina.openapi.service.mapper.model.AdditionalData; import io.ballerina.openapi.service.mapper.model.ModuleMemberVisitor; import io.ballerina.openapi.service.mapper.model.OASGenerationMetaInfo; @@ -100,8 +99,6 @@ public static List generateOAS3Definition(Project project, SyntaxTree null, serviceName, availableService.toString()); diagnostics.add(error); } - // Extract HATEOAS meta-data for the all the service-declarations in the current project - extractHateoasLinkMetadata(project); // Generating openapi specification for selected services for (Map.Entry serviceNode : servicesToGenerate.entrySet()) { String openApiName = getOpenApiFileName(syntaxTree.filePath(), serviceNode.getKey(), needJson); @@ -273,17 +270,4 @@ private static boolean isTreatNilableAsOptionalParameter(ServiceDeclarationNode } return true; } - - private static void extractHateoasLinkMetadata(Project project) { - String packageId = project.currentPackage().packageId().id().toString(); - project.currentPackage().moduleIds().forEach(moduleId -> { - Module module = project.currentPackage().module(moduleId); - SemanticModel semanticModel = project.currentPackage().getCompilation().getSemanticModel(moduleId); - HateoasMetadataVisitor hateoasMetadataVisitor = new HateoasMetadataVisitor(packageId, semanticModel); - module.documentIds().forEach(documentId -> { - SyntaxTree syntaxTreeDoc = module.document(documentId).syntaxTree(); - syntaxTreeDoc.rootNode().accept(hateoasMetadataVisitor); - }); - }); - } } diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java deleted file mode 100644 index ee8b66d44..000000000 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasContextHolder.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.ballerina.openapi.service.mapper.hateoas; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -/** - * A context-holder to share HATEOAS meta-data information. - * - * @since 1.9.0 - */ -public final class HateoasContextHolder { - private static HateoasContextHolder instance; - - private final List hateoasServices; - - public HateoasContextHolder() { - this.hateoasServices = new ArrayList<>(); - } - - public static HateoasContextHolder getHateoasContextHolder() { - synchronized (HateoasContextHolder.class) { - if (Objects.isNull(instance)) { - instance = new HateoasContextHolder(); - } - } - return instance; - } - - public void updateHateoasResource(String packageId, int serviceId, String resourceName, Resource resource) { - Optional hateoasService = this.hateoasServices.stream() - .filter(svc -> packageId.equals(svc.getPackageId()) && svc.getServiceId() == serviceId) - .findFirst(); - if (hateoasService.isEmpty()) { - Service service = new Service(packageId, serviceId); - service.addResource(resourceName, resource); - this.hateoasServices.add(service); - return; - } - Service service = hateoasService.get(); - service.addResource(resourceName, resource); - } - - public Optional getHateoasResource(String packageId, int serviceId, String resourceName, - String resourceMethod) { - return this.hateoasServices.stream() - .filter(svc -> svc.getPackageId().equals(packageId) && svc.getServiceId() == serviceId) - .findFirst() - .flatMap(svc -> svc.getHateoasResourceMapping().entrySet().stream() - .filter(resources -> resourceName.equals(resources.getKey())) - .findFirst() - ).flatMap(hateoasResourceMapping -> hateoasResourceMapping.getValue().stream() - .filter(resource -> resourceMethod.equals(resource.resourceMethod())) - .findFirst() - ); - } -} diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java index 929faf253..5436699c4 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java @@ -70,7 +70,7 @@ public void setOpenApiLinks(ServiceDeclarationNode serviceNode, OpenAPI openAPI) } private Service extractHateoasMetaInfo(ServiceDeclarationNode serviceNode) { - Service service = new Service("ss", 0); + Service service = new Service(); for (Node child : serviceNode.children()) { if (SyntaxKind.RESOURCE_ACCESSOR_DEFINITION.equals(child.kind())) { FunctionDefinitionNode resourceFunction = (FunctionDefinitionNode) child; diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java deleted file mode 100644 index b7d9776d3..000000000 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMetadataVisitor.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.ballerina.openapi.service.mapper.hateoas; - -import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.compiler.api.symbols.ServiceDeclarationSymbol; -import io.ballerina.compiler.api.symbols.Symbol; -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.Node; -import io.ballerina.compiler.syntax.tree.NodeVisitor; -import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; -import io.ballerina.compiler.syntax.tree.SyntaxKind; -import io.ballerina.openapi.service.mapper.utils.MapperCommonUtils; - -import java.util.Optional; - -import static io.ballerina.openapi.service.mapper.hateoas.HateoasContextHolder.getHateoasContextHolder; -import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getResourceConfigAnnotation; -import static io.ballerina.openapi.service.mapper.utils.MapperCommonUtils.getValueForAnnotationFields; - -/** - * Visitor to retrieve the Hateoas meta-data from resource functions within a {@link ServiceDeclarationNode}. - * - * @since 1.6.0 - */ -public class HateoasMetadataVisitor extends NodeVisitor { - private final String packageId; - private final SemanticModel semanticModel; - - public HateoasMetadataVisitor(String packageId, SemanticModel semanticModel) { - this.packageId = packageId; - this.semanticModel = semanticModel; - } - - @Override - public void visit(ServiceDeclarationNode serviceNode) { - boolean isHttpService = MapperCommonUtils.isHttpService(serviceNode, semanticModel); - if (!isHttpService) { - return; - } - Optional serviceDeclarationOpt = semanticModel.symbol(serviceNode); - if (serviceDeclarationOpt.isEmpty()) { - return; - } - ServiceDeclarationSymbol serviceSymbol = (ServiceDeclarationSymbol) serviceDeclarationOpt.get(); - int serviceId = serviceSymbol.hashCode(); - for (Node child : serviceNode.children()) { - if (SyntaxKind.RESOURCE_ACCESSOR_DEFINITION.equals(child.kind())) { - FunctionDefinitionNode resourceFunction = (FunctionDefinitionNode) child; - String resourceMethod = resourceFunction.functionName().text(); - String operationId = MapperCommonUtils.getOperationId(resourceFunction); - Optional resourceName = getResourceConfigAnnotation(resourceFunction) - .flatMap(resourceConfig -> getValueForAnnotationFields(resourceConfig, "name")); - if (resourceName.isEmpty()) { - return; - } - String cleanedResourceName = resourceName.get().replaceAll("\"", ""); - Resource hateoasResource = new Resource(resourceMethod, operationId); - getHateoasContextHolder().updateHateoasResource( - packageId, serviceId, cleanedResourceName, hateoasResource); - } - } - } -} diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java index d39960142..19cab76e2 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java @@ -29,15 +29,8 @@ * @since 1.6.0 */ public class Service { - private final String packageId; - private final int serviceId; private final Map> hateoasResourceMapping = new HashMap<>(); - public Service(String packageId, int serviceId) { - this.packageId = packageId; - this.serviceId = serviceId; - } - public void addResource(String resourceName, Resource resource) { if (hateoasResourceMapping.containsKey(resourceName)) { hateoasResourceMapping.get(resourceName).add(resource); @@ -46,14 +39,6 @@ public void addResource(String resourceName, Resource resource) { hateoasResourceMapping.put(resourceName, Arrays.asList(resource)); } - public String getPackageId() { - return packageId; - } - - public int getServiceId() { - return serviceId; - } - public Map> getHateoasResourceMapping() { return hateoasResourceMapping; } From e78ea811a6aa5759a1c6e89acc251b6f36932e0b Mon Sep 17 00:00:00 2001 From: Sachin Akash Date: Wed, 31 Jan 2024 11:43:21 +0530 Subject: [PATCH 82/86] Update license header Co-authored-by: Sumudu Nissanka --- .../openapi/service/mapper/hateoas/HateoasMapperImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java index 5436699c4..883ca02f4 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except From 58d80c2ff9f45fc7abec7153fd97f266e2ae0889 Mon Sep 17 00:00:00 2001 From: Sachin Akash Date: Wed, 31 Jan 2024 12:00:35 +0530 Subject: [PATCH 83/86] Update ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java Co-authored-by: Sumudu Nissanka --- .../io/ballerina/openapi/service/mapper/hateoas/Resource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java index 92874e5ec..c19e940a3 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except From 58dc4492d6b730aff76fa261e1049853810ec699 Mon Sep 17 00:00:00 2001 From: Sachin Akash Date: Wed, 31 Jan 2024 12:00:49 +0530 Subject: [PATCH 84/86] Update ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java Co-authored-by: Sumudu Nissanka --- .../io/ballerina/openapi/service/mapper/hateoas/Resource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java index c19e940a3..0eb33d695 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Resource.java @@ -22,7 +22,7 @@ * A context-holder to save and retrieve HATEOAS meta-data for a given resources. * @param resourceMethod http resource for the method * @param operationId generated operationId - * @since 1.6.0 + * @since 1.9.0 */ public record Resource(String resourceMethod, String operationId) { } From a2e46df3e601ff3c5e0651a4c9b3edb02197acf7 Mon Sep 17 00:00:00 2001 From: SachinAkash01 Date: Wed, 31 Jan 2024 14:02:41 +0530 Subject: [PATCH 85/86] Update license headers --- .../ballerina/openapi/service/mapper/hateoas/HateoasLink.java | 2 +- .../ballerina/openapi/service/mapper/hateoas/HateoasMapper.java | 2 +- .../openapi/service/mapper/hateoas/HateoasMapperImpl.java | 2 +- .../io/ballerina/openapi/service/mapper/hateoas/Service.java | 2 +- .../openapi/service/mapper/response/ResponseMapper.java | 2 +- .../io/ballerina/openapi/generators/openapi/HateoasTests.java | 2 +- .../ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal | 2 +- .../ballerina-to-openapi/hateoas/hateoas_multiple_links.bal | 2 +- .../resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal | 2 +- .../resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasLink.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasLink.java index d33aa83c4..46e04e16f 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasLink.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasLink.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java index b97fec8f1..c31243f55 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java index 5436699c4..883ca02f4 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/HateoasMapperImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java index 19cab76e2..11f8ac1b6 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Service.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java index 1673466a9..941e3c315 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/response/ResponseMapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java index 0b4c17d15..985cfea8f 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/openapi/HateoasTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal index cabd3937f..1609648bd 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_automatic_linking.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal index 1c0ed1493..42c65ae2b 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_multiple_links.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal index e44368ee7..60dc804fd 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/hateoas_self_rel.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal index 9c3831d05..1ad07caaf 100644 --- a/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal +++ b/openapi-cli/src/test/resources/ballerina-to-openapi/hateoas/snowpeak_hateoas.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except From 09a0acbf8c6b13f60f8beae0bd50e314cb2258d2 Mon Sep 17 00:00:00 2001 From: "K.D.S. Akash" Date: Fri, 2 Feb 2024 16:24:10 +0530 Subject: [PATCH 86/86] Add missing license headers --- .../service/mapper/constraint/Constants.java | 18 ++++++++++++++++++ .../service/mapper/hateoas/Constants.java | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/Constants.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/Constants.java index ceff94309..317ba06f2 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/Constants.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/constraint/Constants.java @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package io.ballerina.openapi.service.mapper.constraint; /** diff --git a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Constants.java b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Constants.java index decccf15a..08a4768aa 100644 --- a/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Constants.java +++ b/ballerina-to-openapi/src/main/java/io/ballerina/openapi/service/mapper/hateoas/Constants.java @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package io.ballerina.openapi.service.mapper.hateoas; public class Constants {