From 49ca174fdf027411d12f658767a59db30deba726 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Mon, 29 Apr 2024 21:41:31 +0530 Subject: [PATCH 01/31] Add client method changes --- .../client/BallerinaClientGenerator.java | 4 +- ...aClientGeneratorWithStatusCodeBinding.java | 9 +- .../client/FunctionBodyGeneratorImp.java | 2 +- .../client/FunctionBodyGeneratorImpNew.java | 694 ++++++++++++++++++ .../ImplFunctionSignatureGeneratorNew.java | 144 ++++ .../RemoteExternalFunctionGenerator.java | 2 +- ...oteExternalFunctionSignatureGenerator.java | 4 +- .../client/RemoteFunctionGenerator.java | 8 +- .../RemoteFunctionSignatureGeneratorNew.java | 242 ++++++ .../ResourceExternalFunctionGenerator.java | 5 +- ...rceExternalFunctionSignatureGenerator.java | 4 +- .../client/ResourceFunctionGenerator.java | 19 +- ...ResourceFunctionSignatureGeneratorNew.java | 224 ++++++ .../parameter/HeadersParameterGenerator.java | 184 +++++ .../parameter/QueriesParameterGenerator.java | 115 +++ .../parameter/RequestBodyGeneratorNew.java | 198 +++++ .../RequestBodyHeaderParameterNew.java | 69 ++ .../generators/common/GeneratorConstants.java | 1 + .../generators/common/GeneratorUtils.java | 7 +- .../ClientDocCommentGeneratorNew.java | 375 ++++++++++ .../document/DocCommentGeneratorImp.java | 2 +- .../document/TypesDocCommentGenerator.java | 115 +-- .../resources/templates/utils_openapi.bal | 14 +- 23 files changed, 2362 insertions(+), 79 deletions(-) create mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImpNew.java create mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGeneratorNew.java create mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGeneratorNew.java create mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGeneratorNew.java create mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java create mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java create mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyGeneratorNew.java create mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyHeaderParameterNew.java create mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGeneratorNew.java 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 242b08fa0..a1f1d22c8 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 @@ -54,7 +54,7 @@ import io.ballerina.openapi.core.generators.common.GeneratorUtils; import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; import io.ballerina.openapi.core.generators.common.model.Filter; -import io.ballerina.openapi.core.generators.document.ClientDocCommentGenerator; +import io.ballerina.openapi.core.generators.document.ClientDocCommentGeneratorNew; import io.ballerina.openapi.core.generators.document.DocCommentsGeneratorUtil; import io.ballerina.tools.text.TextDocument; import io.ballerina.tools.text.TextDocuments; @@ -198,7 +198,7 @@ public SyntaxTree generateSyntaxTree() throws BallerinaOpenApiException, ClientE SyntaxTree syntaxTree = SyntaxTree.from(textDocument); syntaxTree = syntaxTree.modifyWith(modulePartNode); //Add comments - ClientDocCommentGenerator clientDocCommentGenerator = new ClientDocCommentGenerator(syntaxTree, openAPI, + ClientDocCommentGeneratorNew clientDocCommentGenerator = new ClientDocCommentGeneratorNew(syntaxTree, openAPI, resourceMode); return clientDocCommentGenerator.updateSyntaxTreeWithDocComments(); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGeneratorWithStatusCodeBinding.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGeneratorWithStatusCodeBinding.java index e520fb854..73db2e4c5 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGeneratorWithStatusCodeBinding.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGeneratorWithStatusCodeBinding.java @@ -175,8 +175,9 @@ protected QualifiedNameReferenceNode getHttpClientTypeName() { private void addClientFunctionImpl(Map.Entry> operation, Map.Entry operationEntry, List clientFunctionNodes) { + FunctionDefinitionNode clientExternFunction = clientFunctionNodes.get(clientFunctionNodes.size() - 1); Optional implFunction = createImplFunction(operation.getKey(), operationEntry, openAPI, - authConfigGeneratorImp, ballerinaUtilGenerator); + authConfigGeneratorImp, ballerinaUtilGenerator, clientExternFunction); if (implFunction.isPresent()) { clientFunctionNodes.add(implFunction.get()); } else { @@ -208,14 +209,14 @@ private Optional createImplFunction(String path, Map.Entry operation, OpenAPI openAPI, AuthConfigGeneratorImp authConfigGeneratorImp, - BallerinaUtilGenerator ballerinaUtilGenerator) { + BallerinaUtilGenerator ballerinaUtilGenerator, + FunctionDefinitionNode clientExternFunction) { //Create qualifier list NodeList qualifierList = createNodeList(createToken(PRIVATE_KEYWORD), createToken(ISOLATED_KEYWORD)); Token functionKeyWord = createToken(FUNCTION_KEYWORD); IdentifierToken functionName = createIdentifierToken(operation.getValue().getOperationId() + "Impl"); // Create function signature - RemoteFunctionSignatureGenerator signatureGenerator = new ImplFunctionSignatureGenerator(operation.getValue(), - openAPI, operation.getKey().toString().toLowerCase(Locale.ROOT), path); + ImplFunctionSignatureGeneratorNew signatureGenerator = new ImplFunctionSignatureGeneratorNew(clientExternFunction); //Create function body FunctionBodyGeneratorImp functionBodyGenerator = new ImplFunctionBodyGenerator(path, operation, openAPI, authConfigGeneratorImp, ballerinaUtilGenerator, imports); diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java index ca7ccb330..ec751d6a2 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java @@ -264,7 +264,7 @@ public void handleQueryParamsAndHeaders(List queryParameters, List", + statementsList.add(getMapForParameters(headerParameters, "map", HEADER_VALUES, headerApiKeyNameList)); statementsList.add(GeneratorUtils.getSimpleExpressionStatementNode( "map " + HTTP_HEADERS + " = getMapForHeaders(headerValues)")); diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImpNew.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImpNew.java new file mode 100644 index 000000000..ac8e71a64 --- /dev/null +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImpNew.java @@ -0,0 +1,694 @@ +/* + * Copyright (c) 2022, 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 + * 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.core.generators.client; + +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.BlockStatementNode; +import io.ballerina.compiler.syntax.tree.BuiltinSimpleNameReferenceNode; +import io.ballerina.compiler.syntax.tree.CaptureBindingPatternNode; +import io.ballerina.compiler.syntax.tree.ExpressionNode; +import io.ballerina.compiler.syntax.tree.ExpressionStatementNode; +import io.ballerina.compiler.syntax.tree.FieldAccessExpressionNode; +import io.ballerina.compiler.syntax.tree.FunctionBodyNode; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.IfElseStatementNode; +import io.ballerina.compiler.syntax.tree.ImportDeclarationNode; +import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.ReturnStatementNode; +import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; +import io.ballerina.compiler.syntax.tree.StatementNode; +import io.ballerina.compiler.syntax.tree.TemplateExpressionNode; +import io.ballerina.compiler.syntax.tree.Token; +import io.ballerina.compiler.syntax.tree.TypedBindingPatternNode; +import io.ballerina.compiler.syntax.tree.VariableDeclarationNode; +import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; +import io.ballerina.openapi.core.generators.client.mime.MimeType; +import io.ballerina.openapi.core.generators.common.GeneratorUtils; +import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.security.SecurityRequirement; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyMinutiaeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createLiteralValueToken; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createSeparatedNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createAssignmentStatementNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createBinaryExpressionNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createBlockStatementNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createBuiltinSimpleNameReferenceNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createCaptureBindingPatternNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createFieldAccessExpressionNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createFunctionBodyBlockNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createIfElseStatementNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createMappingConstructorExpressionNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createReturnStatementNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createSimpleNameReferenceNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createTemplateExpressionNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createTypedBindingPatternNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createVariableDeclarationNode; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.BACKTICK_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_BRACE_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.DOT_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.EQUAL_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.IF_KEYWORD; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.IS_KEYWORD; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_BRACE_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.QUESTION_MARK_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.SEMICOLON_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.STRING_KEYWORD; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.API_KEYS_CONFIG; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.API_KEY_CONFIG_PARAM; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.DELETE; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.ENCODING; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.EXECUTE; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEAD; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEADERS; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEADER_VALUES; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HTTP_HEADERS; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HTTP_REQUEST; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.NEW; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.PATCH; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.POST; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.PUT; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERIES; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERY_PARAM; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.REQUEST; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.RESOURCE_PATH; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.RETURN; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.SELF; +import static io.ballerina.openapi.core.generators.common.GeneratorUtils.escapeIdentifier; +import static io.ballerina.openapi.core.generators.common.GeneratorUtils.extractReferenceType; +import static io.ballerina.openapi.core.generators.common.GeneratorUtils.getOpenAPIType; +import static io.ballerina.openapi.core.generators.common.GeneratorUtils.getValidName; +import static io.ballerina.openapi.core.generators.common.GeneratorUtils.isComposedSchema; + +/** + * This Util class uses for generating remote function body {@link FunctionBodyNode}. + * + * @since 1.3.0 + */ +public class FunctionBodyGeneratorImpNew implements FunctionBodyGenerator { + + private List imports = new ArrayList<>(); + private final String path; + private final Map.Entry operation; + private final OpenAPI openAPI; + private final BallerinaUtilGenerator ballerinaUtilGenerator; + private final AuthConfigGeneratorImp ballerinaAuthConfigGeneratorImp; + + private final boolean hasHeaders; + private final boolean hasQueries; + private final boolean hasDefaultHeaders; + + public List getImports() { + return imports; + } + + + public FunctionBodyGeneratorImpNew(String path, Map.Entry operation, + OpenAPI openAPI, AuthConfigGeneratorImp ballerinaAuthConfigGeneratorImp, + BallerinaUtilGenerator ballerinaUtilGenerator, + List imports, boolean hasHeaders, + boolean hasDefaultHeaders, boolean hasQueries) { + this.path = path; + this.operation = operation; + this.openAPI = openAPI; + this.ballerinaUtilGenerator = ballerinaUtilGenerator; + this.ballerinaAuthConfigGeneratorImp = ballerinaAuthConfigGeneratorImp; + this.imports = imports; + this.hasHeaders = hasHeaders; + this.hasQueries = hasQueries; + this.hasDefaultHeaders = hasDefaultHeaders; + } + + /** + * Generate function body node for the remote function. + * + * @return - {@link FunctionBodyNode} + * @throws BallerinaOpenApiException - throws exception if generating FunctionBodyNode fails. + */ + @Override + public Optional getFunctionBodyNode() { + + NodeList annotationNodes = createEmptyNodeList(); + // Create statements + List statementsList = new ArrayList<>(); + //string path - common for every remote functions + VariableDeclarationNode pathInt = getPathStatement(path, annotationNodes); + statementsList.add(pathInt); + try { + //Handle query parameter map + handleParameterSchemaInOperation(operation, statementsList); + + String method = operation.getKey().name().trim().toLowerCase(Locale.ENGLISH); + // Statement Generator for requestBody + if (operation.getValue().getRequestBody() != null) { + RequestBody requestBody = operation.getValue().getRequestBody(); + handleRequestBodyInOperation(statementsList, method, requestBody); + } else { + createCommonFunctionBodyStatements(statementsList, method); + } + + //Create statements + NodeList statements = createNodeList(statementsList); + return Optional.of(createFunctionBodyBlockNode(createToken(OPEN_BRACE_TOKEN), null, + statements, + createToken(CLOSE_BRACE_TOKEN), null)); + } catch (BallerinaOpenApiException e) { + //todo diagnostic message + return Optional.empty(); + } + } + + /** + * Generate statements for query parameters and headers. + */ + private void handleParameterSchemaInOperation(Map.Entry operation, + List statementsList) throws + BallerinaOpenApiException { + + List queryApiKeyNameList = new ArrayList<>(); + List headerApiKeyNameList = new ArrayList<>(); + + Set securitySchemesAvailable = getSecurityRequirementForOperation(operation.getValue()); + + if (securitySchemesAvailable.size() > 0) { + Map queryApiKeyMap = ballerinaAuthConfigGeneratorImp.getQueryApiKeyNameList(); + Map headerApiKeyMap = ballerinaAuthConfigGeneratorImp.getHeaderApiKeyNameList(); + for (String schemaName : securitySchemesAvailable) { + if (queryApiKeyMap.containsKey(schemaName)) { + queryApiKeyNameList.add(queryApiKeyMap.get(schemaName)); + } else if (headerApiKeyMap.containsKey(schemaName)) { + headerApiKeyNameList.add(headerApiKeyMap.get(schemaName)); + } + } + } + + List queryParameters = new ArrayList<>(); + List headerParameters = new ArrayList<>(); + + handleQueryParamsAndHeaders(queryParameters, headerParameters, statementsList, queryApiKeyNameList, + headerApiKeyNameList); + } + + /** + * Handle query parameters and headers within a remote function. + */ + public void handleQueryParamsAndHeaders(List queryParameters, List headerParameters, + List statementsList, List queryApiKeyNameList, + List headerApiKeyNameList) throws BallerinaOpenApiException { + + boolean combinationOfApiKeyAndHTTPOAuth = ballerinaAuthConfigGeneratorImp.isHttpOROAuth() && + ballerinaAuthConfigGeneratorImp.isApiKey(); + if (combinationOfApiKeyAndHTTPOAuth) { + addUpdatedPathAndHeaders(statementsList, queryApiKeyNameList, queryParameters, + headerApiKeyNameList); + } else { + if (hasQueries || !queryApiKeyNameList.isEmpty()) { + ballerinaUtilGenerator.setQueryParamsFound(true); + getUpdatedPathHandlingQueryParamEncoding(statementsList, queryParameters); + } + if (hasHeaders || !headerApiKeyNameList.isEmpty()) { + if (hasDefaultHeaders) { + statementsList.add(GeneratorUtils.getSimpleExpressionStatementNode( + "map " + HTTP_HEADERS + " = " + HEADERS)); + } else { + statementsList.add(GeneratorUtils.getSimpleExpressionStatementNode( + "map " + HTTP_HEADERS + " = getMapForHeaders(" + HEADERS + ")")); + } + ballerinaUtilGenerator.setHeadersFound(true); + } + } + } + + /** + * Generate statements for query parameters and headers when a client supports both ApiKey and HTTPOrOAuth + * authentication. + */ + private void addUpdatedPathAndHeaders(List statementsList, List queryApiKeyNameList, + List queryParameters, List headerApiKeyNameList) + throws BallerinaOpenApiException { + + List ifBodyStatementsList = new ArrayList<>(); + + if (hasHeaders || !headerApiKeyNameList.isEmpty()) { + String defaultValue = "{}"; + if (hasHeaders) { + defaultValue = "{..." + HEADERS + "}"; + } + + ExpressionStatementNode headerMapCreation = GeneratorUtils.getSimpleExpressionStatementNode( + "map " + HEADER_VALUES + " = " + defaultValue); + statementsList.add(headerMapCreation); + + if (!headerApiKeyNameList.isEmpty()) { + // update headerValues Map within the if block + // `headerValues["api-key"] = self.apiKeyConfig?.apiKey;` + addApiKeysToMap(HEADER_VALUES, headerApiKeyNameList, ifBodyStatementsList); + } + ballerinaUtilGenerator.setHeadersFound(true); + } + + if (hasQueries || !queryApiKeyNameList.isEmpty()) { + String defaultValue = "{}"; + if (hasQueries) { + defaultValue = "{..." + QUERIES + "}"; + } + + ExpressionStatementNode queryParamMapCreation = GeneratorUtils.getSimpleExpressionStatementNode( + "map " + QUERY_PARAM + " = " + defaultValue); + statementsList.add(queryParamMapCreation); + + if (!queryApiKeyNameList.isEmpty()) { + // update queryParam Map within the if block + // `queryParam["api-key"] = self.apiKeyConfig?.apiKey;` + addApiKeysToMap(QUERY_PARAM, queryApiKeyNameList, ifBodyStatementsList); + } + ballerinaUtilGenerator.setQueryParamsFound(true); + } + + generateIfBlockToAddApiKeysToMaps(statementsList, ifBodyStatementsList); + + if (hasQueries || !queryApiKeyNameList.isEmpty()) { + getUpdatedPathHandlingQueryParamEncoding(statementsList, queryParameters); + } + if (hasHeaders || !headerApiKeyNameList.isEmpty()) { + statementsList.add(GeneratorUtils.getSimpleExpressionStatementNode( + "map " + HTTP_HEADERS + " = getMapForHeaders(headerValues)")); + } + } + + /** + * Add apiKeys to a given map (queryParam or headerValues). + *

+ * `queryParam["api-key"] = self.apiKeyConfig?.apiKey;` + * `headerValues["api-key"] = self.apiKeyConfig?.apiKey;` + */ + private void addApiKeysToMap(String mapName, List apiKeyNames, List statementNodeList) { + + if (!apiKeyNames.isEmpty()) { + for (String apiKey : apiKeyNames) { + IdentifierToken fieldName = createIdentifierToken(mapName + "[" + '"' + apiKey.trim() + '"' + "]"); + Token equal = createToken(EQUAL_TOKEN); + FieldAccessExpressionNode fieldExpr = createFieldAccessExpressionNode( + createSimpleNameReferenceNode(createIdentifierToken(SELF)), createToken(DOT_TOKEN), + createSimpleNameReferenceNode(createIdentifierToken(API_KEY_CONFIG_PARAM + + QUESTION_MARK_TOKEN.stringValue()))); + SimpleNameReferenceNode valueExpr = createSimpleNameReferenceNode(createIdentifierToken( + getValidName(getValidName(apiKey, false), false))); + ExpressionNode apiKeyExpr = createFieldAccessExpressionNode( + fieldExpr, createToken(DOT_TOKEN), valueExpr); + statementNodeList.add(createAssignmentStatementNode(fieldName, equal, apiKeyExpr, createToken( + SEMICOLON_TOKEN))); + } + } + } + + /** + * Get updated path considering queryParamEncodingMap. + */ + private void getUpdatedPathHandlingQueryParamEncoding(List statementsList, List + queryParameters) throws BallerinaOpenApiException { + + VariableDeclarationNode queryParamEncodingMap = getQueryParameterEncodingMap(queryParameters); + if (queryParamEncodingMap != null) { + statementsList.add(queryParamEncodingMap); + ExpressionStatementNode updatedPath = GeneratorUtils.getSimpleExpressionStatementNode( + RESOURCE_PATH + " = " + RESOURCE_PATH + " + check getPathForQueryParam(" + QUERIES + ", " + + "queryParamEncoding)"); + statementsList.add(updatedPath); + } else { + ExpressionStatementNode updatedPath = GeneratorUtils.getSimpleExpressionStatementNode( + RESOURCE_PATH + " = " + RESOURCE_PATH + " + check getPathForQueryParam(" + QUERIES + ")"); + statementsList.add(updatedPath); + } + } + + /** + * Generate if block when a client supports both ApiKey and HTTPOrOAuth authentication. + * + *

+     * if self.apiKeyConfig is ApiKeysConfig {
+     *      --- given statements ---
+     * }
+     * 
+ */ + private void generateIfBlockToAddApiKeysToMaps(List statementsList, + List ifBodyStatementsList) { + + if (!ifBodyStatementsList.isEmpty()) { + NodeList ifBodyStatementsNodeList = createNodeList(ifBodyStatementsList); + BlockStatementNode ifBody = createBlockStatementNode(createToken(OPEN_BRACE_TOKEN), + ifBodyStatementsNodeList, createToken(CLOSE_BRACE_TOKEN)); + + // Create expression `self.apiKeyConfig is ApiKeysConfig` + ExpressionNode condition = createBinaryExpressionNode(null, createIdentifierToken(SELF + + DOT_TOKEN.stringValue() + API_KEY_CONFIG_PARAM), + createToken(IS_KEYWORD), + createIdentifierToken(API_KEYS_CONFIG)); + IfElseStatementNode ifBlock = createIfElseStatementNode(createToken(IF_KEYWORD), condition, ifBody, null); + statementsList.add(ifBlock); + } + } + + /** + * Generate VariableDeclarationNode for query parameter encoding map which includes the data related serialization + * mechanism that needs to be used with object or array type parameters. Parameters in primitive types will not be + * included to the map even when the serialization mechanisms are specified. These data is given in the `style` and + * `explode` sections of the OpenAPI definition. Style defines how multiple values are delimited and explode + * specifies whether arrays and objects should generate separate parameters + *

+ * --ex: {@code map queryParamEncoding = {"expand": ["deepObject", true]};} + * + * @param queryParameters List of query parameters defined in a particular function + * @return {@link VariableDeclarationNode} + * @throws BallerinaOpenApiException When invalid referenced schema is given. + */ + private VariableDeclarationNode getQueryParameterEncodingMap(List queryParameters) + throws BallerinaOpenApiException { + + List filedOfMap = new ArrayList<>(); + BuiltinSimpleNameReferenceNode mapType = createBuiltinSimpleNameReferenceNode(null, + createIdentifierToken("map<" + ENCODING + ">")); + CaptureBindingPatternNode bindingPattern = createCaptureBindingPatternNode( + createIdentifierToken("queryParamEncoding")); + TypedBindingPatternNode bindingPatternNode = createTypedBindingPatternNode(mapType, bindingPattern); + + for (Parameter parameter : queryParameters) { + Schema paramSchema = parameter.getSchema(); + if (paramSchema != null && paramSchema.get$ref() != null) { + paramSchema = openAPI.getComponents().getSchemas().get( + getValidName(extractReferenceType(paramSchema.get$ref()), true)); + } + if (paramSchema != null && (paramSchema.getProperties() != null || + (getOpenAPIType(paramSchema) != null && getOpenAPIType(paramSchema).equals("array")) || + (isComposedSchema(paramSchema)))) { + if (parameter.getStyle() != null || parameter.getExplode() != null) { + GeneratorUtils.createEncodingMap(filedOfMap, parameter.getStyle().toString(), + parameter.getExplode(), parameter.getName().trim()); + } + } + } + if (!filedOfMap.isEmpty()) { + filedOfMap.remove(filedOfMap.size() - 1); + MappingConstructorExpressionNode initialize = createMappingConstructorExpressionNode( + createToken(OPEN_BRACE_TOKEN), createSeparatedNodeList(filedOfMap), + createToken(CLOSE_BRACE_TOKEN)); + return createVariableDeclarationNode(createEmptyNodeList(), + null, bindingPatternNode, createToken(EQUAL_TOKEN), initialize, + createToken(SEMICOLON_TOKEN)); + } + return null; + + } + + /** + * Provides the list of security schemes available for the given operation. + * + * @param operation Current operation + * @return Security schemes that can be used to authorize the given operation + */ + private Set getSecurityRequirementForOperation(Operation operation) { + + Set securitySchemasAvailable = new LinkedHashSet<>(); + List securityRequirements = new ArrayList<>(); + if (operation.getSecurity() != null) { + securityRequirements = operation.getSecurity(); + } else if (openAPI.getSecurity() != null) { + securityRequirements = openAPI.getSecurity(); + } + + if (securityRequirements.size() > 0) { + for (SecurityRequirement requirement : securityRequirements) { + securitySchemasAvailable.addAll(requirement.keySet()); + } + } + return securitySchemasAvailable; + } + + /** + * Handle request body in operation. + */ + private void handleRequestBodyInOperation(List statementsList, String method, + RequestBody requestBody) + throws BallerinaOpenApiException { + + if (requestBody.getContent() != null) { + Content rbContent = requestBody.getContent(); + Set> entries = rbContent.entrySet(); + Iterator> iterator = entries.iterator(); + //Currently align with first content of the requestBody + while (iterator.hasNext()) { + createRequestBodyStatements(statementsList, method, iterator); + break; + } + } else if (requestBody.get$ref() != null) { + RequestBody requestBodySchema = + openAPI.getComponents().getRequestBodies().get(extractReferenceType(requestBody.get$ref())); + Content rbContent = requestBodySchema.getContent(); + Set> entries = rbContent.entrySet(); + Iterator> iterator = entries.iterator(); + //Currently align with first content of the requestBody + while (iterator.hasNext()) { + createRequestBodyStatements(statementsList, method, iterator); + break; + } + } + } + + /** + * Generate common statements in function bosy. + */ + private void createCommonFunctionBodyStatements(List statementsList, String method) { + + String clientCallStatement; + + // This condition for several methods. + boolean isEntityBodyMethods = method.equals(POST) || method.equals(PUT) || method.equals(PATCH) + || method.equals(EXECUTE); + if (hasHeaders) { + if (isEntityBodyMethods) { + ExpressionStatementNode requestStatementNode = GeneratorUtils.getSimpleExpressionStatementNode( + "http:Request request = new"); + statementsList.add(requestStatementNode); + clientCallStatement = getClientCallWithRequestAndHeaders().formatted(method, RESOURCE_PATH, + HTTP_HEADERS); + + } else if (method.equals(DELETE)) { + clientCallStatement = getClientCallWithHeadersParam().formatted(method, RESOURCE_PATH, + HTTP_HEADERS); + } else if (method.equals(HEAD)) { + clientCallStatement = getClientCallWithHeaders().formatted(method, RESOURCE_PATH, + HTTP_HEADERS); + } else { + clientCallStatement = getClientCallWithHeaders().formatted(method, RESOURCE_PATH, + HTTP_HEADERS); + } + } else if (method.equals(DELETE)) { + clientCallStatement = getSimpleClientCall().formatted(method, RESOURCE_PATH); + } else if (isEntityBodyMethods) { + ExpressionStatementNode requestStatementNode = GeneratorUtils.getSimpleExpressionStatementNode( + "http:Request request = new"); + statementsList.add(requestStatementNode); + clientCallStatement = getClientCallWithRequest().formatted(method, RESOURCE_PATH); + } else { + clientCallStatement = getSimpleClientCall().formatted(method, RESOURCE_PATH); + } + //Return Variable + generateReturnStatement(statementsList, clientCallStatement); + } + + protected String getClientCallWithHeadersParam() { + return "self.clientEp->%s(%s, headers = %s)"; + } + + protected String getClientCallWithRequestAndHeaders() { + return "self.clientEp->%s(%s, request, %s)"; + } + + protected String getClientCallWithHeaders() { + return "self.clientEp->%s(%s, %s)"; + } + + protected String getClientCallWithRequest() { + return "self.clientEp->%s(%s, request)"; + } + + protected String getSimpleClientCall() { + return "self.clientEp->%s(%s)"; + } + + /** + * This method use to generate Path statement inside the function body node. + *

+ * ex: + *

 string  path = string `/weather`; 
+ * + * @param path - Given path + * @param annotationNodes - Node list for path implementation + * @return - VariableDeclarationNode for path statement. + */ + private VariableDeclarationNode getPathStatement(String path, NodeList annotationNodes) { + + TypedBindingPatternNode typedBindingPatternNode = createTypedBindingPatternNode(createSimpleNameReferenceNode( + createToken(STRING_KEYWORD)), createCaptureBindingPatternNode( + createIdentifierToken(RESOURCE_PATH))); + // Create initializer + // Content should decide with /pet and /pet/{pet} + path = generatePathWithPathParameter(path); + //String path generator + NodeList content = createNodeList(createLiteralValueToken(null, path, createEmptyMinutiaeList(), + createEmptyMinutiaeList())); + TemplateExpressionNode initializer = createTemplateExpressionNode(null, createToken(STRING_KEYWORD), + createToken(BACKTICK_TOKEN), content, createToken(BACKTICK_TOKEN)); + return createVariableDeclarationNode(annotationNodes, null, + typedBindingPatternNode, createToken(EQUAL_TOKEN), initializer, createToken(SEMICOLON_TOKEN)); + } + + /** + * This method is to used for generating path when it has path parameters. + * + * @param path - yaml contract path + * @return string of path + */ + public String generatePathWithPathParameter(String path) { + + if (path.contains("{")) { + String refinedPath = path; + Pattern p = Pattern.compile("\\{[^}]*}"); + Matcher m = p.matcher(path); + while (m.find()) { + String pathVariable = path.substring(m.start(), m.end()); + if (pathVariable.startsWith("{") && pathVariable.endsWith("}")) { + String d = pathVariable.replace("{", "").replace("}", ""); + String replaceVariable = "{getEncodedUri(" + escapeIdentifier(d) + ")}"; + refinedPath = refinedPath.replace(pathVariable, replaceVariable); + } + } + path = refinedPath.replaceAll("[{]", "\\${"); + } + ballerinaUtilGenerator.setPathParametersFound(true); + return path; + } + + /** + * This function for creating requestBody statements. + * -- ex: Request body with json payload. + *
+     *    http:Request request = new;
+     *    json jsonBody = payload.toJson();
+     *    request.setPayload(jsonBody, "application/json");
+     *    self.clientEp->put(path, request);
+     * 
+ * + * @param statementsList - StatementNode list in body node + * @param method - Operation method name. + * @param iterator - RequestBody media type + */ + private void createRequestBodyStatements(List statementsList, String method, + Iterator> iterator) + throws BallerinaOpenApiException { + + //Create Request statement + Map.Entry mediaTypeEntry = iterator.next(); + if (GeneratorUtils.isSupportedMediaType(mediaTypeEntry)) { + VariableDeclarationNode requestVariable = GeneratorUtils.getSimpleStatement(HTTP_REQUEST, + REQUEST, NEW); + statementsList.add(requestVariable); + } + if (mediaTypeEntry.getValue() != null && GeneratorUtils.isSupportedMediaType(mediaTypeEntry)) { + genStatementsForRequestMediaType(statementsList, mediaTypeEntry); + // TODO:Fill with other mime type + } else { + // Add default value comment + ExpressionStatementNode expressionStatementNode = GeneratorUtils.getSimpleExpressionStatementNode( + "// TODO: Update the request as needed"); + statementsList.add(expressionStatementNode); + } + // POST, PUT, PATCH, DELETE, EXECUTE + String requestStatement = getClientCallWithRequest().formatted(method, RESOURCE_PATH); + if (hasHeaders) { + if (method.equals(POST) || method.equals(PUT) || method.equals(PATCH) || method.equals(DELETE) + || method.equals(EXECUTE)) { + requestStatement = getClientCallWithRequestAndHeaders().formatted(method, RESOURCE_PATH, + HTTP_HEADERS); + generateReturnStatement(statementsList, requestStatement); + } + } else { + generateReturnStatement(statementsList, requestStatement); + } + } + + /** + * This function is used for generating return statement. + * + * @param statementsList - Previous statements list + * @param returnStatement - Request statement + */ + private static void generateReturnStatement(List statementsList, String returnStatement) { + Token returnKeyWord = createIdentifierToken(RETURN); + SimpleNameReferenceNode returns; + returns = createSimpleNameReferenceNode(createIdentifierToken(returnStatement)); + ReturnStatementNode returnStatementNode = createReturnStatementNode(returnKeyWord, returns, + createToken(SEMICOLON_TOKEN)); + statementsList.add(returnStatementNode); + } + + /** + * This function is used for generating function body statements according to the given request body media type. + * + * @param statementsList - Previous statements list + * @param mediaTypeEntry - Media type entry + */ + private void genStatementsForRequestMediaType(List statementsList, + Map.Entry mediaTypeEntry) + throws BallerinaOpenApiException { + MimeFactory factory = new MimeFactory(); + MimeType mimeType = factory.getMimeType(mediaTypeEntry, ballerinaUtilGenerator, imports); + mimeType.setPayload(statementsList, mediaTypeEntry); + } + + @Override + public List getDiagnostics() { + return new ArrayList<>(); + } +} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGeneratorNew.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGeneratorNew.java new file mode 100644 index 000000000..89b4503e1 --- /dev/null +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGeneratorNew.java @@ -0,0 +1,144 @@ +/* + * 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.core.generators.client; + +import io.ballerina.compiler.syntax.tree.DefaultableParameterNode; +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.ParameterNode; +import io.ballerina.compiler.syntax.tree.ParameterizedTypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.ResourcePathParameterNode; +import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.TypeParameterNode; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createSeparatedNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createFunctionSignatureNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createRequiredParameterNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createReturnTypeDescriptorNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createSimpleNameReferenceNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createUnionTypeDescriptorNode; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_PAREN_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.COMMA_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_PAREN_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.PIPE_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.RETURNS_KEYWORD; + +/** + * This class is used to generate the function signature of the client external method's implementation function. + * + * @since 1.9.0 + */ +public class ImplFunctionSignatureGeneratorNew { + + List parameterNodes = new ArrayList<>(); + ReturnTypeDescriptorNode returnTypeDescriptorNode = null; + + + public ImplFunctionSignatureGeneratorNew(FunctionDefinitionNode clientExternFunction) { + FunctionSignatureNode functionSignatureNode = clientExternFunction.functionSignature(); + if (Objects.isNull(functionSignatureNode)) { + return; + } + + populateParameterNodes(clientExternFunction); + populateReturnTypeDesc(clientExternFunction); + } + + private void populateReturnTypeDesc(FunctionDefinitionNode clientExternFunction) { + FunctionSignatureNode functionSignatureNode = clientExternFunction.functionSignature(); + Optional targetType = findTargetType(functionSignatureNode); + targetType.ifPresent(this::setReturnTypeDescriptor); + } + + private Optional findTargetType(FunctionSignatureNode functionSignatureNode) { + return functionSignatureNode.parameters().stream().filter( + parameterNode -> parameterNode instanceof DefaultableParameterNode defaultableParameterNode && + defaultableParameterNode.paramName().isPresent() && + defaultableParameterNode.paramName().get().text().equals("targetType") + ).findFirst(); + } + + private void setReturnTypeDescriptor(ParameterNode targetType) { + Node node = ((DefaultableParameterNode) targetType).typeName(); + if (node instanceof ParameterizedTypeDescriptorNode targetTypeNode) { + Optional typeParameterNode = targetTypeNode.typeParamNode(); + typeParameterNode.ifPresent(parameterNode -> + returnTypeDescriptorNode = createReturnTypeDescriptorNode(createToken(RETURNS_KEYWORD), + createEmptyNodeList(), createUnionTypeDescriptorNode(typeParameterNode.get().typeNode(), + createToken(PIPE_TOKEN), + createSimpleNameReferenceNode(createIdentifierToken("error"))))); + } + } + + private void populateParameterNodes(FunctionDefinitionNode clientExternFunction) { + addParametersFromPath(clientExternFunction); + addParametersFromSignature(clientExternFunction); + } + + private void addParametersFromSignature(FunctionDefinitionNode clientExternFunction) { + FunctionSignatureNode functionSignatureNode = clientExternFunction.functionSignature(); + for (ParameterNode paramNode : functionSignatureNode.parameters()) { + parameterNodes.add(removeDefaultValue(paramNode)); + parameterNodes.add(createToken(COMMA_TOKEN)); + } + } + + private void addParametersFromPath(FunctionDefinitionNode clientExternFunction) { + NodeList pathParams = clientExternFunction.relativeResourcePath(); + if (Objects.isNull(pathParams) || pathParams.isEmpty()) { + return; + } + + for (Node node : pathParams) { + if (node instanceof ResourcePathParameterNode pathParameterNode) { + Node pathParam = createRequiredParameterNode(createEmptyNodeList(), pathParameterNode.typeDescriptor(), + pathParameterNode.paramName().orElse(null)); + parameterNodes.add(pathParam); + parameterNodes.add(createToken(COMMA_TOKEN)); + } + } + } + + private Node removeDefaultValue(ParameterNode parameterNode) { + if (parameterNode instanceof DefaultableParameterNode defaultableParameterNode) { + return createRequiredParameterNode(defaultableParameterNode.annotations(), + defaultableParameterNode.typeName(), defaultableParameterNode.paramName().orElse(null)); + } + return parameterNode; + } + + public Optional generateFunctionSignature() { + if (!parameterNodes.isEmpty()) { + parameterNodes.remove(parameterNodes.size() - 1); + } + SeparatedNodeList parameterNodeList = createSeparatedNodeList(parameterNodes); + return Optional.of(createFunctionSignatureNode(createToken(OPEN_PAREN_TOKEN), parameterNodeList, + createToken(CLOSE_PAREN_TOKEN), returnTypeDescriptorNode)); + } +} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionGenerator.java index c0b1050f4..8d4af41c0 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionGenerator.java @@ -136,7 +136,7 @@ protected Optional getFunctionBodyNode(List } @Override - protected RemoteFunctionSignatureGenerator getSignatureGenerator() { + protected RemoteFunctionSignatureGeneratorNew getSignatureGenerator() { return new RemoteExternalFunctionSignatureGenerator(operation.getValue(), openAPI, operation.getKey().toString().toLowerCase(Locale.ROOT), path); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionSignatureGenerator.java index a080ad20d..e29f04cb0 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionSignatureGenerator.java @@ -49,11 +49,11 @@ * * @since 1.9.0 */ -public class RemoteExternalFunctionSignatureGenerator extends RemoteFunctionSignatureGenerator { +public class RemoteExternalFunctionSignatureGenerator extends RemoteFunctionSignatureGeneratorNew { public RemoteExternalFunctionSignatureGenerator(Operation operation, OpenAPI openAPI, String httpMethod, String path) { - super(operation, openAPI, httpMethod); + super(operation, openAPI, httpMethod, path); this.functionReturnTypeGenerator = new FunctionExternalReturnTypeGenerator(operation, openAPI, httpMethod, path); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionGenerator.java index 6b1b10a9f..4cf8201f6 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionGenerator.java @@ -61,7 +61,7 @@ public Optional generateFunction() { Token functionKeyWord = createToken(FUNCTION_KEYWORD); IdentifierToken functionName = createIdentifierToken(operation.getValue().getOperationId()); // Create function signature - RemoteFunctionSignatureGenerator signatureGenerator = getSignatureGenerator(); + RemoteFunctionSignatureGeneratorNew signatureGenerator = getSignatureGenerator(); Optional signatureNodeOptional = signatureGenerator.generateFunctionSignature(); diagnostics.addAll(signatureGenerator.getDiagnostics()); @@ -90,9 +90,9 @@ protected Optional getFunctionBodyNode(List return functionBodyNodeResult; } - protected RemoteFunctionSignatureGenerator getSignatureGenerator() { - return new RemoteFunctionSignatureGenerator(operation.getValue(), openAPI, - operation.getKey().toString().toLowerCase(Locale.ROOT)); + protected RemoteFunctionSignatureGeneratorNew getSignatureGenerator() { + return new RemoteFunctionSignatureGeneratorNew(operation.getValue(), openAPI, + operation.getKey().toString().toLowerCase(Locale.ROOT), path); } protected Optional getFunctionDefinitionNode(NodeList qualifierList, diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGeneratorNew.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGeneratorNew.java new file mode 100644 index 000000000..3c9a5a62a --- /dev/null +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGeneratorNew.java @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.core.generators.client; + +import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeFactory; +import io.ballerina.compiler.syntax.tree.ParameterNode; +import io.ballerina.compiler.syntax.tree.RequiredParameterNode; +import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.Token; +import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; +import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp; +import io.ballerina.openapi.core.generators.client.parameter.HeadersParameterGenerator; +import io.ballerina.openapi.core.generators.client.parameter.PathParameterGenerator; +import io.ballerina.openapi.core.generators.client.parameter.QueriesParameterGenerator; +import io.ballerina.openapi.core.generators.client.parameter.RequestBodyGeneratorNew; +import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.parameters.Parameter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createSeparatedNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_PAREN_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.COMMA_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_PAREN_TOKEN; +import static io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages.OAS_CLIENT_100; +import static io.ballerina.openapi.core.generators.common.GeneratorUtils.extractReferenceType; + +public class RemoteFunctionSignatureGeneratorNew implements FunctionSignatureGenerator { + OpenAPI openAPI; + Operation operation; + List diagnostics = new ArrayList<>(); + boolean treatDefaultableAsRequired = false; + FunctionReturnTypeGeneratorImp functionReturnTypeGenerator; + private final String httpMethod; + private final String path; + + private boolean hasDefaultHeader = false; + private boolean hasHeadersParam = false; + private boolean hasQueriesParam = false; + + public RemoteFunctionSignatureGeneratorNew(Operation operation, OpenAPI openAPI, String httpMethod, + String path) { + this.operation = operation; + this.openAPI = openAPI; + this.httpMethod = httpMethod; + this.path = path; + this.functionReturnTypeGenerator = new FunctionReturnTypeGeneratorImp(operation, openAPI, httpMethod); + } + + @Override + public Optional generateFunctionSignature() { + List parameters = operation.getParameters(); + ParametersInfo parametersInfo = getParametersInfo(parameters); + + if (parametersInfo == null) { + return Optional.empty(); + } + + List defaultable = parametersInfo.defaultable(); + List parameterList = parametersInfo.parameterList(); + + // filter defaultable parameters + if (!defaultable.isEmpty()) { + parameterList.addAll(defaultable); + } + // Remove the last comma + if (!parameterList.isEmpty()) { + parameterList.remove(parameterList.size() - 1); + } + SeparatedNodeList parameterNodes = createSeparatedNodeList(parameterList); + + // 3. return statements + FunctionReturnTypeGeneratorImp returnTypeGenerator = getFunctionReturnTypeGenerator(); + Optional returnType = returnTypeGenerator.getReturnType(); + diagnostics.addAll(returnTypeGenerator.getDiagnostics()); + if (returnType.isEmpty()) { + return Optional.empty(); + } + + return returnType.map(returnTypeDescriptorNode -> NodeFactory.createFunctionSignatureNode( + createToken(OPEN_PAREN_TOKEN), parameterNodes, + createToken(CLOSE_PAREN_TOKEN), returnTypeDescriptorNode)); + } + + protected ParametersInfo getParametersInfo(List parameters) { + List parameterList = new ArrayList<>(); + List defaultable = new ArrayList<>(); + Token comma = createToken(COMMA_TOKEN); + + List headerParameters = new ArrayList<>(); + List queryParameters = new ArrayList<>(); + + // 1. path parameters + if (parameters != null) { + for (Parameter parameter : parameters) { + if (parameter.getIn().equals("path")) { + PathParameterGenerator paramGenerator = new PathParameterGenerator(parameter, openAPI); + Optional param = paramGenerator.generateParameterNode( + treatDefaultableAsRequired); + if (param.isEmpty()) { + diagnostics.addAll(paramGenerator.getDiagnostics()); + return null; + } + // Path parameters are always required. + parameterList.add(param.get()); + parameterList.add(comma); + } + } + } + + // 1. requestBody + if (operation.getRequestBody() != null) { + RequestBodyGeneratorNew requestBodyGenerator = new RequestBodyGeneratorNew(operation.getRequestBody(), + openAPI); + Optional requestBody = requestBodyGenerator.generateParameterNode( + treatDefaultableAsRequired); + if (requestBody.isEmpty()) { + diagnostics.addAll(requestBodyGenerator.getDiagnostics()); + return null; + } + headerParameters = requestBodyGenerator.getHeaderSchemas(); + parameterList.add(requestBody.get()); + parameterList.add(comma); + } + + // 2. parameters - query, headers + if (parameters != null) { + populateQueryAndHeaderParameters(parameters, queryParameters, headerParameters); + + HeadersParameterGenerator headersParameterGenerator = new HeadersParameterGenerator(headerParameters, + openAPI, operation, httpMethod, path); + Optional headers; + if (headerParameters.isEmpty()) { + hasDefaultHeader = true; + headers = headersParameterGenerator.getDefaultParameterNode(treatDefaultableAsRequired); + } else { + headers = headersParameterGenerator.generateParameterNode(); + } + + if (headers.isPresent()) { + hasHeadersParam = true; + if (headers.get() instanceof RequiredParameterNode headerNode) { + parameterList.add(headerNode); + parameterList.add(comma); + } else { + defaultable.add(headers.get()); + defaultable.add(comma); + } + } else if (!headerParameters.isEmpty()) { + diagnostics.addAll(headersParameterGenerator.getDiagnostics()); + return null; + } + + QueriesParameterGenerator queriesParameterGenerator = new QueriesParameterGenerator(queryParameters, + openAPI, operation, httpMethod, path); + Optional queries = queriesParameterGenerator.generateParameterNode(); + if (queries.isPresent()) { + hasQueriesParam = true; + parameterList.add(queries.get()); + parameterList.add(comma); + } else if (!queryParameters.isEmpty()) { + diagnostics.addAll(queriesParameterGenerator.getDiagnostics()); + return null; + } + } + return new ParametersInfo(parameterList, defaultable); + } + + private void populateQueryAndHeaderParameters(List parameters, List queryParameters, + List headerParameters) { + for (Parameter parameter : parameters) { + if (parameter.get$ref() != null) { + String paramType = null; + try { + paramType = extractReferenceType(parameter.get$ref()); + } catch (BallerinaOpenApiException e) { + ClientDiagnosticImp clientDiagnostic = new ClientDiagnosticImp(OAS_CLIENT_100, + parameter.get$ref()); + diagnostics.add(clientDiagnostic); + } + parameter = openAPI.getComponents().getParameters().get(paramType); + } + + String in = parameter.getIn(); + + switch (in) { + case "query": + queryParameters.add(parameter); + break; + case "header": + headerParameters.add(parameter); + break; + default: + break; + } + } +} + + protected FunctionReturnTypeGeneratorImp getFunctionReturnTypeGenerator() { + return functionReturnTypeGenerator; + } + + public List getDiagnostics() { + return diagnostics; + } + + public boolean hasDefaultHeaders() { + return hasDefaultHeader; + } + + public boolean hasHeaders() { + return hasHeadersParam; + } + + public boolean hasQueries() { + return hasQueriesParam; + } +} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionGenerator.java index f01d6bbf7..ac1c244f4 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionGenerator.java @@ -109,7 +109,8 @@ protected Optional getFunctionDefinitionNode(NodeList getFunctionBodyNode(List diagnostics) { + protected Optional getFunctionBodyNode(List diagnostics, boolean hasHeaders, + boolean hasDefaultHeaders, boolean hasQueries) { QualifiedNameReferenceNode javaMethodToken = createQualifiedNameReferenceNode( createIdentifierToken("java"), createToken(COLON_TOKEN), createIdentifierToken("Method")); BasicLiteralNode classValueExp = createBasicLiteralNode(STRING_LITERAL, @@ -142,7 +143,7 @@ private String getNativeMethodName() { } @Override - protected ResourceFunctionSignatureGenerator getSignatureGenerator() { + protected ResourceFunctionSignatureGeneratorNew getSignatureGenerator() { return new ResourceExternalFunctionSignatureGenerator(operation.getValue(), openAPI, operation.getKey().toString().toLowerCase(Locale.ROOT), path); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionSignatureGenerator.java index 87f5d30a6..e6b1c8211 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionSignatureGenerator.java @@ -49,11 +49,11 @@ * * @since 1.9.0 */ -public class ResourceExternalFunctionSignatureGenerator extends ResourceFunctionSignatureGenerator { +public class ResourceExternalFunctionSignatureGenerator extends ResourceFunctionSignatureGeneratorNew { public ResourceExternalFunctionSignatureGenerator(Operation operation, OpenAPI openAPI, String httpMethod, String path) { - super(operation, openAPI, httpMethod); + super(operation, openAPI, httpMethod, path); this.functionReturnTypeGenerator = new FunctionExternalReturnTypeGenerator(operation, openAPI, httpMethod, path); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionGenerator.java index e0c6b86a2..5ea75218d 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionGenerator.java @@ -82,14 +82,16 @@ public Optional generateFunction() { return Optional.empty(); } // Create function signature - ResourceFunctionSignatureGenerator signatureGenerator = getSignatureGenerator(); + ResourceFunctionSignatureGeneratorNew signatureGenerator = getSignatureGenerator(); Optional signatureNodeOptional = signatureGenerator.generateFunctionSignature(); diagnostics.addAll(signatureGenerator.getDiagnostics()); if (signatureNodeOptional.isEmpty()) { return Optional.empty(); } //Create function body - Optional functionBodyNodeResult = getFunctionBodyNode(diagnostics); + Optional functionBodyNodeResult = getFunctionBodyNode(diagnostics, + signatureGenerator.hasHeaders(), signatureGenerator.hasDefaultHeaders(), + signatureGenerator.hasQueries()); if (functionBodyNodeResult.isEmpty()) { return Optional.empty(); } @@ -100,9 +102,10 @@ public Optional generateFunction() { } } - protected Optional getFunctionBodyNode(List diagnostics) { - FunctionBodyGeneratorImp functionBodyGenerator = new FunctionBodyGeneratorImp(path, operation, openAPI, - authConfigGeneratorImp, ballerinaUtilGenerator, imports); + protected Optional getFunctionBodyNode(List diagnostics, boolean hasHeaders, + boolean hasDefaultHeaders, boolean hasQueries) { + FunctionBodyGeneratorImpNew functionBodyGenerator = new FunctionBodyGeneratorImpNew(path, operation, openAPI, + authConfigGeneratorImp, ballerinaUtilGenerator, imports, hasHeaders, hasDefaultHeaders, hasQueries); Optional functionBodyNodeResult = functionBodyGenerator.getFunctionBodyNode(); if (functionBodyNodeResult.isEmpty()) { diagnostics.addAll(functionBodyGenerator.getDiagnostics()); @@ -110,9 +113,9 @@ protected Optional getFunctionBodyNode(List return functionBodyNodeResult; } - protected ResourceFunctionSignatureGenerator getSignatureGenerator() { - return new ResourceFunctionSignatureGenerator(operation.getValue(), openAPI, - operation.getKey().toString().toLowerCase(Locale.ROOT)); + protected ResourceFunctionSignatureGeneratorNew getSignatureGenerator() { + return new ResourceFunctionSignatureGeneratorNew(operation.getValue(), openAPI, + operation.getKey().toString().toLowerCase(Locale.ROOT), path); } protected Optional getFunctionDefinitionNode(NodeList qualifierList, diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGeneratorNew.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGeneratorNew.java new file mode 100644 index 000000000..8ca3e9746 --- /dev/null +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGeneratorNew.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.core.generators.client; + +import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeFactory; +import io.ballerina.compiler.syntax.tree.ParameterNode; +import io.ballerina.compiler.syntax.tree.RequiredParameterNode; +import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.Token; +import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; +import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp; +import io.ballerina.openapi.core.generators.client.parameter.HeadersParameterGenerator; +import io.ballerina.openapi.core.generators.client.parameter.QueriesParameterGenerator; +import io.ballerina.openapi.core.generators.client.parameter.RequestBodyGeneratorNew; +import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.parameters.Parameter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createSeparatedNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_PAREN_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.COMMA_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_PAREN_TOKEN; +import static io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages.OAS_CLIENT_100; +import static io.ballerina.openapi.core.generators.common.GeneratorUtils.extractReferenceType; + +public class ResourceFunctionSignatureGeneratorNew implements FunctionSignatureGenerator { + protected final Operation operation; + protected final OpenAPI openAPI; + protected final List diagnostics = new ArrayList<>(); + protected FunctionReturnTypeGeneratorImp functionReturnTypeGenerator; + private final String httpMethod; + private final String path; + + private boolean hasDefaultHeader = false; + private boolean hasHeadersParam = false; + private boolean hasQueriesParam = false; + + public ResourceFunctionSignatureGeneratorNew(Operation operation, OpenAPI openAPI, String httpMethod, + String path) { + this.operation = operation; + this.openAPI = openAPI; + this.httpMethod = httpMethod; + this.path = path; + this.functionReturnTypeGenerator = new FunctionReturnTypeGeneratorImp(operation, openAPI, httpMethod); + } + + @Override + public Optional generateFunctionSignature() { + // 1. parameters - path , query, requestBody, headers + List parameters = operation.getParameters(); + ParametersInfo parametersInfo = getParametersInfo(parameters); + + if (parametersInfo == null) { + return Optional.empty(); + } + + List defaultableParameters = parametersInfo.defaultable(); + List parameterList = parametersInfo.parameterList(); + + //filter defaultable parameters + if (!defaultableParameters.isEmpty()) { + parameterList.addAll(defaultableParameters); + } + // Remove the last comma + if (!parameterList.isEmpty()) { + parameterList.remove(parameterList.size() - 1); + } + SeparatedNodeList parameterNodes = createSeparatedNodeList(parameterList); + + // 3. return statements + FunctionReturnTypeGeneratorImp functionReturnType = getFunctionReturnTypeGenerator(); + Optional returnType = functionReturnType.getReturnType(); + diagnostics.addAll(functionReturnType.getDiagnostics()); + if (returnType.isEmpty()) { + return Optional.empty(); + } + return returnType.map(returnTypeDescriptorNode -> NodeFactory.createFunctionSignatureNode( + createToken(OPEN_PAREN_TOKEN), parameterNodes, + createToken(CLOSE_PAREN_TOKEN), returnTypeDescriptorNode)); + //create function signature node + } + + protected ParametersInfo getParametersInfo(List parameters) { + List parameterList = new ArrayList<>(); + List defaultable = new ArrayList<>(); + Token comma = createToken(COMMA_TOKEN); + + List headerParameters = new ArrayList<>(); + List queryParameters = new ArrayList<>(); + + // 1. requestBody + if (operation.getRequestBody() != null) { + RequestBodyGeneratorNew requestBodyGenerator = new RequestBodyGeneratorNew(operation.getRequestBody(), + openAPI); + Optional requestBody = requestBodyGenerator.generateParameterNode(); + if (requestBody.isEmpty()) { + diagnostics.addAll(requestBodyGenerator.getDiagnostics()); + return null; + } + headerParameters = requestBodyGenerator.getHeaderSchemas(); + parameterList.add(requestBody.get()); + parameterList.add(comma); + } + + // 2. parameters - query, requestBody, headers + if (parameters != null) { + populateHeaderAndQueryParameters(parameters, queryParameters, headerParameters); + + HeadersParameterGenerator headersParameterGenerator = new HeadersParameterGenerator(headerParameters, + openAPI, operation, httpMethod, path); + Optional headers; + if (headerParameters.isEmpty()) { + hasDefaultHeader = true; + headers = headersParameterGenerator.getDefaultParameterNode(); + } else { + headers = headersParameterGenerator.generateParameterNode(); + } + + if (headers.isPresent()) { + hasHeadersParam = true; + if (headers.get() instanceof RequiredParameterNode headerNode) { + parameterList.add(headerNode); + parameterList.add(comma); + } else { + defaultable.add(headers.get()); + defaultable.add(comma); + } + } else if (!headerParameters.isEmpty()) { + diagnostics.addAll(headersParameterGenerator.getDiagnostics()); + return null; + } + + QueriesParameterGenerator queriesParameterGenerator = new QueriesParameterGenerator(queryParameters, + openAPI, operation, httpMethod, path); + Optional queries = queriesParameterGenerator.generateParameterNode(); + if (queries.isPresent()) { + hasQueriesParam = true; + parameterList.add(queries.get()); + parameterList.add(comma); + } else if (!queryParameters.isEmpty()) { + diagnostics.addAll(queriesParameterGenerator.getDiagnostics()); + return null; + } + } + + return new ParametersInfo(parameterList, defaultable); + } + + private void populateHeaderAndQueryParameters(List parameters, List queryParameters, + List headerParameters) { + for (Parameter parameter : parameters) { + if (parameter.get$ref() != null) { + String paramType = null; + try { + paramType = extractReferenceType(parameter.get$ref()); + } catch (BallerinaOpenApiException e) { + ClientDiagnosticImp clientDiagnostic = new ClientDiagnosticImp(OAS_CLIENT_100, + parameter.get$ref()); + diagnostics.add(clientDiagnostic); + } + parameter = openAPI.getComponents().getParameters().get(paramType); + } + + String in = parameter.getIn(); + + switch (in) { + case "query": + queryParameters.add(parameter); + break; + case "header": + headerParameters.add(parameter); + break; + default: + break; + } + } + } + + protected FunctionReturnTypeGeneratorImp getFunctionReturnTypeGenerator() { + return functionReturnTypeGenerator; + } + + @Override + public List getDiagnostics() { + return diagnostics; + } + + public boolean hasDefaultHeaders() { + return hasDefaultHeader; + } + + public boolean hasHeaders() { + return hasHeadersParam; + } + + public boolean hasQueries() { + return hasQueriesParam; + } +} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java new file mode 100644 index 000000000..c14937935 --- /dev/null +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java @@ -0,0 +1,184 @@ +package io.ballerina.openapi.core.generators.client.parameter; + +import io.ballerina.compiler.syntax.tree.ArrayDimensionNode; +import io.ballerina.compiler.syntax.tree.BasicLiteralNode; +import io.ballerina.compiler.syntax.tree.LiteralValueToken; +import io.ballerina.compiler.syntax.tree.NodeFactory; +import io.ballerina.compiler.syntax.tree.ParameterNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.compiler.syntax.tree.TypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.TypeParameterNode; +import io.ballerina.compiler.syntax.tree.UnionTypeDescriptorNode; +import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; +import io.ballerina.openapi.core.generators.common.GeneratorConstants; +import io.ballerina.openapi.core.generators.common.GeneratorUtils; +import io.ballerina.openapi.core.generators.common.TypeHandler; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyMinutiaeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createLiteralValueToken; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createArrayTypeDescriptorNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createBasicLiteralNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createDefaultableParameterNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createMapTypeDescriptorNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createRequiredParameterNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createSimpleNameReferenceNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createTypeParameterNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createUnionTypeDescriptorNode; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.EQUAL_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.LT_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.MAP_KEYWORD; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.PIPE_TOKEN; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEADERS; +import static io.ballerina.openapi.core.generators.common.GeneratorUtils.escapeIdentifier; + +public class HeadersParameterGenerator implements ParameterGenerator { + private final List parameters; + private final List diagnostics = new ArrayList<>(); + private final OpenAPI openAPI; + private final Operation operation; + private final String httpMethod; + private final String path; + + public HeadersParameterGenerator(List parameters, OpenAPI openAPI, Operation operation, + String httpMethod, String path) { + this.parameters = parameters; + this.openAPI = openAPI; + this.operation = operation; + this.httpMethod = httpMethod; + this.path = path; + } + + @Override + public Optional generateParameterNode() { + return generateParameterNode(false); + } + + @Override + public Optional generateParameterNode(boolean treatDefaultableAsRequired) { + ObjectSchema headersSchema = getHeadersSchema(treatDefaultableAsRequired); + String operationId = GeneratorUtils.generateOperationUniqueId(operation, path, httpMethod); + headersSchema.setDescription("Represents the Headers record for the operation id: " + operationId); + String headersName = GeneratorUtils.getValidName(operationId + "Headers", true); + openAPI.getComponents().addSchemas(headersName, headersSchema); + + Schema headersRefSchema = new ObjectSchema().$ref(headersName); + Optional headersType = TypeHandler.getInstance().getTypeNodeFromOASSchema( + headersRefSchema, true); + + if (headersType.isEmpty()) { + // This should be some error scenario + return Optional.empty(); + } + + boolean isDefaultable = !treatDefaultableAsRequired && isDefaultable(headersSchema); + ParameterNode parameterNode = createParameterNode(headersType.get(), isDefaultable); + + return Optional.of(parameterNode); + } + + private boolean isDefaultable(ObjectSchema headersSchema) { + List requiredHeaders = headersSchema.getRequired(); + Map headers = headersSchema.getProperties(); + + return Objects.isNull(requiredHeaders) || requiredHeaders.isEmpty() || requiredHeaders.stream() + .allMatch(header -> Objects.nonNull(headers.get(header).getDefault())); + } + + private ParameterNode createParameterNode(TypeDescriptorNode headersType, boolean isDefaultable) { + if (isDefaultable) { + LiteralValueToken defaultMapVal = createLiteralValueToken(null, "{}", createEmptyMinutiaeList(), + createEmptyMinutiaeList()); + BasicLiteralNode defaultMapExp = createBasicLiteralNode(null, defaultMapVal); + return createDefaultableParameterNode(createEmptyNodeList(), headersType, + createIdentifierToken(HEADERS), createToken(EQUAL_TOKEN), defaultMapExp); + } else { + return createRequiredParameterNode(createEmptyNodeList(), headersType, + createIdentifierToken(HEADERS)); + } + } + + public Optional getDefaultParameterNode() { + return getDefaultParameterNode(false); + } + + public Optional getDefaultParameterNode(boolean treatDefaultableAsRequired) { + TypeDescriptorNode stringType = createSimpleNameReferenceNode( + createIdentifierToken(GeneratorConstants.STRING)); + + ArrayDimensionNode dimensionNode = NodeFactory.createArrayDimensionNode( + createToken(SyntaxKind.OPEN_BRACKET_TOKEN), null, + createToken(SyntaxKind.CLOSE_BRACKET_TOKEN)); + TypeDescriptorNode stringArrType = createArrayTypeDescriptorNode(stringType, + createNodeList(dimensionNode)); + + UnionTypeDescriptorNode unionType = createUnionTypeDescriptorNode(stringType, createToken(PIPE_TOKEN), + stringArrType); + TypeParameterNode headerParamNode = createTypeParameterNode(createToken(LT_TOKEN), unionType, + createToken(SyntaxKind.GT_TOKEN)); + TypeDescriptorNode defaultHeadersType = createMapTypeDescriptorNode(createToken(MAP_KEYWORD), headerParamNode); + + if (treatDefaultableAsRequired) { + return Optional.of(createRequiredParameterNode(createEmptyNodeList(), defaultHeadersType, + createIdentifierToken(HEADERS))); + } + + LiteralValueToken defaultMapVal = createLiteralValueToken(null, "{}", createEmptyMinutiaeList(), + createEmptyMinutiaeList()); + BasicLiteralNode defaultMapExp = createBasicLiteralNode(null, defaultMapVal); + return Optional.of(createDefaultableParameterNode(createEmptyNodeList(), defaultHeadersType, + createIdentifierToken(HEADERS), createToken(EQUAL_TOKEN), defaultMapExp)); + } + + @Override + public List getDiagnostics() { + return diagnostics; + } + + private ObjectSchema getHeadersSchema(boolean treatDefaultableAsRequired) { + Map properties = parameters.stream() + .collect(Collectors.toMap( + parameter -> escapeIdentifier(parameter.getName()), + parameter -> getSchemaWithDescription(parameter, treatDefaultableAsRequired)) + ); + + List requiredFields = parameters.stream() + .filter(parameter -> Boolean.TRUE.equals(parameter.getRequired())) + .map(parameter -> escapeIdentifier(parameter.getName())) + .toList(); + + ObjectSchema headersSchema = new ObjectSchema(); + headersSchema.setProperties(properties); + if (!requiredFields.isEmpty()) { + headersSchema.setRequired(requiredFields); + } + return headersSchema; + } + + private Schema getSchemaWithDescription(Parameter parameter, boolean treatDefaultableAsRequired) { + Schema schema = parameter.getSchema(); + schema.setDescription(parameter.getDescription()); + if (treatDefaultableAsRequired) { + schema.setDefault(null); + } + if (!Boolean.TRUE.equals(parameter.getRequired())) { + schema.setNullable(true); + } + return schema; + } +} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java new file mode 100644 index 000000000..02c3fcca4 --- /dev/null +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.core.generators.client.parameter; + +import io.ballerina.compiler.syntax.tree.ParameterNode; +import io.ballerina.compiler.syntax.tree.TypeDescriptorNode; +import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; +import io.ballerina.openapi.core.generators.common.GeneratorUtils; +import io.ballerina.openapi.core.generators.common.TypeHandler; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createIncludedRecordParameterNode; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.ASTERISK_TOKEN; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERIES; + +public class QueriesParameterGenerator implements ParameterGenerator { + + private final List parameters; + private final OpenAPI openAPI; + private final List diagnostics = new ArrayList<>(); + private final Operation operation; + private final String httpMethod; + private final String path; + + public QueriesParameterGenerator(List parameters, OpenAPI openAPI, Operation operation, + String httpMethod, String path) { + this.parameters = parameters; + this.openAPI = openAPI; + this.operation = operation; + this.httpMethod = httpMethod; + this.path = path; + } + + @Override + public Optional generateParameterNode() { + return generateParameterNode(false); + } + + @Override + public Optional generateParameterNode(boolean treatDefaultableAsRequired) { + if (parameters.isEmpty()) { + return Optional.empty(); + } + + ObjectSchema queriesSchema = getQueriesSchema(); + String operationId = GeneratorUtils.generateOperationUniqueId(operation, path, httpMethod); + queriesSchema.setDescription("Represents the Queries record for the operation id: " + operationId); + String queriesName = GeneratorUtils.getValidName(operationId + "Queries", true); + openAPI.getComponents().addSchemas(queriesName, queriesSchema); + Schema queriesRefSchema = new ObjectSchema().$ref(queriesName); + Optional queriesType = TypeHandler.getInstance().getTypeNodeFromOASSchema(queriesRefSchema, + true); + + return queriesType.map(typeDescriptorNode -> createIncludedRecordParameterNode(createEmptyNodeList(), + createToken(ASTERISK_TOKEN), typeDescriptorNode, createIdentifierToken(QUERIES))); + + } + + @Override + public List getDiagnostics() { + return diagnostics; + } + + private ObjectSchema getQueriesSchema() { + Map properties = parameters.stream() + .collect(Collectors.toMap(Parameter::getName, this::getSchemaWithDescription)); + + List requiredFields = parameters.stream() + .filter(parameter -> Boolean.TRUE.equals(parameter.getRequired())) + .map(Parameter::getName) + .toList(); + + ObjectSchema queriesSchema = new ObjectSchema(); + queriesSchema.setProperties(properties); + if (!requiredFields.isEmpty()) { + queriesSchema.setRequired(requiredFields); + } + return queriesSchema; + } + + private Schema getSchemaWithDescription(Parameter parameter) { + Schema schema = parameter.getSchema(); + schema.setDescription(parameter.getDescription()); + return schema; + } +} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyGeneratorNew.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyGeneratorNew.java new file mode 100644 index 000000000..3000cfa5b --- /dev/null +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyGeneratorNew.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.core.generators.client.parameter; + +import io.ballerina.compiler.syntax.tree.ParameterNode; +import io.ballerina.compiler.syntax.tree.TypeDescriptorNode; +import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; +import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp; +import io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages; +import io.ballerina.openapi.core.generators.common.GeneratorUtils; +import io.ballerina.openapi.core.generators.common.TypeHandler; +import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.headers.Header; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.Encoding; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createRequiredParameterNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createSimpleNameReferenceNode; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.APPLICATION_OCTET_STREAM; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HTTP_REQUEST; +import static io.ballerina.openapi.core.generators.common.GeneratorUtils.escapeIdentifier; +import static io.ballerina.openapi.core.generators.common.GeneratorUtils.extractReferenceType; +import static io.ballerina.openapi.core.generators.common.GeneratorUtils.getBallerinaMediaType; +import static io.ballerina.openapi.core.generators.common.GeneratorUtils.getOpenAPIType; + +public class RequestBodyGeneratorNew implements ParameterGenerator { + OpenAPI openAPI; + RequestBody requestBody; + List diagnostics = new ArrayList<>(); + List headerSchemas = new ArrayList<>(); + + public RequestBodyGeneratorNew(RequestBody requestBody, OpenAPI openAPI) { + this.requestBody = requestBody; + this.openAPI = openAPI; + } + + public List getHeaderSchemas() { + return headerSchemas; + } + + @Override + public Optional generateParameterNode() { + return generateParameterNode(false); + } + + @Override + public Optional generateParameterNode(boolean treatDefaultableAsRequired) { + Content requestBodyContent; + String referencedRequestBodyName; + TypeDescriptorNode typeDescNode = null; + if (requestBody.get$ref() != null) { + try { + referencedRequestBodyName = extractReferenceType(requestBody.get$ref()).trim(); + } catch (BallerinaOpenApiException e) { + ClientDiagnosticImp diagnostic = new ClientDiagnosticImp(DiagnosticMessages.OAS_CLIENT_109, + requestBody.get$ref()); + diagnostics.add(diagnostic); + return Optional.empty(); + } + RequestBody referencedRequestBody = openAPI.getComponents() + .getRequestBodies().get(referencedRequestBodyName); + requestBodyContent = referencedRequestBody.getContent(); + // note : when there is referenced request body, the description at the reference is ignored. + // Need to consider the description at the component level + requestBody.setDescription(referencedRequestBody.getDescription()); + } else { + requestBodyContent = requestBody.getContent(); + } + if (requestBodyContent == null || requestBodyContent.isEmpty()) { + return Optional.empty(); + } + for (Map.Entry mediaTypeEntry : requestBodyContent.entrySet()) { + // This implementation currently for first content type + Schema schema = mediaTypeEntry.getValue().getSchema(); + String paramType; + //Take payload type + if (schema != null && GeneratorUtils.isSupportedMediaType(mediaTypeEntry)) { + String mediaTypeEntryKey = mediaTypeEntry.getKey(); + if (mediaTypeEntryKey.equals(APPLICATION_OCTET_STREAM) || + mediaTypeEntryKey.matches("application/.*\\+octet-stream")) { + paramType = getBallerinaMediaType(mediaTypeEntryKey, true); + typeDescNode = createSimpleNameReferenceNode(createIdentifierToken(paramType)); + } else { + if (schema.get$ref() != null) { + Optional node = TypeHandler.getInstance().getTypeNodeFromOASSchema(schema); + if (node.isEmpty()) { + return Optional.empty(); + } + typeDescNode = node.get(); + } else if (getOpenAPIType(schema) != null || schema.getProperties() != null) { + Optional resultNode = TypeHandler.getInstance(). + getTypeNodeFromOASSchema(schema); + + if (resultNode.isEmpty()) { + if (schema instanceof ArraySchema) { + paramType = getBallerinaMediaType(mediaTypeEntry.getKey(), true) + "[]"; + typeDescNode = createSimpleNameReferenceNode(createIdentifierToken(paramType)); + } else { + paramType = getBallerinaMediaType(mediaTypeEntryKey, true); + typeDescNode = createSimpleNameReferenceNode(createIdentifierToken(paramType)); + } + } else { + typeDescNode = resultNode.get(); + if (schema instanceof ArraySchema && typeDescNode.toSourceCode().contains("anydata")) { + paramType = getBallerinaMediaType(mediaTypeEntry.getKey(), true) + "[]"; + typeDescNode = createSimpleNameReferenceNode(createIdentifierToken(paramType)); + } + } + + } else { + paramType = getBallerinaMediaType(mediaTypeEntryKey, true); + typeDescNode = createSimpleNameReferenceNode(createIdentifierToken(paramType)); + } + } + + //handle headers + if (mediaTypeEntryKey.equals(javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA) || + mediaTypeEntryKey.matches("multipart/.*\\+form-data")) { + extractHeaders(mediaTypeEntry); + } + } else { + if (mediaTypeEntry.getKey().equals(javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA) + && mediaTypeEntry.getValue().getEncoding() != null) { + extractHeaders(mediaTypeEntry); + } else { + paramType = getBallerinaMediaType(mediaTypeEntry.getKey(), true); + typeDescNode = createSimpleNameReferenceNode(createIdentifierToken(paramType)); + } + } + break; + } + if (typeDescNode != null) { + String reqBody = typeDescNode.toSourceCode().equals(HTTP_REQUEST) ? "request" : "payload"; + return Optional.of(createRequiredParameterNode(createEmptyNodeList(), typeDescNode, + createIdentifierToken(reqBody))); + } else { + return Optional.empty(); + } + } + + private void extractHeaders(Map.Entry mediaTypeEntry) { + Map encodingHeaders = mediaTypeEntry.getValue().getEncoding(); + if (encodingHeaders == null) { + return; + } + List headerList = new ArrayList<>(); + for (Map.Entry entry : encodingHeaders.entrySet()) { + Map headers = entry.getValue().getHeaders(); + if (headers != null) { + for (Map.Entry header : headers.entrySet()) { + if (!headerList.contains(escapeIdentifier(header.getKey()))) { + RequestBodyHeaderParameterNew requestBodyHeaderParameter = new RequestBodyHeaderParameterNew( + header); + Optional parameter = requestBodyHeaderParameter.generateParameterSchema(); + + parameter.ifPresent(param -> headerSchemas.add(param)); + diagnostics.addAll(requestBodyHeaderParameter.getDiagnostics()); + headerList.add(escapeIdentifier(header.getKey())); + } + } + } + } + } + + @Override + public List getDiagnostics() { + return diagnostics; + } +} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyHeaderParameterNew.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyHeaderParameterNew.java new file mode 100644 index 000000000..215134e27 --- /dev/null +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyHeaderParameterNew.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.core.generators.client.parameter; + +import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; +import io.swagger.v3.oas.models.headers.Header; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class RequestBodyHeaderParameterNew { + Map.Entry header; + List diagnostics = new ArrayList<>(); + + RequestBodyHeaderParameterNew(Map.Entry header) { + this.header = header; + } + + public Optional generateParameterSchema() { + return generateParameterNode(false); + } + + public Optional generateParameterNode(boolean treatDefaultableAsRequired) { + Schema schema = getSchemaWithDescription(header.getValue(), treatDefaultableAsRequired); + Parameter parameter = new Parameter(); + parameter.setSchema(schema); + parameter.setName(header.getKey()); + parameter.setIn("header"); + parameter.setRequired(header.getValue().getRequired()); + return Optional.of(parameter); + } + + private Schema getSchemaWithDescription(Header header, boolean treatDefaultableAsRequired) { + Schema schema = header.getSchema(); + schema.setDescription(header.getDescription()); + if (treatDefaultableAsRequired) { + schema.setDefault(null); + } + if (!Boolean.TRUE.equals(header.getRequired())) { + schema.setNullable(true); + } + return schema; + } + + public List getDiagnostics() { + return diagnostics; + } + +} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/common/GeneratorConstants.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/common/GeneratorConstants.java index 0da87acf6..68edbe2ef 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/common/GeneratorConstants.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/common/GeneratorConstants.java @@ -126,6 +126,7 @@ public String getValue() { public static final String HTTP_REQUEST = "http:Request"; public static final String PDF = "pdf"; public static final String QUERY_PARAM = "queryParam"; + public static final String QUERIES = "queries"; public static final String SELF = "self"; public static final String TEXT_PREFIX = "text/"; public static final String XML_DATA = "xmldata"; diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/common/GeneratorUtils.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/common/GeneratorUtils.java index 8be12b22e..0e9c0a0ef 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/common/GeneratorUtils.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/common/GeneratorUtils.java @@ -1019,7 +1019,7 @@ public static Schema getHeadersTypeSchema(ApiResponse apiResponse) { return headersSchema; } - private static Schema getValidatedHeaderSchema(Schema headerSchema) { + public static Schema getValidatedHeaderSchema(Schema headerSchema) { return getValidatedHeaderSchema(headerSchema, headerSchema); } @@ -1216,4 +1216,9 @@ public static void addCommonParamsToOperationParams(Map.Entry operationDetailsMap = new HashMap<>(); + Paths paths = openAPI.getPaths(); + extractOperations(operationDetailsMap, paths); + //Generate type doc comments + Node rootNode = syntaxTree.rootNode(); + ModulePartNode modulePartNode = (ModulePartNode) rootNode; + NodeList members = modulePartNode.members(); + List updatedMembers = new ArrayList<>(); + members.forEach(member -> { + if (member.kind().equals(SyntaxKind.CLASS_DEFINITION)) { + ClassDefinitionNode classDef = (ClassDefinitionNode) member; + HashMap updatedList = new HashMap<>(); + NodeList classMembers = classDef.members(); + //sort these members according to .toString(); + List storeMembers = new ArrayList<>(); + List clientInitNodes = new ArrayList<>(); + classMembers.forEach(classMember -> { + String sortKey = ""; + if (classMember.kind().equals(SyntaxKind.OBJECT_METHOD_DEFINITION) || + classMember.kind().equals(SyntaxKind.RESOURCE_ACCESSOR_DEFINITION)) { + FunctionDefinitionNode funcDef = (FunctionDefinitionNode) classMember; + + //remote : operationId + if (isResource) { + sortKey = funcDef.toSourceCode(); + storeMembers.add(sortKey); + NodeList nodes = funcDef.relativeResourcePath(); + StringBuilder path = new StringBuilder(); + for (Node node: nodes) { + path.append(node.toString().replace("\"", "")); + } + String key = replaceContentWithinBrackets(path.toString(), "XXX") + "_" + + funcDef.functionName().text(); + funcDef = updateDocCommentsForFunctionNode(operationDetailsMap, funcDef, key); + } else { + sortKey = funcDef.functionName().text(); + storeMembers.add(sortKey); + String key = funcDef.functionName().text(); + funcDef = updateDocCommentsForFunctionNode(operationDetailsMap, funcDef, key); + } + classMember = funcDef; + } else { + clientInitNodes.add(classMember); + } + updatedList.put(sortKey, classMember); + }); + //sort the members + List sortedNodes = new ArrayList<>(); + sortedNodes.addAll(clientInitNodes); + storeMembers.sort(String::compareTo); + for (String memberStr: storeMembers) { + sortedNodes.add(updatedList.get(memberStr)); + } + classDef = classDef.modify( + classDef.metadata().orElse(null), + classDef.visibilityQualifier().orElse(null), + classDef.classTypeQualifiers(), + classDef.classKeyword(), + classDef.className(), + classDef.openBrace(), + updatedList.isEmpty() ? classDef.members() : createNodeList(sortedNodes), + classDef.closeBrace(), + classDef.semicolonToken().orElse(null)); + member = classDef; + } + + updatedMembers.add(member); + NodeList clientMembers = AbstractNodeFactory.createNodeList(updatedMembers); + ModulePartNode updatedmodulePartNode = modulePartNode.modify(modulePartNode.imports(), clientMembers, + modulePartNode.eofToken()); + + syntaxTree = syntaxTree.modifyWith(updatedmodulePartNode); + }); + return syntaxTree; + } + + private void extractOperations(HashMap operationDetailsMap, Paths paths) { + paths.forEach((path, pathItem) -> { + for (Map.Entry entry : pathItem.readOperationsMap().entrySet()) { + PathItem.HttpMethod method = entry.getKey(); + Operation operation = entry.getValue(); + if (!isResource) { + operationDetailsMap.put(operation.getOperationId(), + new OperationDetails(operation.getOperationId(), operation, path, method.name())); + } else { + path = path.equals("/") ? "." : path; + String key = replaceContentWithinBrackets(path.replaceFirst("/", ""), + "XXX") + "_" + method.name().toLowerCase(Locale.ENGLISH); + operationDetailsMap.put(key, new OperationDetails(operation.getOperationId(), + operation, path, method.name())); + } + } + }); + } + + private FunctionDefinitionNode updateDocCommentsForFunctionNode( + HashMap operationDetailsMap, FunctionDefinitionNode funcDef, String key) { + OperationDetails operationDetails = operationDetailsMap.get(key); + if (operationDetails != null) { + List docs = new ArrayList<>(); + List annotations = new ArrayList<>(); + Operation operation = operationDetails.operation(); + //function main comment + if (operation.getSummary() != null) { + docs.addAll(createAPIDescriptionDoc(operation.getSummary(), true)); + } else if (operation.getDescription() != null) { + docs.addAll(createAPIDescriptionDoc(operation.getDescription(), true)); + } + //function display annotation + if (operation.getExtensions() != null) { + extractDisplayAnnotation(operation.getExtensions(), annotations); + } + FunctionSignatureNode functionSignatureNode = funcDef.functionSignature(); + //function parameters + if (operation.getParameters() != null) { + SeparatedNodeList parameters = functionSignatureNode.parameters(); + List updatedParamsRequired = new ArrayList<>(); + List updatedParamsDefault = new ArrayList<>(); + List updatedIncludedParam = new ArrayList<>(); + + HashMap collection = getParameterNodeHashMap(parameters); + //todo parameter reference + updateParameterNodes(isResource, docs, operation, updatedParamsRequired, updatedParamsDefault, + collection); + if (collection.size() > 0) { + collection.forEach((keyParam, value) -> { + if (value instanceof RequiredParameterNode reParam) { + updatedParamsRequired.add(reParam); + updatedParamsRequired.add(createToken(SyntaxKind.COMMA_TOKEN)); + } else if (value instanceof DefaultableParameterNode deParam) { + updatedParamsDefault.add(deParam); + updatedParamsDefault.add(createToken(SyntaxKind.COMMA_TOKEN)); + } else if (value instanceof IncludedRecordParameterNode incParam) { + updatedIncludedParam.add(incParam); + updatedIncludedParam.add(createToken(SyntaxKind.COMMA_TOKEN)); + } + }); + } + updatedParamsRequired.addAll(updatedParamsDefault); + updatedParamsRequired.addAll(updatedIncludedParam); + if (!updatedParamsRequired.isEmpty()) { + if (updatedParamsRequired.get(updatedParamsRequired.size() - 1) instanceof Token) { + updatedParamsRequired.remove(updatedParamsRequired.size() - 1); + } + functionSignatureNode = functionSignatureNode.modify( + functionSignatureNode.openParenToken(), + createSeparatedNodeList(updatedParamsRequired), + functionSignatureNode.closeParenToken(), + functionSignatureNode.returnTypeDesc().orElse(null)); + + } + } + RequestBody requestBody = operation.getRequestBody(); + if (requestBody != null) { + addRequestBodyDoc(docs, requestBody); + } + //todo response + if (operation.getResponses() != null) { + ApiResponses responses = operation.getResponses(); + Collection values = responses.values(); + Iterator iteratorRes = values.iterator(); + if (iteratorRes.hasNext()) { + ApiResponse response = iteratorRes.next(); + if (response.getDescription() != null && !response.getDescription().isBlank()) { + MarkdownParameterDocumentationLineNode returnDoc = createAPIParamDoc("return", + response.getDescription()); + docs.add(returnDoc); + } + } + } + if (operation.getDeprecated() != null && operation.getDeprecated()) { + extractDeprecatedAnnotation(operation.getExtensions(), + docs, annotations); + } + + MarkdownDocumentationNode documentationNode = createMarkdownDocumentationNode(createNodeList(docs)); + Optional metadata = funcDef.metadata(); + MetadataNode metadataNode; + if (metadata.isEmpty()) { + metadataNode = createMetadataNode(documentationNode, createNodeList(annotations)); + } else { + metadataNode = metadata.get(); + metadataNode = createMetadataNode(documentationNode, + metadataNode.annotations().isEmpty() ? createNodeList(annotations) : + metadataNode.annotations().addAll(annotations)); + } + funcDef = funcDef.modify( + funcDef.kind(), + metadataNode, + funcDef.qualifierList(), + funcDef.functionKeyword(), + funcDef.functionName(), + funcDef.relativeResourcePath(), + functionSignatureNode, + funcDef.functionBody()); + } + return funcDef; + } + + private void addRequestBodyDoc(List docs, RequestBody requestBody) { + if (requestBody.get$ref() != null) { + try { + requestBody = openAPI.getComponents().getRequestBodies().get( + extractReferenceType(requestBody.get$ref())); + } catch (BallerinaOpenApiException e) { + requestBody = new RequestBody(); + } + } + Content content = requestBody.getContent(); + final String[] paramName = {"http:Request"}; + if (content != null) { + content.entrySet().stream().findFirst().ifPresent(mediaType -> { + paramName[0] = getBallerinaMediaType(mediaType.getKey(), true); + }); + } else { + paramName[0] = "http:Request"; + } + + if (requestBody.getDescription() != null) { + String description = requestBody.getDescription().split("\n")[0]; + docs.add(createAPIParamDoc(paramName[0].equals("http:Request") ? "request" : "payload", description)); + } + } + + private static void updateParameterNodes(boolean isResource, List docs, Operation operation, + List updatedParamsRequired, List updatedParamsDefault, + HashMap collection) { + List deprecatedParamDocComments = new ArrayList<>(); + operation.getParameters().forEach(parameter -> { + if (isResource && (parameter.getIn().equals("header") || parameter.getIn().equals("query"))) { + return; + } + + List paramAnnot = new ArrayList<>(); + String parameterDescription; + String parameterName = parameter.getName(); + + if (parameter.getIn().equals("path") || parameter.getIn().equals("header") + || parameter.getIn().equals("query")) { + parameterName = escapeIdentifier(parameter.getName()); + // add deprecated annotation + if (parameter.getDeprecated() != null && parameter.getDeprecated()) { + extractDeprecatedAnnotationDetails(deprecatedParamDocComments, parameter, paramAnnot); + } + if (parameter.getExtensions() != null) { + extractDisplayAnnotation(parameter.getExtensions(), paramAnnot); + + } + ParameterNode parameterNode = collection.get(parameterName); + updatedAnnotationInParameterNode(updatedParamsRequired, updatedParamsDefault, + paramAnnot, parameterNode); + collection.remove(parameterName); + } + if (parameter.getDescription() != null) { + parameterDescription = parameter.getDescription(); + docs.add(createAPIParamDocFromString(parameterName, parameterDescription)); + } + }); + if (isResource) { + if (collection.containsKey(HEADERS)) { + docs.add(createAPIParamDoc(HEADERS, "Headers to be sent with the request")); + } + if (collection.containsKey(QUERIES)) { + docs.add(createAPIParamDoc(QUERIES, "Queries to be sent with the request")); + } + } + docs.addAll(deprecatedParamDocComments); + } + + + private static HashMap getParameterNodeHashMap(SeparatedNodeList parameters) { + HashMap collection = new HashMap<>(); + for (ParameterNode node : parameters) { + if (node instanceof RequiredParameterNode reParam) { + collection.put(reParam.paramName().get().toString(), reParam); + } else if (node instanceof DefaultableParameterNode deParam) { + collection.put(deParam.paramName().get().toString(), deParam); + } else if (node instanceof IncludedRecordParameterNode incParam) { + collection.put(incParam.paramName().get().toString(), incParam); + } + } + return collection; + } +} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/DocCommentGeneratorImp.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/DocCommentGeneratorImp.java index e0d2fb599..147f35b43 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/DocCommentGeneratorImp.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/DocCommentGeneratorImp.java @@ -43,7 +43,7 @@ public SyntaxTree updateSyntaxTreeWithDocComments() { switch (type) { case GEN_CLIENT: //generate client doc comments - ClientDocCommentGenerator clientDocCommentGenerator = new ClientDocCommentGenerator(syntaxTree, + ClientDocCommentGeneratorNew clientDocCommentGenerator = new ClientDocCommentGeneratorNew(syntaxTree, openAPI, true); syntaxTree = clientDocCommentGenerator.updateSyntaxTreeWithDocComments(); break; diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/TypesDocCommentGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/TypesDocCommentGenerator.java index 6c4c30c19..2875fe6c9 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/TypesDocCommentGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/TypesDocCommentGenerator.java @@ -33,10 +33,13 @@ import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.compiler.syntax.tree.SyntaxTree; import io.ballerina.compiler.syntax.tree.TypeDefinitionNode; +import io.ballerina.openapi.core.generators.common.GeneratorUtils; import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.ComposedSchema; import io.swagger.v3.oas.models.media.Schema; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -79,51 +82,24 @@ public SyntaxTree updateSyntaxTreeWithDocComments() { if (schema != null) { if (finalTypeDef.typeDescriptor().kind().equals(SyntaxKind.RECORD_TYPE_DESC)) { List updatedFields = new ArrayList<>(); - RecordTypeDescriptorNode record = (RecordTypeDescriptorNode) finalTypeDef.typeDescriptor(); NodeList fields = record.fields(); - if (schema.getProperties() != null) { - schema.getProperties().forEach((key, value) -> { - fields.forEach(field -> { - if (field instanceof RecordFieldNode recordFieldNode) { - if (recordFieldNode.fieldName().text().replace("'", "") - .trim().equals(key)) { - if (value.getDescription() != null) { - Optional metadata = recordFieldNode.metadata(); - MetadataNode metadataNode = updateMetadataNode(metadata, value); - recordFieldNode = recordFieldNode.modify(metadataNode, - recordFieldNode.readonlyKeyword().orElse(null), - recordFieldNode.typeName(), - recordFieldNode.fieldName(), - recordFieldNode.questionMarkToken().orElse(null), - recordFieldNode.semicolonToken()); - } - updatedFields.add(recordFieldNode); - } - } else if (field instanceof RecordFieldWithDefaultValueNode - recordFieldWithDefaultValueNode) { - if (recordFieldWithDefaultValueNode.fieldName().text() - .replace("'", "") - .trim().equals(key)) { - if (value.getDescription() != null) { - Optional metadata = recordFieldWithDefaultValueNode - .metadata(); - MetadataNode metadataNode = updateMetadataNode(metadata, value); - recordFieldWithDefaultValueNode = recordFieldWithDefaultValueNode - .modify(metadataNode, - recordFieldWithDefaultValueNode.readonlyKeyword().orElse(null), - recordFieldWithDefaultValueNode.typeName(), - recordFieldWithDefaultValueNode.fieldName(), - recordFieldWithDefaultValueNode.equalsToken(), - recordFieldWithDefaultValueNode.expression(), - recordFieldWithDefaultValueNode.semicolonToken()); - } - updatedFields.add(recordFieldWithDefaultValueNode); - } + if (schema instanceof ComposedSchema composedSchema) { + List allOf = composedSchema.getAllOf(); + //handle special scenarios which allOf has inline objects + Map properties = new HashMap<>(); + if (allOf != null) { + for (Schema allOfSchema: allOf) { + if (allOfSchema.getProperties() != null) { + properties.putAll(allOfSchema.getProperties()); } - }); - }); + } + } + schema.setProperties(properties); } + + updateRecordFields(schema, updatedFields, fields); + typeDef = typeDef.modify(typeDef.metadata().orElse(null), typeDef.visibilityQualifier().get(), typeDef.typeKeyword(), @@ -158,6 +134,63 @@ public SyntaxTree updateSyntaxTreeWithDocComments() { return syntaxTree.modifyWith(modulePartNode); } + private static void updateRecordFields(Schema schema, List updatedFields, NodeList fields) { + if (schema.getProperties() != null) { + for (Node field : fields) { + boolean isUpdated = false; + for (Map.Entry entry : schema.getProperties().entrySet()) { + String key = GeneratorUtils.escapeIdentifier(entry.getKey()); + Schema value = entry.getValue(); + if (field instanceof RecordFieldNode recordFieldNode) { + if (recordFieldNode.fieldName().text().trim().equals(key)) { + if (value.getDescription() != null) { + Optional metadata = recordFieldNode.metadata(); + MetadataNode metadataNode = updateMetadataNode(metadata, value); + recordFieldNode = recordFieldNode.modify(metadataNode, + recordFieldNode.readonlyKeyword().orElse(null), + recordFieldNode.typeName(), + recordFieldNode.fieldName(), + recordFieldNode.questionMarkToken().orElse(null), + recordFieldNode.semicolonToken()); + updatedFields.add(recordFieldNode); + isUpdated = true; + } + } + + } else if (field instanceof RecordFieldWithDefaultValueNode + recordFieldWithDefaultValueNode) { + if (recordFieldWithDefaultValueNode.fieldName().text() + .replace("'", "") + .trim().equals(key)) { + if (value.getDescription() != null) { + Optional metadata = recordFieldWithDefaultValueNode + .metadata(); + MetadataNode metadataNode = updateMetadataNode(metadata, value); + recordFieldWithDefaultValueNode = recordFieldWithDefaultValueNode + .modify(metadataNode, + recordFieldWithDefaultValueNode.readonlyKeyword().orElse(null), + recordFieldWithDefaultValueNode.typeName(), + recordFieldWithDefaultValueNode.fieldName(), + recordFieldWithDefaultValueNode.equalsToken(), + recordFieldWithDefaultValueNode.expression(), + recordFieldWithDefaultValueNode.semicolonToken()); + + updatedFields.add(recordFieldWithDefaultValueNode); + isUpdated = true; + } + } + } + if (isUpdated) { + break; + } + } + if (!isUpdated) { + updatedFields.add(field); + } + } + } + } + private static MetadataNode updateMetadataNode(Optional metadata, Schema schema) { List schemaDoc = new ArrayList<>(); List typeAnnotations = new ArrayList<>(); diff --git a/openapi-core/src/main/resources/templates/utils_openapi.bal b/openapi-core/src/main/resources/templates/utils_openapi.bal index 0b1b72547..9cff4152c 100644 --- a/openapi-core/src/main/resources/templates/utils_openapi.bal +++ b/openapi-core/src/main/resources/templates/utils_openapi.bal @@ -233,18 +233,12 @@ isolated function getPathForQueryParam(map queryParam, map en # # + headerParam - Headers map # + return - Returns generated map or error at failure of client initialization -isolated function getMapForHeaders(map headerParam) returns map { +isolated function getMapForHeaders(map headerParam) returns map { map headerMap = {}; foreach var [key, value] in headerParam.entries() { - if value is string || value is string[] { - headerMap[key] = value; - } else if value is int[] { - string[] stringArray = []; - foreach int intValue in value { - stringArray.push(intValue.toString()); - } - headerMap[key] = stringArray; - } else if value is SimpleBasicType { + if value is SimpleBasicType[] { + headerMap[key] = from SimpleBasicType data in value select data.toString(); + } else { headerMap[key] = value.toString(); } } From 7e4eeefb4635706e0d9c4587c200ed52db1d6fcf Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Mon, 29 Apr 2024 21:59:33 +0530 Subject: [PATCH 02/31] Refactor code --- .../client/FunctionBodyNodeTests.java | 2 +- .../client/FunctionSignatureNodeTests.java | 24 +- .../client/BallerinaClientGenerator.java | 4 +- ...aClientGeneratorWithStatusCodeBinding.java | 6 +- .../client/FunctionBodyGeneratorImp.java | 208 ++---- .../client/FunctionBodyGeneratorImpNew.java | 694 ------------------ .../client/ImplFunctionBodyGenerator.java | 6 +- .../ImplFunctionSignatureGenerator.java | 139 +++- .../ImplFunctionSignatureGeneratorNew.java | 144 ---- .../RemoteExternalFunctionGenerator.java | 5 +- ...oteExternalFunctionSignatureGenerator.java | 2 +- .../client/RemoteFunctionGenerator.java | 15 +- .../RemoteFunctionSignatureGenerator.java | 213 +++--- .../RemoteFunctionSignatureGeneratorNew.java | 242 ------ .../ResourceExternalFunctionGenerator.java | 2 +- ...rceExternalFunctionSignatureGenerator.java | 2 +- .../client/ResourceFunctionGenerator.java | 8 +- .../ResourceFunctionSignatureGenerator.java | 186 +++-- ...ResourceFunctionSignatureGeneratorNew.java | 224 ------ .../parameter/HeaderParameterGenerator.java | 121 --- .../parameter/HeadersParameterGenerator.java | 29 +- .../client/parameter/ParameterGenerator.java | 1 - .../parameter/PathParameterGenerator.java | 5 - .../parameter/QueriesParameterGenerator.java | 5 - .../parameter/QueryParameterGenerator.java | 146 ---- .../parameter/RequestBodyGenerator.java | 19 +- .../parameter/RequestBodyGeneratorNew.java | 198 ----- .../parameter/RequestBodyHeaderParameter.java | 63 +- .../RequestBodyHeaderParameterNew.java | 69 -- .../document/ClientDocCommentGenerator.java | 35 +- .../ClientDocCommentGeneratorNew.java | 375 ---------- .../document/DocCommentGeneratorImp.java | 2 +- 32 files changed, 524 insertions(+), 2670 deletions(-) delete mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImpNew.java delete mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGeneratorNew.java delete mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGeneratorNew.java delete mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGeneratorNew.java delete mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeaderParameterGenerator.java delete mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueryParameterGenerator.java delete mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyGeneratorNew.java delete mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyHeaderParameterNew.java delete mode 100644 openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGeneratorNew.java diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionBodyNodeTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionBodyNodeTests.java index 96858ba8e..003d77709 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionBodyNodeTests.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionBodyNodeTests.java @@ -66,7 +66,7 @@ public void getFunctionBodyNodes(String yamlFile, String path, String content) t TypeHandler.createInstance(openapi, false); FunctionBodyGeneratorImp functionBodyGeneratorImp = new FunctionBodyGeneratorImp(path, operation, openapi, new AuthConfigGeneratorImp(false, false), - new BallerinaUtilGenerator(), new ArrayList<>()); + new BallerinaUtilGenerator(), new ArrayList<>(), false, false, false); Optional bodyNode = functionBodyGeneratorImp.getFunctionBodyNode(); content = content.trim().replaceAll("\n", "").replaceAll("\\s+", ""); String bodyNodeContent = bodyNode.get().toString().trim().replaceAll("\n", "") diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionSignatureNodeTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionSignatureNodeTests.java index d07dfe2b8..5a768575c 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionSignatureNodeTests.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionSignatureNodeTests.java @@ -61,7 +61,7 @@ public void getFunctionSignatureNodeTests() throws IOException, BallerinaOpenApi Operation operation = openAPI.getPaths().get("/products/{country}").getGet(); TypeHandler.createInstance(openAPI, false); RemoteFunctionSignatureGenerator functionSignatureGenerator = new RemoteFunctionSignatureGenerator(operation, - openAPI, "get"); + openAPI, "get", "products/{country}"); Optional signature = functionSignatureGenerator.generateFunctionSignature(); SeparatedNodeList parameters = signature.get().parameters(); Assert.assertFalse(parameters.isEmpty()); @@ -92,7 +92,7 @@ public void testFunctionSignatureNodeForXMLPayload() throws IOException, Balleri Operation operation = openAPI.getPaths().get("/pets").getPost(); TypeHandler.createInstance(openAPI, false); RemoteFunctionSignatureGenerator functionSignatureGenerator = new RemoteFunctionSignatureGenerator(operation, - openAPI, "post"); + openAPI, "post", "/pets"); FunctionSignatureNode signature = functionSignatureGenerator.generateFunctionSignature().get(); SeparatedNodeList parameters = signature.parameters(); Assert.assertFalse(parameters.isEmpty()); @@ -111,7 +111,7 @@ public void testFunctionSignatureNodeForJSONPayload() throws IOException, Baller Operation operation = openAPI.getPaths().get("/pets").getPost(); TypeHandler.createInstance(openAPI, false); RemoteFunctionSignatureGenerator functionSignatureGenerator = new RemoteFunctionSignatureGenerator(operation, - openAPI, "post"); + openAPI, "post", "/pets"); FunctionSignatureNode signature = functionSignatureGenerator.generateFunctionSignature().get(); SeparatedNodeList parameters = signature.parameters(); Assert.assertFalse(parameters.isEmpty()); @@ -131,7 +131,7 @@ public void testFunctionSignatureNodeForMultipartCustomHeader() throws IOExcepti Operation operation = openAPI.getPaths().get("/pets").getPost(); TypeHandler.createInstance(openAPI, false); RemoteFunctionSignatureGenerator functionSignatureGenerator = new RemoteFunctionSignatureGenerator(operation, - openAPI, "post"); + openAPI, "post", "/pets"); FunctionSignatureNode signature = functionSignatureGenerator.generateFunctionSignature().get(); SeparatedNodeList parameters = signature.parameters(); Assert.assertFalse(parameters.isEmpty()); @@ -157,7 +157,7 @@ public void getFunctionSignatureForNestedArrayResponse() throws IOException, Bal OpenAPI openAPI = getOpenAPI(RESDIR.resolve("swagger/response_nested_array.yaml")); TypeHandler.createInstance(openAPI, false); RemoteFunctionSignatureGenerator functionSignatureGenerator = new RemoteFunctionSignatureGenerator( - openAPI.getPaths().get("/timestags").getGet(), openAPI, "get"); + openAPI.getPaths().get("/timestags").getGet(), openAPI, "get", "/timestags"); FunctionSignatureNode signature = functionSignatureGenerator.generateFunctionSignature().get(); ReturnTypeDescriptorNode returnTypeNode = signature.returnTypeDesc().orElseThrow(); Assert.assertEquals(returnTypeNode.type().toString(), "string[][]|error"); @@ -168,7 +168,7 @@ public void getFunctionSignatureForStringArrayResponse() throws IOException, Bal OpenAPI openAPI = getOpenAPI(RESDIR.resolve("swagger/response_string_array.yaml")); TypeHandler.createInstance(openAPI, false); RemoteFunctionSignatureGenerator functionSignatureGenerator = new RemoteFunctionSignatureGenerator( - openAPI.getPaths().get("/timestags").getGet(), openAPI, "get"); + openAPI.getPaths().get("/timestags").getGet(), openAPI, "get", "/timestags"); FunctionSignatureNode signature = functionSignatureGenerator.generateFunctionSignature().get(); ReturnTypeDescriptorNode returnTypeNode = signature.returnTypeDesc().orElseThrow(); Assert.assertEquals(returnTypeNode.type().toString(), "string[]|error"); @@ -179,7 +179,7 @@ public void getFunctionSignatureForRequestBodyWithRef() throws IOException, Ball OpenAPI openAPI = getOpenAPI(RESDIR.resolve("swagger/request_body_with_ref.yaml")); TypeHandler.createInstance(openAPI, false); RemoteFunctionSignatureGenerator functionSignatureGenerator = new RemoteFunctionSignatureGenerator( - openAPI.getPaths().get("/pets").getPost(), openAPI, "post"); + openAPI.getPaths().get("/pets").getPost(), openAPI, "post", "/pets"); FunctionSignatureNode signature = functionSignatureGenerator.generateFunctionSignature().get(); SeparatedNodeList parameters = signature.parameters(); Assert.assertFalse(parameters.isEmpty()); @@ -193,7 +193,7 @@ public void getFunctionSignatureForUnsupportedRequests() throws IOException, Bal OpenAPI openAPI = getOpenAPI(RESDIR.resolve("swagger/pdf_payload.yaml")); TypeHandler.createInstance(openAPI, false); RemoteFunctionSignatureGenerator functionSignatureGenerator = new RemoteFunctionSignatureGenerator( - openAPI.getPaths().get("/pets").getPost(), openAPI, "post"); + openAPI.getPaths().get("/pets").getPost(), openAPI, "post", "/pets"); FunctionSignatureNode signature = functionSignatureGenerator.generateFunctionSignature().get(); SeparatedNodeList parameters = signature.parameters(); Assert.assertFalse(parameters.isEmpty()); @@ -210,7 +210,7 @@ public void testNestedArrayQueryParamGeneration() throws IOException, BallerinaO OpenAPI openAPI = getOpenAPI(RESDIR.resolve("swagger/invalid_array_query_params.yaml")); TypeHandler.createInstance(openAPI, false); RemoteFunctionSignatureGenerator functionSignatureGenerator = new RemoteFunctionSignatureGenerator( - openAPI.getPaths().get("/pets").getPost(), openAPI, "post"); + openAPI.getPaths().get("/pets").getPost(), openAPI, "post", "/pets"); FunctionSignatureNode signature = functionSignatureGenerator.generateFunctionSignature().get(); } @@ -223,7 +223,7 @@ public void testArrayQueryParamWithNoTypeGeneration() throws IOException, Baller OpenAPI openAPI = getOpenAPI(RESDIR.resolve("swagger/invalid_array_query_params.yaml")); TypeHandler.createInstance(openAPI, false); RemoteFunctionSignatureGenerator functionSignatureGenerator = new RemoteFunctionSignatureGenerator( - openAPI.getPaths().get("/dogs").getGet(), openAPI, "get"); + openAPI.getPaths().get("/dogs").getGet(), openAPI, "get", "/dogs"); FunctionSignatureNode signature = functionSignatureGenerator.generateFunctionSignature().get(); } @@ -233,10 +233,10 @@ public void testNumericFunctionSignatureJSONPayload() throws IOException, Baller OpenAPI openAPI = getOpenAPI(RESDIR.resolve("swagger/integer_request_payload.yaml")); TypeHandler.createInstance(openAPI, false); RemoteFunctionSignatureGenerator signature = new RemoteFunctionSignatureGenerator(openAPI.getPaths(). - get("/pets").getPost(), openAPI, "post"); + get("/pets").getPost(), openAPI, "post", "/pets"); FunctionSignatureNode petSignature = signature.generateFunctionSignature().get(); RemoteFunctionSignatureGenerator owSignature = new RemoteFunctionSignatureGenerator(openAPI.getPaths(). - get("/owners").getPost(), openAPI, "post"); + get("/owners").getPost(), openAPI, "post", "/owners"); FunctionSignatureNode ownerSignature = owSignature.generateFunctionSignature().get(); SeparatedNodeList parameters = petSignature.parameters(); Assert.assertFalse(parameters.isEmpty()); 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 a1f1d22c8..242b08fa0 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 @@ -54,7 +54,7 @@ import io.ballerina.openapi.core.generators.common.GeneratorUtils; import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; import io.ballerina.openapi.core.generators.common.model.Filter; -import io.ballerina.openapi.core.generators.document.ClientDocCommentGeneratorNew; +import io.ballerina.openapi.core.generators.document.ClientDocCommentGenerator; import io.ballerina.openapi.core.generators.document.DocCommentsGeneratorUtil; import io.ballerina.tools.text.TextDocument; import io.ballerina.tools.text.TextDocuments; @@ -198,7 +198,7 @@ public SyntaxTree generateSyntaxTree() throws BallerinaOpenApiException, ClientE SyntaxTree syntaxTree = SyntaxTree.from(textDocument); syntaxTree = syntaxTree.modifyWith(modulePartNode); //Add comments - ClientDocCommentGeneratorNew clientDocCommentGenerator = new ClientDocCommentGeneratorNew(syntaxTree, openAPI, + ClientDocCommentGenerator clientDocCommentGenerator = new ClientDocCommentGenerator(syntaxTree, openAPI, resourceMode); return clientDocCommentGenerator.updateSyntaxTreeWithDocComments(); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGeneratorWithStatusCodeBinding.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGeneratorWithStatusCodeBinding.java index 73db2e4c5..b35644091 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGeneratorWithStatusCodeBinding.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGeneratorWithStatusCodeBinding.java @@ -216,10 +216,12 @@ private Optional createImplFunction(String path, Token functionKeyWord = createToken(FUNCTION_KEYWORD); IdentifierToken functionName = createIdentifierToken(operation.getValue().getOperationId() + "Impl"); // Create function signature - ImplFunctionSignatureGeneratorNew signatureGenerator = new ImplFunctionSignatureGeneratorNew(clientExternFunction); + ImplFunctionSignatureGenerator signatureGenerator = new ImplFunctionSignatureGenerator(operation.getValue(), + openAPI, operation.getKey().toString().toLowerCase(Locale.ROOT), path, clientExternFunction); //Create function body FunctionBodyGeneratorImp functionBodyGenerator = new ImplFunctionBodyGenerator(path, operation, openAPI, - authConfigGeneratorImp, ballerinaUtilGenerator, imports); + authConfigGeneratorImp, ballerinaUtilGenerator, imports, signatureGenerator.hasHeaders(), + signatureGenerator.hasDefaultHeaders(), signatureGenerator.hasQueries()); FunctionBodyNode functionBodyNode; Optional functionBodyNodeResult = functionBodyGenerator.getFunctionBodyNode(); if (functionBodyNodeResult.isEmpty()) { diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java index ec751d6a2..09c2a3c29 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java @@ -34,7 +34,6 @@ import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.ReturnStatementNode; import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; -import io.ballerina.compiler.syntax.tree.SpecificFieldNode; import io.ballerina.compiler.syntax.tree.StatementNode; import io.ballerina.compiler.syntax.tree.TemplateExpressionNode; import io.ballerina.compiler.syntax.tree.Token; @@ -83,14 +82,11 @@ import static io.ballerina.compiler.syntax.tree.NodeFactory.createMappingConstructorExpressionNode; import static io.ballerina.compiler.syntax.tree.NodeFactory.createReturnStatementNode; import static io.ballerina.compiler.syntax.tree.NodeFactory.createSimpleNameReferenceNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createSpecificFieldNode; import static io.ballerina.compiler.syntax.tree.NodeFactory.createTemplateExpressionNode; import static io.ballerina.compiler.syntax.tree.NodeFactory.createTypedBindingPatternNode; import static io.ballerina.compiler.syntax.tree.NodeFactory.createVariableDeclarationNode; import static io.ballerina.compiler.syntax.tree.SyntaxKind.BACKTICK_TOKEN; import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_BRACE_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.COLON_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.COMMA_TOKEN; import static io.ballerina.compiler.syntax.tree.SyntaxKind.DOT_TOKEN; import static io.ballerina.compiler.syntax.tree.SyntaxKind.EQUAL_TOKEN; import static io.ballerina.compiler.syntax.tree.SyntaxKind.IF_KEYWORD; @@ -105,7 +101,7 @@ import static io.ballerina.openapi.core.generators.common.GeneratorConstants.ENCODING; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.EXECUTE; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEAD; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEADER; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEADERS; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEADER_VALUES; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HTTP_HEADERS; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HTTP_REQUEST; @@ -113,7 +109,7 @@ import static io.ballerina.openapi.core.generators.common.GeneratorConstants.PATCH; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.POST; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.PUT; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERY; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERIES; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERY_PARAM; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.REQUEST; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.RESOURCE_PATH; @@ -133,29 +129,35 @@ public class FunctionBodyGeneratorImp implements FunctionBodyGenerator { private List imports = new ArrayList<>(); - private boolean isHeader; private final String path; private final Map.Entry operation; private final OpenAPI openAPI; private final BallerinaUtilGenerator ballerinaUtilGenerator; private final AuthConfigGeneratorImp ballerinaAuthConfigGeneratorImp; + private final boolean hasHeaders; + private final boolean hasQueries; + private final boolean hasDefaultHeaders; + public List getImports() { return imports; } - public FunctionBodyGeneratorImp(String path, Map.Entry operation, OpenAPI openAPI, - AuthConfigGeneratorImp ballerinaAuthConfigGeneratorImp, + public FunctionBodyGeneratorImp(String path, Map.Entry operation, + OpenAPI openAPI, AuthConfigGeneratorImp ballerinaAuthConfigGeneratorImp, BallerinaUtilGenerator ballerinaUtilGenerator, - List imports) { + List imports, boolean hasHeaders, + boolean hasDefaultHeaders, boolean hasQueries) { this.path = path; this.operation = operation; - this.isHeader = false; this.openAPI = openAPI; this.ballerinaUtilGenerator = ballerinaUtilGenerator; this.ballerinaAuthConfigGeneratorImp = ballerinaAuthConfigGeneratorImp; this.imports = imports; + this.hasHeaders = hasHeaders; + this.hasQueries = hasQueries; + this.hasDefaultHeaders = hasDefaultHeaders; } /** @@ -168,31 +170,29 @@ public FunctionBodyGeneratorImp(String path, Map.Entry getFunctionBodyNode() { NodeList annotationNodes = createEmptyNodeList(); - isHeader = false; // Create statements List statementsList = new ArrayList<>(); //string path - common for every remote functions VariableDeclarationNode pathInt = getPathStatement(path, annotationNodes); statementsList.add(pathInt); try { + //Handle query parameter map + handleParameterSchemaInOperation(operation, statementsList); + + String method = operation.getKey().name().trim().toLowerCase(Locale.ENGLISH); + // Statement Generator for requestBody + if (operation.getValue().getRequestBody() != null) { + RequestBody requestBody = operation.getValue().getRequestBody(); + handleRequestBodyInOperation(statementsList, method, requestBody); + } else { + createCommonFunctionBodyStatements(statementsList, method); + } - //Handel query parameter map - handleParameterSchemaInOperation(operation, statementsList); - - String method = operation.getKey().name().trim().toLowerCase(Locale.ENGLISH); - // Statement Generator for requestBody - if (operation.getValue().getRequestBody() != null) { - RequestBody requestBody = operation.getValue().getRequestBody(); - handleRequestBodyInOperation(statementsList, method, requestBody); - } else { - createCommonFunctionBodyStatements(statementsList, method); - } - - //Create statements - NodeList statements = createNodeList(statementsList); - return Optional.of(createFunctionBodyBlockNode(createToken(OPEN_BRACE_TOKEN), null, - statements, - createToken(CLOSE_BRACE_TOKEN), null)); + //Create statements + NodeList statements = createNodeList(statementsList); + return Optional.of(createFunctionBodyBlockNode(createToken(OPEN_BRACE_TOKEN), null, + statements, + createToken(CLOSE_BRACE_TOKEN), null)); } catch (BallerinaOpenApiException e) { //todo diagnostic message return Optional.empty(); @@ -226,20 +226,6 @@ private void handleParameterSchemaInOperation(Map.Entry queryParameters = new ArrayList<>(); List headerParameters = new ArrayList<>(); - if (operation.getValue().getParameters() != null) { - List parameters = operation.getValue().getParameters(); - for (Parameter parameter : parameters) { - if (parameter.get$ref() != null) { - String[] splits = parameter.get$ref().split("/"); - parameter = openAPI.getComponents().getParameters().get(splits[splits.length - 1]); - } - if (parameter.getIn().trim().equals(QUERY)) { - queryParameters.add(parameter); - } else if (parameter.getIn().trim().equals(HEADER)) { - headerParameters.add(parameter); - } - } - } handleQueryParamsAndHeaders(queryParameters, headerParameters, statementsList, queryApiKeyNameList, headerApiKeyNameList); } @@ -255,20 +241,20 @@ public void handleQueryParamsAndHeaders(List queryParameters, List", - QUERY_PARAM, queryApiKeyNameList)); getUpdatedPathHandlingQueryParamEncoding(statementsList, queryParameters); } - if (!headerParameters.isEmpty() || !headerApiKeyNameList.isEmpty()) { - statementsList.add(getMapForParameters(headerParameters, "map", - HEADER_VALUES, headerApiKeyNameList)); - statementsList.add(GeneratorUtils.getSimpleExpressionStatementNode( - "map " + HTTP_HEADERS + " = getMapForHeaders(headerValues)")); - isHeader = true; + if (hasHeaders || !headerApiKeyNameList.isEmpty()) { + if (hasDefaultHeaders) { + statementsList.add(GeneratorUtils.getSimpleExpressionStatementNode( + "map " + HTTP_HEADERS + " = " + HEADERS)); + } else { + statementsList.add(GeneratorUtils.getSimpleExpressionStatementNode( + "map " + HTTP_HEADERS + " = getMapForHeaders(" + HEADERS + ")")); + } ballerinaUtilGenerator.setHeadersFound(true); } } @@ -279,54 +265,53 @@ public void handleQueryParamsAndHeaders(List queryParameters, List statementsList, List queryApiKeyNameList, - List queryParameters, List headerApiKeyNameList, - List headerParameters) throws BallerinaOpenApiException { + List queryParameters, List headerApiKeyNameList) + throws BallerinaOpenApiException { List ifBodyStatementsList = new ArrayList<>(); - if (!headerParameters.isEmpty() || !headerApiKeyNameList.isEmpty()) { - if (!headerParameters.isEmpty()) { - statementsList.add(getMapForParameters(headerParameters, "map", - HEADER_VALUES, new ArrayList<>())); - } else { - ExpressionStatementNode headerMapCreation = GeneratorUtils.getSimpleExpressionStatementNode( - "map " + HEADER_VALUES + " = {}"); - statementsList.add(headerMapCreation); + if (hasHeaders || !headerApiKeyNameList.isEmpty()) { + String defaultValue = "{}"; + if (hasHeaders) { + defaultValue = "{..." + HEADERS + "}"; } + ExpressionStatementNode headerMapCreation = GeneratorUtils.getSimpleExpressionStatementNode( + "map " + HEADER_VALUES + " = " + defaultValue); + statementsList.add(headerMapCreation); + if (!headerApiKeyNameList.isEmpty()) { // update headerValues Map within the if block // `headerValues["api-key"] = self.apiKeyConfig?.apiKey;` addApiKeysToMap(HEADER_VALUES, headerApiKeyNameList, ifBodyStatementsList); } - isHeader = true; ballerinaUtilGenerator.setHeadersFound(true); } - if (!queryParameters.isEmpty() || !queryApiKeyNameList.isEmpty()) { - ballerinaUtilGenerator.setQueryParamsFound(true); - if (!queryParameters.isEmpty()) { - statementsList.add(getMapForParameters(queryParameters, "map", - QUERY_PARAM, new ArrayList<>())); - } else { - ExpressionStatementNode queryParamMapCreation = GeneratorUtils.getSimpleExpressionStatementNode( - "map " + QUERY_PARAM + " = {}"); - statementsList.add(queryParamMapCreation); + if (hasQueries || !queryApiKeyNameList.isEmpty()) { + String defaultValue = "{}"; + if (hasQueries) { + defaultValue = "{..." + QUERIES + "}"; } + ExpressionStatementNode queryParamMapCreation = GeneratorUtils.getSimpleExpressionStatementNode( + "map " + QUERY_PARAM + " = " + defaultValue); + statementsList.add(queryParamMapCreation); + if (!queryApiKeyNameList.isEmpty()) { // update queryParam Map within the if block // `queryParam["api-key"] = self.apiKeyConfig?.apiKey;` addApiKeysToMap(QUERY_PARAM, queryApiKeyNameList, ifBodyStatementsList); } + ballerinaUtilGenerator.setQueryParamsFound(true); } generateIfBlockToAddApiKeysToMaps(statementsList, ifBodyStatementsList); - if (!queryParameters.isEmpty() || !queryApiKeyNameList.isEmpty()) { + if (hasQueries || !queryApiKeyNameList.isEmpty()) { getUpdatedPathHandlingQueryParamEncoding(statementsList, queryParameters); } - if (!headerParameters.isEmpty() || !headerApiKeyNameList.isEmpty()) { + if (hasHeaders || !headerApiKeyNameList.isEmpty()) { statementsList.add(GeneratorUtils.getSimpleExpressionStatementNode( "map " + HTTP_HEADERS + " = getMapForHeaders(headerValues)")); } @@ -368,12 +353,12 @@ private void getUpdatedPathHandlingQueryParamEncoding(List statem if (queryParamEncodingMap != null) { statementsList.add(queryParamEncodingMap); ExpressionStatementNode updatedPath = GeneratorUtils.getSimpleExpressionStatementNode( - RESOURCE_PATH + " = " + RESOURCE_PATH + " + check getPathForQueryParam(queryParam, " + + RESOURCE_PATH + " = " + RESOURCE_PATH + " + check getPathForQueryParam(" + QUERIES + ", " + "queryParamEncoding)"); statementsList.add(updatedPath); } else { ExpressionStatementNode updatedPath = GeneratorUtils.getSimpleExpressionStatementNode( - RESOURCE_PATH + " = " + RESOURCE_PATH + " + check getPathForQueryParam(queryParam)"); + RESOURCE_PATH + " = " + RESOURCE_PATH + " + check getPathForQueryParam(" + QUERIES + ")"); statementsList.add(updatedPath); } } @@ -493,7 +478,7 @@ private void handleRequestBodyInOperation(List statementsList, St Iterator> iterator = entries.iterator(); //Currently align with first content of the requestBody while (iterator.hasNext()) { - createRequestBodyStatements(isHeader, statementsList, method, iterator); + createRequestBodyStatements(statementsList, method, iterator); break; } } else if (requestBody.get$ref() != null) { @@ -504,7 +489,7 @@ private void handleRequestBodyInOperation(List statementsList, St Iterator> iterator = entries.iterator(); //Currently align with first content of the requestBody while (iterator.hasNext()) { - createRequestBodyStatements(isHeader, statementsList, method, iterator); + createRequestBodyStatements(statementsList, method, iterator); break; } } @@ -520,7 +505,7 @@ private void createCommonFunctionBodyStatements(List statementsLi // This condition for several methods. boolean isEntityBodyMethods = method.equals(POST) || method.equals(PUT) || method.equals(PATCH) || method.equals(EXECUTE); - if (isHeader) { + if (hasHeaders) { if (isEntityBodyMethods) { ExpressionStatementNode requestStatementNode = GeneratorUtils.getSimpleExpressionStatementNode( "http:Request request = new"); @@ -635,12 +620,11 @@ public String generatePathWithPathParameter(String path) { * self.clientEp->put(path, request); * * - * @param isHeader - Boolean value for header availability. * @param statementsList - StatementNode list in body node * @param method - Operation method name. * @param iterator - RequestBody media type */ - private void createRequestBodyStatements(boolean isHeader, List statementsList, String method, + private void createRequestBodyStatements(List statementsList, String method, Iterator> iterator) throws BallerinaOpenApiException { @@ -662,7 +646,7 @@ private void createRequestBodyStatements(boolean isHeader, List s } // POST, PUT, PATCH, DELETE, EXECUTE String requestStatement = getClientCallWithRequest().formatted(method, RESOURCE_PATH); - if (isHeader) { + if (hasHeaders) { if (method.equals(POST) || method.equals(PUT) || method.equals(PATCH) || method.equals(DELETE) || method.equals(EXECUTE)) { requestStatement = getClientCallWithRequestAndHeaders().formatted(method, RESOURCE_PATH, @@ -703,64 +687,6 @@ private void genStatementsForRequestMediaType(List statementsList mimeType.setPayload(statementsList, mediaTypeEntry); } - /** - * Generate map variable for query parameters and headers. - */ - private VariableDeclarationNode getMapForParameters(List parameters, String mapDataType, - String mapName, List apiKeyNames) { - List filedOfMap = new ArrayList<>(); - BuiltinSimpleNameReferenceNode mapType = createBuiltinSimpleNameReferenceNode(null, - createIdentifierToken(mapDataType)); - CaptureBindingPatternNode bindingPattern = createCaptureBindingPatternNode( - createIdentifierToken(mapName)); - TypedBindingPatternNode bindingPatternNode = createTypedBindingPatternNode(mapType, bindingPattern); - - for (Parameter parameter : parameters) { - // Initializer - IdentifierToken fieldName = createIdentifierToken('"' + (parameter.getName().trim()) + '"'); - Token colon = createToken(COLON_TOKEN); - SimpleNameReferenceNode valueExpr = createSimpleNameReferenceNode( - createIdentifierToken(escapeIdentifier(parameter.getName().trim()))); - SpecificFieldNode specificFieldNode = createSpecificFieldNode(null, - fieldName, colon, valueExpr); - filedOfMap.add(specificFieldNode); - filedOfMap.add(createToken(COMMA_TOKEN)); - } - - if (!apiKeyNames.isEmpty()) { - for (String apiKey : apiKeyNames) { - IdentifierToken fieldName = createIdentifierToken('"' + apiKey.trim() + '"'); - Token colon = createToken(COLON_TOKEN); - IdentifierToken apiKeyConfigIdentifierToken = createIdentifierToken(API_KEY_CONFIG_PARAM); - if (ballerinaAuthConfigGeneratorImp.isHttpOROAuth() && ballerinaAuthConfigGeneratorImp.isApiKey()) { - apiKeyConfigIdentifierToken = createIdentifierToken(API_KEY_CONFIG_PARAM + - QUESTION_MARK_TOKEN.stringValue()); - } - SimpleNameReferenceNode apiKeyConfigParamNode = createSimpleNameReferenceNode( - apiKeyConfigIdentifierToken); - FieldAccessExpressionNode fieldExpr = createFieldAccessExpressionNode( - createSimpleNameReferenceNode(createIdentifierToken(SELF)), createToken(DOT_TOKEN), - apiKeyConfigParamNode); - SimpleNameReferenceNode valueExpr = createSimpleNameReferenceNode(createIdentifierToken( - getValidName(apiKey, false))); - SpecificFieldNode specificFieldNode; - ExpressionNode apiKeyExpr = createFieldAccessExpressionNode( - fieldExpr, createToken(DOT_TOKEN), valueExpr); - specificFieldNode = createSpecificFieldNode(null, fieldName, colon, apiKeyExpr); - filedOfMap.add(specificFieldNode); - filedOfMap.add(createToken(COMMA_TOKEN)); - } - } - - filedOfMap.remove(filedOfMap.size() - 1); - MappingConstructorExpressionNode initialize = createMappingConstructorExpressionNode( - createToken(OPEN_BRACE_TOKEN), createSeparatedNodeList(filedOfMap), - createToken(CLOSE_BRACE_TOKEN)); - return createVariableDeclarationNode(createEmptyNodeList(), - null, bindingPatternNode, createToken(EQUAL_TOKEN), initialize, - createToken(SEMICOLON_TOKEN)); - } - @Override public List getDiagnostics() { return new ArrayList<>(); diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImpNew.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImpNew.java deleted file mode 100644 index ac8e71a64..000000000 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImpNew.java +++ /dev/null @@ -1,694 +0,0 @@ -/* - * Copyright (c) 2022, 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 - * 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.core.generators.client; - -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.BlockStatementNode; -import io.ballerina.compiler.syntax.tree.BuiltinSimpleNameReferenceNode; -import io.ballerina.compiler.syntax.tree.CaptureBindingPatternNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.ExpressionStatementNode; -import io.ballerina.compiler.syntax.tree.FieldAccessExpressionNode; -import io.ballerina.compiler.syntax.tree.FunctionBodyNode; -import io.ballerina.compiler.syntax.tree.IdentifierToken; -import io.ballerina.compiler.syntax.tree.IfElseStatementNode; -import io.ballerina.compiler.syntax.tree.ImportDeclarationNode; -import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; -import io.ballerina.compiler.syntax.tree.Node; -import io.ballerina.compiler.syntax.tree.NodeList; -import io.ballerina.compiler.syntax.tree.ReturnStatementNode; -import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; -import io.ballerina.compiler.syntax.tree.StatementNode; -import io.ballerina.compiler.syntax.tree.TemplateExpressionNode; -import io.ballerina.compiler.syntax.tree.Token; -import io.ballerina.compiler.syntax.tree.TypedBindingPatternNode; -import io.ballerina.compiler.syntax.tree.VariableDeclarationNode; -import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; -import io.ballerina.openapi.core.generators.client.mime.MimeType; -import io.ballerina.openapi.core.generators.common.GeneratorUtils; -import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.PathItem; -import io.swagger.v3.oas.models.media.Content; -import io.swagger.v3.oas.models.media.MediaType; -import io.swagger.v3.oas.models.media.Schema; -import io.swagger.v3.oas.models.parameters.Parameter; -import io.swagger.v3.oas.models.parameters.RequestBody; -import io.swagger.v3.oas.models.security.SecurityRequirement; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyMinutiaeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createLiteralValueToken; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createNodeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createSeparatedNodeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createAssignmentStatementNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createBinaryExpressionNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createBlockStatementNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createBuiltinSimpleNameReferenceNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createCaptureBindingPatternNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createFieldAccessExpressionNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createFunctionBodyBlockNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createIfElseStatementNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createMappingConstructorExpressionNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createReturnStatementNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createSimpleNameReferenceNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createTemplateExpressionNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createTypedBindingPatternNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createVariableDeclarationNode; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.BACKTICK_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_BRACE_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.DOT_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.EQUAL_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.IF_KEYWORD; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.IS_KEYWORD; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_BRACE_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.QUESTION_MARK_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.SEMICOLON_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.STRING_KEYWORD; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.API_KEYS_CONFIG; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.API_KEY_CONFIG_PARAM; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.DELETE; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.ENCODING; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.EXECUTE; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEAD; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEADERS; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEADER_VALUES; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HTTP_HEADERS; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HTTP_REQUEST; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.NEW; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.PATCH; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.POST; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.PUT; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERIES; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERY_PARAM; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.REQUEST; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.RESOURCE_PATH; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.RETURN; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.SELF; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.escapeIdentifier; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.extractReferenceType; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.getOpenAPIType; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.getValidName; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.isComposedSchema; - -/** - * This Util class uses for generating remote function body {@link FunctionBodyNode}. - * - * @since 1.3.0 - */ -public class FunctionBodyGeneratorImpNew implements FunctionBodyGenerator { - - private List imports = new ArrayList<>(); - private final String path; - private final Map.Entry operation; - private final OpenAPI openAPI; - private final BallerinaUtilGenerator ballerinaUtilGenerator; - private final AuthConfigGeneratorImp ballerinaAuthConfigGeneratorImp; - - private final boolean hasHeaders; - private final boolean hasQueries; - private final boolean hasDefaultHeaders; - - public List getImports() { - return imports; - } - - - public FunctionBodyGeneratorImpNew(String path, Map.Entry operation, - OpenAPI openAPI, AuthConfigGeneratorImp ballerinaAuthConfigGeneratorImp, - BallerinaUtilGenerator ballerinaUtilGenerator, - List imports, boolean hasHeaders, - boolean hasDefaultHeaders, boolean hasQueries) { - this.path = path; - this.operation = operation; - this.openAPI = openAPI; - this.ballerinaUtilGenerator = ballerinaUtilGenerator; - this.ballerinaAuthConfigGeneratorImp = ballerinaAuthConfigGeneratorImp; - this.imports = imports; - this.hasHeaders = hasHeaders; - this.hasQueries = hasQueries; - this.hasDefaultHeaders = hasDefaultHeaders; - } - - /** - * Generate function body node for the remote function. - * - * @return - {@link FunctionBodyNode} - * @throws BallerinaOpenApiException - throws exception if generating FunctionBodyNode fails. - */ - @Override - public Optional getFunctionBodyNode() { - - NodeList annotationNodes = createEmptyNodeList(); - // Create statements - List statementsList = new ArrayList<>(); - //string path - common for every remote functions - VariableDeclarationNode pathInt = getPathStatement(path, annotationNodes); - statementsList.add(pathInt); - try { - //Handle query parameter map - handleParameterSchemaInOperation(operation, statementsList); - - String method = operation.getKey().name().trim().toLowerCase(Locale.ENGLISH); - // Statement Generator for requestBody - if (operation.getValue().getRequestBody() != null) { - RequestBody requestBody = operation.getValue().getRequestBody(); - handleRequestBodyInOperation(statementsList, method, requestBody); - } else { - createCommonFunctionBodyStatements(statementsList, method); - } - - //Create statements - NodeList statements = createNodeList(statementsList); - return Optional.of(createFunctionBodyBlockNode(createToken(OPEN_BRACE_TOKEN), null, - statements, - createToken(CLOSE_BRACE_TOKEN), null)); - } catch (BallerinaOpenApiException e) { - //todo diagnostic message - return Optional.empty(); - } - } - - /** - * Generate statements for query parameters and headers. - */ - private void handleParameterSchemaInOperation(Map.Entry operation, - List statementsList) throws - BallerinaOpenApiException { - - List queryApiKeyNameList = new ArrayList<>(); - List headerApiKeyNameList = new ArrayList<>(); - - Set securitySchemesAvailable = getSecurityRequirementForOperation(operation.getValue()); - - if (securitySchemesAvailable.size() > 0) { - Map queryApiKeyMap = ballerinaAuthConfigGeneratorImp.getQueryApiKeyNameList(); - Map headerApiKeyMap = ballerinaAuthConfigGeneratorImp.getHeaderApiKeyNameList(); - for (String schemaName : securitySchemesAvailable) { - if (queryApiKeyMap.containsKey(schemaName)) { - queryApiKeyNameList.add(queryApiKeyMap.get(schemaName)); - } else if (headerApiKeyMap.containsKey(schemaName)) { - headerApiKeyNameList.add(headerApiKeyMap.get(schemaName)); - } - } - } - - List queryParameters = new ArrayList<>(); - List headerParameters = new ArrayList<>(); - - handleQueryParamsAndHeaders(queryParameters, headerParameters, statementsList, queryApiKeyNameList, - headerApiKeyNameList); - } - - /** - * Handle query parameters and headers within a remote function. - */ - public void handleQueryParamsAndHeaders(List queryParameters, List headerParameters, - List statementsList, List queryApiKeyNameList, - List headerApiKeyNameList) throws BallerinaOpenApiException { - - boolean combinationOfApiKeyAndHTTPOAuth = ballerinaAuthConfigGeneratorImp.isHttpOROAuth() && - ballerinaAuthConfigGeneratorImp.isApiKey(); - if (combinationOfApiKeyAndHTTPOAuth) { - addUpdatedPathAndHeaders(statementsList, queryApiKeyNameList, queryParameters, - headerApiKeyNameList); - } else { - if (hasQueries || !queryApiKeyNameList.isEmpty()) { - ballerinaUtilGenerator.setQueryParamsFound(true); - getUpdatedPathHandlingQueryParamEncoding(statementsList, queryParameters); - } - if (hasHeaders || !headerApiKeyNameList.isEmpty()) { - if (hasDefaultHeaders) { - statementsList.add(GeneratorUtils.getSimpleExpressionStatementNode( - "map " + HTTP_HEADERS + " = " + HEADERS)); - } else { - statementsList.add(GeneratorUtils.getSimpleExpressionStatementNode( - "map " + HTTP_HEADERS + " = getMapForHeaders(" + HEADERS + ")")); - } - ballerinaUtilGenerator.setHeadersFound(true); - } - } - } - - /** - * Generate statements for query parameters and headers when a client supports both ApiKey and HTTPOrOAuth - * authentication. - */ - private void addUpdatedPathAndHeaders(List statementsList, List queryApiKeyNameList, - List queryParameters, List headerApiKeyNameList) - throws BallerinaOpenApiException { - - List ifBodyStatementsList = new ArrayList<>(); - - if (hasHeaders || !headerApiKeyNameList.isEmpty()) { - String defaultValue = "{}"; - if (hasHeaders) { - defaultValue = "{..." + HEADERS + "}"; - } - - ExpressionStatementNode headerMapCreation = GeneratorUtils.getSimpleExpressionStatementNode( - "map " + HEADER_VALUES + " = " + defaultValue); - statementsList.add(headerMapCreation); - - if (!headerApiKeyNameList.isEmpty()) { - // update headerValues Map within the if block - // `headerValues["api-key"] = self.apiKeyConfig?.apiKey;` - addApiKeysToMap(HEADER_VALUES, headerApiKeyNameList, ifBodyStatementsList); - } - ballerinaUtilGenerator.setHeadersFound(true); - } - - if (hasQueries || !queryApiKeyNameList.isEmpty()) { - String defaultValue = "{}"; - if (hasQueries) { - defaultValue = "{..." + QUERIES + "}"; - } - - ExpressionStatementNode queryParamMapCreation = GeneratorUtils.getSimpleExpressionStatementNode( - "map " + QUERY_PARAM + " = " + defaultValue); - statementsList.add(queryParamMapCreation); - - if (!queryApiKeyNameList.isEmpty()) { - // update queryParam Map within the if block - // `queryParam["api-key"] = self.apiKeyConfig?.apiKey;` - addApiKeysToMap(QUERY_PARAM, queryApiKeyNameList, ifBodyStatementsList); - } - ballerinaUtilGenerator.setQueryParamsFound(true); - } - - generateIfBlockToAddApiKeysToMaps(statementsList, ifBodyStatementsList); - - if (hasQueries || !queryApiKeyNameList.isEmpty()) { - getUpdatedPathHandlingQueryParamEncoding(statementsList, queryParameters); - } - if (hasHeaders || !headerApiKeyNameList.isEmpty()) { - statementsList.add(GeneratorUtils.getSimpleExpressionStatementNode( - "map " + HTTP_HEADERS + " = getMapForHeaders(headerValues)")); - } - } - - /** - * Add apiKeys to a given map (queryParam or headerValues). - *

- * `queryParam["api-key"] = self.apiKeyConfig?.apiKey;` - * `headerValues["api-key"] = self.apiKeyConfig?.apiKey;` - */ - private void addApiKeysToMap(String mapName, List apiKeyNames, List statementNodeList) { - - if (!apiKeyNames.isEmpty()) { - for (String apiKey : apiKeyNames) { - IdentifierToken fieldName = createIdentifierToken(mapName + "[" + '"' + apiKey.trim() + '"' + "]"); - Token equal = createToken(EQUAL_TOKEN); - FieldAccessExpressionNode fieldExpr = createFieldAccessExpressionNode( - createSimpleNameReferenceNode(createIdentifierToken(SELF)), createToken(DOT_TOKEN), - createSimpleNameReferenceNode(createIdentifierToken(API_KEY_CONFIG_PARAM + - QUESTION_MARK_TOKEN.stringValue()))); - SimpleNameReferenceNode valueExpr = createSimpleNameReferenceNode(createIdentifierToken( - getValidName(getValidName(apiKey, false), false))); - ExpressionNode apiKeyExpr = createFieldAccessExpressionNode( - fieldExpr, createToken(DOT_TOKEN), valueExpr); - statementNodeList.add(createAssignmentStatementNode(fieldName, equal, apiKeyExpr, createToken( - SEMICOLON_TOKEN))); - } - } - } - - /** - * Get updated path considering queryParamEncodingMap. - */ - private void getUpdatedPathHandlingQueryParamEncoding(List statementsList, List - queryParameters) throws BallerinaOpenApiException { - - VariableDeclarationNode queryParamEncodingMap = getQueryParameterEncodingMap(queryParameters); - if (queryParamEncodingMap != null) { - statementsList.add(queryParamEncodingMap); - ExpressionStatementNode updatedPath = GeneratorUtils.getSimpleExpressionStatementNode( - RESOURCE_PATH + " = " + RESOURCE_PATH + " + check getPathForQueryParam(" + QUERIES + ", " + - "queryParamEncoding)"); - statementsList.add(updatedPath); - } else { - ExpressionStatementNode updatedPath = GeneratorUtils.getSimpleExpressionStatementNode( - RESOURCE_PATH + " = " + RESOURCE_PATH + " + check getPathForQueryParam(" + QUERIES + ")"); - statementsList.add(updatedPath); - } - } - - /** - * Generate if block when a client supports both ApiKey and HTTPOrOAuth authentication. - * - *

-     * if self.apiKeyConfig is ApiKeysConfig {
-     *      --- given statements ---
-     * }
-     * 
- */ - private void generateIfBlockToAddApiKeysToMaps(List statementsList, - List ifBodyStatementsList) { - - if (!ifBodyStatementsList.isEmpty()) { - NodeList ifBodyStatementsNodeList = createNodeList(ifBodyStatementsList); - BlockStatementNode ifBody = createBlockStatementNode(createToken(OPEN_BRACE_TOKEN), - ifBodyStatementsNodeList, createToken(CLOSE_BRACE_TOKEN)); - - // Create expression `self.apiKeyConfig is ApiKeysConfig` - ExpressionNode condition = createBinaryExpressionNode(null, createIdentifierToken(SELF + - DOT_TOKEN.stringValue() + API_KEY_CONFIG_PARAM), - createToken(IS_KEYWORD), - createIdentifierToken(API_KEYS_CONFIG)); - IfElseStatementNode ifBlock = createIfElseStatementNode(createToken(IF_KEYWORD), condition, ifBody, null); - statementsList.add(ifBlock); - } - } - - /** - * Generate VariableDeclarationNode for query parameter encoding map which includes the data related serialization - * mechanism that needs to be used with object or array type parameters. Parameters in primitive types will not be - * included to the map even when the serialization mechanisms are specified. These data is given in the `style` and - * `explode` sections of the OpenAPI definition. Style defines how multiple values are delimited and explode - * specifies whether arrays and objects should generate separate parameters - *

- * --ex: {@code map queryParamEncoding = {"expand": ["deepObject", true]};} - * - * @param queryParameters List of query parameters defined in a particular function - * @return {@link VariableDeclarationNode} - * @throws BallerinaOpenApiException When invalid referenced schema is given. - */ - private VariableDeclarationNode getQueryParameterEncodingMap(List queryParameters) - throws BallerinaOpenApiException { - - List filedOfMap = new ArrayList<>(); - BuiltinSimpleNameReferenceNode mapType = createBuiltinSimpleNameReferenceNode(null, - createIdentifierToken("map<" + ENCODING + ">")); - CaptureBindingPatternNode bindingPattern = createCaptureBindingPatternNode( - createIdentifierToken("queryParamEncoding")); - TypedBindingPatternNode bindingPatternNode = createTypedBindingPatternNode(mapType, bindingPattern); - - for (Parameter parameter : queryParameters) { - Schema paramSchema = parameter.getSchema(); - if (paramSchema != null && paramSchema.get$ref() != null) { - paramSchema = openAPI.getComponents().getSchemas().get( - getValidName(extractReferenceType(paramSchema.get$ref()), true)); - } - if (paramSchema != null && (paramSchema.getProperties() != null || - (getOpenAPIType(paramSchema) != null && getOpenAPIType(paramSchema).equals("array")) || - (isComposedSchema(paramSchema)))) { - if (parameter.getStyle() != null || parameter.getExplode() != null) { - GeneratorUtils.createEncodingMap(filedOfMap, parameter.getStyle().toString(), - parameter.getExplode(), parameter.getName().trim()); - } - } - } - if (!filedOfMap.isEmpty()) { - filedOfMap.remove(filedOfMap.size() - 1); - MappingConstructorExpressionNode initialize = createMappingConstructorExpressionNode( - createToken(OPEN_BRACE_TOKEN), createSeparatedNodeList(filedOfMap), - createToken(CLOSE_BRACE_TOKEN)); - return createVariableDeclarationNode(createEmptyNodeList(), - null, bindingPatternNode, createToken(EQUAL_TOKEN), initialize, - createToken(SEMICOLON_TOKEN)); - } - return null; - - } - - /** - * Provides the list of security schemes available for the given operation. - * - * @param operation Current operation - * @return Security schemes that can be used to authorize the given operation - */ - private Set getSecurityRequirementForOperation(Operation operation) { - - Set securitySchemasAvailable = new LinkedHashSet<>(); - List securityRequirements = new ArrayList<>(); - if (operation.getSecurity() != null) { - securityRequirements = operation.getSecurity(); - } else if (openAPI.getSecurity() != null) { - securityRequirements = openAPI.getSecurity(); - } - - if (securityRequirements.size() > 0) { - for (SecurityRequirement requirement : securityRequirements) { - securitySchemasAvailable.addAll(requirement.keySet()); - } - } - return securitySchemasAvailable; - } - - /** - * Handle request body in operation. - */ - private void handleRequestBodyInOperation(List statementsList, String method, - RequestBody requestBody) - throws BallerinaOpenApiException { - - if (requestBody.getContent() != null) { - Content rbContent = requestBody.getContent(); - Set> entries = rbContent.entrySet(); - Iterator> iterator = entries.iterator(); - //Currently align with first content of the requestBody - while (iterator.hasNext()) { - createRequestBodyStatements(statementsList, method, iterator); - break; - } - } else if (requestBody.get$ref() != null) { - RequestBody requestBodySchema = - openAPI.getComponents().getRequestBodies().get(extractReferenceType(requestBody.get$ref())); - Content rbContent = requestBodySchema.getContent(); - Set> entries = rbContent.entrySet(); - Iterator> iterator = entries.iterator(); - //Currently align with first content of the requestBody - while (iterator.hasNext()) { - createRequestBodyStatements(statementsList, method, iterator); - break; - } - } - } - - /** - * Generate common statements in function bosy. - */ - private void createCommonFunctionBodyStatements(List statementsList, String method) { - - String clientCallStatement; - - // This condition for several methods. - boolean isEntityBodyMethods = method.equals(POST) || method.equals(PUT) || method.equals(PATCH) - || method.equals(EXECUTE); - if (hasHeaders) { - if (isEntityBodyMethods) { - ExpressionStatementNode requestStatementNode = GeneratorUtils.getSimpleExpressionStatementNode( - "http:Request request = new"); - statementsList.add(requestStatementNode); - clientCallStatement = getClientCallWithRequestAndHeaders().formatted(method, RESOURCE_PATH, - HTTP_HEADERS); - - } else if (method.equals(DELETE)) { - clientCallStatement = getClientCallWithHeadersParam().formatted(method, RESOURCE_PATH, - HTTP_HEADERS); - } else if (method.equals(HEAD)) { - clientCallStatement = getClientCallWithHeaders().formatted(method, RESOURCE_PATH, - HTTP_HEADERS); - } else { - clientCallStatement = getClientCallWithHeaders().formatted(method, RESOURCE_PATH, - HTTP_HEADERS); - } - } else if (method.equals(DELETE)) { - clientCallStatement = getSimpleClientCall().formatted(method, RESOURCE_PATH); - } else if (isEntityBodyMethods) { - ExpressionStatementNode requestStatementNode = GeneratorUtils.getSimpleExpressionStatementNode( - "http:Request request = new"); - statementsList.add(requestStatementNode); - clientCallStatement = getClientCallWithRequest().formatted(method, RESOURCE_PATH); - } else { - clientCallStatement = getSimpleClientCall().formatted(method, RESOURCE_PATH); - } - //Return Variable - generateReturnStatement(statementsList, clientCallStatement); - } - - protected String getClientCallWithHeadersParam() { - return "self.clientEp->%s(%s, headers = %s)"; - } - - protected String getClientCallWithRequestAndHeaders() { - return "self.clientEp->%s(%s, request, %s)"; - } - - protected String getClientCallWithHeaders() { - return "self.clientEp->%s(%s, %s)"; - } - - protected String getClientCallWithRequest() { - return "self.clientEp->%s(%s, request)"; - } - - protected String getSimpleClientCall() { - return "self.clientEp->%s(%s)"; - } - - /** - * This method use to generate Path statement inside the function body node. - *

- * ex: - *

 string  path = string `/weather`; 
- * - * @param path - Given path - * @param annotationNodes - Node list for path implementation - * @return - VariableDeclarationNode for path statement. - */ - private VariableDeclarationNode getPathStatement(String path, NodeList annotationNodes) { - - TypedBindingPatternNode typedBindingPatternNode = createTypedBindingPatternNode(createSimpleNameReferenceNode( - createToken(STRING_KEYWORD)), createCaptureBindingPatternNode( - createIdentifierToken(RESOURCE_PATH))); - // Create initializer - // Content should decide with /pet and /pet/{pet} - path = generatePathWithPathParameter(path); - //String path generator - NodeList content = createNodeList(createLiteralValueToken(null, path, createEmptyMinutiaeList(), - createEmptyMinutiaeList())); - TemplateExpressionNode initializer = createTemplateExpressionNode(null, createToken(STRING_KEYWORD), - createToken(BACKTICK_TOKEN), content, createToken(BACKTICK_TOKEN)); - return createVariableDeclarationNode(annotationNodes, null, - typedBindingPatternNode, createToken(EQUAL_TOKEN), initializer, createToken(SEMICOLON_TOKEN)); - } - - /** - * This method is to used for generating path when it has path parameters. - * - * @param path - yaml contract path - * @return string of path - */ - public String generatePathWithPathParameter(String path) { - - if (path.contains("{")) { - String refinedPath = path; - Pattern p = Pattern.compile("\\{[^}]*}"); - Matcher m = p.matcher(path); - while (m.find()) { - String pathVariable = path.substring(m.start(), m.end()); - if (pathVariable.startsWith("{") && pathVariable.endsWith("}")) { - String d = pathVariable.replace("{", "").replace("}", ""); - String replaceVariable = "{getEncodedUri(" + escapeIdentifier(d) + ")}"; - refinedPath = refinedPath.replace(pathVariable, replaceVariable); - } - } - path = refinedPath.replaceAll("[{]", "\\${"); - } - ballerinaUtilGenerator.setPathParametersFound(true); - return path; - } - - /** - * This function for creating requestBody statements. - * -- ex: Request body with json payload. - *
-     *    http:Request request = new;
-     *    json jsonBody = payload.toJson();
-     *    request.setPayload(jsonBody, "application/json");
-     *    self.clientEp->put(path, request);
-     * 
- * - * @param statementsList - StatementNode list in body node - * @param method - Operation method name. - * @param iterator - RequestBody media type - */ - private void createRequestBodyStatements(List statementsList, String method, - Iterator> iterator) - throws BallerinaOpenApiException { - - //Create Request statement - Map.Entry mediaTypeEntry = iterator.next(); - if (GeneratorUtils.isSupportedMediaType(mediaTypeEntry)) { - VariableDeclarationNode requestVariable = GeneratorUtils.getSimpleStatement(HTTP_REQUEST, - REQUEST, NEW); - statementsList.add(requestVariable); - } - if (mediaTypeEntry.getValue() != null && GeneratorUtils.isSupportedMediaType(mediaTypeEntry)) { - genStatementsForRequestMediaType(statementsList, mediaTypeEntry); - // TODO:Fill with other mime type - } else { - // Add default value comment - ExpressionStatementNode expressionStatementNode = GeneratorUtils.getSimpleExpressionStatementNode( - "// TODO: Update the request as needed"); - statementsList.add(expressionStatementNode); - } - // POST, PUT, PATCH, DELETE, EXECUTE - String requestStatement = getClientCallWithRequest().formatted(method, RESOURCE_PATH); - if (hasHeaders) { - if (method.equals(POST) || method.equals(PUT) || method.equals(PATCH) || method.equals(DELETE) - || method.equals(EXECUTE)) { - requestStatement = getClientCallWithRequestAndHeaders().formatted(method, RESOURCE_PATH, - HTTP_HEADERS); - generateReturnStatement(statementsList, requestStatement); - } - } else { - generateReturnStatement(statementsList, requestStatement); - } - } - - /** - * This function is used for generating return statement. - * - * @param statementsList - Previous statements list - * @param returnStatement - Request statement - */ - private static void generateReturnStatement(List statementsList, String returnStatement) { - Token returnKeyWord = createIdentifierToken(RETURN); - SimpleNameReferenceNode returns; - returns = createSimpleNameReferenceNode(createIdentifierToken(returnStatement)); - ReturnStatementNode returnStatementNode = createReturnStatementNode(returnKeyWord, returns, - createToken(SEMICOLON_TOKEN)); - statementsList.add(returnStatementNode); - } - - /** - * This function is used for generating function body statements according to the given request body media type. - * - * @param statementsList - Previous statements list - * @param mediaTypeEntry - Media type entry - */ - private void genStatementsForRequestMediaType(List statementsList, - Map.Entry mediaTypeEntry) - throws BallerinaOpenApiException { - MimeFactory factory = new MimeFactory(); - MimeType mimeType = factory.getMimeType(mediaTypeEntry, ballerinaUtilGenerator, imports); - mimeType.setPayload(statementsList, mediaTypeEntry); - } - - @Override - public List getDiagnostics() { - return new ArrayList<>(); - } -} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionBodyGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionBodyGenerator.java index 947541068..f48d4b12f 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionBodyGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionBodyGenerator.java @@ -35,8 +35,10 @@ public class ImplFunctionBodyGenerator extends FunctionBodyGeneratorImp { public ImplFunctionBodyGenerator(String path, Map.Entry operation, OpenAPI openAPI, AuthConfigGeneratorImp ballerinaAuthConfigGeneratorImp, BallerinaUtilGenerator ballerinaUtilGenerator, - List imports) { - super(path, operation, openAPI, ballerinaAuthConfigGeneratorImp, ballerinaUtilGenerator, imports); + List imports, boolean hasHeaders, + boolean hasDefaultHeaders, boolean hasQueries) { + super(path, operation, openAPI, ballerinaAuthConfigGeneratorImp, ballerinaUtilGenerator, imports, + hasHeaders, hasDefaultHeaders, hasQueries); } @Override diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGenerator.java index 0785e32a6..c04a4b038 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGenerator.java @@ -17,37 +17,150 @@ */ package io.ballerina.openapi.core.generators.client; +import io.ballerina.compiler.syntax.tree.DefaultableParameterNode; +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.ParameterNode; -import io.ballerina.compiler.syntax.tree.TypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.ParameterizedTypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.ResourcePathParameterNode; +import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.TypeParameterNode; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createSeparatedNodeList; import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createFunctionSignatureNode; import static io.ballerina.compiler.syntax.tree.NodeFactory.createRequiredParameterNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createReturnTypeDescriptorNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createSimpleNameReferenceNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createUnionTypeDescriptorNode; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_PAREN_TOKEN; import static io.ballerina.compiler.syntax.tree.SyntaxKind.COMMA_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_PAREN_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.PIPE_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.RETURNS_KEYWORD; /** * This class is used to generate the function signature of the client external method's implementation function. * * @since 1.9.0 */ -public class ImplFunctionSignatureGenerator extends RemoteExternalFunctionSignatureGenerator { +public class ImplFunctionSignatureGenerator { + + List parameterNodes = new ArrayList<>(); + ReturnTypeDescriptorNode returnTypeDescriptorNode = null; + + ResourceFunctionSignatureGenerator resourceFunctionSignatureGenerator; + - public ImplFunctionSignatureGenerator(Operation operation, OpenAPI openAPI, String httpMethod, String path) { - super(operation, openAPI, httpMethod, path); - this.treatDefaultableAsRequired = true; - this.functionReturnTypeGenerator = new FunctionStatusCodeReturnTypeGenerator(operation, openAPI, httpMethod, + public ImplFunctionSignatureGenerator(Operation operation, OpenAPI openAPI, String httpMethod, String path, + FunctionDefinitionNode clientExternFunction) { + FunctionSignatureNode functionSignatureNode = clientExternFunction.functionSignature(); + if (Objects.isNull(functionSignatureNode)) { + return; + } + + populateParameterNodes(clientExternFunction); + populateReturnTypeDesc(clientExternFunction); + + // TODO: Find a better way to get the information about parameters + resourceFunctionSignatureGenerator = new ResourceFunctionSignatureGenerator(operation, openAPI, httpMethod, path); + resourceFunctionSignatureGenerator.generateFunctionSignature(); + } + + private void populateReturnTypeDesc(FunctionDefinitionNode clientExternFunction) { + FunctionSignatureNode functionSignatureNode = clientExternFunction.functionSignature(); + Optional targetType = findTargetType(functionSignatureNode); + targetType.ifPresent(this::setReturnTypeDescriptor); + } + + private Optional findTargetType(FunctionSignatureNode functionSignatureNode) { + return functionSignatureNode.parameters().stream().filter( + parameterNode -> parameterNode instanceof DefaultableParameterNode defaultableParameterNode && + defaultableParameterNode.paramName().isPresent() && + defaultableParameterNode.paramName().get().text().equals("targetType") + ).findFirst(); + } + + private void setReturnTypeDescriptor(ParameterNode targetType) { + Node node = ((DefaultableParameterNode) targetType).typeName(); + if (node instanceof ParameterizedTypeDescriptorNode targetTypeNode) { + Optional typeParameterNode = targetTypeNode.typeParamNode(); + typeParameterNode.ifPresent(parameterNode -> + returnTypeDescriptorNode = createReturnTypeDescriptorNode(createToken(RETURNS_KEYWORD), + createEmptyNodeList(), createUnionTypeDescriptorNode(typeParameterNode.get().typeNode(), + createToken(PIPE_TOKEN), + createSimpleNameReferenceNode(createIdentifierToken("error"))))); + } + } + + private void populateParameterNodes(FunctionDefinitionNode clientExternFunction) { + addParametersFromPath(clientExternFunction); + addParametersFromSignature(clientExternFunction); + } + + private void addParametersFromSignature(FunctionDefinitionNode clientExternFunction) { + FunctionSignatureNode functionSignatureNode = clientExternFunction.functionSignature(); + for (ParameterNode paramNode : functionSignatureNode.parameters()) { + parameterNodes.add(removeDefaultValue(paramNode)); + parameterNodes.add(createToken(COMMA_TOKEN)); + } + } + + private void addParametersFromPath(FunctionDefinitionNode clientExternFunction) { + NodeList pathParams = clientExternFunction.relativeResourcePath(); + if (Objects.isNull(pathParams) || pathParams.isEmpty()) { + return; + } + + for (Node node : pathParams) { + if (node instanceof ResourcePathParameterNode pathParameterNode) { + Node pathParam = createRequiredParameterNode(createEmptyNodeList(), pathParameterNode.typeDescriptor(), + pathParameterNode.paramName().orElse(null)); + parameterNodes.add(pathParam); + parameterNodes.add(createToken(COMMA_TOKEN)); + } + } + } + + private Node removeDefaultValue(ParameterNode parameterNode) { + if (parameterNode instanceof DefaultableParameterNode defaultableParameterNode) { + return createRequiredParameterNode(defaultableParameterNode.annotations(), + defaultableParameterNode.typeName(), defaultableParameterNode.paramName().orElse(null)); + } + return parameterNode; + } + + public Optional generateFunctionSignature() { + if (!parameterNodes.isEmpty()) { + parameterNodes.remove(parameterNodes.size() - 1); + } + SeparatedNodeList parameterNodeList = createSeparatedNodeList(parameterNodes); + return Optional.of(createFunctionSignatureNode(createToken(OPEN_PAREN_TOKEN), parameterNodeList, + createToken(CLOSE_PAREN_TOKEN), returnTypeDescriptorNode)); + } + + public boolean hasDefaultHeaders() { + return resourceFunctionSignatureGenerator.hasDefaultHeaders(); + } + + public boolean hasHeaders() { + return resourceFunctionSignatureGenerator.hasHeaders(); } - @Override - protected ParametersInfo populateTargetTypeParam(TypeDescriptorNode targetType, ParametersInfo parametersInfo) { - ParameterNode targetTypeParam = createRequiredParameterNode(createEmptyNodeList(), targetType, - createIdentifierToken("targetType")); - parametersInfo.parameterList().add(targetTypeParam); - parametersInfo.parameterList().add(createToken(COMMA_TOKEN)); - return parametersInfo; + public boolean hasQueries() { + return resourceFunctionSignatureGenerator.hasQueries(); } } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGeneratorNew.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGeneratorNew.java deleted file mode 100644 index 89b4503e1..000000000 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGeneratorNew.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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.core.generators.client; - -import io.ballerina.compiler.syntax.tree.DefaultableParameterNode; -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; -import io.ballerina.compiler.syntax.tree.Node; -import io.ballerina.compiler.syntax.tree.NodeList; -import io.ballerina.compiler.syntax.tree.ParameterNode; -import io.ballerina.compiler.syntax.tree.ParameterizedTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.ResourcePathParameterNode; -import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.SeparatedNodeList; -import io.ballerina.compiler.syntax.tree.TypeParameterNode; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createSeparatedNodeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createFunctionSignatureNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createRequiredParameterNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createReturnTypeDescriptorNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createSimpleNameReferenceNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createUnionTypeDescriptorNode; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_PAREN_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.COMMA_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_PAREN_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.PIPE_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.RETURNS_KEYWORD; - -/** - * This class is used to generate the function signature of the client external method's implementation function. - * - * @since 1.9.0 - */ -public class ImplFunctionSignatureGeneratorNew { - - List parameterNodes = new ArrayList<>(); - ReturnTypeDescriptorNode returnTypeDescriptorNode = null; - - - public ImplFunctionSignatureGeneratorNew(FunctionDefinitionNode clientExternFunction) { - FunctionSignatureNode functionSignatureNode = clientExternFunction.functionSignature(); - if (Objects.isNull(functionSignatureNode)) { - return; - } - - populateParameterNodes(clientExternFunction); - populateReturnTypeDesc(clientExternFunction); - } - - private void populateReturnTypeDesc(FunctionDefinitionNode clientExternFunction) { - FunctionSignatureNode functionSignatureNode = clientExternFunction.functionSignature(); - Optional targetType = findTargetType(functionSignatureNode); - targetType.ifPresent(this::setReturnTypeDescriptor); - } - - private Optional findTargetType(FunctionSignatureNode functionSignatureNode) { - return functionSignatureNode.parameters().stream().filter( - parameterNode -> parameterNode instanceof DefaultableParameterNode defaultableParameterNode && - defaultableParameterNode.paramName().isPresent() && - defaultableParameterNode.paramName().get().text().equals("targetType") - ).findFirst(); - } - - private void setReturnTypeDescriptor(ParameterNode targetType) { - Node node = ((DefaultableParameterNode) targetType).typeName(); - if (node instanceof ParameterizedTypeDescriptorNode targetTypeNode) { - Optional typeParameterNode = targetTypeNode.typeParamNode(); - typeParameterNode.ifPresent(parameterNode -> - returnTypeDescriptorNode = createReturnTypeDescriptorNode(createToken(RETURNS_KEYWORD), - createEmptyNodeList(), createUnionTypeDescriptorNode(typeParameterNode.get().typeNode(), - createToken(PIPE_TOKEN), - createSimpleNameReferenceNode(createIdentifierToken("error"))))); - } - } - - private void populateParameterNodes(FunctionDefinitionNode clientExternFunction) { - addParametersFromPath(clientExternFunction); - addParametersFromSignature(clientExternFunction); - } - - private void addParametersFromSignature(FunctionDefinitionNode clientExternFunction) { - FunctionSignatureNode functionSignatureNode = clientExternFunction.functionSignature(); - for (ParameterNode paramNode : functionSignatureNode.parameters()) { - parameterNodes.add(removeDefaultValue(paramNode)); - parameterNodes.add(createToken(COMMA_TOKEN)); - } - } - - private void addParametersFromPath(FunctionDefinitionNode clientExternFunction) { - NodeList pathParams = clientExternFunction.relativeResourcePath(); - if (Objects.isNull(pathParams) || pathParams.isEmpty()) { - return; - } - - for (Node node : pathParams) { - if (node instanceof ResourcePathParameterNode pathParameterNode) { - Node pathParam = createRequiredParameterNode(createEmptyNodeList(), pathParameterNode.typeDescriptor(), - pathParameterNode.paramName().orElse(null)); - parameterNodes.add(pathParam); - parameterNodes.add(createToken(COMMA_TOKEN)); - } - } - } - - private Node removeDefaultValue(ParameterNode parameterNode) { - if (parameterNode instanceof DefaultableParameterNode defaultableParameterNode) { - return createRequiredParameterNode(defaultableParameterNode.annotations(), - defaultableParameterNode.typeName(), defaultableParameterNode.paramName().orElse(null)); - } - return parameterNode; - } - - public Optional generateFunctionSignature() { - if (!parameterNodes.isEmpty()) { - parameterNodes.remove(parameterNodes.size() - 1); - } - SeparatedNodeList parameterNodeList = createSeparatedNodeList(parameterNodes); - return Optional.of(createFunctionSignatureNode(createToken(OPEN_PAREN_TOKEN), parameterNodeList, - createToken(CLOSE_PAREN_TOKEN), returnTypeDescriptorNode)); - } -} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionGenerator.java index 8d4af41c0..5d1e6540b 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionGenerator.java @@ -109,7 +109,8 @@ qualifierList, functionKeyWord, functionName, createEmptyNodeList(), functionSig } @Override - protected Optional getFunctionBodyNode(List diagnostics) { + protected Optional getFunctionBodyNode(List diagnostics, boolean hasHeaders, + boolean hasDefaultHeaders, boolean hasQueries) { QualifiedNameReferenceNode javaMethodToken = createQualifiedNameReferenceNode( createIdentifierToken("java"), createToken(COLON_TOKEN), createIdentifierToken("Method")); BasicLiteralNode classValueExp = createBasicLiteralNode(STRING_LITERAL, @@ -136,7 +137,7 @@ protected Optional getFunctionBodyNode(List } @Override - protected RemoteFunctionSignatureGeneratorNew getSignatureGenerator() { + protected RemoteFunctionSignatureGenerator getSignatureGenerator() { return new RemoteExternalFunctionSignatureGenerator(operation.getValue(), openAPI, operation.getKey().toString().toLowerCase(Locale.ROOT), path); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionSignatureGenerator.java index e29f04cb0..9c01f8370 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionSignatureGenerator.java @@ -49,7 +49,7 @@ * * @since 1.9.0 */ -public class RemoteExternalFunctionSignatureGenerator extends RemoteFunctionSignatureGeneratorNew { +public class RemoteExternalFunctionSignatureGenerator extends RemoteFunctionSignatureGenerator { public RemoteExternalFunctionSignatureGenerator(Operation operation, OpenAPI openAPI, String httpMethod, String path) { diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionGenerator.java index 4cf8201f6..b2f2b5c21 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionGenerator.java @@ -61,7 +61,7 @@ public Optional generateFunction() { Token functionKeyWord = createToken(FUNCTION_KEYWORD); IdentifierToken functionName = createIdentifierToken(operation.getValue().getOperationId()); // Create function signature - RemoteFunctionSignatureGeneratorNew signatureGenerator = getSignatureGenerator(); + RemoteFunctionSignatureGenerator signatureGenerator = getSignatureGenerator(); Optional signatureNodeOptional = signatureGenerator.generateFunctionSignature(); diagnostics.addAll(signatureGenerator.getDiagnostics()); @@ -70,7 +70,9 @@ public Optional generateFunction() { } FunctionSignatureNode functionSignatureNode = signatureNodeOptional.get(); //Create function body - Optional functionBodyNodeResult = getFunctionBodyNode(diagnostics); + Optional functionBodyNodeResult = getFunctionBodyNode(diagnostics, + signatureGenerator.hasHeaders(), signatureGenerator.hasDefaultHeaders(), + signatureGenerator.hasQueries()); if (functionBodyNodeResult.isEmpty()) { return Optional.empty(); } @@ -80,9 +82,10 @@ public Optional generateFunction() { functionBodyNode); } - protected Optional getFunctionBodyNode(List diagnostics) { + protected Optional getFunctionBodyNode(List diagnostics, boolean hasHeaders, + boolean hasDefaultHeaders, boolean hasQueries) { FunctionBodyGeneratorImp functionBodyGenerator = new FunctionBodyGeneratorImp(path, operation, openAPI, - authConfigGeneratorImp, ballerinaUtilGenerator, imports); + authConfigGeneratorImp, ballerinaUtilGenerator, imports, hasHeaders, hasDefaultHeaders, hasQueries); Optional functionBodyNodeResult = functionBodyGenerator.getFunctionBodyNode(); if (functionBodyNodeResult.isEmpty()) { diagnostics.addAll(functionBodyGenerator.getDiagnostics()); @@ -90,8 +93,8 @@ protected Optional getFunctionBodyNode(List return functionBodyNodeResult; } - protected RemoteFunctionSignatureGeneratorNew getSignatureGenerator() { - return new RemoteFunctionSignatureGeneratorNew(operation.getValue(), openAPI, + protected RemoteFunctionSignatureGenerator getSignatureGenerator() { + return new RemoteFunctionSignatureGenerator(operation.getValue(), openAPI, operation.getKey().toString().toLowerCase(Locale.ROOT), path); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGenerator.java index d2121f762..bfeef0a65 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGenerator.java @@ -18,7 +18,6 @@ package io.ballerina.openapi.core.generators.client; -import io.ballerina.compiler.syntax.tree.DefaultableParameterNode; import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeFactory; @@ -29,9 +28,9 @@ import io.ballerina.compiler.syntax.tree.Token; import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp; -import io.ballerina.openapi.core.generators.client.parameter.HeaderParameterGenerator; +import io.ballerina.openapi.core.generators.client.parameter.HeadersParameterGenerator; import io.ballerina.openapi.core.generators.client.parameter.PathParameterGenerator; -import io.ballerina.openapi.core.generators.client.parameter.QueryParameterGenerator; +import io.ballerina.openapi.core.generators.client.parameter.QueriesParameterGenerator; import io.ballerina.openapi.core.generators.client.parameter.RequestBodyGenerator; import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; import io.swagger.v3.oas.models.OpenAPI; @@ -54,12 +53,20 @@ public class RemoteFunctionSignatureGenerator implements FunctionSignatureGenera OpenAPI openAPI; Operation operation; List diagnostics = new ArrayList<>(); - boolean treatDefaultableAsRequired = false; FunctionReturnTypeGeneratorImp functionReturnTypeGenerator; + private final String httpMethod; + private final String path; - public RemoteFunctionSignatureGenerator(Operation operation, OpenAPI openAPI, String httpMethod) { + private boolean hasDefaultHeader = false; + private boolean hasHeadersParam = false; + private boolean hasQueriesParam = false; + + public RemoteFunctionSignatureGenerator(Operation operation, OpenAPI openAPI, String httpMethod, + String path) { this.operation = operation; this.openAPI = openAPI; + this.httpMethod = httpMethod; + this.path = path; this.functionReturnTypeGenerator = new FunctionReturnTypeGeneratorImp(operation, openAPI, httpMethod); } @@ -99,114 +106,124 @@ public Optional generateFunctionSignature() { } protected ParametersInfo getParametersInfo(List parameters) { - List paramName = new ArrayList<>(); - // 1. parameters - path , query, requestBody, headers List parameterList = new ArrayList<>(); List defaultable = new ArrayList<>(); Token comma = createToken(COMMA_TOKEN); + + List headerParameters = new ArrayList<>(); + List queryParameters = new ArrayList<>(); + + // 1. path parameters if (parameters != null) { for (Parameter parameter : parameters) { - if (parameter.get$ref() != null) { - String paramType = null; - try { - paramType = extractReferenceType(parameter.get$ref()); - } catch (BallerinaOpenApiException e) { - ClientDiagnosticImp clientDiagnostic = new ClientDiagnosticImp(OAS_CLIENT_100, - parameter.get$ref()); - diagnostics.add(clientDiagnostic); + if (parameter.getIn().equals("path")) { + PathParameterGenerator paramGenerator = new PathParameterGenerator(parameter, openAPI); + Optional param = paramGenerator.generateParameterNode(); + if (param.isEmpty()) { + diagnostics.addAll(paramGenerator.getDiagnostics()); + return null; } - parameter = openAPI.getComponents().getParameters().get(paramType); - } - - String in = parameter.getIn(); - - switch (in) { - case "path": - PathParameterGenerator paramGenerator = new PathParameterGenerator(parameter, openAPI); - Optional param = paramGenerator.generateParameterNode( - treatDefaultableAsRequired); - if (param.isEmpty()) { - diagnostics.addAll(paramGenerator.getDiagnostics()); - return null; - } - // Path parameters are always required. - parameterList.add(param.get()); - parameterList.add(comma); - paramName.add(param.get().toString()); - break; - case "query": - QueryParameterGenerator queryParameterGenerator = new QueryParameterGenerator(parameter, - openAPI); - Optional queryParam = queryParameterGenerator.generateParameterNode( - treatDefaultableAsRequired); - if (queryParam.isEmpty()) { - diagnostics.addAll(queryParameterGenerator.getDiagnostics()); - return null; - } - - paramName.add(queryParam.get().toString()); - if (queryParam.get() instanceof RequiredParameterNode requiredParameterNode) { - parameterList.add(requiredParameterNode); - parameterList.add(comma); - } else { - defaultable.add(queryParam.get()); - defaultable.add(comma); - } - break; - case "header": - HeaderParameterGenerator headerParameterGenerator = new HeaderParameterGenerator(parameter, - openAPI); - Optional headerParam = headerParameterGenerator.generateParameterNode( - treatDefaultableAsRequired); - if (headerParam.isEmpty()) { - diagnostics.addAll(headerParameterGenerator.getDiagnostics()); - return null; - } - paramName.add(headerParam.get().toString()); - - if (headerParam.get() instanceof RequiredParameterNode headerNode) { - parameterList.add(headerNode); - parameterList.add(comma); - } else { - defaultable.add(headerParam.get()); - defaultable.add(comma); - } - break; - default: - break; + // Path parameters are always required. + parameterList.add(param.get()); + parameterList.add(comma); } } } - // 2. requestBody + + // 1. requestBody if (operation.getRequestBody() != null) { - RequestBodyGenerator requestBodyGenerator = new RequestBodyGenerator(operation.getRequestBody(), openAPI); - Optional requestBody = requestBodyGenerator.generateParameterNode( - treatDefaultableAsRequired); + RequestBodyGenerator requestBodyGenerator = new RequestBodyGenerator(operation.getRequestBody(), + openAPI); + Optional requestBody = requestBodyGenerator.generateParameterNode(); if (requestBody.isEmpty()) { diagnostics.addAll(requestBodyGenerator.getDiagnostics()); return null; } - List rBheaderParameters = requestBodyGenerator.getHeaderParameters(); + headerParameters = requestBodyGenerator.getHeaderSchemas(); parameterList.add(requestBody.get()); parameterList.add(comma); - if (!rBheaderParameters.isEmpty()) { - rBheaderParameters.forEach(header -> { - if (!paramName.contains(header.toString())) { - paramName.add(header.toString()); - if (header instanceof DefaultableParameterNode defaultableParameterNode) { - defaultable.add(defaultableParameterNode); - defaultable.add(comma); - } else { - parameterList.add(header); - parameterList.add(comma); - } - } - }); + } + + // 2. parameters - query, headers + if (parameters != null) { + populateQueryAndHeaderParameters(parameters, queryParameters, headerParameters); + + HeadersParameterGenerator headersParameterGenerator = new HeadersParameterGenerator(headerParameters, + openAPI, operation, httpMethod, path); + Optional headers; + if (headerParameters.isEmpty()) { + hasDefaultHeader = true; + headers = HeadersParameterGenerator.getDefaultParameterNode(); + } else { + headers = headersParameterGenerator.generateParameterNode(); + } + + if (headers.isPresent()) { + hasHeadersParam = true; + if (headers.get() instanceof RequiredParameterNode headerNode) { + parameterList.add(headerNode); + parameterList.add(comma); + } else { + defaultable.add(headers.get()); + defaultable.add(comma); + } + } else if (!headerParameters.isEmpty()) { + diagnostics.addAll(headersParameterGenerator.getDiagnostics()); + return null; + } + + QueriesParameterGenerator queriesParameterGenerator = new QueriesParameterGenerator(queryParameters, + openAPI, operation, httpMethod, path); + Optional queries = queriesParameterGenerator.generateParameterNode(); + if (queries.isPresent()) { + hasQueriesParam = true; + parameterList.add(queries.get()); + parameterList.add(comma); + } else if (!queryParameters.isEmpty()) { + diagnostics.addAll(queriesParameterGenerator.getDiagnostics()); + return null; + } + } else { + ParameterNode defaultHeaderParam = HeadersParameterGenerator.getDefaultParameterNode().orElse(null); + if (defaultHeaderParam != null) { + hasDefaultHeader = true; + defaultable.add(defaultHeaderParam); + defaultable.add(comma); } } return new ParametersInfo(parameterList, defaultable); } + private void populateQueryAndHeaderParameters(List parameters, List queryParameters, + List headerParameters) { + for (Parameter parameter : parameters) { + if (parameter.get$ref() != null) { + String paramType = null; + try { + paramType = extractReferenceType(parameter.get$ref()); + } catch (BallerinaOpenApiException e) { + ClientDiagnosticImp clientDiagnostic = new ClientDiagnosticImp(OAS_CLIENT_100, + parameter.get$ref()); + diagnostics.add(clientDiagnostic); + } + parameter = openAPI.getComponents().getParameters().get(paramType); + } + + String in = parameter.getIn(); + + switch (in) { + case "query": + queryParameters.add(parameter); + break; + case "header": + headerParameters.add(parameter); + break; + default: + break; + } + } +} + protected FunctionReturnTypeGeneratorImp getFunctionReturnTypeGenerator() { return functionReturnTypeGenerator; } @@ -214,4 +231,16 @@ protected FunctionReturnTypeGeneratorImp getFunctionReturnTypeGenerator() { public List getDiagnostics() { return diagnostics; } + + public boolean hasDefaultHeaders() { + return hasDefaultHeader; + } + + public boolean hasHeaders() { + return hasHeadersParam; + } + + public boolean hasQueries() { + return hasQueriesParam; + } } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGeneratorNew.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGeneratorNew.java deleted file mode 100644 index 3c9a5a62a..000000000 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGeneratorNew.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.ballerina.openapi.core.generators.client; - -import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; -import io.ballerina.compiler.syntax.tree.Node; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.ParameterNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.SeparatedNodeList; -import io.ballerina.compiler.syntax.tree.Token; -import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; -import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp; -import io.ballerina.openapi.core.generators.client.parameter.HeadersParameterGenerator; -import io.ballerina.openapi.core.generators.client.parameter.PathParameterGenerator; -import io.ballerina.openapi.core.generators.client.parameter.QueriesParameterGenerator; -import io.ballerina.openapi.core.generators.client.parameter.RequestBodyGeneratorNew; -import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.parameters.Parameter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createSeparatedNodeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_PAREN_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.COMMA_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_PAREN_TOKEN; -import static io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages.OAS_CLIENT_100; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.extractReferenceType; - -public class RemoteFunctionSignatureGeneratorNew implements FunctionSignatureGenerator { - OpenAPI openAPI; - Operation operation; - List diagnostics = new ArrayList<>(); - boolean treatDefaultableAsRequired = false; - FunctionReturnTypeGeneratorImp functionReturnTypeGenerator; - private final String httpMethod; - private final String path; - - private boolean hasDefaultHeader = false; - private boolean hasHeadersParam = false; - private boolean hasQueriesParam = false; - - public RemoteFunctionSignatureGeneratorNew(Operation operation, OpenAPI openAPI, String httpMethod, - String path) { - this.operation = operation; - this.openAPI = openAPI; - this.httpMethod = httpMethod; - this.path = path; - this.functionReturnTypeGenerator = new FunctionReturnTypeGeneratorImp(operation, openAPI, httpMethod); - } - - @Override - public Optional generateFunctionSignature() { - List parameters = operation.getParameters(); - ParametersInfo parametersInfo = getParametersInfo(parameters); - - if (parametersInfo == null) { - return Optional.empty(); - } - - List defaultable = parametersInfo.defaultable(); - List parameterList = parametersInfo.parameterList(); - - // filter defaultable parameters - if (!defaultable.isEmpty()) { - parameterList.addAll(defaultable); - } - // Remove the last comma - if (!parameterList.isEmpty()) { - parameterList.remove(parameterList.size() - 1); - } - SeparatedNodeList parameterNodes = createSeparatedNodeList(parameterList); - - // 3. return statements - FunctionReturnTypeGeneratorImp returnTypeGenerator = getFunctionReturnTypeGenerator(); - Optional returnType = returnTypeGenerator.getReturnType(); - diagnostics.addAll(returnTypeGenerator.getDiagnostics()); - if (returnType.isEmpty()) { - return Optional.empty(); - } - - return returnType.map(returnTypeDescriptorNode -> NodeFactory.createFunctionSignatureNode( - createToken(OPEN_PAREN_TOKEN), parameterNodes, - createToken(CLOSE_PAREN_TOKEN), returnTypeDescriptorNode)); - } - - protected ParametersInfo getParametersInfo(List parameters) { - List parameterList = new ArrayList<>(); - List defaultable = new ArrayList<>(); - Token comma = createToken(COMMA_TOKEN); - - List headerParameters = new ArrayList<>(); - List queryParameters = new ArrayList<>(); - - // 1. path parameters - if (parameters != null) { - for (Parameter parameter : parameters) { - if (parameter.getIn().equals("path")) { - PathParameterGenerator paramGenerator = new PathParameterGenerator(parameter, openAPI); - Optional param = paramGenerator.generateParameterNode( - treatDefaultableAsRequired); - if (param.isEmpty()) { - diagnostics.addAll(paramGenerator.getDiagnostics()); - return null; - } - // Path parameters are always required. - parameterList.add(param.get()); - parameterList.add(comma); - } - } - } - - // 1. requestBody - if (operation.getRequestBody() != null) { - RequestBodyGeneratorNew requestBodyGenerator = new RequestBodyGeneratorNew(operation.getRequestBody(), - openAPI); - Optional requestBody = requestBodyGenerator.generateParameterNode( - treatDefaultableAsRequired); - if (requestBody.isEmpty()) { - diagnostics.addAll(requestBodyGenerator.getDiagnostics()); - return null; - } - headerParameters = requestBodyGenerator.getHeaderSchemas(); - parameterList.add(requestBody.get()); - parameterList.add(comma); - } - - // 2. parameters - query, headers - if (parameters != null) { - populateQueryAndHeaderParameters(parameters, queryParameters, headerParameters); - - HeadersParameterGenerator headersParameterGenerator = new HeadersParameterGenerator(headerParameters, - openAPI, operation, httpMethod, path); - Optional headers; - if (headerParameters.isEmpty()) { - hasDefaultHeader = true; - headers = headersParameterGenerator.getDefaultParameterNode(treatDefaultableAsRequired); - } else { - headers = headersParameterGenerator.generateParameterNode(); - } - - if (headers.isPresent()) { - hasHeadersParam = true; - if (headers.get() instanceof RequiredParameterNode headerNode) { - parameterList.add(headerNode); - parameterList.add(comma); - } else { - defaultable.add(headers.get()); - defaultable.add(comma); - } - } else if (!headerParameters.isEmpty()) { - diagnostics.addAll(headersParameterGenerator.getDiagnostics()); - return null; - } - - QueriesParameterGenerator queriesParameterGenerator = new QueriesParameterGenerator(queryParameters, - openAPI, operation, httpMethod, path); - Optional queries = queriesParameterGenerator.generateParameterNode(); - if (queries.isPresent()) { - hasQueriesParam = true; - parameterList.add(queries.get()); - parameterList.add(comma); - } else if (!queryParameters.isEmpty()) { - diagnostics.addAll(queriesParameterGenerator.getDiagnostics()); - return null; - } - } - return new ParametersInfo(parameterList, defaultable); - } - - private void populateQueryAndHeaderParameters(List parameters, List queryParameters, - List headerParameters) { - for (Parameter parameter : parameters) { - if (parameter.get$ref() != null) { - String paramType = null; - try { - paramType = extractReferenceType(parameter.get$ref()); - } catch (BallerinaOpenApiException e) { - ClientDiagnosticImp clientDiagnostic = new ClientDiagnosticImp(OAS_CLIENT_100, - parameter.get$ref()); - diagnostics.add(clientDiagnostic); - } - parameter = openAPI.getComponents().getParameters().get(paramType); - } - - String in = parameter.getIn(); - - switch (in) { - case "query": - queryParameters.add(parameter); - break; - case "header": - headerParameters.add(parameter); - break; - default: - break; - } - } -} - - protected FunctionReturnTypeGeneratorImp getFunctionReturnTypeGenerator() { - return functionReturnTypeGenerator; - } - - public List getDiagnostics() { - return diagnostics; - } - - public boolean hasDefaultHeaders() { - return hasDefaultHeader; - } - - public boolean hasHeaders() { - return hasHeadersParam; - } - - public boolean hasQueries() { - return hasQueriesParam; - } -} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionGenerator.java index ac1c244f4..4d6e029f2 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionGenerator.java @@ -143,7 +143,7 @@ private String getNativeMethodName() { } @Override - protected ResourceFunctionSignatureGeneratorNew getSignatureGenerator() { + protected ResourceFunctionSignatureGenerator getSignatureGenerator() { return new ResourceExternalFunctionSignatureGenerator(operation.getValue(), openAPI, operation.getKey().toString().toLowerCase(Locale.ROOT), path); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionSignatureGenerator.java index e6b1c8211..af64d19ce 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionSignatureGenerator.java @@ -49,7 +49,7 @@ * * @since 1.9.0 */ -public class ResourceExternalFunctionSignatureGenerator extends ResourceFunctionSignatureGeneratorNew { +public class ResourceExternalFunctionSignatureGenerator extends ResourceFunctionSignatureGenerator { public ResourceExternalFunctionSignatureGenerator(Operation operation, OpenAPI openAPI, String httpMethod, String path) { diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionGenerator.java index 5ea75218d..cabd46ed7 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionGenerator.java @@ -82,7 +82,7 @@ public Optional generateFunction() { return Optional.empty(); } // Create function signature - ResourceFunctionSignatureGeneratorNew signatureGenerator = getSignatureGenerator(); + ResourceFunctionSignatureGenerator signatureGenerator = getSignatureGenerator(); Optional signatureNodeOptional = signatureGenerator.generateFunctionSignature(); diagnostics.addAll(signatureGenerator.getDiagnostics()); if (signatureNodeOptional.isEmpty()) { @@ -104,7 +104,7 @@ public Optional generateFunction() { protected Optional getFunctionBodyNode(List diagnostics, boolean hasHeaders, boolean hasDefaultHeaders, boolean hasQueries) { - FunctionBodyGeneratorImpNew functionBodyGenerator = new FunctionBodyGeneratorImpNew(path, operation, openAPI, + FunctionBodyGeneratorImp functionBodyGenerator = new FunctionBodyGeneratorImp(path, operation, openAPI, authConfigGeneratorImp, ballerinaUtilGenerator, imports, hasHeaders, hasDefaultHeaders, hasQueries); Optional functionBodyNodeResult = functionBodyGenerator.getFunctionBodyNode(); if (functionBodyNodeResult.isEmpty()) { @@ -113,8 +113,8 @@ protected Optional getFunctionBodyNode(List return functionBodyNodeResult; } - protected ResourceFunctionSignatureGeneratorNew getSignatureGenerator() { - return new ResourceFunctionSignatureGeneratorNew(operation.getValue(), openAPI, + protected ResourceFunctionSignatureGenerator getSignatureGenerator() { + return new ResourceFunctionSignatureGenerator(operation.getValue(), openAPI, operation.getKey().toString().toLowerCase(Locale.ROOT), path); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGenerator.java index 1da5e2884..268f0075c 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGenerator.java @@ -18,7 +18,6 @@ package io.ballerina.openapi.core.generators.client; -import io.ballerina.compiler.syntax.tree.DefaultableParameterNode; import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeFactory; @@ -29,9 +28,8 @@ import io.ballerina.compiler.syntax.tree.Token; import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp; -import io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages; -import io.ballerina.openapi.core.generators.client.parameter.HeaderParameterGenerator; -import io.ballerina.openapi.core.generators.client.parameter.QueryParameterGenerator; +import io.ballerina.openapi.core.generators.client.parameter.HeadersParameterGenerator; +import io.ballerina.openapi.core.generators.client.parameter.QueriesParameterGenerator; import io.ballerina.openapi.core.generators.client.parameter.RequestBodyGenerator; import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; import io.swagger.v3.oas.models.OpenAPI; @@ -55,10 +53,19 @@ public class ResourceFunctionSignatureGenerator implements FunctionSignatureGene protected final OpenAPI openAPI; protected final List diagnostics = new ArrayList<>(); protected FunctionReturnTypeGeneratorImp functionReturnTypeGenerator; + private final String httpMethod; + private final String path; - public ResourceFunctionSignatureGenerator(Operation operation, OpenAPI openAPI, String httpMethod) { + private boolean hasDefaultHeader = false; + private boolean hasHeadersParam = false; + private boolean hasQueriesParam = false; + + public ResourceFunctionSignatureGenerator(Operation operation, OpenAPI openAPI, String httpMethod, + String path) { this.operation = operation; this.openAPI = openAPI; + this.httpMethod = httpMethod; + this.path = path; this.functionReturnTypeGenerator = new FunctionReturnTypeGeneratorImp(operation, openAPI, httpMethod); } @@ -99,95 +106,108 @@ public Optional generateFunctionSignature() { } protected ParametersInfo getParametersInfo(List parameters) { - // 1. parameters - path , query, requestBody, headers - List paramName = new ArrayList<>(); List parameterList = new ArrayList<>(); List defaultable = new ArrayList<>(); Token comma = createToken(COMMA_TOKEN); - if (parameters != null) { - for (Parameter parameter : parameters) { - if (parameter.get$ref() != null) { - String paramType = null; - try { - paramType = extractReferenceType(parameter.get$ref()); - } catch (BallerinaOpenApiException e) { - DiagnosticMessages diagnostic = OAS_CLIENT_100; - ClientDiagnosticImp clientDiagnostic = new ClientDiagnosticImp(diagnostic, - parameter.get$ref()); - diagnostics.add(clientDiagnostic); - } - parameter = openAPI.getComponents().getParameters().get(paramType); - } - String in = parameter.getIn(); - - switch (in) { - case "query": - QueryParameterGenerator queryParameterGenerator = new QueryParameterGenerator(parameter, - openAPI); - Optional queryParam = queryParameterGenerator.generateParameterNode(); - if (queryParam.isEmpty()) { - diagnostics.addAll(queryParameterGenerator.getDiagnostics()); - return null; - } - if (queryParam.get() instanceof RequiredParameterNode requiredParameterNode) { - parameterList.add(requiredParameterNode); - parameterList.add(comma); - } else { - defaultable.add(queryParam.get()); - defaultable.add(comma); - } - break; - case "header": - HeaderParameterGenerator headerParameterGenerator = new HeaderParameterGenerator(parameter, - openAPI); - Optional headerParam = headerParameterGenerator.generateParameterNode(); - if (headerParam.isEmpty()) { - diagnostics.addAll(headerParameterGenerator.getDiagnostics()); - return null; - } - if (headerParam.get() instanceof RequiredParameterNode headerNode) { - parameterList.add(headerNode); - parameterList.add(comma); - } else { - defaultable.add(headerParam.get()); - defaultable.add(comma); - } - break; - default: - break; - } - } - } - // 2. requestBody + List headerParameters = new ArrayList<>(); + List queryParameters = new ArrayList<>(); + + // 1. requestBody if (operation.getRequestBody() != null) { - RequestBodyGenerator requestBodyGenerator = new RequestBodyGenerator(operation.getRequestBody(), openAPI); + RequestBodyGenerator requestBodyGenerator = new RequestBodyGenerator(operation.getRequestBody(), + openAPI); Optional requestBody = requestBodyGenerator.generateParameterNode(); if (requestBody.isEmpty()) { diagnostics.addAll(requestBodyGenerator.getDiagnostics()); return null; } - List rBheaderParameters = requestBodyGenerator.getHeaderParameters(); + headerParameters = requestBodyGenerator.getHeaderSchemas(); parameterList.add(requestBody.get()); parameterList.add(comma); - if (!rBheaderParameters.isEmpty()) { - rBheaderParameters.forEach(header -> { - if (!paramName.contains(header.toString())) { - paramName.add(header.toString()); - if (header instanceof DefaultableParameterNode defaultableParameterNode) { - defaultable.add(defaultableParameterNode); - defaultable.add(comma); - } else { - parameterList.add(header); - parameterList.add(comma); - } - } - }); + } + + // 2. parameters - query, requestBody, headers + if (parameters != null) { + populateHeaderAndQueryParameters(parameters, queryParameters, headerParameters); + + HeadersParameterGenerator headersParameterGenerator = new HeadersParameterGenerator(headerParameters, + openAPI, operation, httpMethod, path); + Optional headers; + if (headerParameters.isEmpty()) { + hasDefaultHeader = true; + headers = HeadersParameterGenerator.getDefaultParameterNode(); + } else { + headers = headersParameterGenerator.generateParameterNode(); + } + + if (headers.isPresent()) { + hasHeadersParam = true; + if (headers.get() instanceof RequiredParameterNode headerNode) { + parameterList.add(headerNode); + parameterList.add(comma); + } else { + defaultable.add(headers.get()); + defaultable.add(comma); + } + } else if (!headerParameters.isEmpty()) { + diagnostics.addAll(headersParameterGenerator.getDiagnostics()); + return null; + } + + QueriesParameterGenerator queriesParameterGenerator = new QueriesParameterGenerator(queryParameters, + openAPI, operation, httpMethod, path); + Optional queries = queriesParameterGenerator.generateParameterNode(); + if (queries.isPresent()) { + hasQueriesParam = true; + parameterList.add(queries.get()); + parameterList.add(comma); + } else if (!queryParameters.isEmpty()) { + diagnostics.addAll(queriesParameterGenerator.getDiagnostics()); + return null; + } + } else { + ParameterNode defaultHeaderParam = HeadersParameterGenerator.getDefaultParameterNode().orElse(null); + if (defaultHeaderParam != null) { + hasDefaultHeader = true; + defaultable.add(defaultHeaderParam); + defaultable.add(comma); } } + return new ParametersInfo(parameterList, defaultable); } + private void populateHeaderAndQueryParameters(List parameters, List queryParameters, + List headerParameters) { + for (Parameter parameter : parameters) { + if (parameter.get$ref() != null) { + String paramType = null; + try { + paramType = extractReferenceType(parameter.get$ref()); + } catch (BallerinaOpenApiException e) { + ClientDiagnosticImp clientDiagnostic = new ClientDiagnosticImp(OAS_CLIENT_100, + parameter.get$ref()); + diagnostics.add(clientDiagnostic); + } + parameter = openAPI.getComponents().getParameters().get(paramType); + } + + String in = parameter.getIn(); + + switch (in) { + case "query": + queryParameters.add(parameter); + break; + case "header": + headerParameters.add(parameter); + break; + default: + break; + } + } + } + protected FunctionReturnTypeGeneratorImp getFunctionReturnTypeGenerator() { return functionReturnTypeGenerator; } @@ -196,4 +216,16 @@ protected FunctionReturnTypeGeneratorImp getFunctionReturnTypeGenerator() { public List getDiagnostics() { return diagnostics; } + + public boolean hasDefaultHeaders() { + return hasDefaultHeader; + } + + public boolean hasHeaders() { + return hasHeadersParam; + } + + public boolean hasQueries() { + return hasQueriesParam; + } } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGeneratorNew.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGeneratorNew.java deleted file mode 100644 index 8ca3e9746..000000000 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGeneratorNew.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.ballerina.openapi.core.generators.client; - -import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; -import io.ballerina.compiler.syntax.tree.Node; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.ParameterNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.SeparatedNodeList; -import io.ballerina.compiler.syntax.tree.Token; -import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; -import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp; -import io.ballerina.openapi.core.generators.client.parameter.HeadersParameterGenerator; -import io.ballerina.openapi.core.generators.client.parameter.QueriesParameterGenerator; -import io.ballerina.openapi.core.generators.client.parameter.RequestBodyGeneratorNew; -import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.parameters.Parameter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createSeparatedNodeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_PAREN_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.COMMA_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_PAREN_TOKEN; -import static io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages.OAS_CLIENT_100; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.extractReferenceType; - -public class ResourceFunctionSignatureGeneratorNew implements FunctionSignatureGenerator { - protected final Operation operation; - protected final OpenAPI openAPI; - protected final List diagnostics = new ArrayList<>(); - protected FunctionReturnTypeGeneratorImp functionReturnTypeGenerator; - private final String httpMethod; - private final String path; - - private boolean hasDefaultHeader = false; - private boolean hasHeadersParam = false; - private boolean hasQueriesParam = false; - - public ResourceFunctionSignatureGeneratorNew(Operation operation, OpenAPI openAPI, String httpMethod, - String path) { - this.operation = operation; - this.openAPI = openAPI; - this.httpMethod = httpMethod; - this.path = path; - this.functionReturnTypeGenerator = new FunctionReturnTypeGeneratorImp(operation, openAPI, httpMethod); - } - - @Override - public Optional generateFunctionSignature() { - // 1. parameters - path , query, requestBody, headers - List parameters = operation.getParameters(); - ParametersInfo parametersInfo = getParametersInfo(parameters); - - if (parametersInfo == null) { - return Optional.empty(); - } - - List defaultableParameters = parametersInfo.defaultable(); - List parameterList = parametersInfo.parameterList(); - - //filter defaultable parameters - if (!defaultableParameters.isEmpty()) { - parameterList.addAll(defaultableParameters); - } - // Remove the last comma - if (!parameterList.isEmpty()) { - parameterList.remove(parameterList.size() - 1); - } - SeparatedNodeList parameterNodes = createSeparatedNodeList(parameterList); - - // 3. return statements - FunctionReturnTypeGeneratorImp functionReturnType = getFunctionReturnTypeGenerator(); - Optional returnType = functionReturnType.getReturnType(); - diagnostics.addAll(functionReturnType.getDiagnostics()); - if (returnType.isEmpty()) { - return Optional.empty(); - } - return returnType.map(returnTypeDescriptorNode -> NodeFactory.createFunctionSignatureNode( - createToken(OPEN_PAREN_TOKEN), parameterNodes, - createToken(CLOSE_PAREN_TOKEN), returnTypeDescriptorNode)); - //create function signature node - } - - protected ParametersInfo getParametersInfo(List parameters) { - List parameterList = new ArrayList<>(); - List defaultable = new ArrayList<>(); - Token comma = createToken(COMMA_TOKEN); - - List headerParameters = new ArrayList<>(); - List queryParameters = new ArrayList<>(); - - // 1. requestBody - if (operation.getRequestBody() != null) { - RequestBodyGeneratorNew requestBodyGenerator = new RequestBodyGeneratorNew(operation.getRequestBody(), - openAPI); - Optional requestBody = requestBodyGenerator.generateParameterNode(); - if (requestBody.isEmpty()) { - diagnostics.addAll(requestBodyGenerator.getDiagnostics()); - return null; - } - headerParameters = requestBodyGenerator.getHeaderSchemas(); - parameterList.add(requestBody.get()); - parameterList.add(comma); - } - - // 2. parameters - query, requestBody, headers - if (parameters != null) { - populateHeaderAndQueryParameters(parameters, queryParameters, headerParameters); - - HeadersParameterGenerator headersParameterGenerator = new HeadersParameterGenerator(headerParameters, - openAPI, operation, httpMethod, path); - Optional headers; - if (headerParameters.isEmpty()) { - hasDefaultHeader = true; - headers = headersParameterGenerator.getDefaultParameterNode(); - } else { - headers = headersParameterGenerator.generateParameterNode(); - } - - if (headers.isPresent()) { - hasHeadersParam = true; - if (headers.get() instanceof RequiredParameterNode headerNode) { - parameterList.add(headerNode); - parameterList.add(comma); - } else { - defaultable.add(headers.get()); - defaultable.add(comma); - } - } else if (!headerParameters.isEmpty()) { - diagnostics.addAll(headersParameterGenerator.getDiagnostics()); - return null; - } - - QueriesParameterGenerator queriesParameterGenerator = new QueriesParameterGenerator(queryParameters, - openAPI, operation, httpMethod, path); - Optional queries = queriesParameterGenerator.generateParameterNode(); - if (queries.isPresent()) { - hasQueriesParam = true; - parameterList.add(queries.get()); - parameterList.add(comma); - } else if (!queryParameters.isEmpty()) { - diagnostics.addAll(queriesParameterGenerator.getDiagnostics()); - return null; - } - } - - return new ParametersInfo(parameterList, defaultable); - } - - private void populateHeaderAndQueryParameters(List parameters, List queryParameters, - List headerParameters) { - for (Parameter parameter : parameters) { - if (parameter.get$ref() != null) { - String paramType = null; - try { - paramType = extractReferenceType(parameter.get$ref()); - } catch (BallerinaOpenApiException e) { - ClientDiagnosticImp clientDiagnostic = new ClientDiagnosticImp(OAS_CLIENT_100, - parameter.get$ref()); - diagnostics.add(clientDiagnostic); - } - parameter = openAPI.getComponents().getParameters().get(paramType); - } - - String in = parameter.getIn(); - - switch (in) { - case "query": - queryParameters.add(parameter); - break; - case "header": - headerParameters.add(parameter); - break; - default: - break; - } - } - } - - protected FunctionReturnTypeGeneratorImp getFunctionReturnTypeGenerator() { - return functionReturnTypeGenerator; - } - - @Override - public List getDiagnostics() { - return diagnostics; - } - - public boolean hasDefaultHeaders() { - return hasDefaultHeader; - } - - public boolean hasHeaders() { - return hasHeadersParam; - } - - public boolean hasQueries() { - return hasQueriesParam; - } -} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeaderParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeaderParameterGenerator.java deleted file mode 100644 index b9ffcc827..000000000 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeaderParameterGenerator.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.ballerina.openapi.core.generators.client.parameter; - -import io.ballerina.compiler.syntax.tree.BuiltinSimpleNameReferenceNode; -import io.ballerina.compiler.syntax.tree.IdentifierToken; -import io.ballerina.compiler.syntax.tree.LiteralValueToken; -import io.ballerina.compiler.syntax.tree.NilLiteralNode; -import io.ballerina.compiler.syntax.tree.ParameterNode; -import io.ballerina.compiler.syntax.tree.TypeDescriptorNode; -import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; -import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp; -import io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages; -import io.ballerina.openapi.core.generators.common.TypeHandler; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.media.Schema; -import io.swagger.v3.oas.models.parameters.Parameter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyMinutiaeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createLiteralValueToken; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createBuiltinSimpleNameReferenceNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createDefaultableParameterNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createNilLiteralNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createRequiredParameterNode; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_PAREN_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.EQUAL_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_PAREN_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.STRING_TYPE_DESC; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.NILLABLE; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.escapeIdentifier; - -public class HeaderParameterGenerator implements ParameterGenerator { - Parameter parameter; - - List diagnostics = new ArrayList<>(); - - public HeaderParameterGenerator(Parameter parameter, OpenAPI openAPI) { - this.parameter = parameter; - } - - @Override - public Optional generateParameterNode() { - return generateParameterNode(false); - } - - @Override - public Optional generateParameterNode(boolean treatDefaultableAsRequired) { - //supported types string, int, boolean, decimal, float , primitive array - if (parameter.getName().isBlank()) { - ClientDiagnosticImp diagnostic = new ClientDiagnosticImp(DiagnosticMessages.OAS_CLIENT_111); - diagnostics.add(diagnostic); - return Optional.empty(); - } - IdentifierToken paramName = createIdentifierToken(escapeIdentifier(parameter.getName().trim())); - Optional typeNodeResult = TypeHandler.getInstance() - .getTypeNodeFromOASSchema(parameter.getSchema(), true); - if (typeNodeResult.isEmpty()) { - ClientDiagnosticImp diagnostic = new ClientDiagnosticImp(DiagnosticMessages.OAS_CLIENT_108, - paramName.text()); - diagnostics.add(diagnostic); - return Optional.empty(); - } - TypeDescriptorNode typeNode = typeNodeResult.get(); - Schema schema = parameter.getSchema(); - if (parameter.getRequired() || treatDefaultableAsRequired) { - return Optional.of(createRequiredParameterNode(createEmptyNodeList(), typeNode, paramName)); - } else if (schema.getDefault() != null) { - LiteralValueToken literalValueToken; - if (typeNode.kind().equals(STRING_TYPE_DESC)) { - literalValueToken = createLiteralValueToken(null, - '"' + schema.getDefault().toString() + '"', createEmptyMinutiaeList(), - createEmptyMinutiaeList()); - } else { - literalValueToken = createLiteralValueToken(null, schema.getDefault().toString(), - createEmptyMinutiaeList(), createEmptyMinutiaeList()); - } - return Optional.of(createDefaultableParameterNode(createEmptyNodeList(), typeNode, paramName, - createToken(EQUAL_TOKEN), literalValueToken)); - } else { - String type = typeNode.toString(); - String nillableType = type.endsWith(NILLABLE) ? type : type + NILLABLE; - BuiltinSimpleNameReferenceNode typeName = createBuiltinSimpleNameReferenceNode(null, - createIdentifierToken(nillableType)); - NilLiteralNode nilLiteralNode = - createNilLiteralNode(createToken(OPEN_PAREN_TOKEN), createToken(CLOSE_PAREN_TOKEN)); - return Optional.of(createDefaultableParameterNode(createEmptyNodeList(), typeName, paramName, - createToken(EQUAL_TOKEN), nilLiteralNode)); - } - } - - - - @Override - public List getDiagnostics() { - return diagnostics; - } - -} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java index c14937935..72fdff093 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java @@ -66,12 +66,7 @@ public HeadersParameterGenerator(List parameters, OpenAPI openAPI, Op @Override public Optional generateParameterNode() { - return generateParameterNode(false); - } - - @Override - public Optional generateParameterNode(boolean treatDefaultableAsRequired) { - ObjectSchema headersSchema = getHeadersSchema(treatDefaultableAsRequired); + ObjectSchema headersSchema = getHeadersSchema(); String operationId = GeneratorUtils.generateOperationUniqueId(operation, path, httpMethod); headersSchema.setDescription("Represents the Headers record for the operation id: " + operationId); String headersName = GeneratorUtils.getValidName(operationId + "Headers", true); @@ -86,7 +81,7 @@ public Optional generateParameterNode(boolean treatDefaultableAsR return Optional.empty(); } - boolean isDefaultable = !treatDefaultableAsRequired && isDefaultable(headersSchema); + boolean isDefaultable = isDefaultable(headersSchema); ParameterNode parameterNode = createParameterNode(headersType.get(), isDefaultable); return Optional.of(parameterNode); @@ -113,11 +108,7 @@ private ParameterNode createParameterNode(TypeDescriptorNode headersType, boolea } } - public Optional getDefaultParameterNode() { - return getDefaultParameterNode(false); - } - - public Optional getDefaultParameterNode(boolean treatDefaultableAsRequired) { + public static Optional getDefaultParameterNode() { TypeDescriptorNode stringType = createSimpleNameReferenceNode( createIdentifierToken(GeneratorConstants.STRING)); @@ -133,11 +124,6 @@ public Optional getDefaultParameterNode(boolean treatDefaultableA createToken(SyntaxKind.GT_TOKEN)); TypeDescriptorNode defaultHeadersType = createMapTypeDescriptorNode(createToken(MAP_KEYWORD), headerParamNode); - if (treatDefaultableAsRequired) { - return Optional.of(createRequiredParameterNode(createEmptyNodeList(), defaultHeadersType, - createIdentifierToken(HEADERS))); - } - LiteralValueToken defaultMapVal = createLiteralValueToken(null, "{}", createEmptyMinutiaeList(), createEmptyMinutiaeList()); BasicLiteralNode defaultMapExp = createBasicLiteralNode(null, defaultMapVal); @@ -150,11 +136,11 @@ public List getDiagnostics() { return diagnostics; } - private ObjectSchema getHeadersSchema(boolean treatDefaultableAsRequired) { + private ObjectSchema getHeadersSchema() { Map properties = parameters.stream() .collect(Collectors.toMap( parameter -> escapeIdentifier(parameter.getName()), - parameter -> getSchemaWithDescription(parameter, treatDefaultableAsRequired)) + parameter -> getSchemaWithDescription(parameter)) ); List requiredFields = parameters.stream() @@ -170,12 +156,9 @@ private ObjectSchema getHeadersSchema(boolean treatDefaultableAsRequired) { return headersSchema; } - private Schema getSchemaWithDescription(Parameter parameter, boolean treatDefaultableAsRequired) { + private Schema getSchemaWithDescription(Parameter parameter) { Schema schema = parameter.getSchema(); schema.setDescription(parameter.getDescription()); - if (treatDefaultableAsRequired) { - schema.setDefault(null); - } if (!Boolean.TRUE.equals(parameter.getRequired())) { schema.setNullable(true); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/ParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/ParameterGenerator.java index 89b1d9bb3..558f06b2e 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/ParameterGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/ParameterGenerator.java @@ -27,7 +27,6 @@ public interface ParameterGenerator { //type handler attribute Optional generateParameterNode(); - Optional generateParameterNode(boolean treatDefaultableAsRequired); List getDiagnostics(); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/PathParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/PathParameterGenerator.java index c9b196d19..09f15ebc6 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/PathParameterGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/PathParameterGenerator.java @@ -50,11 +50,6 @@ public PathParameterGenerator(Parameter parameter, OpenAPI openAPI) { @Override public Optional generateParameterNode() { - return generateParameterNode(false); - } - - @Override - public Optional generateParameterNode(boolean treatDefaultableAsRequired) { IdentifierToken paramName = createIdentifierToken(escapeIdentifier(parameter.getName())); // type should be a any type node. Schema parameterSchema = parameter.getSchema(); diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java index 02c3fcca4..65f9f4266 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java @@ -62,11 +62,6 @@ public QueriesParameterGenerator(List parameters, OpenAPI openAPI, Op @Override public Optional generateParameterNode() { - return generateParameterNode(false); - } - - @Override - public Optional generateParameterNode(boolean treatDefaultableAsRequired) { if (parameters.isEmpty()) { return Optional.empty(); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueryParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueryParameterGenerator.java deleted file mode 100644 index c92c025b4..000000000 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueryParameterGenerator.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.ballerina.openapi.core.generators.client.parameter; - -import io.ballerina.compiler.syntax.tree.IdentifierToken; -import io.ballerina.compiler.syntax.tree.LiteralValueToken; -import io.ballerina.compiler.syntax.tree.NilLiteralNode; -import io.ballerina.compiler.syntax.tree.ParameterNode; -import io.ballerina.compiler.syntax.tree.TypeDescriptorNode; -import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; -import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp; -import io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages; -import io.ballerina.openapi.core.generators.common.TypeHandler; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.media.Schema; -import io.swagger.v3.oas.models.parameters.Parameter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyMinutiaeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createLiteralValueToken; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createDefaultableParameterNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createNilLiteralNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createRequiredParameterNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createSimpleNameReferenceNode; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_PAREN_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.EQUAL_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_PAREN_TOKEN; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.STRING; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.escapeIdentifier; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.getOpenAPIType; - -public class QueryParameterGenerator implements ParameterGenerator { - OpenAPI openAPI; - Parameter parameter; - - List diagnostics = new ArrayList<>(); - public QueryParameterGenerator(Parameter parameter, OpenAPI openAPI) { - this.parameter = parameter; - this.openAPI = openAPI; - } - - @Override - public Optional generateParameterNode() { - return generateParameterNode(false); - } - - @Override - public Optional generateParameterNode(boolean treatDefaultableAsRequired) { - - TypeDescriptorNode typeNode; - - Schema parameterSchema = parameter.getSchema(); - if (parameter.getSchema() == null || parameterSchema.getType() != null && - !isQueryParamTypeSupported(parameterSchema.getType())) { - DiagnosticMessages unsupportedType = DiagnosticMessages.OAS_CLIENT_102; - ClientDiagnostic diagnostic = new ClientDiagnosticImp(unsupportedType, parameter.getName()); - diagnostics.add(diagnostic); - return Optional.empty(); - } - - //supported type: type BasicType boolean|int|float|decimal|string|map|enum; - //public type QueryParamType ()|BasicType|BasicType[]; - Optional result = TypeHandler.getInstance().getTypeNodeFromOASSchema(parameterSchema, - true); - if (result.isEmpty()) { - DiagnosticMessages unsupportedType = DiagnosticMessages.OAS_CLIENT_102; - ClientDiagnostic diagnostic = new ClientDiagnosticImp(unsupportedType, parameter.getName()); - diagnostics.add(diagnostic); - return Optional.empty(); - } - typeNode = result.get(); - // required parameter - if (parameter.getRequired() || treatDefaultableAsRequired) { - IdentifierToken paramName = - createIdentifierToken(escapeIdentifier(parameter.getName().trim())); - return Optional.of(createRequiredParameterNode(createEmptyNodeList(), typeNode, paramName)); - } else { - IdentifierToken paramName = - createIdentifierToken(escapeIdentifier(parameter.getName().trim())); - // Handle given default values in query parameter. - if (parameterSchema.get$ref() != null) { - Schema schema = openAPI.getComponents().getSchemas().get(parameterSchema.get$ref().split("/")[3]); - if (schema != null) { - parameterSchema = schema; - } - } - if (parameterSchema.getDefault() != null) { - LiteralValueToken literalValueToken; - if (Objects.equals(getOpenAPIType(parameterSchema), STRING)) { - literalValueToken = createLiteralValueToken(null, - '"' + parameterSchema.getDefault().toString() + '"', createEmptyMinutiaeList(), - createEmptyMinutiaeList()); - } else { - literalValueToken = - createLiteralValueToken(null, parameterSchema.getDefault().toString(), - createEmptyMinutiaeList(), - createEmptyMinutiaeList()); - - } - return Optional.of(createDefaultableParameterNode(createEmptyNodeList(), typeNode, paramName, - createToken(EQUAL_TOKEN), literalValueToken)); - } else { - if (!typeNode.toString().endsWith("?")) { - typeNode = createSimpleNameReferenceNode(createIdentifierToken(typeNode.toString() + "?")); - } - NilLiteralNode nilLiteralNode = - createNilLiteralNode(createToken(OPEN_PAREN_TOKEN), createToken(CLOSE_PAREN_TOKEN)); - return Optional.of(createDefaultableParameterNode(createEmptyNodeList(), typeNode, paramName, - createToken(EQUAL_TOKEN), nilLiteralNode)); - } - } - } - - @Override - public List getDiagnostics() { - return diagnostics; - } - - private boolean isQueryParamTypeSupported(String type) { - return type.equals("boolean") || type.equals("integer") || type.equals("number") || - type.equals("array") || type.equals("string") || type.equals("object"); - } -} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyGenerator.java index a62cc142a..96af8bd44 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyGenerator.java @@ -33,6 +33,7 @@ import io.swagger.v3.oas.models.media.Encoding; import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.RequestBody; import java.util.ArrayList; @@ -55,24 +56,19 @@ public class RequestBodyGenerator implements ParameterGenerator { OpenAPI openAPI; RequestBody requestBody; List diagnostics = new ArrayList<>(); - List headerParameters = new ArrayList<>(); + List headerSchemas = new ArrayList<>(); public RequestBodyGenerator(RequestBody requestBody, OpenAPI openAPI) { this.requestBody = requestBody; this.openAPI = openAPI; } - public List getHeaderParameters() { - return headerParameters; + public List getHeaderSchemas() { + return headerSchemas; } @Override public Optional generateParameterNode() { - return generateParameterNode(false); - } - - @Override - public Optional generateParameterNode(boolean treatDefaultableAsRequired) { Content requestBodyContent; String referencedRequestBodyName; TypeDescriptorNode typeDescNode = null; @@ -177,10 +173,11 @@ private void extractHeaders(Map.Entry mediaTypeEntry) { if (headers != null) { for (Map.Entry header : headers.entrySet()) { if (!headerList.contains(escapeIdentifier(header.getKey()))) { - RequestBodyHeaderParameter requestBodyHeaderParameter = new RequestBodyHeaderParameter(header); - Optional parameterNode = requestBodyHeaderParameter.generateParameterNode(); + RequestBodyHeaderParameter requestBodyHeaderParameter = new RequestBodyHeaderParameter( + header); + Optional parameter = requestBodyHeaderParameter.generateParameterSchema(); - parameterNode.ifPresent(node -> headerParameters.add(node)); + parameter.ifPresent(param -> headerSchemas.add(param)); diagnostics.addAll(requestBodyHeaderParameter.getDiagnostics()); headerList.add(escapeIdentifier(header.getKey())); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyGeneratorNew.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyGeneratorNew.java deleted file mode 100644 index 3000cfa5b..000000000 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyGeneratorNew.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.ballerina.openapi.core.generators.client.parameter; - -import io.ballerina.compiler.syntax.tree.ParameterNode; -import io.ballerina.compiler.syntax.tree.TypeDescriptorNode; -import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; -import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp; -import io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages; -import io.ballerina.openapi.core.generators.common.GeneratorUtils; -import io.ballerina.openapi.core.generators.common.TypeHandler; -import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.headers.Header; -import io.swagger.v3.oas.models.media.ArraySchema; -import io.swagger.v3.oas.models.media.Content; -import io.swagger.v3.oas.models.media.Encoding; -import io.swagger.v3.oas.models.media.MediaType; -import io.swagger.v3.oas.models.media.Schema; -import io.swagger.v3.oas.models.parameters.Parameter; -import io.swagger.v3.oas.models.parameters.RequestBody; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createRequiredParameterNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createSimpleNameReferenceNode; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.APPLICATION_OCTET_STREAM; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HTTP_REQUEST; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.escapeIdentifier; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.extractReferenceType; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.getBallerinaMediaType; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.getOpenAPIType; - -public class RequestBodyGeneratorNew implements ParameterGenerator { - OpenAPI openAPI; - RequestBody requestBody; - List diagnostics = new ArrayList<>(); - List headerSchemas = new ArrayList<>(); - - public RequestBodyGeneratorNew(RequestBody requestBody, OpenAPI openAPI) { - this.requestBody = requestBody; - this.openAPI = openAPI; - } - - public List getHeaderSchemas() { - return headerSchemas; - } - - @Override - public Optional generateParameterNode() { - return generateParameterNode(false); - } - - @Override - public Optional generateParameterNode(boolean treatDefaultableAsRequired) { - Content requestBodyContent; - String referencedRequestBodyName; - TypeDescriptorNode typeDescNode = null; - if (requestBody.get$ref() != null) { - try { - referencedRequestBodyName = extractReferenceType(requestBody.get$ref()).trim(); - } catch (BallerinaOpenApiException e) { - ClientDiagnosticImp diagnostic = new ClientDiagnosticImp(DiagnosticMessages.OAS_CLIENT_109, - requestBody.get$ref()); - diagnostics.add(diagnostic); - return Optional.empty(); - } - RequestBody referencedRequestBody = openAPI.getComponents() - .getRequestBodies().get(referencedRequestBodyName); - requestBodyContent = referencedRequestBody.getContent(); - // note : when there is referenced request body, the description at the reference is ignored. - // Need to consider the description at the component level - requestBody.setDescription(referencedRequestBody.getDescription()); - } else { - requestBodyContent = requestBody.getContent(); - } - if (requestBodyContent == null || requestBodyContent.isEmpty()) { - return Optional.empty(); - } - for (Map.Entry mediaTypeEntry : requestBodyContent.entrySet()) { - // This implementation currently for first content type - Schema schema = mediaTypeEntry.getValue().getSchema(); - String paramType; - //Take payload type - if (schema != null && GeneratorUtils.isSupportedMediaType(mediaTypeEntry)) { - String mediaTypeEntryKey = mediaTypeEntry.getKey(); - if (mediaTypeEntryKey.equals(APPLICATION_OCTET_STREAM) || - mediaTypeEntryKey.matches("application/.*\\+octet-stream")) { - paramType = getBallerinaMediaType(mediaTypeEntryKey, true); - typeDescNode = createSimpleNameReferenceNode(createIdentifierToken(paramType)); - } else { - if (schema.get$ref() != null) { - Optional node = TypeHandler.getInstance().getTypeNodeFromOASSchema(schema); - if (node.isEmpty()) { - return Optional.empty(); - } - typeDescNode = node.get(); - } else if (getOpenAPIType(schema) != null || schema.getProperties() != null) { - Optional resultNode = TypeHandler.getInstance(). - getTypeNodeFromOASSchema(schema); - - if (resultNode.isEmpty()) { - if (schema instanceof ArraySchema) { - paramType = getBallerinaMediaType(mediaTypeEntry.getKey(), true) + "[]"; - typeDescNode = createSimpleNameReferenceNode(createIdentifierToken(paramType)); - } else { - paramType = getBallerinaMediaType(mediaTypeEntryKey, true); - typeDescNode = createSimpleNameReferenceNode(createIdentifierToken(paramType)); - } - } else { - typeDescNode = resultNode.get(); - if (schema instanceof ArraySchema && typeDescNode.toSourceCode().contains("anydata")) { - paramType = getBallerinaMediaType(mediaTypeEntry.getKey(), true) + "[]"; - typeDescNode = createSimpleNameReferenceNode(createIdentifierToken(paramType)); - } - } - - } else { - paramType = getBallerinaMediaType(mediaTypeEntryKey, true); - typeDescNode = createSimpleNameReferenceNode(createIdentifierToken(paramType)); - } - } - - //handle headers - if (mediaTypeEntryKey.equals(javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA) || - mediaTypeEntryKey.matches("multipart/.*\\+form-data")) { - extractHeaders(mediaTypeEntry); - } - } else { - if (mediaTypeEntry.getKey().equals(javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA) - && mediaTypeEntry.getValue().getEncoding() != null) { - extractHeaders(mediaTypeEntry); - } else { - paramType = getBallerinaMediaType(mediaTypeEntry.getKey(), true); - typeDescNode = createSimpleNameReferenceNode(createIdentifierToken(paramType)); - } - } - break; - } - if (typeDescNode != null) { - String reqBody = typeDescNode.toSourceCode().equals(HTTP_REQUEST) ? "request" : "payload"; - return Optional.of(createRequiredParameterNode(createEmptyNodeList(), typeDescNode, - createIdentifierToken(reqBody))); - } else { - return Optional.empty(); - } - } - - private void extractHeaders(Map.Entry mediaTypeEntry) { - Map encodingHeaders = mediaTypeEntry.getValue().getEncoding(); - if (encodingHeaders == null) { - return; - } - List headerList = new ArrayList<>(); - for (Map.Entry entry : encodingHeaders.entrySet()) { - Map headers = entry.getValue().getHeaders(); - if (headers != null) { - for (Map.Entry header : headers.entrySet()) { - if (!headerList.contains(escapeIdentifier(header.getKey()))) { - RequestBodyHeaderParameterNew requestBodyHeaderParameter = new RequestBodyHeaderParameterNew( - header); - Optional parameter = requestBodyHeaderParameter.generateParameterSchema(); - - parameter.ifPresent(param -> headerSchemas.add(param)); - diagnostics.addAll(requestBodyHeaderParameter.getDiagnostics()); - headerList.add(escapeIdentifier(header.getKey())); - } - } - } - } - } - - @Override - public List getDiagnostics() { - return diagnostics; - } -} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyHeaderParameter.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyHeaderParameter.java index 5b2f5fb07..8a4e69d8c 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyHeaderParameter.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyHeaderParameter.java @@ -18,34 +18,17 @@ package io.ballerina.openapi.core.generators.client.parameter; -import io.ballerina.compiler.syntax.tree.NilLiteralNode; -import io.ballerina.compiler.syntax.tree.ParameterNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.TypeDescriptorNode; import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; -import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp; -import io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages; -import io.ballerina.openapi.core.generators.common.TypeHandler; import io.swagger.v3.oas.models.headers.Header; import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createDefaultableParameterNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createNilLiteralNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createRequiredParameterNode; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_PAREN_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.EQUAL_TOKEN; -import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_PAREN_TOKEN; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.escapeIdentifier; - -public class RequestBodyHeaderParameter implements ParameterGenerator { +public class RequestBodyHeaderParameter { Map.Entry header; List diagnostics = new ArrayList<>(); @@ -53,41 +36,25 @@ public class RequestBodyHeaderParameter implements ParameterGenerator { this.header = header; } - @Override - public Optional generateParameterNode() { - return generateParameterNode(false); + public Optional generateParameterSchema() { + Schema schema = getSchemaWithDescription(header.getValue()); + Parameter parameter = new Parameter(); + parameter.setSchema(schema); + parameter.setName(header.getKey()); + parameter.setIn("header"); + parameter.setRequired(header.getValue().getRequired()); + return Optional.of(parameter); } - @Override - public Optional generateParameterNode(boolean treatDefaultableAsRequired) { - Schema schema = header.getValue().getSchema(); - Boolean required = header.getValue().getRequired(); - if (required == null) { - required = false; + private Schema getSchemaWithDescription(Header header) { + Schema schema = header.getSchema(); + schema.setDescription(header.getDescription()); + if (!Boolean.TRUE.equals(header.getRequired())) { schema.setNullable(true); } - Optional typeNodeResult = TypeHandler.getInstance() - .getTypeNodeFromOASSchema(schema, true); - if (typeNodeResult.isEmpty()) { - ClientDiagnosticImp diagnosticImp = new ClientDiagnosticImp(DiagnosticMessages.OAS_CLIENT_108, - header.getKey()); - diagnostics.add(diagnosticImp); - return Optional.empty(); - } - if (required || treatDefaultableAsRequired) { - RequiredParameterNode requiredParameterNode = createRequiredParameterNode(createEmptyNodeList(), - typeNodeResult.get(), createIdentifierToken(escapeIdentifier(header.getKey()))); - return Optional.of(requiredParameterNode); - } else { - NilLiteralNode nilLiteralNode = - createNilLiteralNode(createToken(OPEN_PAREN_TOKEN), createToken(CLOSE_PAREN_TOKEN)); - return Optional.of(createDefaultableParameterNode(createEmptyNodeList(), typeNodeResult.get(), - createIdentifierToken(escapeIdentifier(header.getKey())), createToken(EQUAL_TOKEN), - nilLiteralNode)); - } + return schema; } - @Override public List getDiagnostics() { return diagnostics; } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyHeaderParameterNew.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyHeaderParameterNew.java deleted file mode 100644 index 215134e27..000000000 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/RequestBodyHeaderParameterNew.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.ballerina.openapi.core.generators.client.parameter; - -import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; -import io.swagger.v3.oas.models.headers.Header; -import io.swagger.v3.oas.models.media.Schema; -import io.swagger.v3.oas.models.parameters.Parameter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class RequestBodyHeaderParameterNew { - Map.Entry header; - List diagnostics = new ArrayList<>(); - - RequestBodyHeaderParameterNew(Map.Entry header) { - this.header = header; - } - - public Optional generateParameterSchema() { - return generateParameterNode(false); - } - - public Optional generateParameterNode(boolean treatDefaultableAsRequired) { - Schema schema = getSchemaWithDescription(header.getValue(), treatDefaultableAsRequired); - Parameter parameter = new Parameter(); - parameter.setSchema(schema); - parameter.setName(header.getKey()); - parameter.setIn("header"); - parameter.setRequired(header.getValue().getRequired()); - return Optional.of(parameter); - } - - private Schema getSchemaWithDescription(Header header, boolean treatDefaultableAsRequired) { - Schema schema = header.getSchema(); - schema.setDescription(header.getDescription()); - if (treatDefaultableAsRequired) { - schema.setDefault(null); - } - if (!Boolean.TRUE.equals(header.getRequired())) { - schema.setNullable(true); - } - return schema; - } - - public List getDiagnostics() { - return diagnostics; - } - -} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java index 190b4d673..891f25fc8 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java @@ -24,6 +24,7 @@ import io.ballerina.compiler.syntax.tree.DefaultableParameterNode; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; +import io.ballerina.compiler.syntax.tree.IncludedRecordParameterNode; import io.ballerina.compiler.syntax.tree.MarkdownDocumentationNode; import io.ballerina.compiler.syntax.tree.MarkdownParameterDocumentationLineNode; import io.ballerina.compiler.syntax.tree.MetadataNode; @@ -61,6 +62,8 @@ import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; import static io.ballerina.compiler.syntax.tree.NodeFactory.createMarkdownDocumentationNode; import static io.ballerina.compiler.syntax.tree.NodeFactory.createMetadataNode; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEADERS; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERIES; import static io.ballerina.openapi.core.generators.common.GeneratorUtils.escapeIdentifier; import static io.ballerina.openapi.core.generators.common.GeneratorUtils.extractReferenceType; import static io.ballerina.openapi.core.generators.common.GeneratorUtils.getBallerinaMediaType; @@ -204,10 +207,12 @@ private FunctionDefinitionNode updateDocCommentsForFunctionNode( SeparatedNodeList parameters = functionSignatureNode.parameters(); List updatedParamsRequired = new ArrayList<>(); List updatedParamsDefault = new ArrayList<>(); + List updatedIncludedParam = new ArrayList<>(); HashMap collection = getParameterNodeHashMap(parameters); //todo parameter reference - updateParameterNodes(docs, operation, updatedParamsRequired, updatedParamsDefault, collection); + updateParameterNodes(isResource, docs, operation, updatedParamsRequired, updatedParamsDefault, + collection); if (collection.size() > 0) { collection.forEach((keyParam, value) -> { if (value instanceof RequiredParameterNode reParam) { @@ -216,10 +221,14 @@ private FunctionDefinitionNode updateDocCommentsForFunctionNode( } else if (value instanceof DefaultableParameterNode deParam) { updatedParamsDefault.add(deParam); updatedParamsDefault.add(createToken(SyntaxKind.COMMA_TOKEN)); + } else if (value instanceof IncludedRecordParameterNode incParam) { + updatedIncludedParam.add(incParam); + updatedIncludedParam.add(createToken(SyntaxKind.COMMA_TOKEN)); } }); } updatedParamsRequired.addAll(updatedParamsDefault); + updatedParamsRequired.addAll(updatedIncludedParam); if (!updatedParamsRequired.isEmpty()) { if (updatedParamsRequired.get(updatedParamsRequired.size() - 1) instanceof Token) { updatedParamsRequired.remove(updatedParamsRequired.size() - 1); @@ -304,17 +313,21 @@ private void addRequestBodyDoc(List docs, RequestBody requestBody) { } } - private static void updateParameterNodes(List docs, Operation operation, List updatedParamsRequired, - List updatedParamsDefault, HashMap collection) { + private static void updateParameterNodes(boolean isResource, List docs, Operation operation, + List updatedParamsRequired, List updatedParamsDefault, + HashMap collection) { List deprecatedParamDocComments = new ArrayList<>(); operation.getParameters().forEach(parameter -> { + if (isResource && (parameter.getIn().equals("header") || parameter.getIn().equals("query"))) { + return; + } + List paramAnnot = new ArrayList<>(); String parameterDescription; String parameterName = parameter.getName(); - if (parameter.getIn().equals("path") || parameter.getIn().equals("header") || - (parameter.getIn().equals("query"))) { + if (parameter.getIn().equals("path") || parameter.getIn().equals("header") + || parameter.getIn().equals("query")) { parameterName = escapeIdentifier(parameter.getName()); // add deprecated annotation if (parameter.getDeprecated() != null && parameter.getDeprecated()) { @@ -334,6 +347,14 @@ private static void updateParameterNodes(List docs, Operation operation, L docs.add(createAPIParamDocFromString(parameterName, parameterDescription)); } }); + if (isResource) { + if (collection.containsKey(HEADERS)) { + docs.add(createAPIParamDoc(HEADERS, "Headers to be sent with the request")); + } + if (collection.containsKey(QUERIES)) { + docs.add(createAPIParamDoc(QUERIES, "Queries to be sent with the request")); + } + } docs.addAll(deprecatedParamDocComments); } @@ -345,6 +366,8 @@ private static HashMap getParameterNodeHashMap(SeparatedN collection.put(reParam.paramName().get().toString(), reParam); } else if (node instanceof DefaultableParameterNode deParam) { collection.put(deParam.paramName().get().toString(), deParam); + } else if (node instanceof IncludedRecordParameterNode incParam) { + collection.put(incParam.paramName().get().toString(), incParam); } } return collection; diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGeneratorNew.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGeneratorNew.java deleted file mode 100644 index 35cb15544..000000000 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGeneratorNew.java +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.ballerina.openapi.core.generators.document; - -import io.ballerina.compiler.syntax.tree.AbstractNodeFactory; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ClassDefinitionNode; -import io.ballerina.compiler.syntax.tree.DefaultableParameterNode; -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; -import io.ballerina.compiler.syntax.tree.IncludedRecordParameterNode; -import io.ballerina.compiler.syntax.tree.MarkdownDocumentationNode; -import io.ballerina.compiler.syntax.tree.MarkdownParameterDocumentationLineNode; -import io.ballerina.compiler.syntax.tree.MetadataNode; -import io.ballerina.compiler.syntax.tree.ModuleMemberDeclarationNode; -import io.ballerina.compiler.syntax.tree.ModulePartNode; -import io.ballerina.compiler.syntax.tree.Node; -import io.ballerina.compiler.syntax.tree.NodeList; -import io.ballerina.compiler.syntax.tree.ParameterNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.SeparatedNodeList; -import io.ballerina.compiler.syntax.tree.SyntaxKind; -import io.ballerina.compiler.syntax.tree.SyntaxTree; -import io.ballerina.compiler.syntax.tree.Token; -import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; -import io.swagger.v3.oas.models.OpenAPI; -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.media.Content; -import io.swagger.v3.oas.models.parameters.RequestBody; -import io.swagger.v3.oas.models.responses.ApiResponse; -import io.swagger.v3.oas.models.responses.ApiResponses; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; - -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createNodeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createSeparatedNodeList; -import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createMarkdownDocumentationNode; -import static io.ballerina.compiler.syntax.tree.NodeFactory.createMetadataNode; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEADERS; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERIES; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.escapeIdentifier; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.extractReferenceType; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.getBallerinaMediaType; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.replaceContentWithinBrackets; -import static io.ballerina.openapi.core.generators.document.DocCommentsGeneratorUtil.createAPIDescriptionDoc; -import static io.ballerina.openapi.core.generators.document.DocCommentsGeneratorUtil.createAPIParamDoc; -import static io.ballerina.openapi.core.generators.document.DocCommentsGeneratorUtil.createAPIParamDocFromString; -import static io.ballerina.openapi.core.generators.document.DocCommentsGeneratorUtil.extractDeprecatedAnnotation; -import static io.ballerina.openapi.core.generators.document.DocCommentsGeneratorUtil.extractDeprecatedAnnotationDetails; -import static io.ballerina.openapi.core.generators.document.DocCommentsGeneratorUtil.extractDisplayAnnotation; -import static io.ballerina.openapi.core.generators.document.DocCommentsGeneratorUtil.updatedAnnotationInParameterNode; - -public class ClientDocCommentGeneratorNew implements DocCommentsGenerator { - OpenAPI openAPI; - SyntaxTree syntaxTree; - boolean isResource; - - public ClientDocCommentGeneratorNew(SyntaxTree syntaxTree, OpenAPI openAPI, boolean isResource) { - this.openAPI = openAPI; - this.syntaxTree = syntaxTree; - this.isResource = isResource; - } - @Override - public SyntaxTree updateSyntaxTreeWithDocComments() { - //collect all the operation details - HashMap operationDetailsMap = new HashMap<>(); - Paths paths = openAPI.getPaths(); - extractOperations(operationDetailsMap, paths); - //Generate type doc comments - Node rootNode = syntaxTree.rootNode(); - ModulePartNode modulePartNode = (ModulePartNode) rootNode; - NodeList members = modulePartNode.members(); - List updatedMembers = new ArrayList<>(); - members.forEach(member -> { - if (member.kind().equals(SyntaxKind.CLASS_DEFINITION)) { - ClassDefinitionNode classDef = (ClassDefinitionNode) member; - HashMap updatedList = new HashMap<>(); - NodeList classMembers = classDef.members(); - //sort these members according to .toString(); - List storeMembers = new ArrayList<>(); - List clientInitNodes = new ArrayList<>(); - classMembers.forEach(classMember -> { - String sortKey = ""; - if (classMember.kind().equals(SyntaxKind.OBJECT_METHOD_DEFINITION) || - classMember.kind().equals(SyntaxKind.RESOURCE_ACCESSOR_DEFINITION)) { - FunctionDefinitionNode funcDef = (FunctionDefinitionNode) classMember; - - //remote : operationId - if (isResource) { - sortKey = funcDef.toSourceCode(); - storeMembers.add(sortKey); - NodeList nodes = funcDef.relativeResourcePath(); - StringBuilder path = new StringBuilder(); - for (Node node: nodes) { - path.append(node.toString().replace("\"", "")); - } - String key = replaceContentWithinBrackets(path.toString(), "XXX") + "_" + - funcDef.functionName().text(); - funcDef = updateDocCommentsForFunctionNode(operationDetailsMap, funcDef, key); - } else { - sortKey = funcDef.functionName().text(); - storeMembers.add(sortKey); - String key = funcDef.functionName().text(); - funcDef = updateDocCommentsForFunctionNode(operationDetailsMap, funcDef, key); - } - classMember = funcDef; - } else { - clientInitNodes.add(classMember); - } - updatedList.put(sortKey, classMember); - }); - //sort the members - List sortedNodes = new ArrayList<>(); - sortedNodes.addAll(clientInitNodes); - storeMembers.sort(String::compareTo); - for (String memberStr: storeMembers) { - sortedNodes.add(updatedList.get(memberStr)); - } - classDef = classDef.modify( - classDef.metadata().orElse(null), - classDef.visibilityQualifier().orElse(null), - classDef.classTypeQualifiers(), - classDef.classKeyword(), - classDef.className(), - classDef.openBrace(), - updatedList.isEmpty() ? classDef.members() : createNodeList(sortedNodes), - classDef.closeBrace(), - classDef.semicolonToken().orElse(null)); - member = classDef; - } - - updatedMembers.add(member); - NodeList clientMembers = AbstractNodeFactory.createNodeList(updatedMembers); - ModulePartNode updatedmodulePartNode = modulePartNode.modify(modulePartNode.imports(), clientMembers, - modulePartNode.eofToken()); - - syntaxTree = syntaxTree.modifyWith(updatedmodulePartNode); - }); - return syntaxTree; - } - - private void extractOperations(HashMap operationDetailsMap, Paths paths) { - paths.forEach((path, pathItem) -> { - for (Map.Entry entry : pathItem.readOperationsMap().entrySet()) { - PathItem.HttpMethod method = entry.getKey(); - Operation operation = entry.getValue(); - if (!isResource) { - operationDetailsMap.put(operation.getOperationId(), - new OperationDetails(operation.getOperationId(), operation, path, method.name())); - } else { - path = path.equals("/") ? "." : path; - String key = replaceContentWithinBrackets(path.replaceFirst("/", ""), - "XXX") + "_" + method.name().toLowerCase(Locale.ENGLISH); - operationDetailsMap.put(key, new OperationDetails(operation.getOperationId(), - operation, path, method.name())); - } - } - }); - } - - private FunctionDefinitionNode updateDocCommentsForFunctionNode( - HashMap operationDetailsMap, FunctionDefinitionNode funcDef, String key) { - OperationDetails operationDetails = operationDetailsMap.get(key); - if (operationDetails != null) { - List docs = new ArrayList<>(); - List annotations = new ArrayList<>(); - Operation operation = operationDetails.operation(); - //function main comment - if (operation.getSummary() != null) { - docs.addAll(createAPIDescriptionDoc(operation.getSummary(), true)); - } else if (operation.getDescription() != null) { - docs.addAll(createAPIDescriptionDoc(operation.getDescription(), true)); - } - //function display annotation - if (operation.getExtensions() != null) { - extractDisplayAnnotation(operation.getExtensions(), annotations); - } - FunctionSignatureNode functionSignatureNode = funcDef.functionSignature(); - //function parameters - if (operation.getParameters() != null) { - SeparatedNodeList parameters = functionSignatureNode.parameters(); - List updatedParamsRequired = new ArrayList<>(); - List updatedParamsDefault = new ArrayList<>(); - List updatedIncludedParam = new ArrayList<>(); - - HashMap collection = getParameterNodeHashMap(parameters); - //todo parameter reference - updateParameterNodes(isResource, docs, operation, updatedParamsRequired, updatedParamsDefault, - collection); - if (collection.size() > 0) { - collection.forEach((keyParam, value) -> { - if (value instanceof RequiredParameterNode reParam) { - updatedParamsRequired.add(reParam); - updatedParamsRequired.add(createToken(SyntaxKind.COMMA_TOKEN)); - } else if (value instanceof DefaultableParameterNode deParam) { - updatedParamsDefault.add(deParam); - updatedParamsDefault.add(createToken(SyntaxKind.COMMA_TOKEN)); - } else if (value instanceof IncludedRecordParameterNode incParam) { - updatedIncludedParam.add(incParam); - updatedIncludedParam.add(createToken(SyntaxKind.COMMA_TOKEN)); - } - }); - } - updatedParamsRequired.addAll(updatedParamsDefault); - updatedParamsRequired.addAll(updatedIncludedParam); - if (!updatedParamsRequired.isEmpty()) { - if (updatedParamsRequired.get(updatedParamsRequired.size() - 1) instanceof Token) { - updatedParamsRequired.remove(updatedParamsRequired.size() - 1); - } - functionSignatureNode = functionSignatureNode.modify( - functionSignatureNode.openParenToken(), - createSeparatedNodeList(updatedParamsRequired), - functionSignatureNode.closeParenToken(), - functionSignatureNode.returnTypeDesc().orElse(null)); - - } - } - RequestBody requestBody = operation.getRequestBody(); - if (requestBody != null) { - addRequestBodyDoc(docs, requestBody); - } - //todo response - if (operation.getResponses() != null) { - ApiResponses responses = operation.getResponses(); - Collection values = responses.values(); - Iterator iteratorRes = values.iterator(); - if (iteratorRes.hasNext()) { - ApiResponse response = iteratorRes.next(); - if (response.getDescription() != null && !response.getDescription().isBlank()) { - MarkdownParameterDocumentationLineNode returnDoc = createAPIParamDoc("return", - response.getDescription()); - docs.add(returnDoc); - } - } - } - if (operation.getDeprecated() != null && operation.getDeprecated()) { - extractDeprecatedAnnotation(operation.getExtensions(), - docs, annotations); - } - - MarkdownDocumentationNode documentationNode = createMarkdownDocumentationNode(createNodeList(docs)); - Optional metadata = funcDef.metadata(); - MetadataNode metadataNode; - if (metadata.isEmpty()) { - metadataNode = createMetadataNode(documentationNode, createNodeList(annotations)); - } else { - metadataNode = metadata.get(); - metadataNode = createMetadataNode(documentationNode, - metadataNode.annotations().isEmpty() ? createNodeList(annotations) : - metadataNode.annotations().addAll(annotations)); - } - funcDef = funcDef.modify( - funcDef.kind(), - metadataNode, - funcDef.qualifierList(), - funcDef.functionKeyword(), - funcDef.functionName(), - funcDef.relativeResourcePath(), - functionSignatureNode, - funcDef.functionBody()); - } - return funcDef; - } - - private void addRequestBodyDoc(List docs, RequestBody requestBody) { - if (requestBody.get$ref() != null) { - try { - requestBody = openAPI.getComponents().getRequestBodies().get( - extractReferenceType(requestBody.get$ref())); - } catch (BallerinaOpenApiException e) { - requestBody = new RequestBody(); - } - } - Content content = requestBody.getContent(); - final String[] paramName = {"http:Request"}; - if (content != null) { - content.entrySet().stream().findFirst().ifPresent(mediaType -> { - paramName[0] = getBallerinaMediaType(mediaType.getKey(), true); - }); - } else { - paramName[0] = "http:Request"; - } - - if (requestBody.getDescription() != null) { - String description = requestBody.getDescription().split("\n")[0]; - docs.add(createAPIParamDoc(paramName[0].equals("http:Request") ? "request" : "payload", description)); - } - } - - private static void updateParameterNodes(boolean isResource, List docs, Operation operation, - List updatedParamsRequired, List updatedParamsDefault, - HashMap collection) { - List deprecatedParamDocComments = new ArrayList<>(); - operation.getParameters().forEach(parameter -> { - if (isResource && (parameter.getIn().equals("header") || parameter.getIn().equals("query"))) { - return; - } - - List paramAnnot = new ArrayList<>(); - String parameterDescription; - String parameterName = parameter.getName(); - - if (parameter.getIn().equals("path") || parameter.getIn().equals("header") - || parameter.getIn().equals("query")) { - parameterName = escapeIdentifier(parameter.getName()); - // add deprecated annotation - if (parameter.getDeprecated() != null && parameter.getDeprecated()) { - extractDeprecatedAnnotationDetails(deprecatedParamDocComments, parameter, paramAnnot); - } - if (parameter.getExtensions() != null) { - extractDisplayAnnotation(parameter.getExtensions(), paramAnnot); - - } - ParameterNode parameterNode = collection.get(parameterName); - updatedAnnotationInParameterNode(updatedParamsRequired, updatedParamsDefault, - paramAnnot, parameterNode); - collection.remove(parameterName); - } - if (parameter.getDescription() != null) { - parameterDescription = parameter.getDescription(); - docs.add(createAPIParamDocFromString(parameterName, parameterDescription)); - } - }); - if (isResource) { - if (collection.containsKey(HEADERS)) { - docs.add(createAPIParamDoc(HEADERS, "Headers to be sent with the request")); - } - if (collection.containsKey(QUERIES)) { - docs.add(createAPIParamDoc(QUERIES, "Queries to be sent with the request")); - } - } - docs.addAll(deprecatedParamDocComments); - } - - - private static HashMap getParameterNodeHashMap(SeparatedNodeList parameters) { - HashMap collection = new HashMap<>(); - for (ParameterNode node : parameters) { - if (node instanceof RequiredParameterNode reParam) { - collection.put(reParam.paramName().get().toString(), reParam); - } else if (node instanceof DefaultableParameterNode deParam) { - collection.put(deParam.paramName().get().toString(), deParam); - } else if (node instanceof IncludedRecordParameterNode incParam) { - collection.put(incParam.paramName().get().toString(), incParam); - } - } - return collection; - } -} diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/DocCommentGeneratorImp.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/DocCommentGeneratorImp.java index 147f35b43..e0d2fb599 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/DocCommentGeneratorImp.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/DocCommentGeneratorImp.java @@ -43,7 +43,7 @@ public SyntaxTree updateSyntaxTreeWithDocComments() { switch (type) { case GEN_CLIENT: //generate client doc comments - ClientDocCommentGeneratorNew clientDocCommentGenerator = new ClientDocCommentGeneratorNew(syntaxTree, + ClientDocCommentGenerator clientDocCommentGenerator = new ClientDocCommentGenerator(syntaxTree, openAPI, true); syntaxTree = clientDocCommentGenerator.updateSyntaxTreeWithDocComments(); break; From 1703d36c3bf0b302da7cb2a67a8adafc5d7857f8 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Tue, 30 Apr 2024 10:19:00 +0530 Subject: [PATCH 03/31] Add doc comment issue and parameter order issue --- .../client/FunctionSignatureGenerator.java | 2 +- ...oteExternalFunctionSignatureGenerator.java | 4 +- .../RemoteFunctionSignatureGenerator.java | 68 +++++++++++-------- ...rceExternalFunctionSignatureGenerator.java | 4 +- .../ResourceFunctionSignatureGenerator.java | 68 +++++++++++-------- .../parameter/HeadersParameterGenerator.java | 10 +-- .../parameter/QueriesParameterGenerator.java | 10 +-- .../generators/common/GeneratorUtils.java | 3 + .../document/ClientDocCommentGenerator.java | 61 ++++++++++------- 9 files changed, 137 insertions(+), 93 deletions(-) diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionSignatureGenerator.java index e2a785d61..e7865960c 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionSignatureGenerator.java @@ -30,6 +30,6 @@ public interface FunctionSignatureGenerator { Optional generateFunctionSignature() throws FunctionSignatureGeneratorException; List getDiagnostics(); - record ParametersInfo(List parameterList, List defaultable) { + record ParametersInfo(List requiredParams, List defaultableParams, List includedParam) { } } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionSignatureGenerator.java index 9c01f8370..5247ce6c8 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteExternalFunctionSignatureGenerator.java @@ -95,8 +95,8 @@ protected ParametersInfo populateTargetTypeParam(TypeDescriptorNode targetType, createToken(GT_TOKEN)); ParameterNode targetTypeParam = createDefaultableParameterNode(createEmptyNodeList(), targetType, createIdentifierToken("targetType"), createToken(EQUAL_TOKEN), inferredToken); - parametersInfo.defaultable().add(targetTypeParam); - parametersInfo.defaultable().add(createToken(COMMA_TOKEN)); + parametersInfo.defaultableParams().add(targetTypeParam); + parametersInfo.defaultableParams().add(createToken(COMMA_TOKEN)); return parametersInfo; } } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGenerator.java index bfeef0a65..c521b8555 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGenerator.java @@ -79,18 +79,8 @@ public Optional generateFunctionSignature() { return Optional.empty(); } - List defaultable = parametersInfo.defaultable(); - List parameterList = parametersInfo.parameterList(); - - // filter defaultable parameters - if (!defaultable.isEmpty()) { - parameterList.addAll(defaultable); - } - // Remove the last comma - if (!parameterList.isEmpty()) { - parameterList.remove(parameterList.size() - 1); - } - SeparatedNodeList parameterNodes = createSeparatedNodeList(parameterList); + List paramList = getParameterNodes(parametersInfo); + SeparatedNodeList parameterNodes = createSeparatedNodeList(paramList); // 3. return statements FunctionReturnTypeGeneratorImp returnTypeGenerator = getFunctionReturnTypeGenerator(); @@ -105,9 +95,32 @@ public Optional generateFunctionSignature() { createToken(CLOSE_PAREN_TOKEN), returnTypeDescriptorNode)); } + private static List getParameterNodes(ParametersInfo parametersInfo) { + List defaultableParams = parametersInfo.defaultableParams(); + List params = parametersInfo.requiredParams(); + List includedParam = parametersInfo.includedParam(); + + // Add defaultableParams parameters + if (!defaultableParams.isEmpty()) { + params.addAll(defaultableParams); + } + + // Add included record parameter + if (!includedParam.isEmpty()) { + params.addAll(includedParam); + } + + // Remove the last comma + if (!params.isEmpty()) { + params.remove(params.size() - 1); + } + return params; + } + protected ParametersInfo getParametersInfo(List parameters) { - List parameterList = new ArrayList<>(); - List defaultable = new ArrayList<>(); + List requiredParams = new ArrayList<>(); + List defaultableParams = new ArrayList<>(); + List includedParam = new ArrayList<>(); Token comma = createToken(COMMA_TOKEN); List headerParameters = new ArrayList<>(); @@ -124,8 +137,8 @@ protected ParametersInfo getParametersInfo(List parameters) { return null; } // Path parameters are always required. - parameterList.add(param.get()); - parameterList.add(comma); + requiredParams.add(param.get()); + requiredParams.add(comma); } } } @@ -140,8 +153,8 @@ protected ParametersInfo getParametersInfo(List parameters) { return null; } headerParameters = requestBodyGenerator.getHeaderSchemas(); - parameterList.add(requestBody.get()); - parameterList.add(comma); + requiredParams.add(requestBody.get()); + requiredParams.add(comma); } // 2. parameters - query, headers @@ -161,11 +174,11 @@ protected ParametersInfo getParametersInfo(List parameters) { if (headers.isPresent()) { hasHeadersParam = true; if (headers.get() instanceof RequiredParameterNode headerNode) { - parameterList.add(headerNode); - parameterList.add(comma); + requiredParams.add(headerNode); + requiredParams.add(comma); } else { - defaultable.add(headers.get()); - defaultable.add(comma); + defaultableParams.add(headers.get()); + defaultableParams.add(comma); } } else if (!headerParameters.isEmpty()) { diagnostics.addAll(headersParameterGenerator.getDiagnostics()); @@ -177,8 +190,8 @@ protected ParametersInfo getParametersInfo(List parameters) { Optional queries = queriesParameterGenerator.generateParameterNode(); if (queries.isPresent()) { hasQueriesParam = true; - parameterList.add(queries.get()); - parameterList.add(comma); + includedParam.add(queries.get()); + includedParam.add(comma); } else if (!queryParameters.isEmpty()) { diagnostics.addAll(queriesParameterGenerator.getDiagnostics()); return null; @@ -187,11 +200,12 @@ protected ParametersInfo getParametersInfo(List parameters) { ParameterNode defaultHeaderParam = HeadersParameterGenerator.getDefaultParameterNode().orElse(null); if (defaultHeaderParam != null) { hasDefaultHeader = true; - defaultable.add(defaultHeaderParam); - defaultable.add(comma); + hasHeadersParam = true; + defaultableParams.add(defaultHeaderParam); + defaultableParams.add(comma); } } - return new ParametersInfo(parameterList, defaultable); + return new ParametersInfo(requiredParams, defaultableParams, includedParam); } private void populateQueryAndHeaderParameters(List parameters, List queryParameters, diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionSignatureGenerator.java index af64d19ce..c2318deb9 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceExternalFunctionSignatureGenerator.java @@ -95,8 +95,8 @@ protected ParametersInfo populateTargetTypeParam(TypeDescriptorNode targetType, createToken(GT_TOKEN)); ParameterNode targetTypeParam = createDefaultableParameterNode(createEmptyNodeList(), targetType, createIdentifierToken("targetType"), createToken(EQUAL_TOKEN), inferredToken); - parametersInfo.defaultable().add(targetTypeParam); - parametersInfo.defaultable().add(createToken(COMMA_TOKEN)); + parametersInfo.defaultableParams().add(targetTypeParam); + parametersInfo.defaultableParams().add(createToken(COMMA_TOKEN)); return parametersInfo; } } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGenerator.java index 268f0075c..9dc022730 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGenerator.java @@ -71,7 +71,6 @@ public ResourceFunctionSignatureGenerator(Operation operation, OpenAPI openAPI, @Override public Optional generateFunctionSignature() { - // 1. parameters - path , query, requestBody, headers List parameters = operation.getParameters(); ParametersInfo parametersInfo = getParametersInfo(parameters); @@ -79,18 +78,8 @@ public Optional generateFunctionSignature() { return Optional.empty(); } - List defaultableParameters = parametersInfo.defaultable(); - List parameterList = parametersInfo.parameterList(); - - //filter defaultable parameters - if (!defaultableParameters.isEmpty()) { - parameterList.addAll(defaultableParameters); - } - // Remove the last comma - if (!parameterList.isEmpty()) { - parameterList.remove(parameterList.size() - 1); - } - SeparatedNodeList parameterNodes = createSeparatedNodeList(parameterList); + List paramList = getParameterNodes(parametersInfo); + SeparatedNodeList parameterNodes = createSeparatedNodeList(paramList); // 3. return statements FunctionReturnTypeGeneratorImp functionReturnType = getFunctionReturnTypeGenerator(); @@ -99,15 +88,39 @@ public Optional generateFunctionSignature() { if (returnType.isEmpty()) { return Optional.empty(); } + + //create function signature node return returnType.map(returnTypeDescriptorNode -> NodeFactory.createFunctionSignatureNode( createToken(OPEN_PAREN_TOKEN), parameterNodes, createToken(CLOSE_PAREN_TOKEN), returnTypeDescriptorNode)); - //create function signature node + } + + private static List getParameterNodes(ParametersInfo parametersInfo) { + List defaultableParams = parametersInfo.defaultableParams(); + List params = parametersInfo.requiredParams(); + List includedParam = parametersInfo.includedParam(); + + // Add defaultable parameters + if (!defaultableParams.isEmpty()) { + params.addAll(defaultableParams); + } + + // Add included record parameter + if (!includedParam.isEmpty()) { + params.addAll(includedParam); + } + + // Remove the last comma + if (!params.isEmpty()) { + params.remove(params.size() - 1); + } + return params; } protected ParametersInfo getParametersInfo(List parameters) { - List parameterList = new ArrayList<>(); - List defaultable = new ArrayList<>(); + List requiredParams = new ArrayList<>(); + List defaultableParams = new ArrayList<>(); + List includedParam = new ArrayList<>(); Token comma = createToken(COMMA_TOKEN); List headerParameters = new ArrayList<>(); @@ -123,8 +136,8 @@ protected ParametersInfo getParametersInfo(List parameters) { return null; } headerParameters = requestBodyGenerator.getHeaderSchemas(); - parameterList.add(requestBody.get()); - parameterList.add(comma); + requiredParams.add(requestBody.get()); + requiredParams.add(comma); } // 2. parameters - query, requestBody, headers @@ -144,11 +157,11 @@ protected ParametersInfo getParametersInfo(List parameters) { if (headers.isPresent()) { hasHeadersParam = true; if (headers.get() instanceof RequiredParameterNode headerNode) { - parameterList.add(headerNode); - parameterList.add(comma); + requiredParams.add(headerNode); + requiredParams.add(comma); } else { - defaultable.add(headers.get()); - defaultable.add(comma); + defaultableParams.add(headers.get()); + defaultableParams.add(comma); } } else if (!headerParameters.isEmpty()) { diagnostics.addAll(headersParameterGenerator.getDiagnostics()); @@ -160,8 +173,8 @@ protected ParametersInfo getParametersInfo(List parameters) { Optional queries = queriesParameterGenerator.generateParameterNode(); if (queries.isPresent()) { hasQueriesParam = true; - parameterList.add(queries.get()); - parameterList.add(comma); + includedParam.add(queries.get()); + includedParam.add(comma); } else if (!queryParameters.isEmpty()) { diagnostics.addAll(queriesParameterGenerator.getDiagnostics()); return null; @@ -170,12 +183,13 @@ protected ParametersInfo getParametersInfo(List parameters) { ParameterNode defaultHeaderParam = HeadersParameterGenerator.getDefaultParameterNode().orElse(null); if (defaultHeaderParam != null) { hasDefaultHeader = true; - defaultable.add(defaultHeaderParam); - defaultable.add(comma); + hasHeadersParam = true; + defaultableParams.add(defaultHeaderParam); + defaultableParams.add(comma); } } - return new ParametersInfo(parameterList, defaultable); + return new ParametersInfo(requiredParams, defaultableParams, includedParam); } private void populateHeaderAndQueryParameters(List parameters, List queryParameters, diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java index 72fdff093..f96a2f4e1 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java @@ -68,8 +68,8 @@ public HeadersParameterGenerator(List parameters, OpenAPI openAPI, Op public Optional generateParameterNode() { ObjectSchema headersSchema = getHeadersSchema(); String operationId = GeneratorUtils.generateOperationUniqueId(operation, path, httpMethod); - headersSchema.setDescription("Represents the Headers record for the operation id: " + operationId); - String headersName = GeneratorUtils.getValidName(operationId + "Headers", true); + headersSchema.setDescription("Represents the Headers record for the operation: " + operationId); + String headersName = GeneratorUtils.getValidName(operationId, true) + "Headers"; openAPI.getComponents().addSchemas(headersName, headersSchema); Schema headersRefSchema = new ObjectSchema().$ref(headersName); @@ -140,7 +140,7 @@ private ObjectSchema getHeadersSchema() { Map properties = parameters.stream() .collect(Collectors.toMap( parameter -> escapeIdentifier(parameter.getName()), - parameter -> getSchemaWithDescription(parameter)) + parameter -> getSchemaWithDetails(parameter)) ); List requiredFields = parameters.stream() @@ -156,9 +156,11 @@ private ObjectSchema getHeadersSchema() { return headersSchema; } - private Schema getSchemaWithDescription(Parameter parameter) { + private Schema getSchemaWithDetails(Parameter parameter) { Schema schema = parameter.getSchema(); schema.setDescription(parameter.getDescription()); + schema.setDeprecated(parameter.getDeprecated()); + schema.extensions(parameter.getExtensions()); if (!Boolean.TRUE.equals(parameter.getRequired())) { schema.setNullable(true); } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java index 65f9f4266..0a2b33f77 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java @@ -68,8 +68,8 @@ public Optional generateParameterNode() { ObjectSchema queriesSchema = getQueriesSchema(); String operationId = GeneratorUtils.generateOperationUniqueId(operation, path, httpMethod); - queriesSchema.setDescription("Represents the Queries record for the operation id: " + operationId); - String queriesName = GeneratorUtils.getValidName(operationId + "Queries", true); + queriesSchema.setDescription("Represents the Queries record for the operation: " + operationId); + String queriesName = GeneratorUtils.getValidName(operationId, true) + "Queries"; openAPI.getComponents().addSchemas(queriesName, queriesSchema); Schema queriesRefSchema = new ObjectSchema().$ref(queriesName); Optional queriesType = TypeHandler.getInstance().getTypeNodeFromOASSchema(queriesRefSchema, @@ -87,7 +87,7 @@ public List getDiagnostics() { private ObjectSchema getQueriesSchema() { Map properties = parameters.stream() - .collect(Collectors.toMap(Parameter::getName, this::getSchemaWithDescription)); + .collect(Collectors.toMap(Parameter::getName, this::getSchemaWithDetails)); List requiredFields = parameters.stream() .filter(parameter -> Boolean.TRUE.equals(parameter.getRequired())) @@ -102,9 +102,11 @@ private ObjectSchema getQueriesSchema() { return queriesSchema; } - private Schema getSchemaWithDescription(Parameter parameter) { + private Schema getSchemaWithDetails(Parameter parameter) { Schema schema = parameter.getSchema(); schema.setDescription(parameter.getDescription()); + schema.setDeprecated(parameter.getDeprecated()); + schema.extensions(parameter.getExtensions()); return schema; } } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/common/GeneratorUtils.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/common/GeneratorUtils.java index 0e9c0a0ef..788a0358c 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/common/GeneratorUtils.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/common/GeneratorUtils.java @@ -1000,6 +1000,9 @@ public static Schema getHeadersTypeSchema(ApiResponse apiResponse) { String headerName = headerEntry.getKey(); Header header = headerEntry.getValue(); Schema headerTypeSchema = getValidatedHeaderSchema(header.getSchema()); + headerTypeSchema.setDescription(header.getDescription()); + headerTypeSchema.deprecated(header.getDeprecated()); + headerTypeSchema.extensions(header.getExtensions()); properties.put(headerName, headerTypeSchema); if (header.getRequired() != null && header.getRequired()) { requiredFields.add(headerName); diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java index 891f25fc8..0bd1f8d8b 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java @@ -39,6 +39,8 @@ import io.ballerina.compiler.syntax.tree.SyntaxTree; import io.ballerina.compiler.syntax.tree.Token; import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; +import io.ballerina.openapi.core.generators.common.exception.InvalidReferenceException; +import io.ballerina.openapi.core.generators.type.model.GeneratorMetaData; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; @@ -55,6 +57,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Optional; import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createNodeList; @@ -211,7 +214,7 @@ private FunctionDefinitionNode updateDocCommentsForFunctionNode( HashMap collection = getParameterNodeHashMap(parameters); //todo parameter reference - updateParameterNodes(isResource, docs, operation, updatedParamsRequired, updatedParamsDefault, + updateParameterNodes(docs, operation, updatedParamsRequired, updatedParamsDefault, collection); if (collection.size() > 0) { collection.forEach((keyParam, value) -> { @@ -240,6 +243,8 @@ private FunctionDefinitionNode updateDocCommentsForFunctionNode( functionSignatureNode.returnTypeDesc().orElse(null)); } + } else { + docs.add(createAPIParamDoc(HEADERS, "Headers to be sent with the request")); } RequestBody requestBody = operation.getRequestBody(); if (requestBody != null) { @@ -313,47 +318,51 @@ private void addRequestBodyDoc(List docs, RequestBody requestBody) { } } - private static void updateParameterNodes(boolean isResource, List docs, Operation operation, - List updatedParamsRequired, List updatedParamsDefault, + private static void updateParameterNodes(List docs, Operation operation, List updatedParamsRequired, + List updatedParamsDefault, HashMap collection) { List deprecatedParamDocComments = new ArrayList<>(); + boolean[] hasQueryParams = {false}; operation.getParameters().forEach(parameter -> { - if (isResource && (parameter.getIn().equals("header") || parameter.getIn().equals("query"))) { + if (parameter.get$ref() != null) { + try { + parameter = GeneratorMetaData.getInstance().getOpenAPI().getComponents() + .getParameters().get(extractReferenceType(parameter.get$ref())); + } catch (InvalidReferenceException e) { + return; + } + } + + if (Objects.isNull(parameter.getIn()) || !parameter.getIn().equals("path")) { + hasQueryParams[0] = parameter.getIn().equals("query"); return; } List paramAnnot = new ArrayList<>(); String parameterDescription; - String parameterName = parameter.getName(); + String parameterName = escapeIdentifier(parameter.getName()); - if (parameter.getIn().equals("path") || parameter.getIn().equals("header") - || parameter.getIn().equals("query")) { - parameterName = escapeIdentifier(parameter.getName()); - // add deprecated annotation - if (parameter.getDeprecated() != null && parameter.getDeprecated()) { - extractDeprecatedAnnotationDetails(deprecatedParamDocComments, parameter, paramAnnot); - } - if (parameter.getExtensions() != null) { - extractDisplayAnnotation(parameter.getExtensions(), paramAnnot); + // add deprecated annotation + if (parameter.getDeprecated() != null && parameter.getDeprecated()) { + extractDeprecatedAnnotationDetails(deprecatedParamDocComments, parameter, paramAnnot); + } + if (parameter.getExtensions() != null) { + extractDisplayAnnotation(parameter.getExtensions(), paramAnnot); - } - ParameterNode parameterNode = collection.get(parameterName); - updatedAnnotationInParameterNode(updatedParamsRequired, updatedParamsDefault, - paramAnnot, parameterNode); - collection.remove(parameterName); } + ParameterNode parameterNode = collection.get(parameterName); + updatedAnnotationInParameterNode(updatedParamsRequired, updatedParamsDefault, + paramAnnot, parameterNode); + collection.remove(parameterName); + if (parameter.getDescription() != null) { parameterDescription = parameter.getDescription(); docs.add(createAPIParamDocFromString(parameterName, parameterDescription)); } }); - if (isResource) { - if (collection.containsKey(HEADERS)) { - docs.add(createAPIParamDoc(HEADERS, "Headers to be sent with the request")); - } - if (collection.containsKey(QUERIES)) { - docs.add(createAPIParamDoc(QUERIES, "Queries to be sent with the request")); - } + docs.add(createAPIParamDoc(HEADERS, "Headers to be sent with the request")); + if (hasQueryParams[0]) { + docs.add(createAPIParamDoc(QUERIES, "Queries to be sent with the request")); } docs.addAll(deprecatedParamDocComments); } From 077a0003657c933abb8a5d88b6f7af7e01009de3 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Tue, 30 Apr 2024 11:28:34 +0530 Subject: [PATCH 04/31] Remove redundant header assignment --- .../client/FunctionBodyGeneratorImp.java | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java index 5edefc00d..47d744fcc 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java @@ -136,7 +136,7 @@ public class FunctionBodyGeneratorImp implements FunctionBodyGenerator { private final boolean hasHeaders; private final boolean hasQueries; - private final boolean hasDefaultHeaders; + private boolean hasDefaultHeaders; public List getImports() { return imports; @@ -247,14 +247,11 @@ public void handleQueryParamsAndHeaders(List queryParameters, List " + HTTP_HEADERS + " = " + HEADERS)); - } else { + if (!hasDefaultHeaders) { statementsList.add(GeneratorUtils.getSimpleExpressionStatementNode( "map " + HTTP_HEADERS + " = getMapForHeaders(" + HEADERS + ")")); + ballerinaUtilGenerator.setHeadersFound(true); } - ballerinaUtilGenerator.setHeadersFound(true); } } } @@ -274,6 +271,7 @@ private void addUpdatedPathAndHeaders(List statementsList, List " + HEADER_VALUES + " = " + defaultValue); @@ -505,22 +503,19 @@ private void createCommonFunctionBodyStatements(List statementsLi boolean isEntityBodyMethods = method.equals(POST) || method.equals(PUT) || method.equals(PATCH) || method.equals(EXECUTE); if (hasHeaders) { + String paramName = hasDefaultHeaders ? HEADERS : HTTP_HEADERS; if (isEntityBodyMethods) { ExpressionStatementNode requestStatementNode = GeneratorUtils.getSimpleExpressionStatementNode( "http:Request request = new"); statementsList.add(requestStatementNode); - clientCallStatement = getClientCallWithRequestAndHeaders().formatted(method, RESOURCE_PATH, - HTTP_HEADERS); + clientCallStatement = getClientCallWithRequestAndHeaders().formatted(method, RESOURCE_PATH, paramName); } else if (method.equals(DELETE)) { - clientCallStatement = getClientCallWithHeadersParam().formatted(method, RESOURCE_PATH, - HTTP_HEADERS); + clientCallStatement = getClientCallWithHeadersParam().formatted(method, RESOURCE_PATH, paramName); } else if (method.equals(HEAD)) { - clientCallStatement = getClientCallWithHeaders().formatted(method, RESOURCE_PATH, - HTTP_HEADERS); + clientCallStatement = getClientCallWithHeaders().formatted(method, RESOURCE_PATH, paramName); } else { - clientCallStatement = getClientCallWithHeaders().formatted(method, RESOURCE_PATH, - HTTP_HEADERS); + clientCallStatement = getClientCallWithHeaders().formatted(method, RESOURCE_PATH, paramName); } } else if (method.equals(DELETE)) { clientCallStatement = getSimpleClientCall().formatted(method, RESOURCE_PATH); @@ -649,7 +644,7 @@ private void createRequestBodyStatements(List statementsList, Str if (method.equals(POST) || method.equals(PUT) || method.equals(PATCH) || method.equals(DELETE) || method.equals(EXECUTE)) { requestStatement = getClientCallWithRequestAndHeaders().formatted(method, RESOURCE_PATH, - HTTP_HEADERS); + hasDefaultHeaders ? HEADERS : HTTP_HEADERS); generateReturnStatement(statementsList, requestStatement); } } else { From e704a7a5a402cc0813d9c05a0b5738a1e97e1a36 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Tue, 30 Apr 2024 15:34:25 +0530 Subject: [PATCH 05/31] Fix issues with function body generator --- .../client/FunctionBodyGeneratorImp.java | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java index 47d744fcc..635fe4a4a 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java @@ -127,7 +127,9 @@ */ public class FunctionBodyGeneratorImp implements FunctionBodyGenerator { - private List imports = new ArrayList<>(); + public static final String MAP_ANYDATA = "map "; + public static final String MAP_STRING_STRING_ARRAY = "map "; + private final List imports; private final String path; private final Map.Entry operation; private final OpenAPI openAPI; @@ -163,7 +165,6 @@ public FunctionBodyGeneratorImp(String path, Map.Entry getFunctionBodyNode() { @@ -210,7 +211,7 @@ private void handleParameterSchemaInOperation(Map.Entry securitySchemesAvailable = getSecurityRequirementForOperation(operation.getValue()); - if (securitySchemesAvailable.size() > 0) { + if (!securitySchemesAvailable.isEmpty()) { Map queryApiKeyMap = ballerinaAuthConfigGeneratorImp.getQueryApiKeyNameList(); Map headerApiKeyMap = ballerinaAuthConfigGeneratorImp.getHeaderApiKeyNameList(); for (String schemaName : securitySchemesAvailable) { @@ -222,19 +223,15 @@ private void handleParameterSchemaInOperation(Map.Entry queryParameters = new ArrayList<>(); - List headerParameters = new ArrayList<>(); - - handleQueryParamsAndHeaders(queryParameters, headerParameters, statementsList, queryApiKeyNameList, - headerApiKeyNameList); + handleQueryParamsAndHeaders(new ArrayList<>(), statementsList, queryApiKeyNameList, headerApiKeyNameList); } /** * Handle query parameters and headers within a remote function. */ - public void handleQueryParamsAndHeaders(List queryParameters, List headerParameters, - List statementsList, List queryApiKeyNameList, - List headerApiKeyNameList) throws BallerinaOpenApiException { + public void handleQueryParamsAndHeaders(List queryParameters, List statementsList, + List queryApiKeyNameList, List headerApiKeyNameList) + throws BallerinaOpenApiException { boolean combinationOfApiKeyAndHTTPOAuth = ballerinaAuthConfigGeneratorImp.isHttpOROAuth() && ballerinaAuthConfigGeneratorImp.isApiKey(); @@ -243,13 +240,38 @@ public void handleQueryParamsAndHeaders(List queryParameters, List " + HTTP_HEADERS + " = getMapForHeaders(" + HEADERS + ")")); + MAP_STRING_STRING_ARRAY + HTTP_HEADERS + " = getMapForHeaders(" + HEADERS + ")")); + ballerinaUtilGenerator.setHeadersFound(true); + } else if (!hasDefaultHeaders) { + statementsList.add(GeneratorUtils.getSimpleExpressionStatementNode( + MAP_STRING_STRING_ARRAY + HTTP_HEADERS + " = getMapForHeaders(" + HEADERS + ")")); ballerinaUtilGenerator.setHeadersFound(true); } } @@ -274,7 +296,7 @@ private void addUpdatedPathAndHeaders(List statementsList, List " + HEADER_VALUES + " = " + defaultValue); + MAP_ANYDATA + HEADER_VALUES + " = " + defaultValue); statementsList.add(headerMapCreation); if (!headerApiKeyNameList.isEmpty()) { @@ -292,7 +314,7 @@ private void addUpdatedPathAndHeaders(List statementsList, List " + QUERY_PARAM + " = " + defaultValue); + MAP_ANYDATA + QUERY_PARAM + " = " + defaultValue); statementsList.add(queryParamMapCreation); if (!queryApiKeyNameList.isEmpty()) { @@ -310,7 +332,7 @@ private void addUpdatedPathAndHeaders(List statementsList, List " + HTTP_HEADERS + " = getMapForHeaders(headerValues)")); + MAP_STRING_STRING_ARRAY + HTTP_HEADERS + " = getMapForHeaders(headerValues)")); } } @@ -326,10 +348,13 @@ private void addApiKeysToMap(String mapName, List apiKeyNames, List getSecurityRequirementForOperation(Operation operation) { securityRequirements = openAPI.getSecurity(); } - if (securityRequirements.size() > 0) { + if (!securityRequirements.isEmpty()) { for (SecurityRequirement requirement : securityRequirements) { securitySchemasAvailable.addAll(requirement.keySet()); } From f83e2132a75cbec214896e9c88ce7523e49ded2f Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Tue, 30 Apr 2024 16:48:52 +0530 Subject: [PATCH 06/31] Fix header param name double escaping --- .../client/parameter/HeadersParameterGenerator.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java index f96a2f4e1..3ecdd851a 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java @@ -138,14 +138,11 @@ public List getDiagnostics() { private ObjectSchema getHeadersSchema() { Map properties = parameters.stream() - .collect(Collectors.toMap( - parameter -> escapeIdentifier(parameter.getName()), - parameter -> getSchemaWithDetails(parameter)) - ); + .collect(Collectors.toMap(Parameter::getName, this::getSchemaWithDetails)); List requiredFields = parameters.stream() .filter(parameter -> Boolean.TRUE.equals(parameter.getRequired())) - .map(parameter -> escapeIdentifier(parameter.getName())) + .map(Parameter::getName) .toList(); ObjectSchema headersSchema = new ObjectSchema(); From 6fbf5f7bc8aed49e7a2e3cec65d87a088a36bce4 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Tue, 30 Apr 2024 16:49:03 +0530 Subject: [PATCH 07/31] Fix ComparedGeneratedFileTests --- .../file_provider/ballerina/api2pdf.bal | 90 +++++++++++-------- .../ballerina/display_annotation.bal | 5 +- .../ballerina/duplicated_response.bal | 5 +- .../ballerina/format_types_v3_0.bal | 15 ++-- .../ballerina/format_types_v3_1.bal | 15 ++-- .../ballerina/header_with_enum.bal | 6 +- .../ballerina/incorrect_format.bal | 11 ++- .../ballerina/multiline_param_comment.bal | 17 ++-- .../ballerina/multiple_pathparam.bal | 5 +- .../ballerina/nillable_response.bal | 5 +- .../ballerina/nillable_union_response.bal | 5 +- .../ballerina/openapi_weather_api.bal | 35 +++----- .../file_provider/ballerina/uber_openapi.bal | 55 ++++++------ 13 files changed, 138 insertions(+), 131 deletions(-) diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/api2pdf.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/api2pdf.bal index 43e42c805..33736bf84 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/api2pdf.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/api2pdf.bal @@ -41,12 +41,14 @@ public isolated client class Client { # Convert raw HTML to PDF # + # + headers - Headers to be sent with the request # + payload - A JSON object as a payload is required within the body of the request. The following attributes of the JSON object are detailed below: # + return - A JSON object containing the url to the PDF and other meta data - remote isolated function chromeFromHtmlPost(ChromeHtmlToPdfRequest payload) returns ApiResponseSuccess|error { + remote isolated function chromeFromHtmlPost(ChromeHtmlToPdfRequest payload, map headers = {}) returns ApiResponseSuccess|error { string resourcePath = string `/chrome/html`; - map headerValues = {"Authorization": self.apiKeyConfig.Authorization}; - map httpHeaders = getMapForHeaders(headerValues); + map headerValues = {...headers}; + headerValues["Authorization"] = self.apiKeyConfig.Authorization; + map httpHeaders = getMapForHeaders(headers); http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); @@ -55,24 +57,27 @@ public isolated client class Client { # Convert URL to PDF # - # + url - Url of the page to convert to PDF. Must start with http:// or https://. - # + output - Specify output=json to receive a JSON output. Defaults to PDF file. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - A PDF file or a JSON object depending on the `output` query parameter - remote isolated function chromeFromUrlGET(string url, string? output = ()) returns ApiResponseSuccess|error { + remote isolated function chromeFromUrlGET(map headers = {}, *ChromeFromUrlGETQueries queries) returns ApiResponseSuccess|error { string resourcePath = string `/chrome/url`; - map queryParam = {"url": url, "output": output, "apikey": self.apiKeyConfig.apikey}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + map queryParam = {...queries}; + queryParam["apikey"] = self.apiKeyConfig.apikey; + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Convert URL to PDF # + # + headers - Headers to be sent with the request # + payload - A JSON object as a payload is required within the body of the request. The following attributes of the JSON object are detailed below: # + return - A JSON object containing the url to the PDF and other meta data - remote isolated function chromeFromUrlPost(ChromeUrlToPdfRequest payload) returns ApiResponseSuccess|error { + remote isolated function chromeFromUrlPost(ChromeUrlToPdfRequest payload, map headers = {}) returns ApiResponseSuccess|error { string resourcePath = string `/chrome/url`; - map headerValues = {"Authorization": self.apiKeyConfig.Authorization}; - map httpHeaders = getMapForHeaders(headerValues); + map headerValues = {...headers}; + headerValues["Authorization"] = self.apiKeyConfig.Authorization; + map httpHeaders = getMapForHeaders(headers); http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); @@ -81,12 +86,14 @@ public isolated client class Client { # Convert office document or image to PDF # + # + headers - Headers to be sent with the request # + payload - A JSON object as a payload is required within the body of the request. The following attributes of the JSON object are detailed below: # + return - A JSON object containing the url to the PDF and other meta data - remote isolated function libreConvertPost(LibreOfficeConvertRequest payload) returns ApiResponseSuccess|error { + remote isolated function libreConvertPost(LibreOfficeConvertRequest payload, map headers = {}) returns ApiResponseSuccess|error { string resourcePath = string `/libreoffice/convert`; - map headerValues = {"Authorization": self.apiKeyConfig.Authorization}; - map httpHeaders = getMapForHeaders(headerValues); + map headerValues = {...headers}; + headerValues["Authorization"] = self.apiKeyConfig.Authorization; + map httpHeaders = getMapForHeaders(headers); http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); @@ -95,12 +102,14 @@ public isolated client class Client { # Merge multiple PDFs together # + # + headers - Headers to be sent with the request # + payload - A JSON object as a payload is required within the body of the request. The following attributes of the JSON object are detailed below: # + return - A JSON object containing the url to the PDF and other meta data - remote isolated function mergePost(MergeRequest payload) returns ApiResponseSuccess|error { + remote isolated function mergePost(MergeRequest payload, map headers = {}) returns ApiResponseSuccess|error { string resourcePath = string `/merge`; - map headerValues = {"Authorization": self.apiKeyConfig.Authorization}; - map httpHeaders = getMapForHeaders(headerValues); + map headerValues = {...headers}; + headerValues["Authorization"] = self.apiKeyConfig.Authorization; + map httpHeaders = getMapForHeaders(headers); http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); @@ -109,12 +118,14 @@ public isolated client class Client { # Convert raw HTML to PDF # + # + headers - Headers to be sent with the request # + payload - A JSON object as a payload is required within the body of the request. The following attributes of the JSON object are detailed below: # + return - A JSON object containing the url to the PDF and other meta data - remote isolated function wkhtmltopdfFromHtmlPost(WkHtmlToPdfHtmlToPdfRequest payload) returns ApiResponseSuccess|error { + remote isolated function wkhtmltopdfFromHtmlPost(WkHtmlToPdfHtmlToPdfRequest payload, map headers = {}) returns ApiResponseSuccess|error { string resourcePath = string `/wkhtmltopdf/html`; - map headerValues = {"Authorization": self.apiKeyConfig.Authorization}; - map httpHeaders = getMapForHeaders(headerValues); + map headerValues = {...headers}; + headerValues["Authorization"] = self.apiKeyConfig.Authorization; + map httpHeaders = getMapForHeaders(headers); http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); @@ -123,24 +134,27 @@ public isolated client class Client { # Convert URL to PDF # - # + url - Url of the page to convert to PDF. Must start with http:// or https://. - # + output - Specify output=json to receive a JSON output. Defaults to PDF file. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - A PDF file or a JSON object depending on the `output` query parameter - remote isolated function wkhtmltopdfFromUrlGET(string url, string? output = ()) returns ApiResponseSuccess|error { + remote isolated function wkhtmltopdfFromUrlGET(map headers = {}, *WkhtmltopdfFromUrlGETQueries queries) returns ApiResponseSuccess|error { string resourcePath = string `/wkhtmltopdf/url`; - map queryParam = {"url": url, "output": output, "apikey": self.apiKeyConfig.apikey}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + map queryParam = {...queries}; + queryParam["apikey"] = self.apiKeyConfig.apikey; + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Convert URL to PDF # + # + headers - Headers to be sent with the request # + payload - A JSON object as a payload is required within the body of the request. The following attributes of the JSON object are detailed below: # + return - A JSON object containing the url to the PDF and other meta data - remote isolated function wkhtmltopdfFromUrlPost(WkHtmlToPdfUrlToPdfRequest payload) returns ApiResponseSuccess|error { + remote isolated function wkhtmltopdfFromUrlPost(WkHtmlToPdfUrlToPdfRequest payload, map headers = {}) returns ApiResponseSuccess|error { string resourcePath = string `/wkhtmltopdf/url`; - map headerValues = {"Authorization": self.apiKeyConfig.Authorization}; - map httpHeaders = getMapForHeaders(headerValues); + map headerValues = {...headers}; + headerValues["Authorization"] = self.apiKeyConfig.Authorization; + map httpHeaders = getMapForHeaders(headers); http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); @@ -149,16 +163,14 @@ public isolated client class Client { # Generate bar codes and QR codes with ZXING. # - # + format - Most common is CODE_39 or QR_CODE - # + value - Specify the text value you want to convert - # + showlabel - Show label of text below barcode - # + height - Height of the barcode generated image - # + width - Width of the barcode generated image + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An image of the generated barcode or QR code - remote isolated function zebraGET(string format, string value, boolean? showlabel = (), int? height = (), int? width = ()) returns byte[]|error { + remote isolated function zebraGET(map headers = {}, *ZebraGETQueries queries) returns byte[]|error { string resourcePath = string `/zebra`; - map queryParam = {"format": format, "value": value, "showlabel": showlabel, "height": height, "width": width, "apikey": self.apiKeyConfig.apikey}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + map queryParam = {...queries}; + queryParam["apikey"] = self.apiKeyConfig.apikey; + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/display_annotation.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/display_annotation.bal index 3fd9bf249..8b6096b4a 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/display_annotation.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/display_annotation.bal @@ -38,13 +38,14 @@ public isolated client class Client { } # Update an existing pet # + # + headers - Headers to be sent with the request # + payload - Pet object that needs to be added to the store # + return - Invalid ID supplied - remote isolated function updatePet(Pet payload) returns http:Response|error { + remote isolated function updatePet(Pet payload, map headers = {}) returns http:Response|error { string resourcePath = string `/pet`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/duplicated_response.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/duplicated_response.bal index b96106e17..62b4e5c3b 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/duplicated_response.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/duplicated_response.bal @@ -36,9 +36,10 @@ public isolated client class Client { } # Get a pet # + # + headers - Headers to be sent with the request # + return - The status information is returned for the requested file upload. - remote isolated function getPet() returns PetDetails|error? { + remote isolated function getPet(map headers = {}) returns PetDetails|error? { string resourcePath = string `/pets`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/format_types_v3_0.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/format_types_v3_0.bal index f88617fc5..6bdb39973 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/format_types_v3_0.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/format_types_v3_0.bal @@ -34,20 +34,23 @@ public isolated client class Client { self.clientEp = httpEp; return; } + # + headers - Headers to be sent with the request # + return - Feature flag approval request response - remote isolated function op1() returns StringObject|error { + remote isolated function op1(map headers = {}) returns StringObject|error { string resourcePath = string `/projects`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } + # + headers - Headers to be sent with the request # + return - Feature flag approval request response - remote isolated function op2() returns IntegerObject|error { + remote isolated function op2(map headers = {}) returns IntegerObject|error { string resourcePath = string `/projects`; http:Request request = new; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } + # + headers - Headers to be sent with the request # + return - Feature flag approval request response - remote isolated function op3() returns NumberObject|error { + remote isolated function op3(map headers = {}) returns NumberObject|error { string resourcePath = string `/projects`; - return self.clientEp->delete(resourcePath); + return self.clientEp->delete(resourcePath, headers = headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/format_types_v3_1.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/format_types_v3_1.bal index f88617fc5..6bdb39973 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/format_types_v3_1.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/format_types_v3_1.bal @@ -34,20 +34,23 @@ public isolated client class Client { self.clientEp = httpEp; return; } + # + headers - Headers to be sent with the request # + return - Feature flag approval request response - remote isolated function op1() returns StringObject|error { + remote isolated function op1(map headers = {}) returns StringObject|error { string resourcePath = string `/projects`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } + # + headers - Headers to be sent with the request # + return - Feature flag approval request response - remote isolated function op2() returns IntegerObject|error { + remote isolated function op2(map headers = {}) returns IntegerObject|error { string resourcePath = string `/projects`; http:Request request = new; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } + # + headers - Headers to be sent with the request # + return - Feature flag approval request response - remote isolated function op3() returns NumberObject|error { + remote isolated function op3(map headers = {}) returns NumberObject|error { string resourcePath = string `/projects`; - return self.clientEp->delete(resourcePath); + return self.clientEp->delete(resourcePath, headers = headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/header_with_enum.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/header_with_enum.bal index a249d5e57..37c63dcff 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/header_with_enum.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/header_with_enum.bal @@ -38,11 +38,11 @@ public isolated client class Client { # Returns pet inventories by status # + # + headers - Headers to be sent with the request # + return - successful operation - remote isolated function getInventory(("X"|"Y"|"Z")[]? X\-CLIENT = ()) returns record {|int:Signed32...;|}|error { + remote isolated function getInventory(GetInventoryHeaders headers = {}) returns record {|int:Signed32...;|}|error { string resourcePath = string `/store/inventory`; - map headerValues = {"X-CLIENT": X\-CLIENT}; - map httpHeaders = getMapForHeaders(headerValues); + map httpHeaders = getMapForHeaders(headers); return self.clientEp->get(resourcePath, httpHeaders); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/incorrect_format.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/incorrect_format.bal index 59f1f8cb0..12750ae7b 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/incorrect_format.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/incorrect_format.bal @@ -44,13 +44,12 @@ public isolated client class Client { } # Finds Pets by status # - # + status - Status values that need to be considered for filter - # + 'limit - The number of client accounts to return on each page of results. The default value is `50`. Entering a `limit` value less than the minimum (`10`) or greater than the maximum (`50`) is ignored and the system uses the default values. Depending on the `limit` you specify, the system determines the `offset` parameter to use (number of records to skip) and includes it in the link used to get the next page of results. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - successful operation - remote isolated function findPetsByStatus("available"|"pending"|"sold" status = "available", string 'limit = "50") returns Pet[]|error { + remote isolated function findPetsByStatus(map headers = {}, *FindPetsByStatusQueries queries) returns Pet[]|error { string resourcePath = string `/pet/findByStatus`; - map queryParam = {"status": status, "limit": 'limit}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/multiline_param_comment.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/multiline_param_comment.bal index dfc70d313..a9c9de2ef 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/multiline_param_comment.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/multiline_param_comment.bal @@ -43,18 +43,15 @@ public isolated client class Client { # Events are created when the job status changes, e.g. running or complete, and when results are uploaded. # # + fine\-tune\-id - The identifier of the fine-tune job. - # + 'stream - A flag indicating whether to stream events for the fine-tune job. If set to true, - # events will be sent as data-only server-sent events as they become available. The stream will terminate with - # a data: [DONE] message when the job is finished (succeeded, cancelled, or failed). - # If set to false, only events generated so far will be returned.. - # + api\-version - The requested API version. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Success - remote isolated function fineTunes_GetEvents(string fine\-tune\-id, string api\-version, boolean? 'stream = ()) returns EventList|error { + remote isolated function fineTunes_GetEvents(string fine\-tune\-id, map headers = {}, *FineTunes_GetEventsQueries queries) returns EventList|error { string resourcePath = string `/fine-tunes/${getEncodedUri(fine\-tune\-id)}/events`; - map queryParam = {"stream": 'stream, "api-version": api\-version}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - map headerValues = {"api-key": self.apiKeyConfig.api\-key}; - map httpHeaders = getMapForHeaders(headerValues); + resourcePath = resourcePath + check getPathForQueryParam(queries); + map headerValues = {...headers}; + headerValues["api-key"] = self.apiKeyConfig.api\-key; + map httpHeaders = getMapForHeaders(headers); return self.clientEp->get(resourcePath, httpHeaders); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/multiple_pathparam.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/multiple_pathparam.bal index 2426abb4c..2b98bdb2c 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/multiple_pathparam.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/multiple_pathparam.bal @@ -37,9 +37,10 @@ public isolated client class Client { } # + version - test # + name - test + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function pathParameter(int version, string name) returns string|error { + remote isolated function pathParameter(int version, string name, map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(version)}/v2/${getEncodedUri(name)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/nillable_response.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/nillable_response.bal index 60c2abe11..6fc4572d1 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/nillable_response.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/nillable_response.bal @@ -36,9 +36,10 @@ public isolated client class Client { } # Get a pet # + # + headers - Headers to be sent with the request # + return - The status information is returned for the requested file upload. - remote isolated function getPet() returns PetDetails|error? { + remote isolated function getPet(map headers = {}) returns PetDetails|error? { string resourcePath = string `/pets`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/nillable_union_response.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/nillable_union_response.bal index 3dd43a713..517185108 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/nillable_union_response.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/nillable_union_response.bal @@ -36,9 +36,10 @@ public isolated client class Client { } # Get a pet # + # + headers - Headers to be sent with the request # + return - The status information is returned for the requested file upload. - remote isolated function getPet() returns PetDetails|PetDetails02|error? { + remote isolated function getPet(map headers = {}) returns PetDetails|PetDetails02|error? { string resourcePath = string `/pets`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/openapi_weather_api.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/openapi_weather_api.bal index 09d92dc63..1b0fd714f 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/openapi_weather_api.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/openapi_weather_api.bal @@ -41,35 +41,28 @@ public isolated client class Client { } # Call current weather data for one location # - # + q - **City name**. *Example: London*. You can call by city name, or by city name and country code. The API responds with a list of results that match a searching word. For the query value, type the city name and optionally the country code divided by comma; use ISO 3166 country codes. - # + id - **City ID**. *Example: `2172797`*. You can call by city ID. API responds with exact result. The List of city IDs can be downloaded [here](http://bulk.openweathermap.org/sample/). You can include multiple cities in parameter — just separate them by commas. The limit of locations is 20. *Note: A single ID counts as a one API call. So, if you have city IDs. it's treated as 3 API calls.* - # + lat - **Latitude**. *Example: 35*. The latitude cordinate of the location of your interest. Must use with `lon`. - # + lon - **Longitude**. *Example: 139*. Longitude cordinate of the location of your interest. Must use with `lat`. - # + zip - **Zip code**. Search by zip code. *Example: 95050,us*. Please note if country is not specified then the search works for USA as a default. - # + units - **Units**. *Example: imperial*. Possible values: `standard`, `metric`, and `imperial`. When you do not use units parameter, format is `standard` by default. - # + lang - **Language**. *Example: en*. You can use lang parameter to get the output in your language. We support the following languages that you can use with the corresponded lang values: Arabic - `ar`, Bulgarian - `bg`, Catalan - `ca`, Czech - `cz`, German - `de`, Greek - `el`, English - `en`, Persian (Farsi) - `fa`, Finnish - `fi`, French - `fr`, Galician - `gl`, Croatian - `hr`, Hungarian - `hu`, Italian - `it`, Japanese - `ja`, Korean - `kr`, Latvian - `la`, Lithuanian - `lt`, Macedonian - `mk`, Dutch - `nl`, Polish - `pl`, Portuguese - `pt`, Romanian - `ro`, Russian - `ru`, Swedish - `se`, Slovak - `sk`, Slovenian - `sl`, Spanish - `es`, Turkish - `tr`, Ukrainian - `ua`, Vietnamese - `vi`, Chinese Simplified - `zh_cn`, Chinese Traditional - `zh_tw`. - # + mode - **Mode**. *Example: html*. Determines format of response. Possible values are `xml` and `html`. If mode parameter is empty the format is `json` by default. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Successful response @display {label: "Current Weather"} - remote isolated function getCurretWeatherData(@display {label: "CityName or StateCode or CountryCode"} string? q = (), @display {label: "City Id"} string? id = (), @display {label: "Latitude"} string? lat = (), @display {label: "Longitude"} string? lon = (), @display {label: "Zip Code"} string? zip = (), @display {label: "Units"} "standard"|"metric"|"imperial" units = "imperial", @display {label: "Language"} "ar"|"bg"|"ca"|"cz"|"de"|"el"|"en"|"fa"|"fi"|"fr"|"gl"|"hr"|"hu"|"it"|"ja"|"kr"|"la"|"lt"|"mk"|"nl"|"pl"|"pt"|"ro"|"ru"|"se"|"sk"|"sl"|"es"|"tr"|"ua"|"vi"|"zh_cn"|"zh_tw" lang = "en", @display {label: "Mode"} "json"|"xml"|"html" mode = "json") returns CurrentWeatherData|error { + remote isolated function getCurretWeatherData(map headers = {}, *GetCurretWeatherDataQueries queries) returns CurrentWeatherData|error { string resourcePath = string `/weather`; - map queryParam = {"q": q, "id": id, "lat": lat, "lon": lon, "zip": zip, "units": units, "lang": lang, "mode": mode, "appid": self.apiKeyConfig.appid}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + map queryParam = {...queries}; + queryParam["appid"] = self.apiKeyConfig.appid; + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Provide weather forecast for any geographical coordinates # - # + lat - Latitude - # + lon - Longtitude - # + exclude - By using this parameter you can exclude some parts of the weather data from the API response. It should be a comma-delimited list (without spaces). - # + units - **Units**. *Example: imperial*. Possible values: `standard`, `metric`, and `imperial`. When you do not use units parameter, format is `standard` by default. - # + lang - **Language**. *Example: en*. You can use lang parameter to get the output in your language. We support the following languages that you can use with the corresponded lang values: Arabic - `ar`, Bulgarian - `bg`, Catalan - `ca`, Czech - `cz`, German - `de`, Greek - `el`, English - `en`, Persian (Farsi) - `fa`, Finnish - `fi`, French - `fr`, Galician - `gl`, Croatian - `hr`, Hungarian - `hu`, Italian - `it`, Japanese - `ja`, Korean - `kr`, Latvian - `la`, Lithuanian - `lt`, Macedonian - `mk`, Dutch - `nl`, Polish - `pl`, Portuguese - `pt`, Romanian - `ro`, Russian - `ru`, Swedish - `se`, Slovak - `sk`, Slovenian - `sl`, Spanish - `es`, Turkish - `tr`, Ukrainian - `ua`, Vietnamese - `vi`, Chinese Simplified - `zh_cn`, Chinese Traditional - `zh_tw`. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Successful response @display {label: "Weather Forecast"} - remote isolated function getWeatherForecast(@display {label: "Latitude"} string lat, @display {label: "Longtitude"} string lon, @display {label: "Exclude"} "current"|"minutely"|"hourly"|"daily"|"alerts"? exclude = (), @display {label: "Units"} string? units = (), @display {label: "Language"} string? lang = ()) returns WeatherForecast|error { + remote isolated function getWeatherForecast(map headers = {}, *GetWeatherForecastQueries queries) returns WeatherForecast|error { string resourcePath = string `/onecall`; - map queryParam = {"lat": lat, "lon": lon, "exclude": exclude, "units": units, "lang": lang, "appid": self.apiKeyConfig.appid}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + map queryParam = {...queries}; + queryParam["appid"] = self.apiKeyConfig.appid; + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/uber_openapi.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/uber_openapi.bal index 166dd6e05..1bb8f251a 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/uber_openapi.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/uber_openapi.bal @@ -40,57 +40,52 @@ public isolated client class Client { } # Price Estimates # - # + start_latitude - Latitude component of start location. - # + start_longitude - Longitude component of start location. - # + end_latitude - Latitude component of end location. - # + end_longitude - Longitude component of end location. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An array of price estimates by product - remote isolated function getPrice(decimal start_latitude, decimal start_longitude, decimal end_latitude, decimal end_longitude) returns PriceEstimate[]|error { + remote isolated function getPrice(map headers = {}, *GetPriceQueries queries) returns PriceEstimate[]|error { string resourcePath = string `/estimates/price`; - map queryParam = {"start_latitude": start_latitude, "start_longitude": start_longitude, "end_latitude": end_latitude, "end_longitude": end_longitude}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Product Types # - # + latitude - Latitude component of location. - # + longitude - Longitude component of location. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An array of products - remote isolated function getProducts(decimal latitude, decimal longitude) returns Product[]|error { + remote isolated function getProducts(map headers = {}, *GetProductsQueries queries) returns Product[]|error { string resourcePath = string `/products`; - map queryParam = {"latitude": latitude, "longitude": longitude, "server_token": self.apiKeyConfig.server_token}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + map queryParam = {...queries}; + queryParam["server_token"] = self.apiKeyConfig.server_token; + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Time Estimates # - # + start_latitude - Latitude component of start location. - # + start_longitude - Longitude component of start location. - # + customer_uuid - Unique customer identifier to be used for experience customization. - # + product_id - Unique identifier representing a specific product for a given latitude & longitude. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An array of products - remote isolated function getTimeEstimates(decimal start_latitude, decimal start_longitude, string? customer_uuid = (), string? product_id = ()) returns Product[]|error { + remote isolated function getTimeEstimates(map headers = {}, *GetTimeEstimatesQueries queries) returns Product[]|error { string resourcePath = string `/estimates/time`; - map queryParam = {"start_latitude": start_latitude, "start_longitude": start_longitude, "customer_uuid": customer_uuid, "product_id": product_id}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # User Activity # - # + offset - Offset the list of returned results by this amount. Default is zero. - # + 'limit - Number of items to retrieve. Default is 5, maximum is 100. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - History information for the given user - remote isolated function getUserActivity(int? offset = (), int? 'limit = ()) returns Activities|error { + remote isolated function getUserActivity(map headers = {}, *GetUserActivityQueries queries) returns Activities|error { string resourcePath = string `/history`; - map queryParam = {"offset": offset, "limit": 'limit}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # User Profile # + # + headers - Headers to be sent with the request # + return - Profile information for a user - remote isolated function getUserProfile() returns Profile|error { + remote isolated function getUserProfile(map headers = {}) returns Profile|error { string resourcePath = string `/me`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } From 82c107bd8c1cfcbe530d85d49c2ea8138744aed9 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Tue, 30 Apr 2024 17:19:35 +0530 Subject: [PATCH 08/31] Fix header and query param name --- .../file_provider/ballerina/api2pdf.bal | 18 ++++++++--------- .../ballerina/multiline_param_comment.bal | 2 +- .../ballerina/openapi_weather_api.bal | 4 ++-- .../file_provider/ballerina/uber_openapi.bal | 2 +- .../client/FunctionBodyGeneratorImp.java | 20 ++++++++++--------- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/api2pdf.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/api2pdf.bal index 33736bf84..7e2a0bc0f 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/api2pdf.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/api2pdf.bal @@ -48,7 +48,7 @@ public isolated client class Client { string resourcePath = string `/chrome/html`; map headerValues = {...headers}; headerValues["Authorization"] = self.apiKeyConfig.Authorization; - map httpHeaders = getMapForHeaders(headers); + map httpHeaders = getMapForHeaders(headerValues); http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); @@ -64,7 +64,7 @@ public isolated client class Client { string resourcePath = string `/chrome/url`; map queryParam = {...queries}; queryParam["apikey"] = self.apiKeyConfig.apikey; - resourcePath = resourcePath + check getPathForQueryParam(queries); + resourcePath = resourcePath + check getPathForQueryParam(queryParam); return self.clientEp->get(resourcePath, headers); } @@ -77,7 +77,7 @@ public isolated client class Client { string resourcePath = string `/chrome/url`; map headerValues = {...headers}; headerValues["Authorization"] = self.apiKeyConfig.Authorization; - map httpHeaders = getMapForHeaders(headers); + map httpHeaders = getMapForHeaders(headerValues); http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); @@ -93,7 +93,7 @@ public isolated client class Client { string resourcePath = string `/libreoffice/convert`; map headerValues = {...headers}; headerValues["Authorization"] = self.apiKeyConfig.Authorization; - map httpHeaders = getMapForHeaders(headers); + map httpHeaders = getMapForHeaders(headerValues); http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); @@ -109,7 +109,7 @@ public isolated client class Client { string resourcePath = string `/merge`; map headerValues = {...headers}; headerValues["Authorization"] = self.apiKeyConfig.Authorization; - map httpHeaders = getMapForHeaders(headers); + map httpHeaders = getMapForHeaders(headerValues); http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); @@ -125,7 +125,7 @@ public isolated client class Client { string resourcePath = string `/wkhtmltopdf/html`; map headerValues = {...headers}; headerValues["Authorization"] = self.apiKeyConfig.Authorization; - map httpHeaders = getMapForHeaders(headers); + map httpHeaders = getMapForHeaders(headerValues); http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); @@ -141,7 +141,7 @@ public isolated client class Client { string resourcePath = string `/wkhtmltopdf/url`; map queryParam = {...queries}; queryParam["apikey"] = self.apiKeyConfig.apikey; - resourcePath = resourcePath + check getPathForQueryParam(queries); + resourcePath = resourcePath + check getPathForQueryParam(queryParam); return self.clientEp->get(resourcePath, headers); } @@ -154,7 +154,7 @@ public isolated client class Client { string resourcePath = string `/wkhtmltopdf/url`; map headerValues = {...headers}; headerValues["Authorization"] = self.apiKeyConfig.Authorization; - map httpHeaders = getMapForHeaders(headers); + map httpHeaders = getMapForHeaders(headerValues); http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); @@ -170,7 +170,7 @@ public isolated client class Client { string resourcePath = string `/zebra`; map queryParam = {...queries}; queryParam["apikey"] = self.apiKeyConfig.apikey; - resourcePath = resourcePath + check getPathForQueryParam(queries); + resourcePath = resourcePath + check getPathForQueryParam(queryParam); return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/multiline_param_comment.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/multiline_param_comment.bal index a9c9de2ef..21f2ac83e 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/multiline_param_comment.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/multiline_param_comment.bal @@ -51,7 +51,7 @@ public isolated client class Client { resourcePath = resourcePath + check getPathForQueryParam(queries); map headerValues = {...headers}; headerValues["api-key"] = self.apiKeyConfig.api\-key; - map httpHeaders = getMapForHeaders(headers); + map httpHeaders = getMapForHeaders(headerValues); return self.clientEp->get(resourcePath, httpHeaders); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/openapi_weather_api.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/openapi_weather_api.bal index 1b0fd714f..f04201da7 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/openapi_weather_api.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/openapi_weather_api.bal @@ -49,7 +49,7 @@ public isolated client class Client { string resourcePath = string `/weather`; map queryParam = {...queries}; queryParam["appid"] = self.apiKeyConfig.appid; - resourcePath = resourcePath + check getPathForQueryParam(queries); + resourcePath = resourcePath + check getPathForQueryParam(queryParam); return self.clientEp->get(resourcePath, headers); } # Provide weather forecast for any geographical coordinates @@ -62,7 +62,7 @@ public isolated client class Client { string resourcePath = string `/onecall`; map queryParam = {...queries}; queryParam["appid"] = self.apiKeyConfig.appid; - resourcePath = resourcePath + check getPathForQueryParam(queries); + resourcePath = resourcePath + check getPathForQueryParam(queryParam); return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/uber_openapi.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/uber_openapi.bal index 1bb8f251a..0b82df62b 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/uber_openapi.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/uber_openapi.bal @@ -57,7 +57,7 @@ public isolated client class Client { string resourcePath = string `/products`; map queryParam = {...queries}; queryParam["server_token"] = self.apiKeyConfig.server_token; - resourcePath = resourcePath + check getPathForQueryParam(queries); + resourcePath = resourcePath + check getPathForQueryParam(queryParam); return self.clientEp->get(resourcePath, headers); } # Time Estimates diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java index 635fe4a4a..419fadbbf 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java @@ -129,6 +129,7 @@ public class FunctionBodyGeneratorImp implements FunctionBodyGenerator { public static final String MAP_ANYDATA = "map "; public static final String MAP_STRING_STRING_ARRAY = "map "; + public static final String GET_MAP_FOR_HEADERS = " = getMapForHeaders("; private final List imports; private final String path; private final Map.Entry operation; @@ -251,8 +252,9 @@ public void handleQueryParamsAndHeaders(List queryParameters, List queryParameters, List statementsList, List apiKeyNames, List statementsList, List - queryParameters) throws BallerinaOpenApiException { - + queryParameters, String queryVarName) throws BallerinaOpenApiException { VariableDeclarationNode queryParamEncodingMap = getQueryParameterEncodingMap(queryParameters); if (queryParamEncodingMap != null) { statementsList.add(queryParamEncodingMap); ExpressionStatementNode updatedPath = GeneratorUtils.getSimpleExpressionStatementNode( - RESOURCE_PATH + " = " + RESOURCE_PATH + " + check getPathForQueryParam(" + QUERIES + ", " + + RESOURCE_PATH + " = " + RESOURCE_PATH + " + check getPathForQueryParam(" + queryVarName + ", " + "queryParamEncoding)"); statementsList.add(updatedPath); } else { ExpressionStatementNode updatedPath = GeneratorUtils.getSimpleExpressionStatementNode( - RESOURCE_PATH + " = " + RESOURCE_PATH + " + check getPathForQueryParam(" + QUERIES + ")"); + RESOURCE_PATH + " = " + RESOURCE_PATH + " + check getPathForQueryParam(" + queryVarName + ")"); statementsList.add(updatedPath); } } From d1740b716d51a244be6e7bd7183ddbcc1ca25f62 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Tue, 30 Apr 2024 18:38:19 +0530 Subject: [PATCH 09/31] Fix OpenAPICmdTests --- .../expected_gen/client_filtered_by_tags.bal | 25 +++++++++++-------- .../generated_client_with_license.bal | 23 +++++++++-------- .../expected_gen/petstore_schema.bal | 8 +++++- .../expected_gen/petstore_schema_2.bal | 6 +++++ .../expected_gen/petstore_schema_type.bal | 13 +++++++++- .../petstore_schema_with_license.bal | 10 ++++++-- 6 files changed, 60 insertions(+), 25 deletions(-) diff --git a/openapi-cli/src/test/resources/expected_gen/client_filtered_by_tags.bal b/openapi-cli/src/test/resources/expected_gen/client_filtered_by_tags.bal index a72db3867..77062e4a3 100644 --- a/openapi-cli/src/test/resources/expected_gen/client_filtered_by_tags.bal +++ b/openapi-cli/src/test/resources/expected_gen/client_filtered_by_tags.bal @@ -39,36 +39,39 @@ public isolated client class Client { } # Create a pet # + # + headers - Headers to be sent with the request # + return - Null response - remote isolated function createPet() returns http:Response|error { + remote isolated function createPet(map headers = {}) returns http:Response|error { string resourcePath = string `/pets`; http:Request request = new; - return self.clientEp-> post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Info for a specific pet # # + dog_id - The id of the pet to retrieve + # + headers - Headers to be sent with the request # + return - Expected response to a valid request - remote isolated function getDogs(string dog_id) returns Dog|error { + remote isolated function getDogs(string dog_id, map headers = {}) returns Dog|error { string resourcePath = string `/pets/dogs/${getEncodedUri(dog_id)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } # List all pets # - # + 'limit - How many items to return at one time (max 100) + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An paged array of pets - remote isolated function listPets(int? 'limit = ()) returns Pets|error { + remote isolated function listPets(map headers = {}, *ListPetsQueries queries) returns Pets|error { string resourcePath = string `/pets`; - map queryParam = {"limit": 'limit}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Info for a specific pet # # + petId - The id of the pet to retrieve + # + headers - Headers to be sent with the request # + return - Expected response to a valid request - remote isolated function showPetById(string petId) returns Pets|error { + remote isolated function showPetById(string petId, map headers = {}) returns Pets|error { string resourcePath = string `/pets/${getEncodedUri(petId)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/expected_gen/generated_client_with_license.bal b/openapi-cli/src/test/resources/expected_gen/generated_client_with_license.bal index 809043186..6c3a45c1d 100644 --- a/openapi-cli/src/test/resources/expected_gen/generated_client_with_license.bal +++ b/openapi-cli/src/test/resources/expected_gen/generated_client_with_license.bal @@ -10,7 +10,8 @@ public isolated client class Client { # + serviceUrl - URL of the target service # + return - An error if connector initialization failed public isolated function init(ConnectionConfig config = {}, string serviceUrl = "https://petstore.swagger.io:443/v2") returns error? { - http:ClientConfiguration httpClientConfig = {httpVersion: config.httpVersion, timeout: config.timeout, forwarded: config.forwarded, poolConfig: config.poolConfig, compression: config.compression, circuitBreaker: config.circuitBreaker, retryConfig: config.retryConfig, validation: config.validation}; do { + http:ClientConfiguration httpClientConfig = {httpVersion: config.httpVersion, timeout: config.timeout, forwarded: config.forwarded, poolConfig: config.poolConfig, compression: config.compression, circuitBreaker: config.circuitBreaker, retryConfig: config.retryConfig, validation: config.validation}; + do { if config.http1Settings is ClientHttp1Settings { ClientHttp1Settings settings = check config.http1Settings.ensureType(ClientHttp1Settings); httpClientConfig.http1Settings = {...settings}; @@ -37,28 +38,30 @@ public isolated client class Client { } # List all pets # - # + 'limit - How many items to return at one time (max 100) + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An paged array of pets - resource isolated function get pets(int? 'limit = ()) returns Pets|error { + resource isolated function get pets(map headers = {}, *ListPetsQueries queries) returns Pets|error { string resourcePath = string `/pets`; - map queryParam = {"limit": 'limit}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Info for a specific pet # # + petId - The id of the pet to retrieve + # + headers - Headers to be sent with the request # + return - Expected response to a valid request - resource isolated function get pets/[string petId]() returns Dog|error { + resource isolated function get pets/[string petId](map headers = {}) returns Dog|error { string resourcePath = string `/pets/${getEncodedUri(petId)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } # Create a pet # + # + headers - Headers to be sent with the request # + return - Null response - resource isolated function post pets() returns http:Response|error { + resource isolated function post pets(map headers = {}) returns http:Response|error { string resourcePath = string `/pets`; http:Request request = new; - return self.clientEp-> post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/expected_gen/petstore_schema.bal b/openapi-cli/src/test/resources/expected_gen/petstore_schema.bal index 17fdc52a1..99b9ac5a2 100644 --- a/openapi-cli/src/test/resources/expected_gen/petstore_schema.bal +++ b/openapi-cli/src/test/resources/expected_gen/petstore_schema.bal @@ -3,6 +3,12 @@ import ballerina/http; +# Represents the Queries record for the operation: listPets +public type ListPetsQueries record { + # How many items to return at one time (max 100) + int 'limit?; +}; + public type Pets Pet[]; # Provides settings related to HTTP/1.x protocol. @@ -28,7 +34,7 @@ public type ProxyConfig record {| string password = ""; |}; -public type Dog record{ +public type Dog record { *Pet; boolean bark?; }; diff --git a/openapi-cli/src/test/resources/expected_gen/petstore_schema_2.bal b/openapi-cli/src/test/resources/expected_gen/petstore_schema_2.bal index 38abf5037..1aef5972e 100644 --- a/openapi-cli/src/test/resources/expected_gen/petstore_schema_2.bal +++ b/openapi-cli/src/test/resources/expected_gen/petstore_schema_2.bal @@ -3,6 +3,12 @@ import ballerina/http; +# Represents the Queries record for the operation: listPets +public type ListPetsQueries record { + # How many items to return at one time (max 100) + int:Signed32 'limit?; +}; + public type Pets Pet[]; # Provides settings related to HTTP/1.x protocol. diff --git a/openapi-cli/src/test/resources/expected_gen/petstore_schema_type.bal b/openapi-cli/src/test/resources/expected_gen/petstore_schema_type.bal index e4c4b212a..ba7ccb75f 100644 --- a/openapi-cli/src/test/resources/expected_gen/petstore_schema_type.bal +++ b/openapi-cli/src/test/resources/expected_gen/petstore_schema_type.bal @@ -1,8 +1,14 @@ -// AUTO-GENERATED FILE. +// AUTO-GENERATED FILE. DO NOT MODIFY. // This file is auto-generated by the Ballerina OpenAPI tool. import ballerina/http; +# Represents the Queries record for the operation: operation_get_/pets +public type Operation_get_PetsQueries record { + # Number of retriving items + int:Signed32 offset; +}; + # Provides settings related to HTTP/1.x protocol. public type ClientHttp1Settings record {| # Specifies whether to reuse a connection for multiple requests @@ -13,6 +19,11 @@ public type ClientHttp1Settings record {| ProxyConfig proxy?; |}; +# Represents the Queries record for the operation: operation_get_/hello +public type Operation_get_HelloQueries record { + string pet?; +}; + # Proxy server configurations to be used with the HTTP client endpoint. public type ProxyConfig record {| # Host name of the proxy server diff --git a/openapi-cli/src/test/resources/expected_gen/petstore_schema_with_license.bal b/openapi-cli/src/test/resources/expected_gen/petstore_schema_with_license.bal index e47339f06..dd4835627 100644 --- a/openapi-cli/src/test/resources/expected_gen/petstore_schema_with_license.bal +++ b/openapi-cli/src/test/resources/expected_gen/petstore_schema_with_license.bal @@ -2,6 +2,12 @@ import ballerina/http; +# Represents the Queries record for the operation: listPets +public type ListPetsQueries record { + # How many items to return at one time (max 100) + int 'limit?; +}; + public type Pets Pet[]; # Provides settings related to HTTP/1.x protocol. @@ -27,9 +33,9 @@ public type ProxyConfig record {| string password = ""; |}; -public type Dog record{ +public type Dog record { *Pet; - booleanbark?; + boolean bark?; }; public type Pet record { From e06d0b4e4f3e59248e49279942e42ce0a67855c1 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Tue, 30 Apr 2024 18:48:14 +0530 Subject: [PATCH 10/31] Fix ConstraintTests --- .../schema/ballerina/constraint/string_pattern.bal | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openapi-cli/src/test/resources/generators/schema/ballerina/constraint/string_pattern.bal b/openapi-cli/src/test/resources/generators/schema/ballerina/constraint/string_pattern.bal index 7cc40b5b0..51a0241f1 100644 --- a/openapi-cli/src/test/resources/generators/schema/ballerina/constraint/string_pattern.bal +++ b/openapi-cli/src/test/resources/generators/schema/ballerina/constraint/string_pattern.bal @@ -3,6 +3,12 @@ import ballerina/constraint; @constraint:String {pattern: re `[ a-zA-Z0-9/.+!@#$%^&*()+\- ]*`} public type PersonHobbyItemsString string; +# Represents the Queries record for the operation: getGreeting +public type GetGreetingQueries record { + # the input string name + string name; +}; + public type Person record { @constraint:String {maxLength: 14} string name?; From 5d59f3af9ee0c6fcb345bea9da45550bb03045b7 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Tue, 30 Apr 2024 19:10:21 +0530 Subject: [PATCH 11/31] Add missing query param encoding config --- .../client/FunctionBodyGeneratorImp.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java index 419fadbbf..d5a4dd97a 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java @@ -110,6 +110,7 @@ import static io.ballerina.openapi.core.generators.common.GeneratorConstants.POST; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.PUT; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERIES; +import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERY; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERY_PARAM; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.REQUEST; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.RESOURCE_PATH; @@ -224,7 +225,22 @@ private void handleParameterSchemaInOperation(Map.Entry(), statementsList, queryApiKeyNameList, headerApiKeyNameList); + List queryParameters = new ArrayList<>(); + + if (operation.getValue().getParameters() != null) { + List parameters = operation.getValue().getParameters(); + for (Parameter parameter : parameters) { + if (parameter.get$ref() != null) { + String[] splits = parameter.get$ref().split("/"); + parameter = openAPI.getComponents().getParameters().get(splits[splits.length - 1]); + } + if (parameter.getIn().trim().equals(QUERY)) { + queryParameters.add(parameter); + } + } + } + + handleQueryParamsAndHeaders(queryParameters, statementsList, queryApiKeyNameList, headerApiKeyNameList); } /** From fa3761472101e0228c4160494da2da72581fbb92 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Tue, 30 Apr 2024 19:10:40 +0530 Subject: [PATCH 12/31] Fix EnumGenerationTests --- .../client/ballerina/parameters_with_enum.bal | 16 +++++----------- .../ballerina/parameters_with_nullable_enums.bal | 15 +++++---------- .../parameters_with_nullable_enums_resource.bal | 15 +++++---------- .../ballerina/paramters_with_enum_resource.bal | 16 +++++----------- 4 files changed, 20 insertions(+), 42 deletions(-) diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/parameters_with_enum.bal b/openapi-cli/src/test/resources/generators/client/ballerina/parameters_with_enum.bal index c12cda501..d9c6b1e80 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/parameters_with_enum.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/parameters_with_enum.bal @@ -47,21 +47,15 @@ public isolated client class Client { # List meetings # - # + 'type - The meeting types. Scheduled, live or upcoming - # + status - Status values that need to be considered for filter # + group - Employee group - # + X\-Date\-Format - Date time format (cf. [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) & [leettime.de](http://leettime.de/)) - # + X\-Time\-Zones - Time Zones of attendees - # + location - Meeting location - # + format - The response format you would like + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - HTTP Status Code:200. List of meetings returned. - remote isolated function listMeetings("Admin"|"HR"|"Engineering" group, ("IST"|"GMT"|"UTC")[] X\-Time\-Zones, "scheduled"|"live"|"upcoming" 'type = "live", ("available"|"pending")[]? status = (), "UTC"|"LOCAL"|"OFFSET"|"EPOCH"|"LEET"? X\-Date\-Format = (), RoomNo location = "R5", "json"|"jsonp"|"msgpack"|"html"? format = ()) returns MeetingList|error { + remote isolated function listMeetings("Admin"|"HR"|"Engineering" group, ListMeetingsHeaders headers, *ListMeetingsQueries queries) returns MeetingList|error { string resourcePath = string `/users/meetings/${getEncodedUri(group)}`; - map queryParam = {"type": 'type, "status": status, "location": location, "format": format}; map queryParamEncoding = {"status": {style: FORM, explode: true}}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam, queryParamEncoding); - map headerValues = {"X-Date-Format": X\-Date\-Format, "X-Time-Zones": X\-Time\-Zones}; - map httpHeaders = getMapForHeaders(headerValues); + resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); + map httpHeaders = getMapForHeaders(headers); return self.clientEp->get(resourcePath, httpHeaders); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/parameters_with_nullable_enums.bal b/openapi-cli/src/test/resources/generators/client/ballerina/parameters_with_nullable_enums.bal index 93e0262f7..e8d688387 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/parameters_with_nullable_enums.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/parameters_with_nullable_enums.bal @@ -47,19 +47,14 @@ public isolated client class Client { # List meetings # - # + 'type - The meeting types. Scheduled, live or upcoming - # + status - Status values that need to be considered for filter - # + X\-Date\-Format - Date time format (cf. [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) & [leettime.de](http://leettime.de/)) - # + location - Meeting location - # + format - The response format you would like + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - HTTP Status Code:200. List of meetings returned. - remote isolated function listMeetings("scheduled"|"live"|"upcoming"? 'type = (), ("available"|"pending"?)[]? status = (), "UTC"|"LOCAL"|"OFFSET"|"EPOCH"|"LEET"? X\-Date\-Format = (), RoomNo location = "R5", "json"|"jsonp"|"msgpack"|"html"? format = ()) returns MeetingList|error { + remote isolated function listMeetings(ListMeetingsHeaders headers = {}, *ListMeetingsQueries queries) returns MeetingList|error { string resourcePath = string `/users/meetings`; - map queryParam = {"type": 'type, "status": status, "location": location, "format": format}; map queryParamEncoding = {"status": {style: FORM, explode: true}}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam, queryParamEncoding); - map headerValues = {"X-Date-Format": X\-Date\-Format}; - map httpHeaders = getMapForHeaders(headerValues); + resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); + map httpHeaders = getMapForHeaders(headers); return self.clientEp->get(resourcePath, httpHeaders); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/parameters_with_nullable_enums_resource.bal b/openapi-cli/src/test/resources/generators/client/ballerina/parameters_with_nullable_enums_resource.bal index a562fb97a..762ab98bf 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/parameters_with_nullable_enums_resource.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/parameters_with_nullable_enums_resource.bal @@ -47,19 +47,14 @@ public isolated client class Client { # List meetings # - # + 'type - The meeting types. Scheduled, live or upcoming - # + status - Status values that need to be considered for filter - # + X\-Date\-Format - Date time format (cf. [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) & [leettime.de](http://leettime.de/)) - # + location - Meeting location - # + format - The response format you would like + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - HTTP Status Code:200. List of meetings returned. - resource isolated function get users/meetings("scheduled"|"live"|"upcoming"? 'type = (), ("available"|"pending"?)[]? status = (), "UTC"|"LOCAL"|"OFFSET"|"EPOCH"|"LEET"? X\-Date\-Format = (), RoomNo location = "R5", "json"|"jsonp"|"msgpack"|"html"? format = ()) returns MeetingList|error { + resource isolated function get users/meetings(ListMeetingsHeaders headers = {}, *ListMeetingsQueries queries) returns MeetingList|error { string resourcePath = string `/users/meetings`; - map queryParam = {"type": 'type, "status": status, "location": location, "format": format}; map queryParamEncoding = {"status": {style: FORM, explode: true}}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam, queryParamEncoding); - map headerValues = {"X-Date-Format": X\-Date\-Format}; - map httpHeaders = getMapForHeaders(headerValues); + resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); + map httpHeaders = getMapForHeaders(headers); return self.clientEp->get(resourcePath, httpHeaders); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/paramters_with_enum_resource.bal b/openapi-cli/src/test/resources/generators/client/ballerina/paramters_with_enum_resource.bal index 84f6b02f5..8e20de193 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/paramters_with_enum_resource.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/paramters_with_enum_resource.bal @@ -47,21 +47,15 @@ public isolated client class Client { # List meetings # - # + 'type - The meeting types. Scheduled, live or upcoming - # + status - Status values that need to be considered for filter # + group - Employee group - # + X\-Date\-Format - Date time format (cf. [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) & [leettime.de](http://leettime.de/)) - # + X\-Time\-Zones - Time Zones of attendees - # + location - Meeting location - # + format - The response format you would like + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - HTTP Status Code:200. List of meetings returned. - resource isolated function get users/meetings/["Admin"|"HR"|"Engineering" group](("IST"|"GMT"|"UTC")[] X\-Time\-Zones, "scheduled"|"live"|"upcoming" 'type = "live", ("available"|"pending")[]? status = (), "UTC"|"LOCAL"|"OFFSET"|"EPOCH"|"LEET"? X\-Date\-Format = (), RoomNo location = "R5", "json"|"jsonp"|"msgpack"|"html"? format = ()) returns MeetingList|error { + resource isolated function get users/meetings/["Admin"|"HR"|"Engineering" group](ListMeetingsHeaders headers, *ListMeetingsQueries queries) returns MeetingList|error { string resourcePath = string `/users/meetings/${getEncodedUri(group)}`; - map queryParam = {"type": 'type, "status": status, "location": location, "format": format}; map queryParamEncoding = {"status": {style: FORM, explode: true}}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam, queryParamEncoding); - map headerValues = {"X-Date-Format": X\-Date\-Format, "X-Time-Zones": X\-Time\-Zones}; - map httpHeaders = getMapForHeaders(headerValues); + resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); + map httpHeaders = getMapForHeaders(headers); return self.clientEp->get(resourcePath, httpHeaders); } } From 418cec7a9928684fc10ab30d0ec66ff2460dbcf9 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 10:23:32 +0530 Subject: [PATCH 13/31] Remove unnecessary statements --- .../expected_gen/petstore_schema_type.bal | 2 +- .../client/FunctionBodyGeneratorImp.java | 57 ++++++++++--------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/openapi-cli/src/test/resources/expected_gen/petstore_schema_type.bal b/openapi-cli/src/test/resources/expected_gen/petstore_schema_type.bal index ba7ccb75f..bef3019f2 100644 --- a/openapi-cli/src/test/resources/expected_gen/petstore_schema_type.bal +++ b/openapi-cli/src/test/resources/expected_gen/petstore_schema_type.bal @@ -1,4 +1,4 @@ -// AUTO-GENERATED FILE. DO NOT MODIFY. +// AUTO-GENERATED FILE. // This file is auto-generated by the Ballerina OpenAPI tool. import ballerina/http; diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java index d5a4dd97a..357178361 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java @@ -305,42 +305,45 @@ private void addUpdatedPathAndHeaders(List statementsList, List ifBodyStatementsList = new ArrayList<>(); + String headerVarName = HEADER_VALUES; if (hasHeaders || !headerApiKeyNameList.isEmpty()) { - String defaultValue = "{}"; - if (hasHeaders) { - defaultValue = "{..." + HEADERS + "}"; - } - hasDefaultHeaders = false; - - ExpressionStatementNode headerMapCreation = GeneratorUtils.getSimpleExpressionStatementNode( - MAP_ANYDATA + HEADER_VALUES + " = " + defaultValue); - statementsList.add(headerMapCreation); - - if (!headerApiKeyNameList.isEmpty()) { + if (headerApiKeyNameList.isEmpty()) { + if (!hasDefaultHeaders) { + headerVarName = HEADERS; + } + } else { + hasDefaultHeaders = false; + String defaultValue = "{}"; + if (hasHeaders) { + defaultValue = "{..." + HEADERS + "}"; + } + ExpressionStatementNode headerMapCreation = GeneratorUtils.getSimpleExpressionStatementNode( + MAP_ANYDATA + HEADER_VALUES + " = " + defaultValue); + statementsList.add(headerMapCreation); // update headerValues Map within the if block // `headerValues["api-key"] = self.apiKeyConfig?.apiKey;` addApiKeysToMap(HEADER_VALUES, headerApiKeyNameList, ifBodyStatementsList); } - ballerinaUtilGenerator.setHeadersFound(true); } if (hasQueries || !queryApiKeyNameList.isEmpty()) { - String defaultValue = "{}"; - if (hasQueries) { - defaultValue = "{..." + QUERIES + "}"; - } + if (!queryApiKeyNameList.isEmpty()) { + String defaultValue = "{}"; + if (hasQueries) { + defaultValue = "{..." + QUERIES + "}"; + } - ExpressionStatementNode queryParamMapCreation = GeneratorUtils.getSimpleExpressionStatementNode( - MAP_ANYDATA + QUERY_PARAM + " = " + defaultValue); - statementsList.add(queryParamMapCreation); + ExpressionStatementNode queryParamMapCreation = GeneratorUtils.getSimpleExpressionStatementNode( + MAP_ANYDATA + QUERY_PARAM + " = " + defaultValue); + statementsList.add(queryParamMapCreation); - if (!queryApiKeyNameList.isEmpty()) { - // update queryParam Map within the if block - // `queryParam["api-key"] = self.apiKeyConfig?.apiKey;` - addApiKeysToMap(QUERY_PARAM, queryApiKeyNameList, ifBodyStatementsList); + if (!queryApiKeyNameList.isEmpty()) { + // update queryParam Map within the if block + // `queryParam["api-key"] = self.apiKeyConfig?.apiKey;` + addApiKeysToMap(QUERY_PARAM, queryApiKeyNameList, ifBodyStatementsList); + } } - ballerinaUtilGenerator.setQueryParamsFound(true); } generateIfBlockToAddApiKeysToMaps(statementsList, ifBodyStatementsList); @@ -348,10 +351,12 @@ private void addUpdatedPathAndHeaders(List statementsList, List Date: Thu, 2 May 2024 10:41:43 +0530 Subject: [PATCH 14/31] Fix CodeGeneratorTest --- .../expected_gen/generate_client.bal | 26 ++-- .../generate_client_requestbody.bal | 6 +- .../expected_gen/nullable_false_types.bal | 6 + .../resources/expected_gen/nullable_types.bal | 6 + .../petstore_catch_all_path_client.bal | 17 +-- .../expected_gen/petstore_client_swagger.bal | 117 ++++++++++-------- .../expected_gen/type_filtered_by_tags.bal | 6 + .../expected_gen/x_init_description.bal | 7 +- 8 files changed, 115 insertions(+), 76 deletions(-) diff --git a/openapi-cli/src/test/resources/expected_gen/generate_client.bal b/openapi-cli/src/test/resources/expected_gen/generate_client.bal index 62ff5b556..3c03c71fc 100644 --- a/openapi-cli/src/test/resources/expected_gen/generate_client.bal +++ b/openapi-cli/src/test/resources/expected_gen/generate_client.bal @@ -39,28 +39,30 @@ public isolated client class Client { } # Create a pet # - # + return - Null response - remote isolated function createPet() returns http:Response|error { + # + headers - Headers to be sent with the request + # + return - Null response + remote isolated function createPet(map headers = {}) returns http:Response|error { string resourcePath = string `/pets`; http:Request request = new; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # List all pets # - # + 'limit - How many items to return at one time (max 100) - # + return - An paged array of pets - remote isolated function listPets(int? 'limit = ()) returns Pets|error { + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - An paged array of pets + remote isolated function listPets(map headers = {}, *ListPetsQueries queries) returns Pets|error { string resourcePath = string `/pets`; - map queryParam = {"limit": 'limit}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Info for a specific pet # # + petId - The id of the pet to retrieve - # + return - Expected response to a valid request - remote isolated function showPetById(string petId) returns Dog|error { + # + headers - Headers to be sent with the request + # + return - Expected response to a valid request + remote isolated function showPetById(string petId, map headers = {}) returns Dog|error { string resourcePath = string `/pets/${getEncodedUri(petId)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/expected_gen/generate_client_requestbody.bal b/openapi-cli/src/test/resources/expected_gen/generate_client_requestbody.bal index 3ec093c35..1a621aeca 100644 --- a/openapi-cli/src/test/resources/expected_gen/generate_client_requestbody.bal +++ b/openapi-cli/src/test/resources/expected_gen/generate_client_requestbody.bal @@ -35,14 +35,16 @@ public isolated client class Client { self.clientEp = httpEp; return; } + # Creates a new user. # + # + headers - Headers to be sent with the request # + return - OK - remote isolated function createUser(User payload) returns error? { + remote isolated function createUser(User payload, map headers = {}) returns error? { string resourcePath = string `/requestBody`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/expected_gen/nullable_false_types.bal b/openapi-cli/src/test/resources/expected_gen/nullable_false_types.bal index 0c104c830..e329fad33 100644 --- a/openapi-cli/src/test/resources/expected_gen/nullable_false_types.bal +++ b/openapi-cli/src/test/resources/expected_gen/nullable_false_types.bal @@ -3,6 +3,12 @@ import ballerina/http; +# Represents the Queries record for the operation: listPets +public type ListPetsQueries record { + # How many items to return at one time (max 100) + int:Signed32 'limit?; +}; + public type Pets Pet[]?; # Provides settings related to HTTP/1.x protocol. diff --git a/openapi-cli/src/test/resources/expected_gen/nullable_types.bal b/openapi-cli/src/test/resources/expected_gen/nullable_types.bal index e0240c84b..4f4880afc 100644 --- a/openapi-cli/src/test/resources/expected_gen/nullable_types.bal +++ b/openapi-cli/src/test/resources/expected_gen/nullable_types.bal @@ -3,6 +3,12 @@ import ballerina/http; +# Represents the Queries record for the operation: listPets +public type ListPetsQueries record { + # How many items to return at one time (max 100) + int 'limit?; +}; + public type Pets Pet[]?; # Provides settings related to HTTP/1.x protocol. diff --git a/openapi-cli/src/test/resources/expected_gen/petstore_catch_all_path_client.bal b/openapi-cli/src/test/resources/expected_gen/petstore_catch_all_path_client.bal index ded803a91..7367bd17f 100644 --- a/openapi-cli/src/test/resources/expected_gen/petstore_catch_all_path_client.bal +++ b/openapi-cli/src/test/resources/expected_gen/petstore_catch_all_path_client.bal @@ -46,39 +46,40 @@ public isolated client class Client { return; } - resource isolated function get \*(int petId) returns Pet|error { + resource isolated function get \*(map headers = {}, *GetPetByIdQueries queries) returns Pet|error { string resourcePath = string `/*`; - map headerValues = {}; - map queryParam = {"petId": petId}; + map headerValues = {...headers}; if self.apiKeyConfig is ApiKeysConfig { headerValues["api_key"] = self.apiKeyConfig?.api_key; } - resourcePath = resourcePath + check getPathForQueryParam(queryParam); + resourcePath = resourcePath + check getPathForQueryParam(queries); map httpHeaders = getMapForHeaders(headerValues); return self.clientEp->get(resourcePath, httpHeaders); } # Add a new pet to the store # + # + headers - Headers to be sent with the request # + payload - Create a new pet in the store # + return - Successful operation - resource isolated function post pet(Pet payload) returns Pet|error { + resource isolated function post pet(Pet payload, map headers = {}) returns Pet|error { string resourcePath = string `/pet`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Update an existing pet # + # + headers - Headers to be sent with the request # + payload - Update an existent pet in the store # + return - Successful operation - resource isolated function put pet(Pet payload) returns Pet|error { + resource isolated function put pet(Pet payload, map headers = {}) returns Pet|error { string resourcePath = string `/pet`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/expected_gen/petstore_client_swagger.bal b/openapi-cli/src/test/resources/expected_gen/petstore_client_swagger.bal index 815bb6499..758c64281 100644 --- a/openapi-cli/src/test/resources/expected_gen/petstore_client_swagger.bal +++ b/openapi-cli/src/test/resources/expected_gen/petstore_client_swagger.bal @@ -49,108 +49,115 @@ public isolated client class Client { # Add a new pet to the store # + # + headers - Headers to be sent with the request # + payload - Pet object that needs to be added to the store # + return - Invalid input - remote isolated function addPet(Pet payload) returns http:Response|error { + remote isolated function addPet(Pet payload, map headers = {}) returns http:Response|error { string resourcePath = string `/pet`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Create user # + # + headers - Headers to be sent with the request # + request - Created user object # + return - successful operation - remote isolated function createUser(http:Request request) returns http:Response|error { + remote isolated function createUser(http:Request request, map headers = {}) returns http:Response|error { string resourcePath = string `/user`; // TODO: Update the request as needed; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Creates list of users with given input array # + # + headers - Headers to be sent with the request # + request - List of user object # + return - successful operation - remote isolated function createUsersWithArrayInput(http:Request request) returns http:Response|error { + remote isolated function createUsersWithArrayInput(http:Request request, map headers = {}) returns http:Response|error { string resourcePath = string `/user/createWithArray`; // TODO: Update the request as needed; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Creates list of users with given input array # + # + headers - Headers to be sent with the request # + request - List of user object # + return - successful operation - remote isolated function createUsersWithListInput(http:Request request) returns http:Response|error { + remote isolated function createUsersWithListInput(http:Request request, map headers = {}) returns http:Response|error { string resourcePath = string `/user/createWithList`; // TODO: Update the request as needed; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Delete purchase order by ID # # + orderId - ID of the order that needs to be deleted + # + headers - Headers to be sent with the request # + return - Invalid ID supplied - remote isolated function deleteOrder(int orderId) returns http:Response|error { + remote isolated function deleteOrder(int orderId, map headers = {}) returns http:Response|error { string resourcePath = string `/store/order/${getEncodedUri(orderId)}`; - return self.clientEp->delete(resourcePath); + return self.clientEp->delete(resourcePath, headers = headers); } # Deletes a pet # # + petId - Pet id to delete + # + headers - Headers to be sent with the request # + return - Invalid ID supplied - remote isolated function deletePet(int petId, string? api_key = ()) returns http:Response|error { + remote isolated function deletePet(int petId, DeletePetHeaders headers = {}) returns http:Response|error { string resourcePath = string `/pet/${getEncodedUri(petId)}`; - map headerValues = {"api_key": api_key}; - map httpHeaders = getMapForHeaders(headerValues); + map httpHeaders = getMapForHeaders(headers); return self.clientEp->delete(resourcePath, headers = httpHeaders); } # Delete user # # + username - The name that needs to be deleted + # + headers - Headers to be sent with the request # + return - Invalid username supplied - remote isolated function deleteUser(string username) returns http:Response|error { + remote isolated function deleteUser(string username, map headers = {}) returns http:Response|error { string resourcePath = string `/user/${getEncodedUri(username)}`; - return self.clientEp->delete(resourcePath); + return self.clientEp->delete(resourcePath, headers = headers); } # Finds Pets by status # - # + status - Status values that need to be considered for filter + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - successful operation - remote isolated function findPetsByStatus(("available"|"pending"|"sold")[] status) returns Pet[]|error { + remote isolated function findPetsByStatus(map headers = {}, *FindPetsByStatusQueries queries) returns Pet[]|error { string resourcePath = string `/pet/findByStatus`; - map queryParam = {"status": status}; map queryParamEncoding = {"status": {style: FORM, explode: true}}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam, queryParamEncoding); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); + return self.clientEp->get(resourcePath, headers); } # Finds Pets by tags # - # + tags - Tags to filter by + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - successful operation # # # Deprecated @deprecated - remote isolated function findPetsByTags(string[] tags) returns Pet[]|error { + remote isolated function findPetsByTags(map headers = {}, *FindPetsByTagsQueries queries) returns Pet[]|error { string resourcePath = string `/pet/findByTags`; - map queryParam = {"tags": tags}; map queryParamEncoding = {"tags": {style: FORM, explode: true}}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam, queryParamEncoding); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); + return self.clientEp->get(resourcePath, headers); } # Returns pet inventories by status # + # + headers - Headers to be sent with the request # + return - successful operation - remote isolated function getInventory() returns record {|int:Signed32...;|}|error { + remote isolated function getInventory(map headers = {}) returns record {|int:Signed32...;|}|error { string resourcePath = string `/store/inventory`; - map headerValues = {}; + map headerValues = {...headers}; if self.apiKeyConfig is ApiKeysConfig { headerValues["api_key"] = self.apiKeyConfig?.api_key; } @@ -161,19 +168,21 @@ public isolated client class Client { # Find purchase order by ID # # + orderId - ID of pet that needs to be fetched + # + headers - Headers to be sent with the request # + return - successful operation - remote isolated function getOrderById(int orderId) returns Order|error { + remote isolated function getOrderById(int orderId, map headers = {}) returns Order|error { string resourcePath = string `/store/order/${getEncodedUri(orderId)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } # Find pet by ID # # + petId - ID of pet to return + # + headers - Headers to be sent with the request # + return - successful operation - remote isolated function getPetById(int petId) returns Pet|error { + remote isolated function getPetById(int petId, map headers = {}) returns Pet|error { string resourcePath = string `/pet/${getEncodedUri(petId)}`; - map headerValues = {}; + map headerValues = {...headers}; if self.apiKeyConfig is ApiKeysConfig { headerValues["api_key"] = self.apiKeyConfig?.api_key; } @@ -184,86 +193,92 @@ public isolated client class Client { # Get user by user name # # + username - The name that needs to be fetched. Use user1 for testing. + # + headers - Headers to be sent with the request # + return - successful operation - remote isolated function getUserByName(string username) returns User|error { + remote isolated function getUserByName(string username, map headers = {}) returns User|error { string resourcePath = string `/user/${getEncodedUri(username)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } # Logs user into the system # - # + username - The user name for login - # + password - The password for login in clear text + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - successful operation - remote isolated function loginUser(string username, string password) returns string|error { + remote isolated function loginUser(map headers = {}, *LoginUserQueries queries) returns string|error { string resourcePath = string `/user/login`; - map queryParam = {"username": username, "password": password}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Logs out current logged in user session # + # + headers - Headers to be sent with the request # + return - successful operation - remote isolated function logoutUser() returns http:Response|error { + remote isolated function logoutUser(map headers = {}) returns http:Response|error { string resourcePath = string `/user/logout`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } # Place an order for a pet # + # + headers - Headers to be sent with the request # + request - order placed for purchasing the pet # + return - successful operation - remote isolated function placeOrder(http:Request request) returns Order|error { + remote isolated function placeOrder(http:Request request, map headers = {}) returns Order|error { string resourcePath = string `/store/order`; // TODO: Update the request as needed; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Update an existing pet # + # + headers - Headers to be sent with the request # + payload - Pet object that needs to be added to the store # + return - Invalid ID supplied - remote isolated function updatePet(Pet payload) returns http:Response|error { + remote isolated function updatePet(Pet payload, map headers = {}) returns http:Response|error { string resourcePath = string `/pet`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } # Updates a pet in the store with form data # # + petId - ID of pet that needs to be updated + # + headers - Headers to be sent with the request # + return - Invalid input - remote isolated function updatePetWithForm(int petId, pet_petId_body payload) returns http:Response|error { + remote isolated function updatePetWithForm(int petId, pet_petId_body payload, map headers = {}) returns http:Response|error { string resourcePath = string `/pet/${getEncodedUri(petId)}`; http:Request request = new; string encodedRequestBody = createFormURLEncodedRequestBody(payload); request.setPayload(encodedRequestBody, "application/x-www-form-urlencoded"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Updated user # # + username - name that need to be updated + # + headers - Headers to be sent with the request # + request - Updated user object # + return - Invalid user supplied - remote isolated function updateUser(string username, http:Request request) returns http:Response|error { + remote isolated function updateUser(string username, http:Request request, map headers = {}) returns http:Response|error { string resourcePath = string `/user/${getEncodedUri(username)}`; // TODO: Update the request as needed; - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } # uploads an image # # + petId - ID of pet to update + # + headers - Headers to be sent with the request # + return - successful operation - remote isolated function uploadFile(int petId, petId_uploadImage_body payload) returns ApiResponse|error { + remote isolated function uploadFile(int petId, petId_uploadImage_body payload, map headers = {}) returns ApiResponse|error { string resourcePath = string `/pet/${getEncodedUri(petId)}/uploadImage`; http:Request request = new; mime:Entity[] bodyParts = check createBodyParts(payload); request.setBodyParts(bodyParts); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/expected_gen/type_filtered_by_tags.bal b/openapi-cli/src/test/resources/expected_gen/type_filtered_by_tags.bal index 633d5baa8..16b0d2801 100644 --- a/openapi-cli/src/test/resources/expected_gen/type_filtered_by_tags.bal +++ b/openapi-cli/src/test/resources/expected_gen/type_filtered_by_tags.bal @@ -3,6 +3,12 @@ import ballerina/http; +# Represents the Queries record for the operation: listPets +public type ListPetsQueries record { + # How many items to return at one time (max 100) + int 'limit?; +}; + public type Pets Pet[]; # Provides settings related to HTTP/1.x protocol. diff --git a/openapi-cli/src/test/resources/expected_gen/x_init_description.bal b/openapi-cli/src/test/resources/expected_gen/x_init_description.bal index 8296b1738..005225c28 100644 --- a/openapi-cli/src/test/resources/expected_gen/x_init_description.bal +++ b/openapi-cli/src/test/resources/expected_gen/x_init_description.bal @@ -47,9 +47,10 @@ public isolated client class Client { # Get movie reviews that are critics' picks. You can either specify the reviewer name or use "all", "full-time", or "part-time". # - # + return - An array of Movie Critics - remote isolated function criticsPicks() returns inline_response_200|error { + # + headers - Headers to be sent with the request + # + return - An array of Movie Critics + remote isolated function criticsPicks(map headers = {}) returns inline_response_200|error { string resourcePath = string `/`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } From 196a9cf882fd0a98514e27eb4b9f110b685151ac Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 10:56:04 +0530 Subject: [PATCH 15/31] Fix BallerinaCodeGeneratorLicenseTests --- .../expected_gen/licenses/client.bal | 20 ++++++++++--------- .../client_with_user_given_license.bal | 20 ++++++++++--------- .../expected_gen/licenses/schema.bal | 6 ++++++ .../schema_for_both_service_client.bal | 6 ++++++ .../schema_with_user_given_license.bal | 6 ++++++ ...es_for_both_service_client_generations.bal | 10 ++++++++-- 6 files changed, 48 insertions(+), 20 deletions(-) diff --git a/openapi-cli/src/test/resources/expected_gen/licenses/client.bal b/openapi-cli/src/test/resources/expected_gen/licenses/client.bal index 593cb0d14..3e5aaa0bd 100644 --- a/openapi-cli/src/test/resources/expected_gen/licenses/client.bal +++ b/openapi-cli/src/test/resources/expected_gen/licenses/client.bal @@ -39,28 +39,30 @@ public isolated client class Client { } # List all pets # - # + 'limit - How many items to return at one time (max 100) + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An paged array of pets - resource isolated function get pets(int? 'limit = ()) returns Pets|error { + resource isolated function get pets(map headers = {}, *ListPetsQueries queries) returns Pets|error { string resourcePath = string `/pets`; - map queryParam = {"limit": 'limit}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Info for a specific pet # # + petId - The id of the pet to retrieve + # + headers - Headers to be sent with the request # + return - Expected response to a valid request - resource isolated function get pets/[string petId]() returns Dog|error { + resource isolated function get pets/[string petId](map headers = {}) returns Dog|error { string resourcePath = string `/pets/${getEncodedUri(petId)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } # Create a pet # + # + headers - Headers to be sent with the request # + return - Null response - resource isolated function post pets() returns http:Response|error { + resource isolated function post pets(map headers = {}) returns http:Response|error { string resourcePath = string `/pets`; http:Request request = new; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/expected_gen/licenses/client_with_user_given_license.bal b/openapi-cli/src/test/resources/expected_gen/licenses/client_with_user_given_license.bal index e625311a1..cf8848835 100644 --- a/openapi-cli/src/test/resources/expected_gen/licenses/client_with_user_given_license.bal +++ b/openapi-cli/src/test/resources/expected_gen/licenses/client_with_user_given_license.bal @@ -38,28 +38,30 @@ public isolated client class Client { } # List all pets # - # + 'limit - How many items to return at one time (max 100) + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An paged array of pets - resource isolated function get pets(int? 'limit = ()) returns Pets|error { + resource isolated function get pets(map headers = {}, *ListPetsQueries queries) returns Pets|error { string resourcePath = string `/pets`; - map queryParam = {"limit": 'limit}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Info for a specific pet # # + petId - The id of the pet to retrieve + # + headers - Headers to be sent with the request # + return - Expected response to a valid request - resource isolated function get pets/[string petId]() returns Dog|error { + resource isolated function get pets/[string petId](map headers = {}) returns Dog|error { string resourcePath = string `/pets/${getEncodedUri(petId)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } # Create a pet # + # + headers - Headers to be sent with the request # + return - Null response - resource isolated function post pets() returns http:Response|error { + resource isolated function post pets(map headers = {}) returns http:Response|error { string resourcePath = string `/pets`; http:Request request = new; - return self.clientEp-> post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/expected_gen/licenses/schema.bal b/openapi-cli/src/test/resources/expected_gen/licenses/schema.bal index 633d5baa8..16b0d2801 100644 --- a/openapi-cli/src/test/resources/expected_gen/licenses/schema.bal +++ b/openapi-cli/src/test/resources/expected_gen/licenses/schema.bal @@ -3,6 +3,12 @@ import ballerina/http; +# Represents the Queries record for the operation: listPets +public type ListPetsQueries record { + # How many items to return at one time (max 100) + int 'limit?; +}; + public type Pets Pet[]; # Provides settings related to HTTP/1.x protocol. diff --git a/openapi-cli/src/test/resources/expected_gen/licenses/schema_for_both_service_client.bal b/openapi-cli/src/test/resources/expected_gen/licenses/schema_for_both_service_client.bal index 8943ac17b..99b9ac5a2 100644 --- a/openapi-cli/src/test/resources/expected_gen/licenses/schema_for_both_service_client.bal +++ b/openapi-cli/src/test/resources/expected_gen/licenses/schema_for_both_service_client.bal @@ -3,6 +3,12 @@ import ballerina/http; +# Represents the Queries record for the operation: listPets +public type ListPetsQueries record { + # How many items to return at one time (max 100) + int 'limit?; +}; + public type Pets Pet[]; # Provides settings related to HTTP/1.x protocol. diff --git a/openapi-cli/src/test/resources/expected_gen/licenses/schema_with_user_given_license.bal b/openapi-cli/src/test/resources/expected_gen/licenses/schema_with_user_given_license.bal index fb8086435..cd3fea317 100644 --- a/openapi-cli/src/test/resources/expected_gen/licenses/schema_with_user_given_license.bal +++ b/openapi-cli/src/test/resources/expected_gen/licenses/schema_with_user_given_license.bal @@ -2,6 +2,12 @@ import ballerina/http; +# Represents the Queries record for the operation: listPets +public type ListPetsQueries record { + # How many items to return at one time (max 100) + int 'limit?; +}; + public type Pets Pet[]; # Provides settings related to HTTP/1.x protocol. diff --git a/openapi-cli/src/test/resources/expected_gen/licenses/types_for_both_service_client_generations.bal b/openapi-cli/src/test/resources/expected_gen/licenses/types_for_both_service_client_generations.bal index 289168817..99b9ac5a2 100644 --- a/openapi-cli/src/test/resources/expected_gen/licenses/types_for_both_service_client_generations.bal +++ b/openapi-cli/src/test/resources/expected_gen/licenses/types_for_both_service_client_generations.bal @@ -3,6 +3,12 @@ import ballerina/http; +# Represents the Queries record for the operation: listPets +public type ListPetsQueries record { + # How many items to return at one time (max 100) + int 'limit?; +}; + public type Pets Pet[]; # Provides settings related to HTTP/1.x protocol. @@ -28,9 +34,9 @@ public type ProxyConfig record {| string password = ""; |}; -public type Dog record{ +public type Dog record { *Pet; - booleanbark?; + boolean bark?; }; public type Pet record { From a0e3461d00b755918ff211dd32d2bf2cb48aa57e Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 11:09:43 +0530 Subject: [PATCH 16/31] Fix FunctionSignatureReturnTypeTests --- .../ballerina/return/no_content_type.bal | 20 ++++++----- .../ballerina/return/return_type_encode.bal | 33 +++++++++---------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/return/no_content_type.bal b/openapi-cli/src/test/resources/generators/client/ballerina/return/no_content_type.bal index 0a69509ed..fb81a2a4e 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/return/no_content_type.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/return/no_content_type.bal @@ -34,38 +34,42 @@ public isolated client class Client { self.clientEp = httpEp; return; } + # + headers - Headers to be sent with the request # + return - Moved Permanently - resource isolated function post user3(User payload) returns error? { + resource isolated function post user3(User payload, map headers = {}) returns error? { string resourcePath = string `/user3`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # This status code will be generate with previous approach till we address the error status code. # + # + headers - Headers to be sent with the request # + return - Unauthorized - resource isolated function post user4(User payload) returns http:Response|error { + resource isolated function post user4(User payload, map headers = {}) returns http:Response|error { string resourcePath = string `/user4`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } + # + headers - Headers to be sent with the request # + return - Switching protocols - resource isolated function post users(User payload) returns error? { + resource isolated function post users(User payload, map headers = {}) returns error? { string resourcePath = string `/users`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } + # + headers - Headers to be sent with the request # + return - Created - resource isolated function post users02(User payload) returns error? { + resource isolated function post users02(User payload, map headers = {}) returns error? { string resourcePath = string `/users02`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/return/return_type_encode.bal b/openapi-cli/src/test/resources/generators/client/ballerina/return/return_type_encode.bal index d9bf42d60..2e6ff66dd 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/return/return_type_encode.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/return/return_type_encode.bal @@ -41,42 +41,39 @@ public isolated client class Client { # Product Types # - # + latitude - Latitude component of location. - # + longitude - Longitude component of location. # + country - Country name. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An array of products - resource isolated function get products/[string country](decimal latitude, decimal longitude) returns Product[]|error { + resource isolated function get products/[string country](map headers = {}, *GetProductsCountryQueries queries) returns Product[]|error { string resourcePath = string `/products/${getEncodedUri(country)}`; - map queryParam = {"latitude": latitude, "longitude": longitude}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Product Types # - # + latitude - Latitude component of location. - # + longitude - Longitude component of location. # + country - Country name. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An array of products - resource isolated function post products/[string country](decimal latitude, decimal longitude) returns xml|error { + resource isolated function post products/[string country](map headers = {}, *PostProductsCountryQueries queries) returns xml|error { string resourcePath = string `/products/${getEncodedUri(country)}`; - map queryParam = {"latitude": latitude, "longitude": longitude}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); + resourcePath = resourcePath + check getPathForQueryParam(queries); http:Request request = new; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Product Types # - # + latitude - Latitude component of location. - # + longitude - Longitude component of location. # + country - Country name. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An array of products - resource isolated function put products/[string country](decimal latitude, decimal longitude) returns Product[]|error { + resource isolated function put products/[string country](map headers = {}, *PutProductsCountryQueries queries) returns Product[]|error { string resourcePath = string `/products/${getEncodedUri(country)}`; - map queryParam = {"latitude": latitude, "longitude": longitude}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); + resourcePath = resourcePath + check getPathForQueryParam(queries); http:Request request = new; - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } } From b3c821e807ce7ba5b9265e3ebb2e8cff9d8aa0a9 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 13:17:26 +0530 Subject: [PATCH 17/31] Fix RequestBodyTests --- .../client/ballerina/any_types_payload.bal | 5 +-- .../binary_format_octet_stream_payload.bal | 6 ++-- .../byte_format_octet_stream_payload.bal | 6 ++-- .../client/ballerina/multipart_binary.bal | 5 +-- .../client/ballerina/multipart_formdata.bal | 10 +++--- .../ballerina/multipart_formdata_empty.bal | 5 +-- .../octet_stream_request_payload.bal | 10 +++--- .../request_body_allOf_scenarios.bal | 15 ++++---- .../client/ballerina/request_body_array.bal | 5 +-- .../request_body_basic_scenarios.bal | 35 +++++++++++-------- .../ballerina/request_body_empty_array.bal | 5 +-- ...dy_has_object_content_without_property.bal | 30 +++++++++------- .../request_body_oneOf_scenarios.bal | 5 +-- .../ballerina/request_body_with_ref.bal | 10 +++--- .../ballerina/request_body_without_schema.bal | 12 +++---- .../ballerina/unsupported_request_body.bal | 5 +-- .../ballerina/unsupported_text_media_type.bal | 5 +-- .../client/ballerina/url_encoded_payload.bal | 10 +++--- .../ballerina/vendor_specific_payload.bal | 5 +-- .../vendor_specific_with_subtype.bal | 10 +++--- 20 files changed, 118 insertions(+), 81 deletions(-) diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/any_types_payload.bal b/openapi-cli/src/test/resources/generators/client/ballerina/any_types_payload.bal index 16b91594d..dec1f6049 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/any_types_payload.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/any_types_payload.bal @@ -36,10 +36,11 @@ public isolated client class Client { } # Create a pet # + # + headers - Headers to be sent with the request # + return - Null response - remote isolated function createPet(http:Request request) returns error? { + remote isolated function createPet(http:Request request, map headers = {}) returns error? { string resourcePath = string `/pets`; // TODO: Update the request as needed; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/binary_format_octet_stream_payload.bal b/openapi-cli/src/test/resources/generators/client/ballerina/binary_format_octet_stream_payload.bal index a3523ad6b..0d8a57d59 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/binary_format_octet_stream_payload.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/binary_format_octet_stream_payload.bal @@ -36,12 +36,12 @@ public isolated client class Client { } # Create a pet # + # + headers - Headers to be sent with the request # + return - Null response - remote isolated function createPet(byte[] payload) returns error? { + remote isolated function createPet(byte[] payload, map headers = {}) returns error? { string resourcePath = string `/pets`; http:Request request = new; request.setPayload(payload, "application/octet-stream"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } - diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/byte_format_octet_stream_payload.bal b/openapi-cli/src/test/resources/generators/client/ballerina/byte_format_octet_stream_payload.bal index 6cbd7c982..c6ddda3b1 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/byte_format_octet_stream_payload.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/byte_format_octet_stream_payload.bal @@ -34,14 +34,16 @@ public isolated client class Client { self.clientEp = httpEp; return; } + # Create a pet # + # + headers - Headers to be sent with the request # + return - Null response - remote isolated function createPet(byte[] payload) returns error? { + remote isolated function createPet(byte[] payload, map headers = {}) returns error? { string resourcePath = string `/pets`; http:Request request = new; string encodedRequestBody = payload.toBase64(); request.setPayload(encodedRequestBody, "application/octet-stream"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/multipart_binary.bal b/openapi-cli/src/test/resources/generators/client/ballerina/multipart_binary.bal index 3bc264d48..eb97fdf93 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/multipart_binary.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/multipart_binary.bal @@ -36,11 +36,12 @@ public isolated client class Client { } # Create a pet # + # + headers - Headers to be sent with the request # + request - Pet # + return - Null response - remote isolated function createPet(http:Request request) returns error? { + remote isolated function createPet(http:Request request, map headers = {}) returns error? { string resourcePath = string `/pets`; // TODO: Update the request as needed; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/multipart_formdata.bal b/openapi-cli/src/test/resources/generators/client/ballerina/multipart_formdata.bal index 5bac0a89b..16fd616f7 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/multipart_formdata.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/multipart_formdata.bal @@ -38,25 +38,27 @@ public isolated client class Client { # Create a pet # + # + headers - Headers to be sent with the request # + request - Pet # + return - Null response - remote isolated function createPet(pets_body payload) returns error? { + remote isolated function createPet(pets_body payload, map headers = {}) returns error? { string resourcePath = string `/pets`; http:Request request = new; mime:Entity[] bodyParts = check createBodyParts(payload); request.setBodyParts(bodyParts); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Create an user # + # + headers - Headers to be sent with the request # + request - User # + return - Null response - remote isolated function createUser(user_body payload) returns error? { + remote isolated function createUser(user_body payload, map headers = {}) returns error? { string resourcePath = string `/user`; http:Request request = new; mime:Entity[] bodyParts = check createBodyParts(payload); request.setBodyParts(bodyParts); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/multipart_formdata_empty.bal b/openapi-cli/src/test/resources/generators/client/ballerina/multipart_formdata_empty.bal index 3bc264d48..eb97fdf93 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/multipart_formdata_empty.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/multipart_formdata_empty.bal @@ -36,11 +36,12 @@ public isolated client class Client { } # Create a pet # + # + headers - Headers to be sent with the request # + request - Pet # + return - Null response - remote isolated function createPet(http:Request request) returns error? { + remote isolated function createPet(http:Request request, map headers = {}) returns error? { string resourcePath = string `/pets`; // TODO: Update the request as needed; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/octet_stream_request_payload.bal b/openapi-cli/src/test/resources/generators/client/ballerina/octet_stream_request_payload.bal index 0c3ad1a3e..c9c5deb6f 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/octet_stream_request_payload.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/octet_stream_request_payload.bal @@ -37,21 +37,23 @@ public isolated client class Client { } # Creates a new payment. # + # + headers - Headers to be sent with the request # + payload - Details of the pet to be purchased # + return - OK - remote isolated function addPayment(byte[] payload) returns error? { + remote isolated function addPayment(byte[] payload, map headers = {}) returns error? { string resourcePath = string `/payment`; http:Request request = new; request.setPayload(payload, "application/octet-stream"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Creates a new user. # + # + headers - Headers to be sent with the request # + return - OK - remote isolated function addUser(byte[] payload) returns error? { + remote isolated function addUser(byte[] payload, map headers = {}) returns error? { string resourcePath = string `/user`; http:Request request = new; request.setPayload(payload, "application/octet-stream"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_allOf_scenarios.bal b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_allOf_scenarios.bal index 60ad17b30..977f44a7e 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_allOf_scenarios.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_allOf_scenarios.bal @@ -39,36 +39,39 @@ public isolated client class Client { # Request Body has nested allOf. # + # + headers - Headers to be sent with the request # + return - OK - remote isolated function postXMLUser(path01_body_1 payload) returns error? { + remote isolated function postXMLUser(path01_body_1 payload, map headers = {}) returns error? { string resourcePath = string `/path01`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Request Body has Array type AllOf. # + # + headers - Headers to be sent with the request # + return - OK - remote isolated function postXMLUserInLineArray(record {*User; boolean? hunts?; int? age?;}[] payload) returns error? { + remote isolated function postXMLUserInLineArray(record {*User; boolean? hunts?; int? age?;}[] payload, map headers = {}) returns error? { string resourcePath = string `/path02`; http:Request request = new; json jsonBody = payload.toJson(); xml? xmlBody = check xmldata:fromJson(jsonBody); request.setPayload(xmlBody, "application/xml"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Request Body has allOf with specific properties. # + # + headers - Headers to be sent with the request # + return - OK - remote isolated function updateXMLUser(path01_body payload) returns error? { + remote isolated function updateXMLUser(path01_body payload, map headers = {}) returns error? { string resourcePath = string `/path01`; http:Request request = new; json jsonBody = payload.toJson(); xml? xmlBody = check xmldata:fromJson(jsonBody); request.setPayload(xmlBody, "application/xml"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_array.bal b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_array.bal index 40bd33e7d..9d8e8ac66 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_array.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_array.bal @@ -37,12 +37,13 @@ public isolated client class Client { } # 02 Example for rb has inline requestbody. # + # + headers - Headers to be sent with the request # + return - OK - remote isolated function updateUser(string[] payload) returns error? { + remote isolated function updateUser(string[] payload, map headers = {}) returns error? { string resourcePath = string `/path01`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_basic_scenarios.bal b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_basic_scenarios.bal index 42259d5e2..a4f2a99a2 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_basic_scenarios.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_basic_scenarios.bal @@ -39,82 +39,89 @@ public isolated client class Client { # 03 Request body with record reference. # + # + headers - Headers to be sent with the request # + return - OK - remote isolated function postNewUser(User[] payload) returns error? { + remote isolated function postNewUser(User[] payload, map headers = {}) returns error? { string resourcePath = string `/path02`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # 01 Request body with reference. # + # + headers - Headers to be sent with the request # + return - OK - remote isolated function postUser(User payload) returns error? { + remote isolated function postUser(User payload, map headers = {}) returns error? { string resourcePath = string `/path01`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # 05 Example for rb has array inline requestbody. # + # + headers - Headers to be sent with the request # + return - OK - remote isolated function postXMLUser(path03_body_1 payload) returns error? { + remote isolated function postXMLUser(path03_body_1 payload, map headers = {}) returns error? { string resourcePath = string `/path03`; http:Request request = new; json jsonBody = payload.toJson(); xml? xmlBody = check xmldata:fromJson(jsonBody); request.setPayload(xmlBody, "application/xml"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # 07 Example for rb has array inline requestbody. # + # + headers - Headers to be sent with the request # + return - OK - remote isolated function postXMLUserInLineArray(path04_body[] payload) returns error? { + remote isolated function postXMLUserInLineArray(path04_body[] payload, map headers = {}) returns error? { string resourcePath = string `/path04`; http:Request request = new; json jsonBody = payload.toJson(); xml? xmlBody = check xmldata:fromJson(jsonBody); request.setPayload(xmlBody, "application/xml"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # 04 Example for rb has inline requestbody. # + # + headers - Headers to be sent with the request # + payload - A JSON object containing pet information # + return - OK - remote isolated function updateNewUser(User payload) returns error? { + remote isolated function updateNewUser(User payload, map headers = {}) returns error? { string resourcePath = string `/path02`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } # 02 Example for rb has inline requestbody. # + # + headers - Headers to be sent with the request # + return - OK - remote isolated function updateUser(path01_body payload) returns error? { + remote isolated function updateUser(path01_body payload, map headers = {}) returns error? { string resourcePath = string `/path01`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } # 06 Example for rb has array inline requestbody. # + # + headers - Headers to be sent with the request # + return - OK - remote isolated function updateXMLUser(path03_body payload) returns error? { + remote isolated function updateXMLUser(path03_body payload, map headers = {}) returns error? { string resourcePath = string `/path03`; http:Request request = new; json jsonBody = payload.toJson(); xml? xmlBody = check xmldata:fromJson(jsonBody); request.setPayload(xmlBody, "application/xml"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_empty_array.bal b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_empty_array.bal index d37c31ccd..96cb5b7dc 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_empty_array.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_empty_array.bal @@ -37,12 +37,13 @@ public isolated client class Client { } # 02 Example for rb has inline requestbody. # + # + headers - Headers to be sent with the request # + return - OK - remote isolated function updateUser(json[] payload) returns error? { + remote isolated function updateUser(json[] payload, map headers = {}) returns error? { string resourcePath = string `/path01`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_has_object_content_without_property.bal b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_has_object_content_without_property.bal index 99ff7e739..d465d89c4 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_has_object_content_without_property.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_has_object_content_without_property.bal @@ -37,62 +37,68 @@ public isolated client class Client { } # The Request body is with reference to the reusable requestBody with content type has object without properties # + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function op01(record {} payload) returns string|error { + remote isolated function op01(record {} payload, map headers = {}) returns string|error { string resourcePath = string `/greeting`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Request body has object schema content without the properties field # + # + headers - Headers to be sent with the request # + payload - A JSON object containing pet information # + return - Ok - remote isolated function op02(record {} payload) returns string|error { + remote isolated function op02(record {} payload, map headers = {}) returns string|error { string resourcePath = string `/greeting`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } # RequestBody has object content without properties with application/xml # + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function op03(record {} payload) returns string|error { + remote isolated function op03(record {} payload, map headers = {}) returns string|error { string resourcePath = string `/greeting02`; http:Request request = new; json jsonBody = payload.toJson(); xml? xmlBody = check xmldata:fromJson(jsonBody); request.setPayload(xmlBody, "application/xml"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # RequestBody has object content without properties with vendor-specific media type vnd.petstore.v3.diff+json # + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function op04(record {} payload) returns string|error { + remote isolated function op04(record {} payload, map headers = {}) returns string|error { string resourcePath = string `/greeting02`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/vnd.petstore.v3.diff+json"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } # Request body has properties with {} value # + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function op05(record {} payload) returns error? { + remote isolated function op05(record {} payload, map headers = {}) returns error? { string resourcePath = string `/greeting03`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } # Request body has non-standard media type application/zip with object content without properties # + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function op06(http:Request request) returns error? { + remote isolated function op06(http:Request request, map headers = {}) returns error? { string resourcePath = string `/greeting03`; // TODO: Update the request as needed; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_oneOf_scenarios.bal b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_oneOf_scenarios.bal index 4a7bd1aea..ab2d6da5d 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_oneOf_scenarios.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_oneOf_scenarios.bal @@ -38,13 +38,14 @@ public isolated client class Client { # Request Body has nested allOf. # + # + headers - Headers to be sent with the request # + payload - A JSON object containing pet information # + return - OK - remote isolated function postXMLUser(path01_body payload) returns error? { + remote isolated function postXMLUser(path01_body payload, map headers = {}) returns error? { string resourcePath = string `/path01`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_with_ref.bal b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_with_ref.bal index 93c38e04f..d1eea0d66 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_with_ref.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_with_ref.bal @@ -36,24 +36,26 @@ public isolated client class Client { } # Create a pet # + # + headers - Headers to be sent with the request # + payload - Return from creating a pet # + return - Successful operation - remote isolated function createMyPet(Pet payload) returns error? { + remote isolated function createMyPet(Pet payload, map headers = {}) returns error? { string resourcePath = string `/my/pets`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Create a pet # + # + headers - Headers to be sent with the request # + payload - Return from creating a pet # + return - Successful operation - remote isolated function createPet(record {string? petId?; string? createdDate?;} payload) returns error? { + remote isolated function createPet(record {string? petId?; string? createdDate?;} payload, map headers = {}) returns error? { string resourcePath = string `/pets`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_without_schema.bal b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_without_schema.bal index 1bfaa48c0..af521feea 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/request_body_without_schema.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/request_body_without_schema.bal @@ -36,15 +36,15 @@ public isolated client class Client { } # List all pets # - # + 'limit - How many items to return at one time (max 100) + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + payload - Pet - # + return - Return json - remote isolated function listPets(json payload, int? 'limit = ()) returns json|error { + # + return - Return json + remote isolated function listPets(json payload, map headers = {}, *ListPetsQueries queries) returns json|error { string resourcePath = string `/pets`; - map queryParam = {"limit": 'limit}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); + resourcePath = resourcePath + check getPathForQueryParam(queries); http:Request request = new; request.setPayload(payload, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/unsupported_request_body.bal b/openapi-cli/src/test/resources/generators/client/ballerina/unsupported_request_body.bal index 8eab91a7c..7f608c1e5 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/unsupported_request_body.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/unsupported_request_body.bal @@ -39,11 +39,12 @@ public isolated client class Client { #

Creates a new customer object.

# # + customer - Customer ID + # + headers - Headers to be sent with the request # + request - Customer Details # + return - Successful response. - remote isolated function postCustomers(string customer, http:Request request) returns customer|error { + remote isolated function postCustomers(string customer, http:Request request, map headers = {}) returns customer|error { string resourcePath = string `/v1/customer/${getEncodedUri(customer)}`; // TODO: Update the request as needed; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/unsupported_text_media_type.bal b/openapi-cli/src/test/resources/generators/client/ballerina/unsupported_text_media_type.bal index 8bc594ce3..3569326cb 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/unsupported_text_media_type.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/unsupported_text_media_type.bal @@ -36,11 +36,12 @@ public isolated client class Client { } # Create a pet # + # + headers - Headers to be sent with the request # + return - Response of create pet - remote isolated function createPet(string payload) returns string|error { + remote isolated function createPet(string payload, map headers = {}) returns string|error { string resourcePath = string `/pets`; http:Request request = new; request.setPayload(payload, "text/xxx"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/url_encoded_payload.bal b/openapi-cli/src/test/resources/generators/client/ballerina/url_encoded_payload.bal index 100d32c97..da29ad40e 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/url_encoded_payload.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/url_encoded_payload.bal @@ -39,22 +39,24 @@ public isolated client class Client { #

Retrieves a PaymentMethod object.

# # + payment_method - Payment Method + # + headers - Headers to be sent with the request # + return - Successful response. - remote isolated function getPaymentMethodsPaymentMethod(string payment_method) returns json|error { + remote isolated function getPaymentMethodsPaymentMethod(string payment_method, map headers = {}) returns json|error { string resourcePath = string `/v1/payment_methods/${getEncodedUri(payment_method)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } #

Creates a new customer object.

# # + customer - Customer ID + # + headers - Headers to be sent with the request # + payload - Customer Details # + return - Successful response. - remote isolated function postCustomers(string customer, customer_customer_body payload) returns Customer|error { + remote isolated function postCustomers(string customer, customer_customer_body payload, map headers = {}) returns Customer|error { string resourcePath = string `/v1/customer/${getEncodedUri(customer)}`; http:Request request = new; string encodedRequestBody = createFormURLEncodedRequestBody(payload); request.setPayload(encodedRequestBody, "application/x-www-form-urlencoded"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/vendor_specific_payload.bal b/openapi-cli/src/test/resources/generators/client/ballerina/vendor_specific_payload.bal index 16b91594d..dec1f6049 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/vendor_specific_payload.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/vendor_specific_payload.bal @@ -36,10 +36,11 @@ public isolated client class Client { } # Create a pet # + # + headers - Headers to be sent with the request # + return - Null response - remote isolated function createPet(http:Request request) returns error? { + remote isolated function createPet(http:Request request, map headers = {}) returns error? { string resourcePath = string `/pets`; // TODO: Update the request as needed; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/vendor_specific_with_subtype.bal b/openapi-cli/src/test/resources/generators/client/ballerina/vendor_specific_with_subtype.bal index 86834d1a8..6ac3c11e9 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/vendor_specific_with_subtype.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/vendor_specific_with_subtype.bal @@ -38,24 +38,26 @@ public isolated client class Client { # Create a pet # + # + headers - Headers to be sent with the request # + return - List of existing pets - remote isolated function createPet(Pet payload) returns Pets|error { + remote isolated function createPet(Pet payload, map headers = {}) returns Pets|error { string resourcePath = string `/pets`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/vnd.petstore.v3.diff+json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Create a pet # + # + headers - Headers to be sent with the request # + return - List of existing pets - remote isolated function createPetV0(Pet payload) returns xml|error { + remote isolated function createPetV0(Pet payload, map headers = {}) returns xml|error { string resourcePath = string `/v0/pets`; http:Request request = new; json jsonBody = payload.toJson(); xml? xmlBody = check xmldata:fromJson(jsonBody); request.setPayload(xmlBody, "application/vnd.petstore.v3.diff+xml"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } From b7650e10b9c9e32a329d7c78bf6a761e9c066493 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 14:11:57 +0530 Subject: [PATCH 18/31] Handle incompatible header and query param --- .../parameter/HeadersParameterGenerator.java | 41 ++++++++++++++++--- .../parameter/QueriesParameterGenerator.java | 37 ++++++++++++++--- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java index 3ecdd851a..fe8109b69 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java @@ -10,6 +10,7 @@ import io.ballerina.compiler.syntax.tree.TypeParameterNode; import io.ballerina.compiler.syntax.tree.UnionTypeDescriptorNode; import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; +import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp; import io.ballerina.openapi.core.generators.common.GeneratorConstants; import io.ballerina.openapi.core.generators.common.GeneratorUtils; import io.ballerina.openapi.core.generators.common.TypeHandler; @@ -20,11 +21,11 @@ import io.swagger.v3.oas.models.parameters.Parameter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyMinutiaeList; import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; @@ -44,8 +45,8 @@ import static io.ballerina.compiler.syntax.tree.SyntaxKind.LT_TOKEN; import static io.ballerina.compiler.syntax.tree.SyntaxKind.MAP_KEYWORD; import static io.ballerina.compiler.syntax.tree.SyntaxKind.PIPE_TOKEN; +import static io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages.OAS_CLIENT_108; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEADERS; -import static io.ballerina.openapi.core.generators.common.GeneratorUtils.escapeIdentifier; public class HeadersParameterGenerator implements ParameterGenerator { private final List parameters; @@ -66,7 +67,15 @@ public HeadersParameterGenerator(List parameters, OpenAPI openAPI, Op @Override public Optional generateParameterNode() { + if (parameters.isEmpty()) { + return Optional.empty(); + } + ObjectSchema headersSchema = getHeadersSchema(); + if (Objects.isNull(headersSchema)) { + return Optional.empty(); + } + String operationId = GeneratorUtils.generateOperationUniqueId(operation, path, httpMethod); headersSchema.setDescription("Represents the Headers record for the operation: " + operationId); String headersName = GeneratorUtils.getValidName(operationId, true) + "Headers"; @@ -137,13 +146,30 @@ public List getDiagnostics() { } private ObjectSchema getHeadersSchema() { - Map properties = parameters.stream() - .collect(Collectors.toMap(Parameter::getName, this::getSchemaWithDetails)); + Map properties = new HashMap<>(); + for (Parameter parameter : parameters) { + properties.put(parameter.getName(), getSchemaWithDetails(parameter)); + } + + properties.entrySet().removeIf(entry -> { + if (Objects.isNull(entry.getValue())) { + ClientDiagnostic diagnostic = new ClientDiagnosticImp(OAS_CLIENT_108, entry.getKey()); + diagnostics.add(diagnostic); + return true; + } + return false; + }); + + if (properties.isEmpty()) { + return null; + } - List requiredFields = parameters.stream() + List requiredFields = new ArrayList<>(parameters.stream() .filter(parameter -> Boolean.TRUE.equals(parameter.getRequired())) .map(Parameter::getName) - .toList(); + .toList()); + + requiredFields.removeIf(field -> !properties.containsKey(field)); ObjectSchema headersSchema = new ObjectSchema(); headersSchema.setProperties(properties); @@ -155,6 +181,9 @@ private ObjectSchema getHeadersSchema() { private Schema getSchemaWithDetails(Parameter parameter) { Schema schema = parameter.getSchema(); + if (Objects.isNull(schema)) { + return null; + } schema.setDescription(parameter.getDescription()); schema.setDeprecated(parameter.getDeprecated()); schema.extensions(parameter.getExtensions()); diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java index 0a2b33f77..2463c03a8 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java @@ -21,6 +21,7 @@ import io.ballerina.compiler.syntax.tree.ParameterNode; import io.ballerina.compiler.syntax.tree.TypeDescriptorNode; import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic; +import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp; import io.ballerina.openapi.core.generators.common.GeneratorUtils; import io.ballerina.openapi.core.generators.common.TypeHandler; import io.swagger.v3.oas.models.OpenAPI; @@ -30,16 +31,18 @@ import io.swagger.v3.oas.models.parameters.Parameter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; import static io.ballerina.compiler.syntax.tree.NodeFactory.createIncludedRecordParameterNode; import static io.ballerina.compiler.syntax.tree.SyntaxKind.ASTERISK_TOKEN; +import static io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages.OAS_CLIENT_102; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.QUERIES; public class QueriesParameterGenerator implements ParameterGenerator { @@ -67,6 +70,10 @@ public Optional generateParameterNode() { } ObjectSchema queriesSchema = getQueriesSchema(); + if (Objects.isNull(queriesSchema)) { + return Optional.empty(); + } + String operationId = GeneratorUtils.generateOperationUniqueId(operation, path, httpMethod); queriesSchema.setDescription("Represents the Queries record for the operation: " + operationId); String queriesName = GeneratorUtils.getValidName(operationId, true) + "Queries"; @@ -86,13 +93,30 @@ public List getDiagnostics() { } private ObjectSchema getQueriesSchema() { - Map properties = parameters.stream() - .collect(Collectors.toMap(Parameter::getName, this::getSchemaWithDetails)); + Map properties = new HashMap<>(); + for (Parameter parameter : parameters) { + properties.put(parameter.getName(), getSchemaWithDetails(parameter)); + } - List requiredFields = parameters.stream() + properties.entrySet().removeIf(entry -> { + if (Objects.isNull(entry.getValue())) { + ClientDiagnostic diagnostic = new ClientDiagnosticImp(OAS_CLIENT_102, entry.getKey()); + diagnostics.add(diagnostic); + return true; + } + return false; + }); + + if (properties.isEmpty()) { + return null; + } + + List requiredFields = new ArrayList<>(parameters.stream() .filter(parameter -> Boolean.TRUE.equals(parameter.getRequired())) .map(Parameter::getName) - .toList(); + .toList()); + + requiredFields.removeIf(field -> !properties.containsKey(field)); ObjectSchema queriesSchema = new ObjectSchema(); queriesSchema.setProperties(properties); @@ -104,6 +128,9 @@ private ObjectSchema getQueriesSchema() { private Schema getSchemaWithDetails(Parameter parameter) { Schema schema = parameter.getSchema(); + if (Objects.isNull(schema)) { + return null; + } schema.setDescription(parameter.getDescription()); schema.setDeprecated(parameter.getDeprecated()); schema.extensions(parameter.getExtensions()); From fd663a9139e3d1d265c0bb28dde611127c12b5d8 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 14:45:02 +0530 Subject: [PATCH 19/31] Skip operation when there is an error from query or header generators --- .../client/RemoteFunctionSignatureGenerator.java | 12 ++++++++++-- .../client/ResourceFunctionSignatureGenerator.java | 12 ++++++++++-- .../client/parameter/HeadersParameterGenerator.java | 6 ++++++ .../client/parameter/QueriesParameterGenerator.java | 6 ++++++ 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGenerator.java index c521b8555..6f2ad33c2 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/RemoteFunctionSignatureGenerator.java @@ -171,6 +171,11 @@ protected ParametersInfo getParametersInfo(List parameters) { headers = headersParameterGenerator.generateParameterNode(); } + diagnostics.addAll(headersParameterGenerator.getDiagnostics()); + if (headersParameterGenerator.hasErrors()) { + return null; + } + if (headers.isPresent()) { hasHeadersParam = true; if (headers.get() instanceof RequiredParameterNode headerNode) { @@ -181,19 +186,22 @@ protected ParametersInfo getParametersInfo(List parameters) { defaultableParams.add(comma); } } else if (!headerParameters.isEmpty()) { - diagnostics.addAll(headersParameterGenerator.getDiagnostics()); return null; } QueriesParameterGenerator queriesParameterGenerator = new QueriesParameterGenerator(queryParameters, openAPI, operation, httpMethod, path); Optional queries = queriesParameterGenerator.generateParameterNode(); + diagnostics.addAll(queriesParameterGenerator.getDiagnostics()); + if (queriesParameterGenerator.hasErrors()) { + return null; + } + if (queries.isPresent()) { hasQueriesParam = true; includedParam.add(queries.get()); includedParam.add(comma); } else if (!queryParameters.isEmpty()) { - diagnostics.addAll(queriesParameterGenerator.getDiagnostics()); return null; } } else { diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGenerator.java index 9dc022730..e44804368 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ResourceFunctionSignatureGenerator.java @@ -154,6 +154,11 @@ protected ParametersInfo getParametersInfo(List parameters) { headers = headersParameterGenerator.generateParameterNode(); } + diagnostics.addAll(headersParameterGenerator.getDiagnostics()); + if (headersParameterGenerator.hasErrors()) { + return null; + } + if (headers.isPresent()) { hasHeadersParam = true; if (headers.get() instanceof RequiredParameterNode headerNode) { @@ -164,19 +169,22 @@ protected ParametersInfo getParametersInfo(List parameters) { defaultableParams.add(comma); } } else if (!headerParameters.isEmpty()) { - diagnostics.addAll(headersParameterGenerator.getDiagnostics()); return null; } QueriesParameterGenerator queriesParameterGenerator = new QueriesParameterGenerator(queryParameters, openAPI, operation, httpMethod, path); Optional queries = queriesParameterGenerator.generateParameterNode(); + diagnostics.addAll(queriesParameterGenerator.getDiagnostics()); + if (queriesParameterGenerator.hasErrors()) { + return null; + } + if (queries.isPresent()) { hasQueriesParam = true; includedParam.add(queries.get()); includedParam.add(comma); } else if (!queryParameters.isEmpty()) { - diagnostics.addAll(queriesParameterGenerator.getDiagnostics()); return null; } } else { diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java index fe8109b69..1c2e7ac7f 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/HeadersParameterGenerator.java @@ -55,6 +55,7 @@ public class HeadersParameterGenerator implements ParameterGenerator { private final Operation operation; private final String httpMethod; private final String path; + private boolean hasErrors = false; public HeadersParameterGenerator(List parameters, OpenAPI openAPI, Operation operation, String httpMethod, String path) { @@ -145,6 +146,10 @@ public List getDiagnostics() { return diagnostics; } + public boolean hasErrors() { + return hasErrors; + } + private ObjectSchema getHeadersSchema() { Map properties = new HashMap<>(); for (Parameter parameter : parameters) { @@ -155,6 +160,7 @@ private ObjectSchema getHeadersSchema() { if (Objects.isNull(entry.getValue())) { ClientDiagnostic diagnostic = new ClientDiagnosticImp(OAS_CLIENT_108, entry.getKey()); diagnostics.add(diagnostic); + hasErrors = true; return true; } return false; diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java index 2463c03a8..8a2ff9eb6 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/parameter/QueriesParameterGenerator.java @@ -53,6 +53,7 @@ public class QueriesParameterGenerator implements ParameterGenerator { private final Operation operation; private final String httpMethod; private final String path; + private boolean hasErrors = false; public QueriesParameterGenerator(List parameters, OpenAPI openAPI, Operation operation, String httpMethod, String path) { @@ -92,6 +93,10 @@ public List getDiagnostics() { return diagnostics; } + public boolean hasErrors() { + return hasErrors; + } + private ObjectSchema getQueriesSchema() { Map properties = new HashMap<>(); for (Parameter parameter : parameters) { @@ -102,6 +107,7 @@ private ObjectSchema getQueriesSchema() { if (Objects.isNull(entry.getValue())) { ClientDiagnostic diagnostic = new ClientDiagnosticImp(OAS_CLIENT_102, entry.getKey()); diagnostics.add(diagnostic); + hasErrors = true; return true; } return false; From 425a130d5d7dec273a68b6841c55f6028fc5ce38 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 14:53:19 +0530 Subject: [PATCH 20/31] Fix QueryParameterTests --- .../combination_of_apikey_and_http_oauth.bal | 29 ++++++++++--------- .../query_param_with_default_value.bal | 13 ++++----- .../query_param_with_integer_value.bal | 13 ++++----- .../ballerina/query_param_with_ref_schema.bal | 13 ++++----- .../query_param_without_default_value.bal | 13 ++++----- .../ballerina/queryparam_encoding_map_gen.bal | 13 ++++----- 6 files changed, 44 insertions(+), 50 deletions(-) diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/combination_of_apikey_and_http_oauth.bal b/openapi-cli/src/test/resources/generators/client/ballerina/combination_of_apikey_and_http_oauth.bal index b6ec43a37..c8ebefd8c 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/combination_of_apikey_and_http_oauth.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/combination_of_apikey_and_http_oauth.bal @@ -44,12 +44,13 @@ public isolated client class Client { # Delete a pet # - # + petId - The id of the pet to delete + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Expected response to a valid request - remote isolated function deletePetInfo(string petId) returns Pet|error { + remote isolated function deletePetInfo(map headers = {}, *DeletePetInfoQueries queries) returns Pet|error { string resourcePath = string `/pets/management`; - map headerValues = {}; - map queryParam = {"petId": petId}; + map headerValues = {...headers}; + map queryParam = {...queries}; if self.apiKeyConfig is ApiKeysConfig { headerValues["api-key"] = self.apiKeyConfig?.api\-key; queryParam["api-key-2"] = self.apiKeyConfig?.api\-key\-2; @@ -61,11 +62,11 @@ public isolated client class Client { # Delete a pet 2 # - # + petId - The id of the pet to delete + # + headers - Headers to be sent with the request # + return - Expected response to a valid request - remote isolated function deletePetInfo2(string petId) returns Pet|error { + remote isolated function deletePetInfo2(DeletePetInfo2Headers headers) returns Pet|error { string resourcePath = string `/pets/management2`; - map headerValues = {"petId": petId}; + map headerValues = {...headers}; map queryParam = {}; if self.apiKeyConfig is ApiKeysConfig { headerValues["api-key"] = self.apiKeyConfig?.api\-key; @@ -78,13 +79,12 @@ public isolated client class Client { # Info for a specific pet # - # + petId - The id of the pet to retrieve - # + headerX - Header X + # + headers - Headers to be sent with the request # + return - Expected response to a valid request - remote isolated function getPetInfo(string petId, string headerX) returns Pet|error { + remote isolated function getPetInfo(GetPetInfoHeaders headers, *GetPetInfoQueries queries) returns Pet|error { string resourcePath = string `/pets/management`; - map headerValues = {"headerX": headerX}; - map queryParam = {"petId": petId}; + map headerValues = {...headers}; + map queryParam = {...queries}; if self.apiKeyConfig is ApiKeysConfig { headerValues["api-key"] = self.apiKeyConfig?.api\-key; queryParam["api-key-2"] = self.apiKeyConfig?.api\-key\-2; @@ -96,10 +96,11 @@ public isolated client class Client { # Vote for a pet # + # + headers - Headers to be sent with the request # + return - Expected response to a valid request - remote isolated function votePet() returns Pet|error { + remote isolated function votePet(map headers = {}) returns Pet|error { string resourcePath = string `/pets/management`; - map headerValues = {}; + map headerValues = {...headers}; map queryParam = {}; if self.apiKeyConfig is ApiKeysConfig { headerValues["api-key"] = self.apiKeyConfig?.api\-key; diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/query_param_with_default_value.bal b/openapi-cli/src/test/resources/generators/client/ballerina/query_param_with_default_value.bal index 292356a0e..2df4fc7da 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/query_param_with_default_value.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/query_param_with_default_value.bal @@ -41,16 +41,15 @@ public isolated client class Client { } # Provide weather forecast for any geographical coordinates # - # + lat - Latitude - # + lon - Longtitude - # + exclude - test - # + units - tests + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Successful response @display {label: "Weather Forecast"} - remote isolated function getWeatherForecast(@display {label: "Latitude"} string lat, @display {label: "Longtitude"} string lon, @display {label: "Exclude"} string exclude = "current", @display {label: "Units"} int units = 12) returns WeatherForecast|error { + remote isolated function getWeatherForecast(map headers = {}, *GetWeatherForecastQueries queries) returns WeatherForecast|error { string resourcePath = string `/onecall`; - map queryParam = {"lat": lat, "lon": lon, "exclude": exclude, "units": units, "appid": self.apiKeyConfig.appid}; + map queryParam = {...queries}; + queryParam["appid"] = self.apiKeyConfig.appid; resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/query_param_with_integer_value.bal b/openapi-cli/src/test/resources/generators/client/ballerina/query_param_with_integer_value.bal index 8c208d341..2df4fc7da 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/query_param_with_integer_value.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/query_param_with_integer_value.bal @@ -41,16 +41,15 @@ public isolated client class Client { } # Provide weather forecast for any geographical coordinates # - # + lat - Latitude - # + lon - Longtitude - # + exclude - test - # + units - units + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Successful response @display {label: "Weather Forecast"} - remote isolated function getWeatherForecast(@display {label: "Latitude"} int:Signed32 lat, @display {label: "Units"} int units, @display {label: "Longtitude"} int:Signed32? lon = (), @display {label: "Exclude"} int exclude = 100) returns WeatherForecast|error { + remote isolated function getWeatherForecast(map headers = {}, *GetWeatherForecastQueries queries) returns WeatherForecast|error { string resourcePath = string `/onecall`; - map queryParam = {"lat": lat, "lon": lon, "exclude": exclude, "units": units, "appid": self.apiKeyConfig.appid}; + map queryParam = {...queries}; + queryParam["appid"] = self.apiKeyConfig.appid; resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/query_param_with_ref_schema.bal b/openapi-cli/src/test/resources/generators/client/ballerina/query_param_with_ref_schema.bal index 398d05a4f..56f1cfaae 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/query_param_with_ref_schema.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/query_param_with_ref_schema.bal @@ -41,16 +41,15 @@ public isolated client class Client { } # Provide weather forecast for any geographical coordinates # - # + lat - Latitude - # + lon - Longtitude - # + exclude - test - # + units - tests + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Successful response @display {label: "Weather Forecast"} - remote isolated function getWeatherForecast(@display {label: "Latitude"} Latitude lat, @display {label: "Longtitude"} string lon, @display {label: "Exclude"} string exclude = "current", @display {label: "Units"} int units = 12) returns json|error { + remote isolated function getWeatherForecast(map headers = {}, *GetWeatherForecastQueries queries) returns json|error { string resourcePath = string `/onecall`; - map queryParam = {"lat": lat, "lon": lon, "exclude": exclude, "units": units, "appid": self.apiKeyConfig.appid}; + map queryParam = {...queries}; + queryParam["appid"] = self.apiKeyConfig.appid; resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/query_param_without_default_value.bal b/openapi-cli/src/test/resources/generators/client/ballerina/query_param_without_default_value.bal index 3fda07d0a..2df4fc7da 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/query_param_without_default_value.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/query_param_without_default_value.bal @@ -41,16 +41,15 @@ public isolated client class Client { } # Provide weather forecast for any geographical coordinates # - # + lat - Latitude - # + lon - Longtitude - # + exclude - test - # + units - tests + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Successful response @display {label: "Weather Forecast"} - remote isolated function getWeatherForecast(@display {label: "Latitude"} string lat, @display {label: "Longtitude"} string lon, @display {label: "Exclude"} string? exclude = (), @display {label: "Units"} int? units = ()) returns WeatherForecast|error { + remote isolated function getWeatherForecast(map headers = {}, *GetWeatherForecastQueries queries) returns WeatherForecast|error { string resourcePath = string `/onecall`; - map queryParam = {"lat": lat, "lon": lon, "exclude": exclude, "units": units, "appid": self.apiKeyConfig.appid}; + map queryParam = {...queries}; + queryParam["appid"] = self.apiKeyConfig.appid; resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/queryparam_encoding_map_gen.bal b/openapi-cli/src/test/resources/generators/client/ballerina/queryparam_encoding_map_gen.bal index 8893693a0..e5dda1666 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/queryparam_encoding_map_gen.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/queryparam_encoding_map_gen.bal @@ -37,16 +37,13 @@ public isolated client class Client { #

You can list all invoices, or list the invoices for a specific customer. The invoices are returned sorted by creation date, with the most recently created invoices appearing first.

# - # + collection_method - The collection method of the invoice to retrieve. Either `charge_automatically` or `send_invoice`. - # + created - A filter on the list based on the object created field. The value can be a string with an integer Unix timestamp, or it can be a dictionary - # + due_date - A filter on the list based on the object due_date field. The value can be an integer Unix timestamp, or it can be a dictionary - # + subscriptions - Only return invoices for the subscription specified by this subscription ID. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Response - remote isolated function listInvoices("charge_automatically"|"send_invoice"? collection_method = (), created? created = (), due_date? due_date = (), string[]? subscriptions = ()) returns json|error { + remote isolated function listInvoices(map headers = {}, *ListInvoicesQueries queries) returns json|error { string resourcePath = string `/v1/invoices`; - map queryParam = {"collection_method": collection_method, "created": created, "due_date": due_date, "subscriptions": subscriptions}; map queryParamEncoding = {"created": {style: DEEPOBJECT, explode: true}, "due_date": {style: DEEPOBJECT, explode: true}, "subscriptions": {style: FORM, explode: true}}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam, queryParamEncoding); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); + return self.clientEp->get(resourcePath, headers); } } From 33cabd7dacddb65f34fc30335e334120dc5f26e3 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 15:19:47 +0530 Subject: [PATCH 21/31] Fix AnnotationTests --- .../client/ballerina/deprecated_functions.bal | 28 +++++++++---------- .../ballerina/deprecated_mix_params.bal | 14 ++++------ .../ballerina/display_and_deprecated.bal | 27 +++++++++--------- 3 files changed, 31 insertions(+), 38 deletions(-) diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/deprecated_functions.bal b/openapi-cli/src/test/resources/generators/client/ballerina/deprecated_functions.bal index 8b7367b0b..753efb1bf 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/deprecated_functions.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/deprecated_functions.bal @@ -36,41 +36,39 @@ public isolated client class Client { } # Create a pet # + # + headers - Headers to be sent with the request # + return - Null response - remote isolated function createPet() returns http:Response|error { + remote isolated function createPet(map headers = {}) returns http:Response|error { string resourcePath = string `/pets`; http:Request request = new; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # List all pets # - # + 'limit - How many items to return at one time (max 100) + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An paged array of pets # # # Deprecated # This methods has deprecated since the Pet has deprecated @deprecated - remote isolated function listPets(int? 'limit = ()) returns Pets|error { + remote isolated function listPets(map headers = {}, *ListPetsQueries queries) returns Pets|error { string resourcePath = string `/pets`; - map queryParam = {"limit": 'limit}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Info for a specific pet # # + petId - The id of the pet to retrieve - # + 'limit - The id of the pet to retrieve - # # Deprecated parameters - # + 'limit - Limit is deprecated from v2 + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Expected response to a valid request # # # Deprecated @deprecated - remote isolated function showPetById(string petId, @deprecated string 'limit) returns Pets|error { + remote isolated function showPetById(string petId, map headers = {}, *ShowPetByIdQueries queries) returns Pets|error { string resourcePath = string `/pets/${getEncodedUri(petId)}`; - map queryParam = {"limit": 'limit}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } } - diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/deprecated_mix_params.bal b/openapi-cli/src/test/resources/generators/client/ballerina/deprecated_mix_params.bal index 85a71cce0..47d9ec178 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/deprecated_mix_params.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/deprecated_mix_params.bal @@ -43,16 +43,12 @@ public isolated client class Client { # Returns the comments posted on the track(track_id). # # + track_id - SoundCloud Track id - # + 'limit - Number of results to return in the collection. - # + offset - Offset of first result. Deprecated, use `linked_partitioning` instead. - # + linked_partitioning - Returns paginated collection of items (recommended, returning a list without pagination is deprecated and should not be used) - # # Deprecated parameters - # + offset - + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Success - remote isolated function getCommentsOnTrack(int track_id, int 'limit = 50, @deprecated int offset = 0, boolean? linked_partitioning = ()) returns inline_response_200|error { + remote isolated function getCommentsOnTrack(int track_id, map headers = {}, *GetCommentsOnTrackQueries queries) returns inline_response_200|error { string resourcePath = string `/tracks/${getEncodedUri(track_id)}/comments`; - map queryParam = {"limit": 'limit, "offset": offset, "linked_partitioning": linked_partitioning}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/display_and_deprecated.bal b/openapi-cli/src/test/resources/generators/client/ballerina/display_and_deprecated.bal index 356333b9d..72459e3c1 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/display_and_deprecated.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/display_and_deprecated.bal @@ -36,41 +36,40 @@ public isolated client class Client { } # Create a pet # + # + headers - Headers to be sent with the request # + return - Null response - remote isolated function createPet() returns http:Response|error { + remote isolated function createPet(map headers = {}) returns http:Response|error { string resourcePath = string `/pets`; http:Request request = new; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # List all pets # - # + 'limit - How many items to return at one time (max 100) + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An paged array of pets # # # Deprecated # This methods has deprecated since the Pet has deprecated @display {label: "List Pets"} @deprecated - remote isolated function listPets(int? 'limit = ()) returns Pets|error { + remote isolated function listPets(map headers = {}, *ListPetsQueries queries) returns Pets|error { string resourcePath = string `/pets`; - map queryParam = {"limit": 'limit}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } # Info for a specific pet # # + petId - The id of the pet to retrieve - # + 'limit - The id of the pet to retrieve - # # Deprecated parameters - # + 'limit - Limit is deprecated from v2 + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Expected response to a valid request # # # Deprecated @deprecated - remote isolated function showPetById(string petId, @deprecated @display {label: "Result limit"} string 'limit) returns Pets|error { + remote isolated function showPetById(string petId, map headers = {}, *ShowPetByIdQueries queries) returns Pets|error { string resourcePath = string `/pets/${getEncodedUri(petId)}`; - map queryParam = {"limit": 'limit}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } } From fb1d9b9b5e88d0ee2dbb157c7dbb7b4b7e3944f2 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 16:06:18 +0530 Subject: [PATCH 22/31] Fix the issue with query param documentation --- .../core/generators/document/ClientDocCommentGenerator.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java index 0bd1f8d8b..328282712 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java @@ -322,7 +322,7 @@ private static void updateParameterNodes(List docs, Operation operation, L List updatedParamsDefault, HashMap collection) { List deprecatedParamDocComments = new ArrayList<>(); - boolean[] hasQueryParams = {false}; + boolean hasQueryParams = operation.getParameters().stream().anyMatch(parameter -> parameter.getIn().equals("query")); operation.getParameters().forEach(parameter -> { if (parameter.get$ref() != null) { try { @@ -334,7 +334,6 @@ private static void updateParameterNodes(List docs, Operation operation, L } if (Objects.isNull(parameter.getIn()) || !parameter.getIn().equals("path")) { - hasQueryParams[0] = parameter.getIn().equals("query"); return; } @@ -361,7 +360,7 @@ private static void updateParameterNodes(List docs, Operation operation, L } }); docs.add(createAPIParamDoc(HEADERS, "Headers to be sent with the request")); - if (hasQueryParams[0]) { + if (hasQueryParams) { docs.add(createAPIParamDoc(QUERIES, "Queries to be sent with the request")); } docs.addAll(deprecatedParamDocComments); From 9f37122c8f98687bf8a6671080d27c67625775de Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 16:18:33 +0530 Subject: [PATCH 23/31] Fix HeadersTests --- .../client/ballerina/delete_with_header.bal | 24 +++++++++---------- .../ballerina/header_integer_signed32.bal | 10 ++++---- .../client/ballerina/header_optional.bal | 16 +++++-------- .../header_param_with_default_value.bal | 16 ++++++------- .../client/ballerina/header_parameter.bal | 9 ++++--- .../ballerina/header_without_parameter.bal | 6 +++-- 6 files changed, 37 insertions(+), 44 deletions(-) diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/delete_with_header.bal b/openapi-cli/src/test/resources/generators/client/ballerina/delete_with_header.bal index 4be845a6a..64b6caf6a 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/delete_with_header.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/delete_with_header.bal @@ -37,23 +37,21 @@ public isolated client class Client { # Delete with header. # - # + X\-Request\-ID - Tests header 01 + # + headers - Headers to be sent with the request # + return - Status OK - remote isolated function deleteHeader(string X\-Request\-ID) returns error? { + remote isolated function deleteHeader(DeleteHeaderHeaders headers) returns error? { string resourcePath = string `/header`; - map headerValues = {"X-Request-ID": X\-Request\-ID}; - map httpHeaders = getMapForHeaders(headerValues); + map httpHeaders = getMapForHeaders(headers); return self.clientEp->delete(resourcePath, headers = httpHeaders); } # Delete with header and request body. # - # + X\-Request\-ID - Tests header 01 + # + headers - Headers to be sent with the request # + return - Status OK - remote isolated function deleteHeaderRequestBody(string X\-Request\-ID, json payload) returns error? { + remote isolated function deleteHeaderRequestBody(DeleteHeaderRequestBodyHeaders headers, json payload) returns error? { string resourcePath = string `/header-with-request-body`; - map headerValues = {"X-Request-ID": X\-Request\-ID}; - map httpHeaders = getMapForHeaders(headerValues); + map httpHeaders = getMapForHeaders(headers); http:Request request = new; request.setPayload(payload, "application/json"); return self.clientEp->delete(resourcePath, request, httpHeaders); @@ -63,19 +61,21 @@ public isolated client class Client { # # + order_id - Order ID # + risk_id - Order Risk ID + # + headers - Headers to be sent with the request # + return - Status OK - remote isolated function delete_order_risk(string order_id, string risk_id) returns error? { + remote isolated function delete_order_risk(string order_id, string risk_id, map headers = {}) returns error? { string resourcePath = string `/admin/api/2021-10/orders/${getEncodedUri(order_id)}/risks/${getEncodedUri(risk_id)}.json`; - return self.clientEp->delete(resourcePath); + return self.clientEp->delete(resourcePath, headers = headers); } # Delete with request body. # + # + headers - Headers to be sent with the request # + return - Status OK - remote isolated function order_risk(json payload) returns error? { + remote isolated function order_risk(json payload, map headers = {}) returns error? { string resourcePath = string `/request-body`; http:Request request = new; request.setPayload(payload, "application/json"); - return self.clientEp->delete(resourcePath, request); + return self.clientEp->delete(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/header_integer_signed32.bal b/openapi-cli/src/test/resources/generators/client/ballerina/header_integer_signed32.bal index 91b1e9fcc..d3a28b43a 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/header_integer_signed32.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/header_integer_signed32.bal @@ -40,14 +40,12 @@ public isolated client class Client { # Info for a specific pet # - # + X\-Request\-ID - Tests header 01 - # + X\-Request\-Client - Tests header 02 - # + X\-Request\-Pet - Tests header 03 - # + X\-Request\-Header - Tests header 04 + # + headers - Headers to be sent with the request # + return - Expected response to a valid request - remote isolated function showPetById(int:Signed32 X\-Request\-ID, int:Signed32[] X\-Request\-Client, Pet[] X\-Request\-Pet, int? X\-Request\-Header = ()) returns http:Response|error { + remote isolated function showPetById(ShowPetByIdHeaders headers) returns http:Response|error { string resourcePath = string `/pets`; - map headerValues = {"X-Request-ID": X\-Request\-ID, "X-Request-Client": X\-Request\-Client, "X-Request-Pet": X\-Request\-Pet, "X-Request-Header": X\-Request\-Header, "X-API-KEY": self.apiKeyConfig.X\-API\-KEY}; + map headerValues = {...headers}; + headerValues["X-API-KEY"] = self.apiKeyConfig.X\-API\-KEY; map httpHeaders = getMapForHeaders(headerValues); return self.clientEp->get(resourcePath, httpHeaders); } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/header_optional.bal b/openapi-cli/src/test/resources/generators/client/ballerina/header_optional.bal index 0e69c17f8..eac5ae07b 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/header_optional.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/header_optional.bal @@ -41,20 +41,16 @@ public isolated client class Client { } # Provide weather forecast for any geographical coordinates # - # + lat - Latitude - # + lon - Longtitude - # + exclude - test - # + units - tests - # + idList - ID list - # + locationList - Location list + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Successful response @display {label: "Weather Forecast"} - remote isolated function getWeatherForecast(@display {label: "Latitude"} string lat, @display {label: "Longtitude"} string lon, @display {label: "Exclude"} string? exclude = (), @display {label: "Units"} int? units = (), @display {label: "ID List"} int[]? idList = (), @display {label: "Location List"} Location[]? locationList = ()) returns WeatherForecast|error { + remote isolated function getWeatherForecast(GetWeatherForecastHeaders headers = {}, *GetWeatherForecastQueries queries) returns WeatherForecast|error { string resourcePath = string `/onecall`; - map queryParam = {"lat": lat, "lon": lon, "appid": self.apiKeyConfig.appid}; + map queryParam = {...queries}; + queryParam["appid"] = self.apiKeyConfig.appid; resourcePath = resourcePath + check getPathForQueryParam(queryParam); - map headerValues = {"exclude": exclude, "units": units, "idList": idList, "locationList": locationList}; - map httpHeaders = getMapForHeaders(headerValues); + map httpHeaders = getMapForHeaders(headers); return self.clientEp->get(resourcePath, httpHeaders); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/header_param_with_default_value.bal b/openapi-cli/src/test/resources/generators/client/ballerina/header_param_with_default_value.bal index 8c783306b..1f58bdb45 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/header_param_with_default_value.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/header_param_with_default_value.bal @@ -50,18 +50,16 @@ public isolated client class Client { } # Provide weather forecast for any geographical coordinates # - # + lat - Latitude - # + lon - Longtitude - # + exclude - test - # + units - tests + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Successful response @display {label: "Weather Forecast"} - remote isolated function getWeatherForecast(@display {label: "Latitude"} string lat, @display {label: "Longtitude"} string lon, @display {label: "Exclude"} string exclude = "current", @display {label: "Units"} int units = 12) returns WeatherForecast|error { + remote isolated function getWeatherForecast(GetWeatherForecastHeaders headers = {}, *GetWeatherForecastQueries queries) returns WeatherForecast|error { string resourcePath = string `/onecall`; - map queryParam = {"lat": lat, "lon": lon, "appid": self.apiKeyConfig.appid}; + map queryParam = {...queries}; + queryParam["appid"] = self.apiKeyConfig.appid; resourcePath = resourcePath + check getPathForQueryParam(queryParam); - map headerValues = {"exclude": exclude, "units": units}; - map httpHeaders = getMapForHeaders(headerValues); - return self.clientEp-> get(resourcePath, httpHeaders); + map httpHeaders = getMapForHeaders(headers); + return self.clientEp->get(resourcePath, httpHeaders); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/header_parameter.bal b/openapi-cli/src/test/resources/generators/client/ballerina/header_parameter.bal index f62fd5e8b..d3a28b43a 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/header_parameter.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/header_parameter.bal @@ -40,13 +40,12 @@ public isolated client class Client { # Info for a specific pet # - # + X\-Request\-ID - Tests header 01 - # + X\-Request\-Client - Tests header 02 - # + X\-Request\-Pet - Tests header 03 + # + headers - Headers to be sent with the request # + return - Expected response to a valid request - remote isolated function showPetById(string X\-Request\-ID, string[] X\-Request\-Client, Pet[] X\-Request\-Pet) returns http:Response|error { + remote isolated function showPetById(ShowPetByIdHeaders headers) returns http:Response|error { string resourcePath = string `/pets`; - map headerValues = {"X-Request-ID": X\-Request\-ID, "X-Request-Client": X\-Request\-Client, "X-Request-Pet": X\-Request\-Pet, "X-API-KEY": self.apiKeyConfig.X\-API\-KEY}; + map headerValues = {...headers}; + headerValues["X-API-KEY"] = self.apiKeyConfig.X\-API\-KEY; map httpHeaders = getMapForHeaders(headerValues); return self.clientEp->get(resourcePath, httpHeaders); } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/header_without_parameter.bal b/openapi-cli/src/test/resources/generators/client/ballerina/header_without_parameter.bal index b86b1d9be..7706036fb 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/header_without_parameter.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/header_without_parameter.bal @@ -40,10 +40,12 @@ public isolated client class Client { # Info for a specific pet # + # + headers - Headers to be sent with the request # + return - Expected response to a valid request - remote isolated function showPetById() returns http:Response|error { + remote isolated function showPetById(map headers = {}) returns http:Response|error { string resourcePath = string `/pets`; - map headerValues = {"X-API-KEY": self.apiKeyConfig.X\-API\-KEY}; + map headerValues = {...headers}; + headerValues["X-API-KEY"] = self.apiKeyConfig.X\-API\-KEY; map httpHeaders = getMapForHeaders(headerValues); return self.clientEp->get(resourcePath, httpHeaders); } From 35a7b438bacdce3fb0d4cf9f7f7f01c1c1757734 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 16:35:59 +0530 Subject: [PATCH 24/31] Fix PathParameterTests --- .../combination_of_apikey_and_http_oauth.bal | 1 + .../integer_signed32_path_parameter.bal | 5 ++-- .../ballerina/path_param_duplicated_name.bal | 5 ++-- .../ballerina/path_param_with_ref_schema.bal | 6 ++-- .../client/ballerina/path_parameter_valid.bal | 30 +++++++++++-------- .../path_parameter_with_special_name.bal | 10 ++++--- .../client/ballerina/union_path_parameter.bal | 10 ++++--- 7 files changed, 41 insertions(+), 26 deletions(-) diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/combination_of_apikey_and_http_oauth.bal b/openapi-cli/src/test/resources/generators/client/ballerina/combination_of_apikey_and_http_oauth.bal index c8ebefd8c..01f653ddf 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/combination_of_apikey_and_http_oauth.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/combination_of_apikey_and_http_oauth.bal @@ -80,6 +80,7 @@ public isolated client class Client { # Info for a specific pet # # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Expected response to a valid request remote isolated function getPetInfo(GetPetInfoHeaders headers, *GetPetInfoQueries queries) returns Pet|error { string resourcePath = string `/pets/management`; diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/integer_signed32_path_parameter.bal b/openapi-cli/src/test/resources/generators/client/ballerina/integer_signed32_path_parameter.bal index d57a6eab9..94e0f2b76 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/integer_signed32_path_parameter.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/integer_signed32_path_parameter.bal @@ -39,9 +39,10 @@ public isolated client class Client { # # + id - id value # + payloadId - payload id value + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function operationId01(int:Signed32 id, int payloadId) returns string|error { + remote isolated function operationId01(int:Signed32 id, int payloadId, map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(id)}/payload/${getEncodedUri(payloadId)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/path_param_duplicated_name.bal b/openapi-cli/src/test/resources/generators/client/ballerina/path_param_duplicated_name.bal index de03dba44..42a4ebf82 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/path_param_duplicated_name.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/path_param_duplicated_name.bal @@ -37,9 +37,10 @@ public isolated client class Client { # + version - Version Id # + version\-name - Version Name + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function operationId04(int version, string version\-name) returns string|error { + remote isolated function operationId04(int version, string version\-name, map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(version)}/version-name/${getEncodedUri(version\-name)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/path_param_with_ref_schema.bal b/openapi-cli/src/test/resources/generators/client/ballerina/path_param_with_ref_schema.bal index fc428e742..4512cc066 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/path_param_with_ref_schema.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/path_param_with_ref_schema.bal @@ -34,10 +34,12 @@ public isolated client class Client { self.clientEp = httpEp; return; } + # + id - id value + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function operationId03(Id id) returns string|error { + remote isolated function operationId03(Id id, map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(id)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/path_parameter_valid.bal b/openapi-cli/src/test/resources/generators/client/ballerina/path_parameter_valid.bal index 394a8501f..b4920accc 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/path_parameter_valid.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/path_parameter_valid.bal @@ -36,38 +36,44 @@ public isolated client class Client { } # op1 # + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function operationId01() returns string|error { + remote isolated function operationId01(map headers = {}) returns string|error { string resourcePath = string `/`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function operationId02() returns string|error { + remote isolated function operationId02(map headers = {}) returns string|error { string resourcePath = string `/`; http:Request request = new; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # op2 # # + id - id value + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function operationId03(int id) returns string|error { + remote isolated function operationId03(int id, map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(id)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function operationId04(int version, string name) returns string|error { + remote isolated function operationId04(int version, string name, map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(version)}/v2/${getEncodedUri(name)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function operationId05(int version, int 'limit) returns string|error { + remote isolated function operationId05(int version, int 'limit, map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(version)}/v2/${getEncodedUri('limit)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function operationId06(int age, string name) returns string|error { + remote isolated function operationId06(int age, string name, map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(age)}/v2/${getEncodedUri(name)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/path_parameter_with_special_name.bal b/openapi-cli/src/test/resources/generators/client/ballerina/path_parameter_with_special_name.bal index 3041d8cda..dcfaaed41 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/path_parameter_with_special_name.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/path_parameter_with_special_name.bal @@ -37,17 +37,19 @@ public isolated client class Client { # + version - Version Id # + version\-name - Version Name + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function operationId04(int version, string version\-name) returns string|error { + remote isolated function operationId04(int version, string version\-name, map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(version)}/v2/${getEncodedUri(version\-name)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } # + version\-id - Version Id # + version\-limit - Version Limit + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function operationId05(int version\-id, int version\-limit) returns string|error { + remote isolated function operationId05(int version\-id, int version\-limit, map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(version\-id)}/v2/${getEncodedUri(version\-limit)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/union_path_parameter.bal b/openapi-cli/src/test/resources/generators/client/ballerina/union_path_parameter.bal index 4c22e151c..0cc78b656 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/union_path_parameter.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/union_path_parameter.bal @@ -36,17 +36,19 @@ public isolated client class Client { } # + id - id anyOf + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function operationId03(id id) returns string|error { + remote isolated function operationId03(id id, map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(id)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } # + id - id oneOf + # + headers - Headers to be sent with the request # + return - Ok - remote isolated function post(id_1 id) returns string|error { + remote isolated function post(id_1 id, map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(id)}`; http:Request request = new; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } From 9805cbdbb26a0061ad1914aa9641250532f360d6 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 16:43:53 +0530 Subject: [PATCH 25/31] Fix FunctionBodyNodeTests --- .../client/FunctionBodyNodeTests.java | 65 +++++++++++-------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionBodyNodeTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionBodyNodeTests.java index 7ca5ed34a..869022568 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionBodyNodeTests.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionBodyNodeTests.java @@ -55,7 +55,8 @@ public class FunctionBodyNodeTests { @Test(description = "Tests functionBodyNodes including statements according to the different scenarios", dataProvider = "dataProviderForFunctionBody") - public void getFunctionBodyNodes(String yamlFile, String path, String content) throws IOException, + public void getFunctionBodyNodes(String yamlFile, String path, boolean hasHeaders, boolean hasDefaultHeaders, + boolean hasQueries, String content) throws IOException, BallerinaOpenApiException { Path definitionPath = RESDIR.resolve(yamlFile); OpenAPI openapi = getOpenAPI(definitionPath); @@ -66,7 +67,7 @@ public void getFunctionBodyNodes(String yamlFile, String path, String content) t TypeHandler.createInstance(openapi, false); FunctionBodyGeneratorImp functionBodyGeneratorImp = new FunctionBodyGeneratorImp(path, operation, openapi, new AuthConfigGeneratorImp(false, false), - new BallerinaUtilGenerator(), new ArrayList<>(), false, false, false); + new BallerinaUtilGenerator(), new ArrayList<>(), hasHeaders, hasDefaultHeaders, hasQueries); Optional bodyNode = functionBodyGeneratorImp.getFunctionBodyNode(); content = content.trim().replaceAll("\n", "").replaceAll("\\s+", ""); String bodyNodeContent = bodyNode.get().toString().trim().replaceAll("\n", "") @@ -76,52 +77,56 @@ public void getFunctionBodyNodes(String yamlFile, String path, String content) t @DataProvider(name = "dataProviderForFunctionBody") public Object[][] dataProviderForFunctionBody() { return new Object[][]{ - {"diagnostic_files/header_parameter.yaml", "/pets", "{string resourcePath=string`/pets`;" + - "mapheaderValues=" + - "{\"X-Request-ID\":X\\-Request\\-ID,\"X-Request-Client\":X\\-Request\\-Client};" + + {"diagnostic_files/header_parameter.yaml", "/pets", true, false, false, + "{string resourcePath=string`/pets`;" + "map" + - "httpHeaders=getMapForHeaders(headerValues);return self.clientEp->" + + "httpHeaders=getMapForHeaders(headers);return self.clientEp->" + "get(resourcePath,httpHeaders);}"}, - {"diagnostic_files/head_operation.yaml", "/{filesystem}", + {"diagnostic_files/head_operation.yaml", "/{filesystem}", true, false, true, "{string resourcePath=string`/${getEncodedUri(filesystem)}`;" + - "mapqueryParam={\"resource\":'resource,\"timeout\":timeout};" + - "resourcePath = resourcePath + check getPathForQueryParam(queryParam);" + - "mapheaderValues={\"x-ms-client-request-id\":x\\-ms\\-client\\-request\\-id," + - "\"x-ms-date\":x\\-ms\\-date,\"x-ms-version\":x\\-ms\\-version};map " + - "httpHeaders = getMapForHeaders(headerValues);" + + "resourcePath = resourcePath + check getPathForQueryParam(queries);" + + "map httpHeaders = getMapForHeaders(headers);" + "return self.clientEp-> head(resourcePath, httpHeaders);}"}, - {"diagnostic_files/operation_delete.yaml", "/pets/{petId}", "{string resourcePath = " + + {"diagnostic_files/operation_delete.yaml", "/pets/{petId}", false, false, false, + "{string resourcePath = " + "string `/pets/${getEncodedUri(petId)}`;" + "return self.clientEp-> delete(resourcePath);}"}, - {"diagnostic_files/json_payload.yaml", "/pets", "{string resourcePath = string `/pets`;" + + {"diagnostic_files/json_payload.yaml", "/pets", false, false, false, + "{string resourcePath = string `/pets`;" + "http:Request request = new; request.setPayload(payload, \"application/json\"); " + "return self.clientEp->post(resourcePath, request);}"}, - {"diagnostic_files/xml_payload.yaml", "/pets", "{string resourcePath = string `/pets`; " + + {"diagnostic_files/xml_payload.yaml", "/pets", false, false, false, + "{string resourcePath = string `/pets`; " + "http:Request request = new;" + "request.setPayload(payload, \"application/xml\"); " + "return self.clientEp->post(resourcePath, request);}"}, - {"diagnostic_files/xml_payload_with_ref.yaml", "/pets", "{string resourcePath = string `/pets`;" + + {"diagnostic_files/xml_payload_with_ref.yaml", "/pets", false, false, false, + "{string resourcePath = string `/pets`;" + "http:Request request = new;" + "json jsonBody = payload.toJson();" + "xml? xmlBody = check xmldata:fromJson(jsonBody);" + "request.setPayload(xmlBody, \"application/xml\");" + "return self.clientEp->post(resourcePath, request);}"}, - {"client/swagger/response_type_order.yaml", "/pet/{petId}", + {"client/swagger/response_type_order.yaml", "/pet/{petId}", false, false, false, "{string resourcePath = string `/pet/${getEncodedUri(petId)}`;" + "return self.clientEp->get(resourcePath);}"}, - {"client/swagger/text_request_payload.yaml", "/pets", "{string resourcePath = string `/pets`;" + + {"client/swagger/text_request_payload.yaml", "/pets", false, false, false, + "{string resourcePath = string `/pets`;" + "http:Request request = new;" + "json jsonBody = payload.toJson();" + "request.setPayload(jsonBody, \"text/json\");" + "return self.clientEp->post(resourcePath, request);}"}, - {"client/swagger/pdf_payload.yaml", "/pets", "{string resourcePath = string `/pets`;" + + {"client/swagger/pdf_payload.yaml", "/pets", false, false, false, + "{string resourcePath = string `/pets`;" + "// TODO: Update the request as needed;\n" + " return self.clientEp->post(resourcePath, request);}"}, - {"client/swagger/image_payload.yaml", "/pets", "{string resourcePath = string `/pets`;" + + {"client/swagger/image_payload.yaml", "/pets", false, false, false, + "{string resourcePath = string `/pets`;" + "http:Request request = new;" + "request.setPayload(payload, \"image/png\");" + " return self.clientEp->post(resourcePath, request);}"}, - {"client/swagger/multipart_formdata_custom.yaml", "/pets", "{string resourcePath = string `/pets`;\n" + + {"client/swagger/multipart_formdata_custom.yaml", "/pets", false, false, false, + "{string resourcePath = string `/pets`;\n" + "http:Request request = new;\n" + "map encodingMap = {\"profileImage\": {contentType: \"image/png\", headers: " + "{\"X-Custom-Header\": X\\-Custom\\-Header}}, \"id\":{headers: {\"X-Custom-Header\": " + @@ -131,21 +136,25 @@ public Object[][] dataProviderForFunctionBody() { "mime:Entity[] bodyParts = check createBodyParts(payload, encodingMap);\n" + "request.setBodyParts(bodyParts);\n" + " return self.clientEp->post(resourcePath, request);\n}"}, - {"client/swagger/empty_object_responnse.yaml", "/pets", "{string resourcePath = string `/pets`;\n" + + {"client/swagger/empty_object_responnse.yaml", "/pets", false, false, false, + "{string resourcePath = string `/pets`;\n" + " // TODO: Update the request as needed;\n" + " return self.clientEp->post(resourcePath, request);}"}, - {"client/swagger/map_schema_response.yaml", "/pets", "{string resourcePath = string `/pets`;\n" + + {"client/swagger/map_schema_response.yaml", "/pets", false, false, false, + "{string resourcePath = string `/pets`;\n" + " // TODO: Update the request as needed;\n" + " return self.clientEp->post(resourcePath, request);}"}, - {"client/swagger/array_response_pdf.yaml", "/pets", "{string resourcePath = string `/pets`;\n" + + {"client/swagger/array_response_pdf.yaml", "/pets", false, false, false, + "{string resourcePath = string `/pets`;\n" + " // TODO: Update the request as needed;\n" + " return self.clientEp->post(resourcePath, request);}"}, - {"client/swagger/any_type_response.yaml", "/pets", "{string resourcePath = string `/pets`;\n" + + {"client/swagger/any_type_response.yaml", "/pets", false, false, false, + "{string resourcePath = string `/pets`;\n" + " // TODO: Update the request as needed;\n" + " return self.clientEp->post(resourcePath, request);}"}, - {"client/swagger/return_type/no_response.yaml", "/pets", "{string resourcePath = string `/pets`;\n" + - " map queryParam = {\"limit\": 'limit};\n" + - " resourcePath = resourcePath + check getPathForQueryParam(queryParam);\n" + + {"client/swagger/return_type/no_response.yaml", "/pets", false, false, true, + "{string resourcePath = string `/pets`;\n" + + " resourcePath = resourcePath + check getPathForQueryParam(queries);\n" + " return self.clientEp->get(resourcePath);}"} }; } From 37ee6f2f2292d77ec9031772dca9686583a69c21 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 16:54:07 +0530 Subject: [PATCH 26/31] Fix ResourceFunctionTests --- .../client/resource/ballerina/all_methods.bal | 38 ++++++++++++------ .../client/resource/ballerina/header.bal | 26 +++++------- .../resource/ballerina/pathParameters.bal | 40 +++++++++++-------- .../resource/ballerina/reference_path.bal | 6 ++- .../resource/ballerina/request_body.bal | 35 +++++++++------- 5 files changed, 84 insertions(+), 61 deletions(-) diff --git a/openapi-cli/src/test/resources/generators/client/resource/ballerina/all_methods.bal b/openapi-cli/src/test/resources/generators/client/resource/ballerina/all_methods.bal index 88f40d13e..8fb45d5de 100644 --- a/openapi-cli/src/test/resources/generators/client/resource/ballerina/all_methods.bal +++ b/openapi-cli/src/test/resources/generators/client/resource/ballerina/all_methods.bal @@ -39,41 +39,53 @@ public isolated client class Client { } # Delete a pet # + # + headers - Headers to be sent with the request # + return - Ok response - resource isolated function delete pets() returns error? { + resource isolated function delete pets(map headers = {}) returns error? { string resourcePath = string `/pets`; - map queryParam = {"appid1": self.apiKeyConfig.appid1, "appid2": self.apiKeyConfig.appid2}; + map queryParam = {}; + queryParam["appid1"] = self.apiKeyConfig.appid1; + queryParam["appid2"] = self.apiKeyConfig.appid2; resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->delete(resourcePath); + return self.clientEp->delete(resourcePath, headers = headers); } # List all pets # - # + 'limit - How many items to return at one time (max 100) + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - An paged array of pets - resource isolated function get pets(int? 'limit = ()) returns Pets|error { + resource isolated function get pets(map headers = {}, *ListPetsQueries queries) returns Pets|error { string resourcePath = string `/pets`; - map queryParam = {"limit": 'limit, "appid1": self.apiKeyConfig.appid1, "appid2": self.apiKeyConfig.appid2}; + map queryParam = {...queries}; + queryParam["appid1"] = self.apiKeyConfig.appid1; + queryParam["appid2"] = self.apiKeyConfig.appid2; resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } # Create a pet # + # + headers - Headers to be sent with the request # + return - Null response - resource isolated function post pets() returns error? { + resource isolated function post pets(map headers = {}) returns error? { string resourcePath = string `/pets`; - map queryParam = {"appid1": self.apiKeyConfig.appid1, "appid2": self.apiKeyConfig.appid2}; + map queryParam = {}; + queryParam["appid1"] = self.apiKeyConfig.appid1; + queryParam["appid2"] = self.apiKeyConfig.appid2; resourcePath = resourcePath + check getPathForQueryParam(queryParam); http:Request request = new; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # Update a pet # + # + headers - Headers to be sent with the request # + return - Null response - resource isolated function put pets() returns error? { + resource isolated function put pets(map headers = {}) returns error? { string resourcePath = string `/pets`; - map queryParam = {"appid1": self.apiKeyConfig.appid1, "appid2": self.apiKeyConfig.appid2}; + map queryParam = {}; + queryParam["appid1"] = self.apiKeyConfig.appid1; + queryParam["appid2"] = self.apiKeyConfig.appid2; resourcePath = resourcePath + check getPathForQueryParam(queryParam); http:Request request = new; - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/resource/ballerina/header.bal b/openapi-cli/src/test/resources/generators/client/resource/ballerina/header.bal index 08720d88a..9422e2f5c 100644 --- a/openapi-cli/src/test/resources/generators/client/resource/ballerina/header.bal +++ b/openapi-cli/src/test/resources/generators/client/resource/ballerina/header.bal @@ -42,33 +42,29 @@ public isolated client class Client { # Provide weather forecast for any geographical coordinates # - # + lat - Latitude - # + lon - Longtitude - # + exclude - test - # + units - tests + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Successful response @display {label: "Weather Forecast"} - resource isolated function get onecall(@display {label: "Latitude"} string lat, @display {label: "Longtitude"} string lon, @display {label: "Exclude"} string exclude = "current", @display {label: "Units"} int units = 12) returns WeatherForecast|error { + resource isolated function get onecall(GetWeatherForecastHeaders headers = {}, *GetWeatherForecastQueries queries) returns WeatherForecast|error { string resourcePath = string `/onecall`; - map queryParam = {"lat": lat, "lon": lon, "appid": self.apiKeyConfig.appid}; + map queryParam = {...queries}; + queryParam["appid"] = self.apiKeyConfig.appid; resourcePath = resourcePath + check getPathForQueryParam(queryParam); - map headerValues = {"exclude": exclude, "units": units}; - map httpHeaders = getMapForHeaders(headerValues); + map httpHeaders = getMapForHeaders(headers); return self.clientEp->get(resourcePath, httpHeaders); } # Info for a specific pet # - # + X\-Request\-ID - Tests header 01 - # + X\-Request\-Client - Tests header 02 - # + X\-Request\-Pet - Tests header 03 + # + headers - Headers to be sent with the request # + return - Expected response to a valid request - resource isolated function get weather(string X\-Request\-ID, string[] X\-Request\-Client, WeatherForecast[] X\-Request\-Pet) returns error? { + resource isolated function get weather(ShowPetByIdHeaders headers) returns error? { string resourcePath = string `/weather`; - map queryParam = {"appid": self.apiKeyConfig.appid}; + map queryParam = {}; + queryParam["appid"] = self.apiKeyConfig.appid; resourcePath = resourcePath + check getPathForQueryParam(queryParam); - map headerValues = {"X-Request-ID": X\-Request\-ID, "X-Request-Client": X\-Request\-Client, "X-Request-Pet": X\-Request\-Pet}; - map httpHeaders = getMapForHeaders(headerValues); + map httpHeaders = getMapForHeaders(headers); return self.clientEp->get(resourcePath, httpHeaders); } } diff --git a/openapi-cli/src/test/resources/generators/client/resource/ballerina/pathParameters.bal b/openapi-cli/src/test/resources/generators/client/resource/ballerina/pathParameters.bal index dc18d748f..b75e6976d 100644 --- a/openapi-cli/src/test/resources/generators/client/resource/ballerina/pathParameters.bal +++ b/openapi-cli/src/test/resources/generators/client/resource/ballerina/pathParameters.bal @@ -36,49 +36,55 @@ public isolated client class Client { } # op1 # + # + headers - Headers to be sent with the request # + return - Ok - resource isolated function get .() returns string|error { + resource isolated function get .(map headers = {}) returns string|error { string resourcePath = string `/`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } # Retrieves a single customer. # # + customer_id - Customer ID - # + fields - Show only certain fields, specified by a comma-separated list of field names. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Requested customer - resource isolated function get admin/api/'2021\-10/customers/[string customer_id](string? fields = ()) returns error? { + resource isolated function get admin/api/'2021\-10/customers/[string customer_id](map headers = {}, *Get_customerQueries queries) returns error? { string resourcePath = string `/admin/api/2021-10/customers/${getEncodedUri(customer_id)}`; - map queryParam = {"fields": fields}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp->get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } + # + headers - Headers to be sent with the request # + return - Ok - resource isolated function get v1/[int age]/v2/[string name]() returns string|error { + resource isolated function get v1/[int age]/v2/[string name](map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(age)}/v2/${getEncodedUri(name)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } # op2 # # + id - id value + # + headers - Headers to be sent with the request # + return - Ok - resource isolated function get v1/[int id]() returns string|error { + resource isolated function get v1/[int id](map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(id)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } + # + headers - Headers to be sent with the request # + return - Ok - resource isolated function get v1/[int version]/v2/[int 'limit]() returns string|error { + resource isolated function get v1/[int version]/v2/[int 'limit](map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(version)}/v2/${getEncodedUri('limit)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } + # + headers - Headers to be sent with the request # + return - Ok - resource isolated function get v1/[int version]/v2/[string name]() returns string|error { + resource isolated function get v1/[int version]/v2/[string name](map headers = {}) returns string|error { string resourcePath = string `/v1/${getEncodedUri(version)}/v2/${getEncodedUri(name)}`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } + # + headers - Headers to be sent with the request # + return - Ok - resource isolated function post .() returns string|error { + resource isolated function post .(map headers = {}) returns string|error { string resourcePath = string `/`; http:Request request = new; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/resource/ballerina/reference_path.bal b/openapi-cli/src/test/resources/generators/client/resource/ballerina/reference_path.bal index 058b79585..11a335a9f 100644 --- a/openapi-cli/src/test/resources/generators/client/resource/ballerina/reference_path.bal +++ b/openapi-cli/src/test/resources/generators/client/resource/ballerina/reference_path.bal @@ -34,11 +34,13 @@ public isolated client class Client { self.clientEp = httpEp; return; } + # + id - Id of the point + # + headers - Headers to be sent with the request # + return - Accepted - resource isolated function post pet/[Param id]() returns error? { + resource isolated function post pet/[Param id](map headers = {}) returns error? { string resourcePath = string `/pet/${getEncodedUri(id)}`; http:Request request = new; - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/resource/ballerina/request_body.bal b/openapi-cli/src/test/resources/generators/client/resource/ballerina/request_body.bal index 342810098..022536daa 100644 --- a/openapi-cli/src/test/resources/generators/client/resource/ballerina/request_body.bal +++ b/openapi-cli/src/test/resources/generators/client/resource/ballerina/request_body.bal @@ -39,82 +39,89 @@ public isolated client class Client { # 01 Request body with reference. # + # + headers - Headers to be sent with the request # + return - OK - resource isolated function post path01(User payload) returns error? { + resource isolated function post path01(User payload, map headers = {}) returns error? { string resourcePath = string `/path01`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # 03 Request body with record reference. # + # + headers - Headers to be sent with the request # + return - OK - resource isolated function post path02(User[] payload) returns error? { + resource isolated function post path02(User[] payload, map headers = {}) returns error? { string resourcePath = string `/path02`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # 05 Example for rb has array inline requestbody. # + # + headers - Headers to be sent with the request # + return - OK - resource isolated function post path03(path03_body_1 payload) returns error? { + resource isolated function post path03(path03_body_1 payload, map headers = {}) returns error? { string resourcePath = string `/path03`; http:Request request = new; json jsonBody = payload.toJson(); xml? xmlBody = check xmldata:fromJson(jsonBody); request.setPayload(xmlBody, "application/xml"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # 07 Example for rb has array inline requestbody. # + # + headers - Headers to be sent with the request # + return - OK - resource isolated function post path04(path04_body[] payload) returns error? { + resource isolated function post path04(path04_body[] payload, map headers = {}) returns error? { string resourcePath = string `/path04`; http:Request request = new; json jsonBody = payload.toJson(); xml? xmlBody = check xmldata:fromJson(jsonBody); request.setPayload(xmlBody, "application/xml"); - return self.clientEp->post(resourcePath, request); + return self.clientEp->post(resourcePath, request, headers); } # 02 Example for rb has inline requestbody. # + # + headers - Headers to be sent with the request # + return - OK - resource isolated function put path01(path01_body payload) returns error? { + resource isolated function put path01(path01_body payload, map headers = {}) returns error? { string resourcePath = string `/path01`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } # 04 Example for rb has inline requestbody. # + # + headers - Headers to be sent with the request # + payload - A JSON object containing pet information # + return - OK - resource isolated function put path02(User payload) returns error? { + resource isolated function put path02(User payload, map headers = {}) returns error? { string resourcePath = string `/path02`; http:Request request = new; json jsonBody = payload.toJson(); request.setPayload(jsonBody, "application/json"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } # 06 Example for rb has array inline requestbody. # + # + headers - Headers to be sent with the request # + return - OK - resource isolated function put path03(path03_body payload) returns error? { + resource isolated function put path03(path03_body payload, map headers = {}) returns error? { string resourcePath = string `/path03`; http:Request request = new; json jsonBody = payload.toJson(); xml? xmlBody = check xmldata:fromJson(jsonBody); request.setPayload(xmlBody, "application/xml"); - return self.clientEp->put(resourcePath, request); + return self.clientEp->put(resourcePath, request, headers); } } From 3ed4c55f3b97d5c0a947516e7282a578decfaf7f Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 16:58:40 +0530 Subject: [PATCH 27/31] Fix NoServerURLTest --- .../client/ballerina/blank_value_server_url.bal | 16 +++++----------- .../client/ballerina/missing_server_url.bal | 16 +++++----------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/blank_value_server_url.bal b/openapi-cli/src/test/resources/generators/client/ballerina/blank_value_server_url.bal index dbf50eb5f..dff75a884 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/blank_value_server_url.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/blank_value_server_url.bal @@ -1,6 +1,5 @@ import ballerina/http; - # Get current weather, daily forecast for 16 days, and 3-hourly forecast 5 days for your city. @display {label: "Open Weather Client"} public isolated client class Client { @@ -39,18 +38,13 @@ public isolated client class Client { } # Provide weather forecast for any geographical coordinates # - # + lat - Latitude - # + lon - Longtitude - # + exclude - test - # + units - tests + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Successful response @display {label: "Weather Forecast"} - remote isolated function getWeatherForecast(@display {label: "Latitude"} string lat, @display {label: "Longtitude"} string lon, @display {label: "Exclude"} string? exclude = (), @display {label: "Units"} int? units = ()) returns WeatherForecast|error { + remote isolated function getWeatherForecast(map headers = {}, *GetWeatherForecastQueries queries) returns WeatherForecast|error { string resourcePath = string `/onecall`; - map queryParam = {"lat": lat, "lon": lon, "exclude": exclude, "units": units}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp-> get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } } - - diff --git a/openapi-cli/src/test/resources/generators/client/ballerina/missing_server_url.bal b/openapi-cli/src/test/resources/generators/client/ballerina/missing_server_url.bal index dbf50eb5f..dff75a884 100644 --- a/openapi-cli/src/test/resources/generators/client/ballerina/missing_server_url.bal +++ b/openapi-cli/src/test/resources/generators/client/ballerina/missing_server_url.bal @@ -1,6 +1,5 @@ import ballerina/http; - # Get current weather, daily forecast for 16 days, and 3-hourly forecast 5 days for your city. @display {label: "Open Weather Client"} public isolated client class Client { @@ -39,18 +38,13 @@ public isolated client class Client { } # Provide weather forecast for any geographical coordinates # - # + lat - Latitude - # + lon - Longtitude - # + exclude - test - # + units - tests + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request # + return - Successful response @display {label: "Weather Forecast"} - remote isolated function getWeatherForecast(@display {label: "Latitude"} string lat, @display {label: "Longtitude"} string lon, @display {label: "Exclude"} string? exclude = (), @display {label: "Units"} int? units = ()) returns WeatherForecast|error { + remote isolated function getWeatherForecast(map headers = {}, *GetWeatherForecastQueries queries) returns WeatherForecast|error { string resourcePath = string `/onecall`; - map queryParam = {"lat": lat, "lon": lon, "exclude": exclude, "units": units}; - resourcePath = resourcePath + check getPathForQueryParam(queryParam); - return self.clientEp-> get(resourcePath); + resourcePath = resourcePath + check getPathForQueryParam(queries); + return self.clientEp->get(resourcePath, headers); } } - - From bb6fdf833dcd3d67d70933a33996cc59ff723726 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 17:05:43 +0530 Subject: [PATCH 28/31] Fix FunctionSignatureNodeTests --- .../client/FunctionSignatureNodeTests.java | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionSignatureNodeTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionSignatureNodeTests.java index 60e517f2f..605ab9963 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionSignatureNodeTests.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionSignatureNodeTests.java @@ -20,6 +20,7 @@ import io.ballerina.compiler.syntax.tree.DefaultableParameterNode; import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; +import io.ballerina.compiler.syntax.tree.IncludedRecordParameterNode; import io.ballerina.compiler.syntax.tree.ParameterNode; import io.ballerina.compiler.syntax.tree.RequiredParameterNode; import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; @@ -28,6 +29,7 @@ import io.ballerina.openapi.core.generators.common.GeneratorUtils; import io.ballerina.openapi.core.generators.common.TypeHandler; import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; +import io.swagger.models.auth.In; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import org.testng.Assert; @@ -66,21 +68,17 @@ public void getFunctionSignatureNodeTests() throws IOException, BallerinaOpenApi SeparatedNodeList parameters = signature.get().parameters(); Assert.assertFalse(parameters.isEmpty()); RequiredParameterNode param01 = (RequiredParameterNode) parameters.get(0); - RequiredParameterNode param02 = (RequiredParameterNode) parameters.get(1); - RequiredParameterNode param03 = (RequiredParameterNode) parameters.get(2); - RequiredParameterNode param04 = (RequiredParameterNode) parameters.get(3); + DefaultableParameterNode param02 = (DefaultableParameterNode) parameters.get(1); + IncludedRecordParameterNode param03 = (IncludedRecordParameterNode) parameters.get(2); - Assert.assertEquals(param01.paramName().orElseThrow().text(), "latitude"); - Assert.assertEquals(param01.typeName().toString(), "decimal"); + Assert.assertEquals(param01.paramName().orElseThrow().text(), "country"); + Assert.assertEquals(param01.typeName().toString(), "string"); - Assert.assertEquals(param02.paramName().orElseThrow().text(), "longitude"); - Assert.assertEquals(param02.typeName().toString(), "decimal"); + Assert.assertEquals(param02.paramName().orElseThrow().text(), "headers"); + Assert.assertEquals(param02.typeName().toString(), "map"); - Assert.assertEquals(param03.paramName().orElseThrow().text(), "country"); - Assert.assertEquals(param03.typeName().toString(), "string"); - - Assert.assertEquals(param04.paramName().orElseThrow().text(), "pages"); - Assert.assertEquals(param04.typeName().toString(), "decimal[]"); + Assert.assertEquals(param03.paramName().orElseThrow().text(), "queries"); + Assert.assertEquals(param03.typeName().toString(), "GetProductsCountryQueries"); ReturnTypeDescriptorNode returnTypeNode = signature.get().returnTypeDesc().orElseThrow(); Assert.assertEquals(returnTypeNode.type().toString(), "Product[]|error"); @@ -140,13 +138,9 @@ public void testFunctionSignatureNodeForMultipartCustomHeader() throws IOExcepti Assert.assertEquals(param01.paramName().orElseThrow().text(), "payload"); Assert.assertEquals(param01.typeName().toString(), "pets_body"); - RequiredParameterNode param02 = (RequiredParameterNode) parameters.get(1); - Assert.assertEquals(param02.paramName().orElseThrow().text(), "X\\-Address\\-Header"); - Assert.assertEquals(param02.typeName().toString(), "string"); - - DefaultableParameterNode param03 = (DefaultableParameterNode) parameters.get(2); - Assert.assertEquals(param03.paramName().orElseThrow().text(), "X\\-Custom\\-Header"); - Assert.assertEquals(param03.typeName().toString(), "string?"); + DefaultableParameterNode param02 = (DefaultableParameterNode) parameters.get(1); + Assert.assertEquals(param02.paramName().orElseThrow().text(), "headers"); + Assert.assertEquals(param02.typeName().toString(), "map"); ReturnTypeDescriptorNode returnTypeNode = signature.returnTypeDesc().orElseThrow(); Assert.assertEquals(returnTypeNode.type().toString(), "error?"); From bed9125378e2df0436dcc82de9d458c54338ef33 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 17:09:00 +0530 Subject: [PATCH 29/31] Fix FilterTests --- .../generators/client/file_provider/ballerina/operation.bal | 6 ++++-- .../generators/client/file_provider/ballerina/tag.bal | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/operation.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/operation.bal index bf13fa467..c744e71d1 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/operation.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/operation.bal @@ -35,11 +35,13 @@ public isolated client class Client { self.clientEp = httpEp; return; } + # List of all countries with COVID-19 cases # + # + headers - Headers to be sent with the request # + return - Default response with array of strings - remote isolated function getCountryList() returns CountryInfo[]|error { + remote isolated function getCountryList(map headers = {}) returns CountryInfo[]|error { string resourcePath = string `/api/v1/countries/list/`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } diff --git a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/tag.bal b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/tag.bal index 6b0305d58..94c6926f6 100644 --- a/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/tag.bal +++ b/openapi-cli/src/test/resources/generators/client/file_provider/ballerina/tag.bal @@ -35,11 +35,13 @@ public isolated client class Client { self.clientEp = httpEp; return; } + # Returns information about all countries # + # + headers - Headers to be sent with the request # + return - A list of countries with all informtion included. - remote isolated function getCovidinAllCountries() returns Countries[]|error { + remote isolated function getCovidinAllCountries(map headers = {}) returns Countries[]|error { string resourcePath = string `/api`; - return self.clientEp->get(resourcePath); + return self.clientEp->get(resourcePath, headers); } } From a3d10ff787a28ef3a14299d387a5b990e8b510e4 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 17:13:27 +0530 Subject: [PATCH 30/31] Fix check style issue --- .../openapi/generators/client/FunctionSignatureNodeTests.java | 1 - .../core/generators/document/ClientDocCommentGenerator.java | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionSignatureNodeTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionSignatureNodeTests.java index 605ab9963..b487f75cf 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionSignatureNodeTests.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/FunctionSignatureNodeTests.java @@ -29,7 +29,6 @@ import io.ballerina.openapi.core.generators.common.GeneratorUtils; import io.ballerina.openapi.core.generators.common.TypeHandler; import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException; -import io.swagger.models.auth.In; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import org.testng.Assert; diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java index 328282712..1608f7015 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/document/ClientDocCommentGenerator.java @@ -322,7 +322,8 @@ private static void updateParameterNodes(List docs, Operation operation, L List updatedParamsDefault, HashMap collection) { List deprecatedParamDocComments = new ArrayList<>(); - boolean hasQueryParams = operation.getParameters().stream().anyMatch(parameter -> parameter.getIn().equals("query")); + boolean hasQueryParams = operation.getParameters().stream().anyMatch( + parameter -> parameter.getIn().equals("query")); operation.getParameters().forEach(parameter -> { if (parameter.get$ref() != null) { try { From 7a62f4e83e7ead8e61ba71fe28de22866efdd932 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 2 May 2024 17:26:43 +0530 Subject: [PATCH 31/31] Fix spot bug issue --- .../core/generators/client/FunctionBodyGeneratorImp.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java index 357178361..eb9c244af 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionBodyGeneratorImp.java @@ -100,7 +100,6 @@ import static io.ballerina.openapi.core.generators.common.GeneratorConstants.DELETE; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.ENCODING; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.EXECUTE; -import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEAD; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEADERS; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HEADER_VALUES; import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HTTP_HEADERS; @@ -327,8 +326,7 @@ private void addUpdatedPathAndHeaders(List statementsList, List statementsList, List statementsLi } else if (method.equals(DELETE)) { clientCallStatement = getClientCallWithHeadersParam().formatted(method, RESOURCE_PATH, paramName); - } else if (method.equals(HEAD)) { - clientCallStatement = getClientCallWithHeaders().formatted(method, RESOURCE_PATH, paramName); } else { clientCallStatement = getClientCallWithHeaders().formatted(method, RESOURCE_PATH, paramName); }