diff --git a/gradle.properties b/gradle.properties index dc71129ce..f66067e21 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ group=io.ballerina version=1.9.0-SNAPSHOT #dependency -ballerinaLangVersion=2201.8.0 +ballerinaLangVersion=2201.9.0-20231120-094700-aa87313f testngVersion=7.6.1 slf4jVersion=1.7.30 org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8 diff --git a/module-ballerina-openapi/build.gradle b/module-ballerina-openapi/build.gradle index efee1502f..6f8330342 100644 --- a/module-ballerina-openapi/build.gradle +++ b/module-ballerina-openapi/build.gradle @@ -29,7 +29,7 @@ dependencies { implementation project(':openapi-validator') implementation project(':openapi-core') implementation project(':openapi-cli') -// implementation project(':openapi-client-idl-plugin') + implementation project(':openapi-bal-task-plugin') implementation project(':openapi-ls-extension') implementation project(':openapi-extension') } @@ -59,8 +59,7 @@ def targetNativeJar = file("""$project.rootDir/${packageName}-validator/build/li def targetOpenApiCommonJar = file("$project.rootDir/openapi-bal-service/build/libs/openapi-bal-service-${project.version}.jar") def targetOpenApiCoreJar = file("$project.rootDir/openapi-core/build/libs/openapi-core-${project.version}.jar") def targetOpenApiCliJar = file("$project.rootDir/openapi-cli/build/libs/openapi-cli-${project.version}.jar") -//def targetOpenApiClientIDLJar = file("$project.rootDir/openapi-client-idl-plugin" + -// "/build/libs/openapi-client-idl-plugin-${project.version}.jar") +def targetOpenApiBalTaskJar = file("$project.rootDir/openapi-bal-task-plugin/build/libs/openapi-bal-task-plugin-${project.version}.jar") def targetOpenApiBuiltInExtensionJar = file("$project.rootDir/openapi-extension/build/libs/openapi-extension-${project.version}.jar") def targetOpenApiBuildExtensionJar = file("$project.rootDir/openapi-build-extension/build/libs/openapi-build-extension-${project.version}.jar") def targetOpenApiLSJar = file("$project.rootDir/openapi-ls-extension/build/libs/openapi-ls-extension-${project.version}.jar") @@ -237,10 +236,10 @@ task ballerinaBuild { into file("$artifactLibParent/libs") } -// copy { -// from targetOpenApiClientIDLJar -// into file("$artifactLibParent/libs") -// } + copy { + from targetOpenApiBalTaskJar + into file("$artifactLibParent/libs") + } copy { from targetOpenApiLSJar @@ -337,6 +336,7 @@ ballerinaBuild.dependsOn ":${packageName}-ls-extension:build" ballerinaBuild.dependsOn ":${packageName}-validator:build" ballerinaBuild.dependsOn ":${packageName}-extension:build" ballerinaBuild.dependsOn ":${packageName}-build-extension:build" +ballerinaBuild.dependsOn ":${packageName}-bal-task-plugin:build" ballerinaBuild.dependsOn updateCompilerTomlFile ballerinaBuild.dependsOn initializeVariables ballerinaBuild.dependsOn generatePomFileForMavenJavaPublication diff --git a/openapi-bal-task-plugin/build.gradle b/openapi-bal-task-plugin/build.gradle new file mode 100644 index 000000000..2fe0f540a --- /dev/null +++ b/openapi-bal-task-plugin/build.gradle @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023, 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. + */ + + +apply from: "$rootDir/gradle/javaProject.gradle" +apply plugin: "com.github.johnrengelman.shadow" +apply plugin: "java-library" + +description = "Ballerina - OpenAPI Tooling - OpenAPI to Ballerina" + +dependencies { + implementation project(':openapi-core') + implementation("io.swagger.parser.v3:swagger-parser:${swaggerParserVersion}") { + exclude group: "io.swagger", module: "swagger-compat-spec-parser" + exclude group: "org.slf4j", module: "slf4j-ext" + exclude group: "javax.validation", module: "validation-api" + } + implementation "org.ballerinalang:ballerina-lang" + implementation "org.ballerinalang:ballerina-parser" + implementation "org.ballerinalang:formatter-core" + implementation "org.ballerinalang:ballerina-cli" + implementation "org.ballerinalang:ballerina-tools-api" + implementation "org.ballerinalang:toml-parser:${ballerinaLangVersion}" + implementation "com.google.code.findbugs:jsr305" + testImplementation "org.testng:testng" +} + +compileJava { + doFirst { + options.compilerArgs = [ + '--module-path', classpath.asPath, + ] + classpath = files() + } +} diff --git a/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/Constants.java b/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/Constants.java new file mode 100644 index 000000000..66ff9db7c --- /dev/null +++ b/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/Constants.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2023, 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.bal.tool; + +import io.ballerina.tools.diagnostics.DiagnosticSeverity; + +/** + * This class includes constants for ballerina package build generator. + * + * @since 1.9.0 + */ +public class Constants { + public static final String TAGS = "tags"; + public static final String OPERATIONS = "operations"; + public static final String NULLABLE = "nullable"; + public static final String CLIENT_METHODS = "clientMethods"; + public static final String LICENSE = "license"; + public static final String TRUE = "true"; + public static final String MODE = "mode"; + public static final String CLIENT = "client"; + + /** + * Enum class for containing diagnostic messages. + */ + public enum DiagnosticMessages { + LICENSE_PATH_BLANK("OAS_CLIENT_01", "given license file path is an empty string.", + DiagnosticSeverity.WARNING), + ERROR_WHILE_READING_LICENSE_FILE("OAS_CLIENT_02", "unexpected error occurred while reading the license", + DiagnosticSeverity.ERROR), + ERROR_WHILE_GENERATING_CLIENT("OAS_CLIENT_03", "unexpected error occurred while generating the client", + DiagnosticSeverity.ERROR), + PARSER_ERROR("OAS_CLIENT_04", "", DiagnosticSeverity.ERROR), + UNEXPECTED_EXCEPTIONS("OAS_CLIENT_05", "unexpected error occurred while reading the contract", + DiagnosticSeverity.ERROR), + EMPTY_CONTRACT_PATH("OAS_CLIENT_06", "given OpenAPI contract file path is an empty string.", + DiagnosticSeverity.WARNING), + WARNING_FOR_OTHER_GENERATION("OAS_CLIENT_07", "`%s` mode does not support for bal build code generation.", + DiagnosticSeverity.WARNING), + WARNING_FOR_UNSUPPORTED_CONTRACT("OAS_CLIENT_08", "unsupported contract type. please use .yml, " + + ".yaml, or .json files for code generation.", + DiagnosticSeverity.WARNING); + + private final String code; + private final String description; + private final DiagnosticSeverity severity; + + DiagnosticMessages(String code, String description, DiagnosticSeverity severity) { + this.code = code; + this.description = description; + this.severity = severity; + } + + public String getCode() { + return code; + } + + public String getDescription() { + return description; + } + + public DiagnosticSeverity getSeverity() { + return severity; + } + } +} diff --git a/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/OpenAPIBuildToolRunner.java b/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/OpenAPIBuildToolRunner.java new file mode 100644 index 000000000..9979c81a5 --- /dev/null +++ b/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/OpenAPIBuildToolRunner.java @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2023, 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.bal.tool; + +import io.ballerina.cli.tool.BuildToolRunner; +import io.ballerina.compiler.syntax.tree.SyntaxTree; +import io.ballerina.compiler.syntax.tree.TypeDefinitionNode; +import io.ballerina.openapi.core.GeneratorUtils; +import io.ballerina.openapi.core.exception.BallerinaOpenApiException; +import io.ballerina.openapi.core.generators.client.BallerinaClientGenerator; +import io.ballerina.openapi.core.generators.client.model.OASClientConfig; +import io.ballerina.openapi.core.generators.schema.BallerinaTypesGenerator; +import io.ballerina.openapi.core.generators.service.model.OASServiceMetadata; +import io.ballerina.openapi.core.model.Filter; +import io.ballerina.openapi.core.model.GenSrcFile; +import io.ballerina.projects.Package; +import io.ballerina.projects.ToolContext; +import io.ballerina.toml.semantic.ast.TomlArrayValueNode; +import io.ballerina.toml.semantic.ast.TomlKeyValueNode; +import io.ballerina.toml.semantic.ast.TomlTableNode; +import io.ballerina.toml.semantic.ast.TomlValueNode; +import io.ballerina.toml.semantic.ast.TopLevelNode; +import io.ballerina.toml.semantic.diagnostics.TomlNodeLocation; +import io.ballerina.tools.diagnostics.Diagnostic; +import io.ballerina.tools.diagnostics.DiagnosticFactory; +import io.ballerina.tools.diagnostics.DiagnosticInfo; +import io.ballerina.tools.diagnostics.Location; +import io.swagger.v3.oas.models.OpenAPI; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.ballerinalang.formatter.core.Formatter; +import org.ballerinalang.formatter.core.FormatterException; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import static io.ballerina.openapi.bal.tool.Constants.CLIENT; +import static io.ballerina.openapi.bal.tool.Constants.CLIENT_METHODS; +import static io.ballerina.openapi.bal.tool.Constants.LICENSE; +import static io.ballerina.openapi.bal.tool.Constants.MODE; +import static io.ballerina.openapi.bal.tool.Constants.NULLABLE; +import static io.ballerina.openapi.bal.tool.Constants.OPERATIONS; +import static io.ballerina.openapi.bal.tool.Constants.TAGS; +import static io.ballerina.openapi.bal.tool.Constants.TRUE; +import static io.ballerina.openapi.core.GeneratorConstants.CLIENT_FILE_NAME; +import static io.ballerina.openapi.core.GeneratorConstants.DO_NOT_MODIFY_FILE_HEADER; +import static io.ballerina.openapi.core.GeneratorConstants.JSON_EXTENSION; +import static io.ballerina.openapi.core.GeneratorConstants.TYPE_FILE_NAME; +import static io.ballerina.openapi.core.GeneratorConstants.UTIL_FILE_NAME; +import static io.ballerina.openapi.core.GeneratorConstants.YAML_EXTENSION; +import static io.ballerina.openapi.core.GeneratorConstants.YML_EXTENSION; +import static io.ballerina.openapi.core.GeneratorUtils.normalizeOpenAPI; + +/** + * This class includes the implementation of the {@code BuildToolRunner} for the OpenAPI tool. + * + * @since 1.9.0 + */ +public class OpenAPIBuildToolRunner implements BuildToolRunner { + static List diagnostics = new ArrayList<>(); + + @Override + public String getToolName() { + return "openapi"; + } + + @Override + public List diagnostics() { + return diagnostics; + } + + @Override + public void executeTool(ToolContext toolContext) { + + ImmutablePair codeGeneratorConfig; + TomlNodeLocation location = toolContext.packageInstance().ballerinaToml().get().tomlAstNode().location(); + try { + // Validate the OAS file whether we can handle within OpenAPI tool + if (!canHandle(toolContext)) { + Constants.DiagnosticMessages error = Constants.DiagnosticMessages.WARNING_FOR_UNSUPPORTED_CONTRACT; + reportDiagnostics(error, error.getDescription(), location); + return; + } + String oasFilePath = toolContext.filePath(); + Path packagePath = toolContext.packageInstance().project().sourceRoot(); + OpenAPI openAPI = getOpenAPIContract(packagePath, Path.of(oasFilePath), location); + if (openAPI == null) { + return; + } + TomlTableNode tomlTableNode = toolContext.optionsTable(); + if (tomlTableNode == null) { + // Default generate client + Filter filter = new Filter(); + OASClientConfig clientConfig = new OASClientConfig.Builder() + .withFilters(filter).withOpenAPI(openAPI).build(); + OASServiceMetadata serviceMetaData = new OASServiceMetadata.Builder() + .withFilters(filter).withOpenAPI(openAPI).build(); + codeGeneratorConfig = new ImmutablePair<>(clientConfig, serviceMetaData); + generateClient(toolContext, codeGeneratorConfig); + } else { + codeGeneratorConfig = extractOptionDetails(toolContext, openAPI); + Map options = tomlTableNode.entries(); + if (options.containsKey(MODE)) { + TopLevelNode value = options.get(MODE); + String mode = value.toNativeObject().toString().trim(); + handleCodeGenerationMode(toolContext, codeGeneratorConfig, location, mode); + } else { + // Create client for the given OAS + generateClient(toolContext, codeGeneratorConfig); + } + } + } catch (BallerinaOpenApiException e) { + Constants.DiagnosticMessages error = Constants.DiagnosticMessages.PARSER_ERROR; + reportDiagnostics(error, e.getMessage(), location); + } catch (IOException | FormatterException e) { + Constants.DiagnosticMessages error = Constants.DiagnosticMessages.ERROR_WHILE_GENERATING_CLIENT; + reportDiagnostics(error, e.getMessage(), location); + } + } + + /** + * This method uses to handle the code generation mode. ex: client, service + */ + private void handleCodeGenerationMode(ToolContext toolContext, + ImmutablePair codeGeneratorConfig, + TomlNodeLocation location, String mode) + throws BallerinaOpenApiException, IOException, FormatterException { + if (mode.equals(CLIENT)) { + // Create client for the given OAS + generateClient(toolContext, codeGeneratorConfig); + } else { + Constants.DiagnosticMessages error = Constants.DiagnosticMessages.WARNING_FOR_OTHER_GENERATION; + reportDiagnostics(error, String.format(error.getDescription(), mode), + location); + } + } + + /** + * This method uses to check whether given specification can be handled via the openapi client generation tool. + * This includes basic requirements like file extension check. + */ + private static boolean canHandle(ToolContext toolContext) { + String oasPath = toolContext.filePath(); + if (oasPath.isBlank()) { + TomlNodeLocation location = toolContext.packageInstance().ballerinaToml().get().tomlAstNode().location(); + Constants.DiagnosticMessages error = Constants.DiagnosticMessages.EMPTY_CONTRACT_PATH; + reportDiagnostics(error, error.getDescription(), location); + return false; + } + return (oasPath.endsWith(YAML_EXTENSION) || oasPath.endsWith(JSON_EXTENSION) || + oasPath.endsWith(YML_EXTENSION)); + } + + /** + * This method uses to read the openapi contract and return the {@code OpenAPI} object. + */ + private OpenAPI getOpenAPIContract(Path ballerinaFilePath, Path openAPIPath, Location location) { + Path relativePath; + try { + Path inputPath = Paths.get(openAPIPath.toString()); + if (inputPath.isAbsolute()) { + relativePath = inputPath; + } else { + File file = new File(ballerinaFilePath.toString()); + File openapiContract = new File(file, openAPIPath.toString()); + relativePath = Paths.get(openapiContract.getCanonicalPath()); + } + return normalizeOpenAPI(Path.of(relativePath.toString()), true); + } catch (IOException | BallerinaOpenApiException e) { + Constants.DiagnosticMessages error = Constants.DiagnosticMessages.UNEXPECTED_EXCEPTIONS; + reportDiagnostics(error, error.getDescription(), location); + } + return null; + } + + /** + * This method uses to extract the options given by the user. + */ + public static ImmutablePair extractOptionDetails(ToolContext toolContext, + OpenAPI openAPI) throws + IOException, BallerinaOpenApiException { + + Filter filter = new Filter(); + OASClientConfig.Builder clientMetaDataBuilder = new OASClientConfig.Builder(); + OASServiceMetadata.Builder serviceMetaDataBuilder = new OASServiceMetadata.Builder(); + clientMetaDataBuilder.withOpenAPI(openAPI); + serviceMetaDataBuilder.withOpenAPI(openAPI); + Map options = toolContext.optionsTable().entries(); + for (Map.Entry field : options.entrySet()) { + TomlValueNode valueNode = ((TomlKeyValueNode) field.getValue()).value(); + String value = valueNode.toNativeValue().toString().trim(); + String fieldName = field.getKey(); + switch (fieldName) { + case TAGS: + filter.setTags(getArrayItems(valueNode)); + break; + case OPERATIONS: + filter.setOperations(getArrayItems(valueNode)); + break; + case NULLABLE: + serviceMetaDataBuilder.withNullable(value.contains(TRUE)); + clientMetaDataBuilder.withNullable(value.contains(TRUE)); + break; + case CLIENT_METHODS: + clientMetaDataBuilder.withResourceMode(value.contains("resource")); + break; + case LICENSE: + String licenseContent = Objects.equals(value, "") ? null : + getLicenseContent(toolContext, Paths.get(value)); + clientMetaDataBuilder.withLicense(licenseContent != null ? licenseContent : + DO_NOT_MODIFY_FILE_HEADER); + break; + default: + break; + } + } + clientMetaDataBuilder.withFilters(filter); + serviceMetaDataBuilder.withFilters(filter); + return new ImmutablePair<>(clientMetaDataBuilder.build(), serviceMetaDataBuilder.build()); + } + + private static List getArrayItems(TomlValueNode valueNode) { + List arrayItems = new ArrayList<>(); + if (valueNode instanceof TomlArrayValueNode arrayValueNode) { + arrayValueNode.elements().forEach(element -> { + arrayItems.add(element.toNativeValue().toString()); + }); + } + return arrayItems; + } + + /** + * This method uses to generate the client module for the given openapi contract. + */ + private void generateClient(ToolContext toolContext, ImmutablePair codeGeneratorConfig) throws BallerinaOpenApiException, IOException, FormatterException { + OASClientConfig clientConfig = codeGeneratorConfig.getLeft(); + List sources = generateClientFiles(clientConfig); + Path outputPath = toolContext.outputPath(); + writeGeneratedSources(sources, outputPath); + } + + /** + * This method uses to generate ballerina files for openapi client stub. + * This will return list of (client.bal, util.bal, types.bal) {@code GenSrcFile}. + */ + private static List generateClientFiles(OASClientConfig oasClientConfig) throws + BallerinaOpenApiException, IOException, FormatterException { + + List sourceFiles = new ArrayList<>(); + + // Generate ballerina client files. + String licenseContent = oasClientConfig.getLicense(); + BallerinaClientGenerator ballerinaClientGenerator = new BallerinaClientGenerator(oasClientConfig); + String mainContent = Formatter.format(ballerinaClientGenerator.generateSyntaxTree()).toString(); + sourceFiles.add(new GenSrcFile(GenSrcFile.GenFileType.GEN_SRC, null, CLIENT_FILE_NAME, + licenseContent == null || licenseContent.isBlank() ? mainContent : + licenseContent + System.lineSeparator() + mainContent)); + String utilContent = Formatter.format( + ballerinaClientGenerator.getBallerinaUtilGenerator().generateUtilSyntaxTree()).toString(); + + if (!utilContent.isBlank()) { + sourceFiles.add(new GenSrcFile(GenSrcFile.GenFileType.UTIL_SRC, null, UTIL_FILE_NAME, + licenseContent == null || licenseContent.isBlank() ? utilContent : + licenseContent + System.lineSeparator() + utilContent)); + } + + // Generate ballerina records to represent schemas. + List typeDefinitionNodeList = new LinkedList<>(); + typeDefinitionNodeList.addAll(ballerinaClientGenerator.getTypeDefinitionNodeList()); + typeDefinitionNodeList.addAll(ballerinaClientGenerator + .getBallerinaAuthConfigGenerator().getAuthRelatedTypeDefinitionNodes()); + BallerinaTypesGenerator ballerinaSchemaGenerator = new BallerinaTypesGenerator(oasClientConfig.getOpenAPI(), + oasClientConfig.isNullable(), typeDefinitionNodeList); + SyntaxTree schemaSyntaxTree = ballerinaSchemaGenerator.generateSyntaxTree(); + String schemaContent = Formatter.format(schemaSyntaxTree).toString(); + + if (oasClientConfig.getFilters().getTags().size() > 0) { + // Remove unused records and enums when generating the client by the tags given. + schemaContent = GeneratorUtils.removeUnusedEntities(schemaSyntaxTree, mainContent, schemaContent, + null); + } + if (!schemaContent.isBlank()) { + sourceFiles.add(new GenSrcFile(GenSrcFile.GenFileType.MODEL_SRC, null, TYPE_FILE_NAME, + licenseContent == null || licenseContent.isBlank() ? schemaContent : + licenseContent + System.lineSeparator() + schemaContent)); + } + + return sourceFiles; + } + + /** + * Util to read license content. + */ + private static String getLicenseContent(ToolContext context, Path licensePath) { + Package packageInstance = context.packageInstance(); + Location location = context.optionsTable().entries().get("license").location(); + Path ballerinaFilePath = Optional.ofNullable(packageInstance.project().sourceRoot()).orElse(null); + try { + Path relativePath = getLicensePath(licensePath, location, ballerinaFilePath); + return (relativePath != null) ? createLicenseContent(relativePath, location) : null; + } catch (IOException e) { + Constants.DiagnosticMessages error = Constants.DiagnosticMessages.ERROR_WHILE_READING_LICENSE_FILE; + reportDiagnostics(error, error.getDescription(), location); + return null; + } + } + + /** + * Util to get license path. + */ + private static Path getLicensePath(Path licensePath, Location location, Path ballerinaFilePath) + throws IOException { + Path relativePath = null; + if (licensePath.toString().isBlank()) { + Constants.DiagnosticMessages error = Constants.DiagnosticMessages.LICENSE_PATH_BLANK; + reportDiagnostics(error, error.getDescription(), location); + } else { + Path finalLicensePath = Paths.get(licensePath.toString()); + if (finalLicensePath.isAbsolute()) { + relativePath = finalLicensePath; + } else { + File openapiContract = new File(ballerinaFilePath.toString(), licensePath.toString()); + relativePath = Paths.get(openapiContract.getCanonicalPath()); + } + } + return relativePath; + } + + /** + * Util to create license content. + */ + private static String createLicenseContent(Path relativePath, Location location) { + String licenseHeader; + try { + String newLine = System.lineSeparator(); + Path filePath = Paths.get((new File(relativePath.toString()).getCanonicalPath())); + licenseHeader = Files.readString(Paths.get(filePath.toString())); + if (!licenseHeader.endsWith(newLine)) { + licenseHeader = licenseHeader + newLine + newLine; + } else if (!licenseHeader.endsWith(newLine + newLine)) { + licenseHeader = licenseHeader + newLine; + } + } catch (IOException e) { + Constants.DiagnosticMessages error = Constants.DiagnosticMessages + .ERROR_WHILE_READING_LICENSE_FILE; + reportDiagnostics(error, error.getDescription(), location); + return null; + } + return licenseHeader; + } + + /** + * This method uses to report the diagnostics. + */ + private static void reportDiagnostics(Constants.DiagnosticMessages error, String error1, Location location) { + DiagnosticInfo diagnosticInfo = new DiagnosticInfo(error.getCode(), error1, + error.getSeverity()); + Diagnostic diagnostic = DiagnosticFactory.createDiagnostic(diagnosticInfo, + location); + diagnostics.add(diagnostic); + } + + /** + * This method uses to write the generated sources into the given output path. + */ + private void writeGeneratedSources(List sources, Path outputPath) throws IOException { + for (GenSrcFile file : sources) { + Path filePath = Paths.get(outputPath.resolve(file.getFileName()).toFile().getCanonicalPath()); + String fileContent = file.getContent(); + writeFile(filePath, fileContent); + } + } + + /** + * This method uses to write the content into the given file path. + */ + public static void writeFile(Path filePath, String content) throws IOException { + File file = new File(filePath.toString()); + // Ensure the directory structure exists + File parentDirectory = file.getParentFile(); + if (!parentDirectory.exists() && !parentDirectory.mkdirs()) { + return; // Directory creation failed + } + try (FileWriter writer = new FileWriter(filePath.toString(), StandardCharsets.UTF_8)) { + writer.write(content); + } + } +} diff --git a/openapi-bal-task-plugin/src/main/java/module-info.java b/openapi-bal-task-plugin/src/main/java/module-info.java new file mode 100644 index 000000000..63da17358 --- /dev/null +++ b/openapi-bal-task-plugin/src/main/java/module-info.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module io.ballerina.openapi.bal.tool { + requires io.ballerina.lang; + requires io.ballerina.parser; + requires io.ballerina.tools.api; + requires io.ballerina.formatter.core; + requires io.ballerina.cli; + requires io.swagger.v3.core; + requires io.swagger.v3.oas.models; + requires jsr305; + requires org.apache.commons.io; + requires org.slf4j; + requires org.apache.commons.lang3; + requires io.ballerina.openapi.core; + requires io.ballerina.toml; +} diff --git a/openapi-bal-task-plugin/src/main/resources/META-INF/services/io.ballerina.cli.tool.BuildToolRunner b/openapi-bal-task-plugin/src/main/resources/META-INF/services/io.ballerina.cli.tool.BuildToolRunner new file mode 100644 index 000000000..31247fa02 --- /dev/null +++ b/openapi-bal-task-plugin/src/main/resources/META-INF/services/io.ballerina.cli.tool.BuildToolRunner @@ -0,0 +1 @@ +io.ballerina.openapi.bal.tool.OpenAPIBuildToolRunner diff --git a/openapi-bal-task-plugin/src/main/resources/openapi-options-schema.json b/openapi-bal-task-plugin/src/main/resources/openapi-options-schema.json new file mode 100644 index 000000000..f938aefe4 --- /dev/null +++ b/openapi-bal-task-plugin/src/main/resources/openapi-options-schema.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "clientMethods": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "nullable": { + "type": "boolean" + }, + "mode": { + "type":"string" + }, + "license": { + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/openapi-bal-task-plugin/src/test/resources/testng.xml b/openapi-bal-task-plugin/src/test/resources/testng.xml new file mode 100644 index 000000000..c01398475 --- /dev/null +++ b/openapi-bal-task-plugin/src/test/resources/testng.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + 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..a9607433e 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 @@ -487,8 +487,7 @@ private List createRemoteFunctions(Paths paths, Filter f if (!filterTags.isEmpty() || !filterOperations.isEmpty()) { // Generate remote function only if it is available in tag filter or operation filter or both if (operationTags != null || ((!filterOperations.isEmpty()) && (operationId != null))) { - if (GeneratorUtils.hasTags(operationTags, filterTags) || - ((operationId != null) && filterOperations.contains(operationId.trim()))) { + if (isaFilteredOperation(filterTags, filterOperations, operationTags, operationId)) { // Generate remote function FunctionDefinitionNode functionDefinitionNode = getClientMethodFunctionDefinitionNode( @@ -508,6 +507,12 @@ private List createRemoteFunctions(Paths paths, Filter f return functionDefinitionNodeList; } + private static boolean isaFilteredOperation(List filterTags, List filterOperations, + List operationTags, String operationId) { + return (operationTags != null && GeneratorUtils.hasTags(operationTags, filterTags)) || + ((operationId != null) && filterOperations.contains(operationId.trim())); + } + /** * Generate function definition node. *
diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/model/OASClientConfig.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/model/OASClientConfig.java
index 5875cc2bb..edc4f9a0f 100644
--- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/model/OASClientConfig.java
+++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/model/OASClientConfig.java
@@ -20,6 +20,8 @@
 import io.ballerina.openapi.core.model.Filter;
 import io.swagger.v3.oas.models.OpenAPI;
 
+import static io.ballerina.openapi.core.GeneratorConstants.DO_NOT_MODIFY_FILE_HEADER;
+
 /**
  * This class stores metadata that related to client code generations.
  *
@@ -31,6 +33,7 @@ public class OASClientConfig {
     private final boolean nullable;
     private final boolean resourceMode;
     private final boolean isPlugin;
+    private final String license;
 
 
     private OASClientConfig(Builder clientConfigBuilder) {
@@ -39,6 +42,7 @@ private OASClientConfig(Builder clientConfigBuilder) {
         this.nullable = clientConfigBuilder.nullable;
         this.isPlugin = clientConfigBuilder.isPlugin;
         this.resourceMode = clientConfigBuilder.resourceMode;
+        this.license = clientConfigBuilder.license;
     }
 
     public OpenAPI getOpenAPI() {
@@ -60,6 +64,9 @@ public boolean isResourceMode() {
     public boolean isPlugin() {
         return isPlugin;
     }
+    public String getLicense() {
+        return license;
+    }
 
     /**
      * Client IDL plugin meta data builder class.
@@ -70,6 +77,7 @@ public static class Builder {
         private boolean nullable = false;
         private boolean resourceMode = true;
         private boolean isPlugin = false;
+        private String license = DO_NOT_MODIFY_FILE_HEADER;
 
         public Builder withOpenAPI(OpenAPI openAPI) {
             this.openAPI = openAPI;
@@ -96,6 +104,11 @@ public Builder withPlugin(boolean isPlugin) {
             return this;
         }
 
+        public Builder withLicense(String license) {
+            this.license = license;
+            return this;
+        }
+
         public OASClientConfig build() {
             return new OASClientConfig(this);
         }
diff --git a/openapi-integration-tests/src/test/java/io/ballerina/openapi/TestUtil.java b/openapi-integration-tests/src/test/java/io/ballerina/openapi/TestUtil.java
index 200c69b9a..8ac620351 100644
--- a/openapi-integration-tests/src/test/java/io/ballerina/openapi/TestUtil.java
+++ b/openapi-integration-tests/src/test/java/io/ballerina/openapi/TestUtil.java
@@ -40,8 +40,8 @@
 import java.util.stream.Stream;
 
 import static io.ballerina.openapi.extension.build.ValidatorTests.WHITESPACE_PATTERN;
-import static io.ballerina.openapi.idl.client.IDLClientGenPluginTests.DISTRIBUTION_FILE_NAME;
-import static io.ballerina.openapi.idl.client.IDLClientGenPluginTests.TEST_RESOURCE;
+import static io.ballerina.openapi.bal.task.client.ClientGenPluginTests.DISTRIBUTION_FILE_NAME;
+import static io.ballerina.openapi.bal.task.client.ClientGenPluginTests.TEST_RESOURCE;
 
 /**
  * This class for storing the test utils for integration tests.
@@ -192,9 +192,9 @@ public static void deleteGeneratedFiles(String generatedFileName) throws IOExcep
     public static File[] getMatchingFiles(String project, List ids) throws IOException, InterruptedException {
         List buildArgs = new LinkedList<>();
         //TODO: Change this function after fixing module name with client declaration alias.
-        Process process = executeRun(DISTRIBUTION_FILE_NAME, TEST_RESOURCE.resolve(project), buildArgs);
-        Assert.assertEquals(process.waitFor(), 0);
-        File dir = new File(RESOURCE.resolve("client-idl-projects/" + project + "/generated/").toString());
+        boolean isExit = executeBuild(DISTRIBUTION_FILE_NAME, TEST_RESOURCE.resolve(project), buildArgs);
+        Assert.assertTrue(isExit);
+        File dir = new File(RESOURCE.resolve("client-bal-task-projects/" + project + "/generated/").toString());
         File[] matchingFiles = dir.listFiles(new FileFilter() {
             public boolean accept(File pathname) {
                 for (String id : ids) {
diff --git a/openapi-integration-tests/src/test/java/io/ballerina/openapi/idl/client/IDLClientGenPluginNegativeTests.java b/openapi-integration-tests/src/test/java/io/ballerina/openapi/bal/task/client/ClientGenPluginNegativeTests.java
similarity index 86%
rename from openapi-integration-tests/src/test/java/io/ballerina/openapi/idl/client/IDLClientGenPluginNegativeTests.java
rename to openapi-integration-tests/src/test/java/io/ballerina/openapi/bal/task/client/ClientGenPluginNegativeTests.java
index f9a1c043a..7c48e7c47 100644
--- a/openapi-integration-tests/src/test/java/io/ballerina/openapi/idl/client/IDLClientGenPluginNegativeTests.java
+++ b/openapi-integration-tests/src/test/java/io/ballerina/openapi/bal/task/client/ClientGenPluginNegativeTests.java
@@ -16,7 +16,7 @@
  * under the License.
  */
 
-package io.ballerina.openapi.idl.client;
+package io.ballerina.openapi.bal.task.client;
 
 import io.ballerina.openapi.OpenAPITest;
 import io.ballerina.openapi.TestUtil;
@@ -43,10 +43,10 @@
 /**
  * Negative tests for OpenAPI client IDL import.
  */
-public class IDLClientGenPluginNegativeTests extends OpenAPITest {
+public class ClientGenPluginNegativeTests extends OpenAPITest {
 
     public static final String DISTRIBUTION_FILE_NAME = DISTRIBUTIONS_DIR.toString();
-    public static final Path TEST_RESOURCE = Paths.get(RESOURCES_PATH.toString() + "/client-idl-projects");
+    public static final Path TEST_RESOURCE = Paths.get(RESOURCES_PATH.toString() + "/client-bal-task-projects");
     @BeforeClass
     public void setupDistributions() throws IOException {
         TestUtil.cleanDistribution();
@@ -56,7 +56,7 @@ public void setupDistributions() throws IOException {
     public void testClientDeclarationInsideFunction() throws IOException, InterruptedException {
         Process process = executeRun(DISTRIBUTION_FILE_NAME, TEST_RESOURCE.resolve("project_04"),
                 new ArrayList<>());
-        File dir = new File(RESOURCE.resolve("client-idl-projects/project_04/generated/").toString());
+        File dir = new File(RESOURCE.resolve("client-bal-task-projects/project_04/generated/").toString());
         Assert.assertFalse(dir.exists());
         String msg = "ERROR [main.bal:(2:5,2:11)] 'client' qualifier not allowed";
         assertOnErrorStream(process, msg);
@@ -66,7 +66,7 @@ public void testClientDeclarationInsideFunction() throws IOException, Interrupte
     public void testNonOpenAPIClientDeclaration() throws IOException, InterruptedException {
         Process process = executeRun(DISTRIBUTION_FILE_NAME, TEST_RESOURCE.resolve("project_07"),
                 new ArrayList<>());
-        File dir = new File(RESOURCE.resolve("client-idl-projects/project_07/generated/").toString());
+        File dir = new File(RESOURCE.resolve("client-bal-task-projects/project_07/generated/").toString());
         Assert.assertFalse(dir.exists());
         String msg = "ERROR [main.bal:(1:1,1:176)] no matching plugin found for client declaration";
         assertOnErrorStream(process, msg);
@@ -76,7 +76,7 @@ public void testNonOpenAPIClientDeclaration() throws IOException, InterruptedExc
     public void testInvalidSwaggerRemotePath() throws IOException, InterruptedException {
         Process process = executeRun(DISTRIBUTION_FILE_NAME, TEST_RESOURCE.resolve("project_10"),
                 new ArrayList<>());
-        File dir = new File(RESOURCE.resolve("client-idl-projects/project_10/generated/").toString());
+        File dir = new File(RESOURCE.resolve("client-bal-task-projects/project_10/generated/").toString());
         Assert.assertFalse(dir.exists());
         String msg = "ERROR [main.bal:(1:1,1:132)] unable to get resource from uri, reason: ";
         assertOnErrorStream(process, msg);
@@ -86,7 +86,7 @@ public void testInvalidSwaggerRemotePath() throws IOException, InterruptedExcept
     public void testInvalidSwaggerLocalPath() throws IOException, InterruptedException {
         Process process = executeRun(DISTRIBUTION_FILE_NAME, TEST_RESOURCE.resolve("project_11"),
                 new ArrayList<>());
-        File dir = new File(RESOURCE.resolve("client-idl-projects/project_11/generated/").toString());
+        File dir = new File(RESOURCE.resolve("client-bal-task-projects/project_11/generated/").toString());
         Assert.assertFalse(dir.exists());
         String msg = "ERROR [main.bal:(1:1,1:31)] could not locate the file:";
         assertOnErrorStream(process, msg);
@@ -96,7 +96,7 @@ public void testInvalidSwaggerLocalPath() throws IOException, InterruptedExcepti
     public void testInvalidSwaggerContract() throws IOException, InterruptedException {
         Process process = executeRun(DISTRIBUTION_FILE_NAME, TEST_RESOURCE.resolve("project_12"),
                 new ArrayList<>());
-        File dir = new File(RESOURCE.resolve("client-idl-projects/project_12/generated/").toString());
+        File dir = new File(RESOURCE.resolve("client-bal-task-projects/project_12/generated/").toString());
         Assert.assertFalse(dir.exists());
         String msg = "ERROR [main.bal:(1:1,1:30)] OpenAPI definition has errors:";
         assertOnErrorStream(process, msg);
@@ -108,7 +108,7 @@ public void testInvalidSwaggerContract() throws IOException, InterruptedExceptio
     public void testUnsupportedSwaggerVersion() throws IOException, InterruptedException {
         Process process = executeRun(DISTRIBUTION_FILE_NAME, TEST_RESOURCE.resolve("project_13"),
                 new ArrayList<>());
-        File dir = new File(RESOURCE.resolve("client-idl-projects/project_13/generated/").toString());
+        File dir = new File(RESOURCE.resolve("client-bal-task-projects/project_13/generated/").toString());
         Assert.assertFalse(dir.exists());
         String msg = "ERROR [main.bal:(2:5,2:119)] provided openAPI contract version is not supported";
         assertOnErrorStream(process, msg);
diff --git a/openapi-integration-tests/src/test/java/io/ballerina/openapi/bal/task/client/ClientGenPluginTests.java b/openapi-integration-tests/src/test/java/io/ballerina/openapi/bal/task/client/ClientGenPluginTests.java
new file mode 100644
index 000000000..b57bb3001
--- /dev/null
+++ b/openapi-integration-tests/src/test/java/io/ballerina/openapi/bal/task/client/ClientGenPluginTests.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2023, 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.bal.task.client;
+
+import io.ballerina.openapi.OpenAPITest;
+import io.ballerina.openapi.TestUtil;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.LinkedList;
+import java.util.List;
+
+import static io.ballerina.openapi.TestUtil.DISTRIBUTIONS_DIR;
+import static io.ballerina.openapi.TestUtil.RESOURCES_PATH;
+import static io.ballerina.openapi.TestUtil.getMatchingFiles;
+
+/**
+ * This class is for client ballerina package integration tests.
+ */
+public class ClientGenPluginTests extends OpenAPITest {
+
+    public static final String DISTRIBUTION_FILE_NAME = DISTRIBUTIONS_DIR.toString();
+    public static final Path TEST_RESOURCE = Paths.get(RESOURCES_PATH.toString() + "/client-bal-task-projects");
+
+    @BeforeClass
+    public void setupDistributions() throws IOException {
+        TestUtil.cleanDistribution();
+    }
+
+    @Test
+    public void testValidSwaggerContract() throws IOException, InterruptedException {
+        List ids = new LinkedList<>();
+        ids.add("delivery01");
+        ids.add("delivery02");
+        ids.add("delivery03");
+        ids.add("delivery04");
+        ids.add("delivery05");
+        File[] matchingFiles = getMatchingFiles("project_01", ids);
+        Assert.assertNotNull(matchingFiles);
+        Assert.assertEquals(matchingFiles.length, 5);
+    }
+}
diff --git a/openapi-integration-tests/src/test/java/io/ballerina/openapi/idl/client/IDLClientGenPluginTests.java b/openapi-integration-tests/src/test/java/io/ballerina/openapi/idl/client/IDLClientGenPluginTests.java
deleted file mode 100644
index aacff40f4..000000000
--- a/openapi-integration-tests/src/test/java/io/ballerina/openapi/idl/client/IDLClientGenPluginTests.java
+++ /dev/null
@@ -1,140 +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.idl.client;
-
-import io.ballerina.openapi.OpenAPITest;
-import io.ballerina.openapi.TestUtil;
-import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static io.ballerina.openapi.TestUtil.DISTRIBUTIONS_DIR;
-import static io.ballerina.openapi.TestUtil.RESOURCE;
-import static io.ballerina.openapi.TestUtil.RESOURCES_PATH;
-import static io.ballerina.openapi.TestUtil.executeRun;
-import static io.ballerina.openapi.TestUtil.getMatchingFiles;
-
-/**
- * This class is for client IDL integration tests.
- */
-public class IDLClientGenPluginTests extends OpenAPITest {
-
-    public static final String DISTRIBUTION_FILE_NAME = DISTRIBUTIONS_DIR.toString();
-    public static final Path TEST_RESOURCE = Paths.get(RESOURCES_PATH.toString() + "/client-idl-projects");
-
-    @BeforeClass
-    public void setupDistributions() throws IOException {
-        TestUtil.cleanDistribution();
-    }
-
-    @Test
-    public void testValidSwaggerContract() throws IOException, InterruptedException {
-        List ids = new LinkedList<>();
-        ids.add("bar");
-        File[] matchingFiles = getMatchingFiles("project_01", ids);
-        Assert.assertNotNull(matchingFiles);
-        Assert.assertEquals(matchingFiles.length, 1);
-    }
-
-    @Test
-    public void testClientDeclarationWithOutAnnotation() throws IOException, InterruptedException {
-        List ids = new LinkedList<>();
-        ids.add("foo");
-        File[] matchingFiles = getMatchingFiles("project_02", ids);
-        Assert.assertNotNull(matchingFiles);
-        Assert.assertEquals(matchingFiles.length, 1);
-    }
-
-    @Test
-    public void testClientDeclarationWithAnnotation() throws IOException, InterruptedException {
-        List ids = new LinkedList<>();
-        ids.add("bar");
-        File[] matchingFiles = getMatchingFiles("project_03", ids);
-        Assert.assertNotNull(matchingFiles);
-        Assert.assertEquals(matchingFiles.length, 1);
-    }
-
-    @Test
-    public void testModuleLevelClientDeclarationNode() throws IOException, InterruptedException {
-        List ids = new LinkedList<>();
-        ids.add("bar");
-        File[] matchingFiles = getMatchingFiles("project_05", ids);
-        Assert.assertNotNull(matchingFiles);
-        Assert.assertEquals(matchingFiles.length, 1);
-    }
-
-    @Test(description = "When multiple client declarations have same annotation")
-    public void testMultipleClientsWithSameAnnotation() throws IOException, InterruptedException {
-        List ids = new LinkedList<>();
-        ids.add("foo");
-        File[] matchingFiles = getMatchingFiles("project_06", ids);
-        Assert.assertNotNull(matchingFiles);
-        Assert.assertEquals(matchingFiles.length, 1);
-    }
-
-    @Test
-    public void testInvokeAPIFromGeneratedClient() throws IOException, InterruptedException {
-        Process successful = executeRun(DISTRIBUTION_FILE_NAME, TEST_RESOURCE.resolve("project_08"),
-                new ArrayList<>());
-        Assert.assertEquals(successful.waitFor(), 0);
-    }
-
-    @Test(description = "test client declarations with local YAML contract paths")
-    public void testWithLocalPathYAML() throws IOException, InterruptedException {
-        List ids = new LinkedList<>();
-        ids.add("bar");
-        File[] matchingFiles = getMatchingFiles("project_09", ids);
-        Assert.assertNotNull(matchingFiles);
-        Assert.assertEquals(matchingFiles.length, 1);
-    }
-
-    @Test(description = "test client declarations with local JSON contract paths")
-    public void testWithLocalPathJSON() throws IOException, InterruptedException {
-        List ids = new LinkedList<>();
-        ids.add("bar");
-        File[] matchingFiles = getMatchingFiles("project_14", ids);
-        Assert.assertNotNull(matchingFiles);
-        Assert.assertEquals(matchingFiles.length, 1);
-    }
-
-    @Test
-    public void testWithClientClassName() throws IOException, InterruptedException {
-        String classContent = "public isolated client class Client {";
-        Process process = executeRun(DISTRIBUTION_FILE_NAME, TEST_RESOURCE.resolve("project_15"), new LinkedList<>());
-        process.waitFor();
-        Path clientFile =
-                Path.of(RESOURCE.resolve("client-idl-projects/project_15/generated/bar/client.bal").toString());
-        Stream expectedServiceLines = Files.lines(clientFile);
-        String generatedContent = expectedServiceLines.collect(Collectors.joining(System.lineSeparator()));
-        expectedServiceLines.close();
-        Assert.assertTrue((generatedContent.trim()).replaceAll("\\s+", "")
-                .contains(classContent.trim().replaceAll("\\s+", "")));
-    }
-}
diff --git a/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_01/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_01/Ballerina.toml
new file mode 100644
index 000000000..2dcba2601
--- /dev/null
+++ b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_01/Ballerina.toml
@@ -0,0 +1,45 @@
+[package]
+org = "hansaninissanka"
+name = "client_bal_task"
+version = "0.1.0"
+distribution = "2201.9.0"
+
+[build-options]
+observabilityIncluded = true
+
+# with input valid yaml
+[[tool.openapi]]
+id = "client"
+filePath = "./openapi.yaml"
+targetModule = "delivery01"
+options.mode = "client"
+
+
+# with input as url
+[[tool.openapi]]
+id = "client02"
+filePath = "openapi.yaml"
+targetModule = "delivery02"
+options.mode = "client"
+
+# without target
+[[tool.openapi]]
+id = "client03"
+filePath = "openapi.yaml"
+targetModule = "delivery03"
+options.mode = "client"
+options.license = "license.txt"
+
+# with license file
+[[tool.openapi]]
+id = "client04"
+filePath = "openapi.yaml"
+targetModule = "delivery04"
+options.mode = "client"
+options.license = "license.txt"
+
+# defualt behaviour
+[[tool.openapi]]
+id = "client05"
+filePath = "openapi.yaml"
+targetModule = "delivery05"
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_02/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_01/license.txt
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_02/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_01/license.txt
diff --git a/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_01/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_01/main.bal
new file mode 100644
index 000000000..6b71ef415
--- /dev/null
+++ b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_01/main.bal
@@ -0,0 +1,2 @@
+public function main() {
+}
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_15/openapi.yaml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_01/openapi.yaml
similarity index 88%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_15/openapi.yaml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_01/openapi.yaml
index f5d6142f8..e67d24d70 100644
--- a/openapi-integration-tests/src/test/resources/client-idl-projects/project_15/openapi.yaml
+++ b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_01/openapi.yaml
@@ -8,8 +8,9 @@ servers:
 paths:
   /users:
     get:
+      operationId: listUsers
       summary: Returns a list of users.
       description: Optional extended description in Markdown.
       responses:
-        '200':
+        200:
           description: OK
diff --git a/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_02/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_02/Ballerina.toml
new file mode 100644
index 000000000..cbfabcbc8
--- /dev/null
+++ b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_02/Ballerina.toml
@@ -0,0 +1,14 @@
+[package]
+org = "openapi_client_bal_task_test"
+name = "project_2"
+version = "0.1.0"
+
+[build-options]
+observabilityIncluded = true
+
+[[tool.openapi]]
+id = "client03"
+filePath = "https://raw.githubusercontent.com/ballerina-platform/openapi-connectors/main/openapi/openweathermap/openapi.yaml"
+targetModule = "delivery"
+options.mode = "client"
+
diff --git a/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_02/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_02/main.bal
new file mode 100644
index 000000000..7dd585277
--- /dev/null
+++ b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_02/main.bal
@@ -0,0 +1,3 @@
+public function main() {
+    foo:client x;
+}
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_03/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_03/Ballerina.toml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_03/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_03/Ballerina.toml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_03/license.txt b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_03/license.txt
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_03/license.txt
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_03/license.txt
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_03/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_03/main.bal
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_03/main.bal
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_03/main.bal
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_04/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_04/Ballerina.toml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_04/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_04/Ballerina.toml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_04/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_04/main.bal
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_04/main.bal
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_04/main.bal
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_05/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_05/Ballerina.toml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_05/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_05/Ballerina.toml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_05/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_05/main.bal
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_05/main.bal
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_05/main.bal
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_06/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_06/Ballerina.toml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_06/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_06/Ballerina.toml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_06/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_06/main.bal
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_06/main.bal
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_06/main.bal
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_07/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_07/Ballerina.toml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_07/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_07/Ballerina.toml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_07/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_07/main.bal
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_07/main.bal
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_07/main.bal
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_08/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_08/Ballerina.toml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_08/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_08/Ballerina.toml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_08/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_08/main.bal
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_08/main.bal
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_08/main.bal
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_01/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_09/Ballerina.toml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_01/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_09/Ballerina.toml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_09/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_09/main.bal
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_09/main.bal
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_09/main.bal
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_01/openapi.yaml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_09/openapi.yaml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_01/openapi.yaml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_09/openapi.yaml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_09/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_10/Ballerina.toml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_09/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_10/Ballerina.toml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_10/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_10/main.bal
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_10/main.bal
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_10/main.bal
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_09/openapi.yaml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_10/openapi.yaml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_09/openapi.yaml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_10/openapi.yaml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_10/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_11/Ballerina.toml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_10/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_11/Ballerina.toml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_11/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_11/main.bal
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_11/main.bal
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_11/main.bal
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_10/openapi.yaml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_11/openapi.yaml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_10/openapi.yaml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_11/openapi.yaml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_11/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_12/Ballerina.toml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_11/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_12/Ballerina.toml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_12/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_12/main.bal
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_12/main.bal
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_12/main.bal
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_12/openapi.yaml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_12/openapi.yaml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_12/openapi.yaml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_12/openapi.yaml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_13/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_13/Ballerina.toml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_13/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_13/Ballerina.toml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_13/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_13/main.bal
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_13/main.bal
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_13/main.bal
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_13/openapi.yaml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_13/openapi.yaml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_13/openapi.yaml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_13/openapi.yaml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_12/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_14/Ballerina.toml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_12/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_14/Ballerina.toml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_14/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_14/main.bal
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_14/main.bal
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_14/main.bal
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_14/openapi.json b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_14/openapi.json
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_14/openapi.json
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_14/openapi.json
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_14/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_15/Ballerina.toml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_14/Ballerina.toml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_15/Ballerina.toml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_15/main.bal b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_15/main.bal
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_15/main.bal
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_15/main.bal
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_11/openapi.yaml b/openapi-integration-tests/src/test/resources/client-bal-task-projects/project_15/openapi.yaml
similarity index 100%
rename from openapi-integration-tests/src/test/resources/client-idl-projects/project_11/openapi.yaml
rename to openapi-integration-tests/src/test/resources/client-bal-task-projects/project_15/openapi.yaml
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_01/main.bal b/openapi-integration-tests/src/test/resources/client-idl-projects/project_01/main.bal
deleted file mode 100644
index d6bb4a209..000000000
--- a/openapi-integration-tests/src/test/resources/client-idl-projects/project_01/main.bal
+++ /dev/null
@@ -1,4 +0,0 @@
-client "https://raw.githubusercontent.com/ballerina-platform/openapi-connectors/main/openapi/openweathermap/openapi.yaml" as bar;
-public function main() {
-    bar:client y;
-}
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_02/main.bal b/openapi-integration-tests/src/test/resources/client-idl-projects/project_02/main.bal
deleted file mode 100644
index ffc7af250..000000000
--- a/openapi-integration-tests/src/test/resources/client-idl-projects/project_02/main.bal
+++ /dev/null
@@ -1,5 +0,0 @@
-client "https://raw.githubusercontent.com/ballerina-platform/openapi-connectors/main/openapi/openweathermap/openapi.yaml" as foo;
-
-public function main() {
-    foo:client x;
-}
diff --git a/openapi-integration-tests/src/test/resources/client-idl-projects/project_15/Ballerina.toml b/openapi-integration-tests/src/test/resources/client-idl-projects/project_15/Ballerina.toml
deleted file mode 100644
index 005e00d52..000000000
--- a/openapi-integration-tests/src/test/resources/client-idl-projects/project_15/Ballerina.toml
+++ /dev/null
@@ -1,8 +0,0 @@
-[package]
-org = "openapi_client_idl_test"
-name = "project_1"
-version = "0.1.0"
-
-[build-options]
-observabilityIncluded = true
-
diff --git a/openapi-integration-tests/src/test/resources/testng.xml b/openapi-integration-tests/src/test/resources/testng.xml
index a04f3eb05..3dd6517ac 100644
--- a/openapi-integration-tests/src/test/resources/testng.xml
+++ b/openapi-integration-tests/src/test/resources/testng.xml
@@ -28,8 +28,8 @@
             
             
             
-
-
+            
+
         
     
 
diff --git a/settings.gradle b/settings.gradle
index 8945be715..ca55016d3 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -26,6 +26,7 @@ include(':openapi-cli')
 include(':openapi-ls-extension')
 include(':openapi-validator')
 include(':openapi-extension')
+include(':openapi-bal-task-plugin')
 include(':openapi-extension-tests')
 include(':openapi-integration-tests')
 //include(':openapi-tests')