diff --git a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BallerinaCodeGenerator.java b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BallerinaCodeGenerator.java index cbbcca05e..420316d5b 100644 --- a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BallerinaCodeGenerator.java +++ b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BallerinaCodeGenerator.java @@ -441,7 +441,7 @@ public List generateBallerinaService(Path openAPI, String serviceNam openAPIDef, nullable, preGeneratedTypeDefNodes); String schemaContent = Formatter.format( ballerinaSchemaGenerator.generateSyntaxTree()).toSourceCode(); - if (!schemaContent.isBlank()) { + if (!schemaContent.isBlank() && !generateWithoutDataBinding) { sourceFiles.add(new GenSrcFile(GenSrcFile.GenFileType.GEN_SRC, srcPackage, TYPE_FILE_NAME, (licenseHeader.isBlank() ? DEFAULT_FILE_HEADER : licenseHeader) + schemaContent)); } diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/cmd/OpenApiGenServiceCmdTest.java b/openapi-cli/src/test/java/io/ballerina/openapi/cmd/OpenApiGenServiceCmdTest.java index c8765cfab..c9d68c09f 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/cmd/OpenApiGenServiceCmdTest.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/cmd/OpenApiGenServiceCmdTest.java @@ -17,8 +17,10 @@ */ package io.ballerina.openapi.cmd; +import io.ballerina.cli.launcher.BLauncherException; import org.testng.Assert; import org.testng.annotations.Test; +import picocli.CommandLine; import java.io.IOException; import java.nio.file.Files; @@ -111,4 +113,34 @@ public void testOneOfSchemaGen() throws IOException { Assert.fail("Service generation for OneOf Schema type failed."); } } + + @Test(description = "Test for --without-data-binding flag") + public void testWithoutDataBinding() throws IOException { + Path yamlPath = resourceDir.resolve(Paths.get("withoutDataBinding.yaml")); + String[] args = {"--input", yamlPath.toString(), "--without-data-binding", "-o", + this.tmpDir.toString(), "--mode", "service"}; + OpenApiCmd cmd = new OpenApiCmd(printStream, this.tmpDir); + new CommandLine(cmd).parseArgs(args); + String output = ""; + try { + cmd.execute(); + } catch (BLauncherException e) { + output = e.getDetailedMessages().get(0); + } + + Path expectedServiceFile = resourceDir.resolve(Paths.get("expected_gen", + "without-data-binding.bal")); + Stream expectedServiceLines = Files.lines(expectedServiceFile); + String expectedService = expectedServiceLines.collect(Collectors.joining("\n")); + expectedServiceLines.close(); + Assert.assertFalse(Files.exists(this.tmpDir.resolve("types.bal"))); + if (Files.exists(this.tmpDir.resolve("withoutdatabinding_service.bal"))) { + String generatedService = getStringFromFile(this.tmpDir.resolve("withoutdatabinding_service.bal")); + Assert.assertEquals(replaceWhiteSpace(generatedService), replaceWhiteSpace(expectedService), + "Expected content and actual generated content is mismatched for: " + yamlPath); + deleteGeneratedFiles("without-data-binding-service.bal"); + } else { + Assert.fail("Service generation for low level service is failed."); + } + } } diff --git a/openapi-cli/src/test/resources/expected_gen/without-data-binding.bal b/openapi-cli/src/test/resources/expected_gen/without-data-binding.bal new file mode 100644 index 000000000..bf56b7a0a --- /dev/null +++ b/openapi-cli/src/test/resources/expected_gen/without-data-binding.bal @@ -0,0 +1,11 @@ +// AUTO-GENERATED FILE. +// This file is auto-generated by the Ballerina OpenAPI tool. + +import ballerina/http; + +listener http:Listener ep0 = new (9090, config = {host: "localhost"}); + +service /v1 on ep0 { + resource function get coupons/[string couponCode]/[int id]/[string limits](http:Caller caller, http:Request request) returns error? { + } +} diff --git a/openapi-cli/src/test/resources/withoutDataBinding.yaml b/openapi-cli/src/test/resources/withoutDataBinding.yaml new file mode 100644 index 000000000..029d20c3a --- /dev/null +++ b/openapi-cli/src/test/resources/withoutDataBinding.yaml @@ -0,0 +1,76 @@ +openapi: 3.0.1 +info: + title: V1 + version: 0.1.0 +servers: + - url: "{server}:{port}/v1" + variables: + server: + default: http://localhost + port: + default: "9090" +paths: + /coupons/{couponCode}/{id}/{limits}: + get: + operationId: getCoupon + parameters: + - $ref: '#/components/parameters/ngwCouponCodePathParam' + - $ref: '#/components/parameters/ngwCouponCodePathParam02' + - $ref: '#/components/parameters/ngwCouponCodePathParam03' + responses: + '200': + description: Successful operation, coupon was found by requested code + content: + application/json: + schema: + $ref: '#/components/schemas/Cat' +components: + parameters: + ngwCouponCodePathParam: + in: path + name: couponCode + required: true + schema: + $ref: '#/components/schemas/Coupon' + ngwCouponCodePathParam02: + in: path + name: id + required: true + schema: + $ref: '#/components/schemas/Id' + ngwCouponCodePathParam03: + in: path + name: limits + required: true + schema: + type: array + items: + type: integer + schemas: + Coupon: + type: string + Id: + $ref: '#/components/schemas/AddressNo' + AddressNo: + type: integer + Pet: + type: object + properties: + name: + type: string + tag: + type: string + required: + - name + - tag + Cat: + type: object + properties: + name: + type: string + parent: + $ref: '#/components/schemas/Pet' + petType: + $ref: '#/components/schemas/Cat' + required: + - name diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/GeneratorUtils.java b/openapi-core/src/main/java/io/ballerina/openapi/core/GeneratorUtils.java index dc0facb62..923a5676e 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/GeneratorUtils.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/GeneratorUtils.java @@ -221,7 +221,8 @@ public static QualifiedNameReferenceNode getQualifiedNameReferenceNode(String mo * @return - node lists * @throws BallerinaOpenApiException */ - public static List getRelativeResourcePath(String path, Operation operation, List resourceFunctionDocs) + public static List getRelativeResourcePath(String path, Operation operation, List resourceFunctionDocs, + Components components, boolean isWithoutDataBinding) throws BallerinaOpenApiException { List functionRelativeResourcePath = new ArrayList<>(); @@ -241,7 +242,7 @@ public static List getRelativeResourcePath(String path, Operation operatio */ if (operation.getParameters() != null) { extractPathParameterDetails(operation, functionRelativeResourcePath, pathNode, - pathParam, resourceFunctionDocs); + pathParam, resourceFunctionDocs, components, isWithoutDataBinding); } } else if (!pathNode.isBlank()) { IdentifierToken idToken = createIdentifierToken(escapeIdentifier(pathNode.trim())); @@ -261,7 +262,9 @@ public static List getRelativeResourcePath(String path, Operation operatio } private static void extractPathParameterDetails(Operation operation, List functionRelativeResourcePath, - String pathNode, String pathParam, List resourceFunctionDocs) + String pathNode, String pathParam, + List resourceFunctionDocs, + Components components, boolean isWithoutDataBinding) throws BallerinaOpenApiException { // check whether path parameter segment has special character String[] split = pathNode.split(CLOSE_CURLY_BRACE, 2); @@ -281,9 +284,10 @@ private static void extractPathParameterDetails(Operation operation, List && parameter.getIn().equals("path")) { String paramType; if (parameter.getSchema().get$ref() != null) { - paramType = getValidName(extractReferenceType(parameter.getSchema().get$ref()), true); + paramType = resolveReferenceType(parameter.getSchema(), components, isWithoutDataBinding, + pathParam); } else { - paramType = convertOpenAPITypeToBallerina(parameter.getSchema()); + paramType = getPathParameterType(parameter.getSchema(), pathParam); if (paramType.endsWith(NILLABLE)) { throw new BallerinaOpenApiException("Path parameter value cannot be null."); } @@ -1102,4 +1106,37 @@ public static boolean isIntegerSchema(Schema fieldSchema) { public static boolean isNumberSchema(Schema fieldSchema) { return Objects.equals(GeneratorUtils.getOpenAPIType(fieldSchema), NUMBER); } + + public static String resolveReferenceType(Schema schema, Components components, boolean isWithoutDataBinding, + String pathParam) throws BallerinaOpenApiException { + String type = GeneratorUtils.extractReferenceType(schema.get$ref()); + + if (isWithoutDataBinding) { + Schema referencedSchema = components.getSchemas().get(getValidName(type, true)); + if (referencedSchema != null) { + if (referencedSchema.get$ref() != null) { + type = resolveReferenceType(referencedSchema, components, isWithoutDataBinding, pathParam); + } else { + type = getPathParameterType(referencedSchema, pathParam); + } + } + } else { + type = getValidName(type, true); + } + return type; + } + + private static String getPathParameterType(Schema typeSchema, String pathParam) + throws BallerinaOpenApiException { + String type; + if (!(isStringSchema(typeSchema) || isNumberSchema(typeSchema) || isBooleanSchema(typeSchema) + || isIntegerSchema(typeSchema))) { + type = STRING; + LOGGER.warn("unsupported path parameter type found in the parameter `" + pathParam + "`. hence the " + + "parameter type is set to string."); + } else { + type = GeneratorUtils.convertOpenAPITypeToBallerina(typeSchema); + } + return type; + } } diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGenerator.java index f34a83f4d..47aa8b535 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGenerator.java @@ -580,7 +580,8 @@ private FunctionDefinitionNode getClientMethodFunctionDefinitionNode(List relativeResourcePath = resourceMode ? - createNodeList(GeneratorUtils.getRelativeResourcePath(path, operation.getValue(), null)) : + createNodeList(GeneratorUtils.getRelativeResourcePath(path, operation.getValue(), + null, openAPI.getComponents(), false)) : createEmptyNodeList(); return createFunctionDefinitionNode(null, metadataNode, qualifierList, functionKeyWord, functionName, relativeResourcePath, diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/service/BallerinaServiceGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/service/BallerinaServiceGenerator.java index 3de6ec406..c3393a31c 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/service/BallerinaServiceGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/service/BallerinaServiceGenerator.java @@ -247,7 +247,8 @@ private List applyFiltersForOperations(Filter filter, String path, filterOperations.contains(operation.getValue().getOperationId().trim()))) { // getRelative resource path List functionRelativeResourcePath = GeneratorUtils.getRelativeResourcePath(path, - operation.getValue(), resourceFunctionDocs); + operation.getValue(), resourceFunctionDocs, openAPI.getComponents(), + generateWithoutDataBinding); // function call FunctionDefinitionNode functionDefinitionNode = generateWithoutDataBinding ? @@ -261,7 +262,7 @@ private List applyFiltersForOperations(Filter filter, String path, } else { // getRelative resource path List relativeResourcePath = GeneratorUtils.getRelativeResourcePath(path, operation.getValue(), - resourceFunctionDocs); + resourceFunctionDocs, openAPI.getComponents(), generateWithoutDataBinding); // function call FunctionDefinitionNode resourceFunction = generateWithoutDataBinding ? generateGenericResourceFunctions(operation,