diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index a9e79194..83ab0854 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -21,3 +21,9 @@ path = "../native/build/libs/salesforce-native-8.0.1-SNAPSHOT.jar" groupId = "io.ballerinax" artifactId = "salesforce" version = "8.0.1-SNAPSHOT" + +[[platform.java17.dependency]] +groupId = "com.opencsv" +artifactId = "opencsv" +version = "5.9" +path = "./lib/opencsv-5.9.jar" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index a529a010..0ff4b82c 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -5,7 +5,7 @@ [ballerina] dependencies-toml-version = "2" -distribution-version = "2201.8.5" +distribution-version = "2201.8.4" [[package]] org = "ballerina" @@ -61,7 +61,7 @@ dependencies = [ [[package]] org = "ballerina" name = "http" -version = "2.10.12" +version = "2.10.13" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "cache"}, @@ -289,7 +289,7 @@ dependencies = [ [[package]] org = "ballerina" name = "observe" -version = "1.2.2" +version = "1.2.3" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] diff --git a/ballerina/build.gradle b/ballerina/build.gradle index a6cfbdbf..226c5165 100644 --- a/ballerina/build.gradle +++ b/ballerina/build.gradle @@ -54,10 +54,19 @@ task updateTomlFiles { doLast { def newConfig = ballerinaTomlFilePlaceHolder.text.replace('@project.version@', project.version.toString()) newConfig = newConfig.replace('@toml.version@', tomlVersion) + newConfig = newConfig.replace("@opencsv.version@", openCsvVersion) ballerinaTomlFile.text = newConfig } } +configurations { + externalJars +} + +dependencies { + externalJars(group: 'com.opencsv', name: 'opencsv', version: '5.9') +} + task commitTomlFiles { doLast { project.exec { @@ -93,6 +102,7 @@ clean { delete 'build' } +updateTomlFiles.dependsOn copyStdlibs build.dependsOn ":${project.packageName}-native:build" build.dependsOn "generatePomFileForMavenPublication" publishToMavenLocal.dependsOn build diff --git a/ballerina/client.bal b/ballerina/client.bal index 064c9392..9f3b57fc 100644 --- a/ballerina/client.bal +++ b/ballerina/client.bal @@ -641,7 +641,7 @@ public isolated client class Client { if textPayload == "" { return []; } - string[][] result = check convertStringToStringList(textPayload); + string[][] result = check parseCsvString(textPayload); return result; } else { json responsePayload = check response.getJsonPayload(); @@ -663,7 +663,7 @@ public isolated client class Client { if textPayload == "" { return []; } - string[][] result = check convertStringToStringList(textPayload); + string[][] result = check parseCsvString(textPayload); return result; } else { json responsePayload = check response.getJsonPayload(); diff --git a/ballerina/utils.bal b/ballerina/utils.bal index 298d8f9b..370acf22 100644 --- a/ballerina/utils.bal +++ b/ballerina/utils.bal @@ -13,27 +13,26 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. - +import ballerina/io; import ballerina/log; import ballerina/time; -import ballerina/io; +import ballerina/jballerina.java; import ballerina/lang.'string as strings; isolated string csvContent = EMPTY_STRING; # Remove decimal places from a civil seconds value -# +# # + civilTime - a time:civil record # + return - a time:civil record with decimal places removed -# +# isolated function removeDecimalPlaces(time:Civil civilTime) returns time:Civil { time:Civil result = civilTime; - time:Seconds seconds= (result.second is ())? 0 : result.second; + time:Seconds seconds = (result.second is ()) ? 0 : result.second; decimal floor = decimal:floor(seconds); result.second = floor; return result; -} - +} # Convert ReadableByteChannel to string. # @@ -65,7 +64,6 @@ isolated function convertToString(io:ReadableByteChannel rbc) returns string|err return textContent; } - # Convert string[][] to string. # # + stringCsvInput - Multi dimentional array of strings @@ -77,7 +75,7 @@ isolated function convertStringListToString(string[][]|stream if stringCsvInput is string[][] { foreach var row in stringCsvInput { lock { - csvContent += row.reduce(isolated function (string s, string t) returns string { + csvContent += row.reduce(isolated function(string s, string t) returns string { return s.concat(",", t); }, EMPTY_STRING).substring(1) + NEW_LINE; } @@ -85,7 +83,7 @@ isolated function convertStringListToString(string[][]|stream } else { check stringCsvInput.forEach(isolated function(string[] row) { lock { - csvContent += row.reduce(isolated function (string s, string t) returns string { + csvContent += row.reduce(isolated function(string s, string t) returns string { return s.concat(",", t); }, EMPTY_STRING).substring(1) + NEW_LINE; @@ -97,16 +95,7 @@ isolated function convertStringListToString(string[][]|stream } } -isolated function convertStringToStringList(string content) returns string[][]|error { - string[][] result = []; - string[] lines = re `\n`.split(content); - foreach string item in lines { - string processedItem = re `"`.replaceAll(item, EMPTY_STRING); - if item == "" { - continue; - } - string[] row = re `,`.split(processedItem); - result.push(row); - } - return result; -}; +isolated function parseCsvString(string stringContent) returns string[][]|error = @java:Method { + 'class: "io.ballerinax.salesforce.CsvParserUtils", + name: "parseCsvToStringArray" +} external; diff --git a/build-config/resources/Ballerina.toml b/build-config/resources/Ballerina.toml index 6941bb7d..d98e0620 100644 --- a/build-config/resources/Ballerina.toml +++ b/build-config/resources/Ballerina.toml @@ -21,3 +21,9 @@ path = "../native/build/libs/salesforce-native-@project.version@.jar" groupId = "io.ballerinax" artifactId = "salesforce" version = "@project.version@" + +[[platform.java17.dependency]] +groupId = "com.opencsv" +artifactId = "opencsv" +version = "@opencsv.version@" +path = "./lib/opencsv-@opencsv.version@.jar" diff --git a/gradle.properties b/gradle.properties index 70fbcb31..a386faa6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,6 +8,7 @@ shadowJarPluginVersion=8.1.1 downloadPluginVersion=5.4.0 releasePluginVersion=2.8.0 testngVersion=7.6.1 +openCsvVersion=5.9 ballerinaGradlePluginVersion=2.2.3 ballerinaLangVersion=2201.8.4 diff --git a/native/build.gradle b/native/build.gradle index 3895d97b..1362ec5b 100644 --- a/native/build.gradle +++ b/native/build.gradle @@ -33,6 +33,7 @@ dependencies { checkstyle project(":checkstyle") checkstyle "com.puppycrawl.tools:checkstyle:${checkstylePluginVersion}" + implementation "com.opencsv:opencsv:${openCsvVersion}" implementation group: 'org.ballerinalang', name: 'ballerina-runtime', version: "${ballerinaLangVersion}" } diff --git a/native/src/main/java/io/ballerinax/salesforce/CsvParserUtils.java b/native/src/main/java/io/ballerinax/salesforce/CsvParserUtils.java new file mode 100644 index 00000000..0b37ed61 --- /dev/null +++ b/native/src/main/java/io/ballerinax/salesforce/CsvParserUtils.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerinax.salesforce; + +import com.opencsv.CSVReader; +import com.opencsv.exceptions.CsvException; +import io.ballerina.runtime.api.creators.ErrorCreator; +import io.ballerina.runtime.api.creators.TypeCreator; +import io.ballerina.runtime.api.creators.ValueCreator; +import io.ballerina.runtime.api.types.ArrayType; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BArray; +import io.ballerina.runtime.api.values.BString; + +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +/** + * This class holds the utility methods involved with parsing csv content. + * + * @since 8.0.1 + */ +public class CsvParserUtils { + public static Object parseCsvToStringArray(BString csvData) { + try (CSVReader reader = new CSVReader(new StringReader(csvData.getValue()))) { + List records = reader.readAll(); + + // Convert each row in records to BArray + BArray[] bArrayData = new BArray[records.size()]; + for (int i = 0; i < records.size(); i++) { + String[] row = records.get(i); + BArray bArrayRow = StringUtils.fromStringArray(row); + bArrayData[i] = bArrayRow; + } + BArray emptyBArray = StringUtils.fromStringArray(new String[0]); + ArrayType stringArrayType = TypeCreator.createArrayType(emptyBArray.getType()); + return ValueCreator.createArrayValue(bArrayData, stringArrayType); // string[][] + } catch (IOException | CsvException e) { + return ErrorCreator.createError(StringUtils.fromString(e.getMessage())); + } + } +} diff --git a/native/src/main/java/io/ballerinax/salesforce/ReadOperationExecutor.java b/native/src/main/java/io/ballerinax/salesforce/ReadOperationExecutor.java index 60c64968..80e27880 100644 --- a/native/src/main/java/io/ballerinax/salesforce/ReadOperationExecutor.java +++ b/native/src/main/java/io/ballerinax/salesforce/ReadOperationExecutor.java @@ -50,14 +50,14 @@ public static Object getRecord(Environment env, BObject client, BString path, BT return invokeClientMethod(env, client, "processGetRecord", paramFeed); } - public static Object getInvocableActions(Environment env, BObject client, BString subContext, - BTypedesc targetType) { + public static Object getInvocableActions(Environment env, BObject client, BString subContext, + BTypedesc targetType) { Object[] paramFeed = {targetType, true, subContext, true}; return invokeClientMethod(env, client, "processGetInvocableActions", paramFeed); } public static Object invokeActions(Environment env, BObject client, BString subContext, BMap payload, - BTypedesc targetType) { + BTypedesc targetType) { Object[] paramFeed = {targetType, true, subContext, true, payload, true}; return invokeClientMethod(env, client, "processInvokeActions", paramFeed); } @@ -71,14 +71,14 @@ public static Object getRecordById(Environment env, BObject client, BString sobj } public static Object getNamedLayouts(Environment env, BObject client, BString sObject, BString name, - BTypedesc targetType) { + BTypedesc targetType) { Object[] paramFeed = {targetType, true, sObject, true, name, true}; return invokeClientMethod(env, client, "processGetNamedLayouts", paramFeed); } - public static Object apexRestExecute(Environment env, BObject client, BString urlPath, - BString methodType, BMap payload, - BTypedesc targetType) { + public static Object apexRestExecute(Environment env, BObject client, BString urlPath, + BString methodType, BMap payload, + BTypedesc targetType) { Object[] paramFeed = {targetType, true, urlPath, true, methodType, true, payload, true}; return invokeClientMethod(env, client, "processApexExecute", paramFeed); } diff --git a/native/src/main/java/module-info.java b/native/src/main/java/module-info.java index 1587c512..c0928bc8 100644 --- a/native/src/main/java/module-info.java +++ b/native/src/main/java/module-info.java @@ -18,5 +18,6 @@ module io.ballerinax.salesforce { requires io.ballerina.runtime; + requires com.opencsv; exports io.ballerinax.salesforce; }