From 3af97759529675192f409aff838ff24a3dc3a527 Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Fri, 25 Oct 2024 17:28:10 +0530 Subject: [PATCH 01/24] [Automated] Update native jar versions in toml files --- ballerina/Ballerina.toml | 6 +++--- ballerina/CompilerPlugin.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index aefa18f..8a7de28 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerinax" name = "persist.sql" -version = "1.4.0" +version = "1.4.1" authors = ["Ballerina"] keywords = ["persist", "sql", "mysql", "mssql", "sql-server"] repository = "https://github.com/ballerina-platform/module-ballerinax-persist.sql" @@ -15,8 +15,8 @@ graalvmCompatible = true [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" artifactId = "persist.sql-native" -version = "1.4.0" -path = "../native/build/libs/persist.sql-native-1.4.0.jar" +version = "1.4.1" +path = "../native/build/libs/persist.sql-native-1.4.1-SNAPSHOT.jar" [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml index b44f1c9..6bd6fbc 100644 --- a/ballerina/CompilerPlugin.toml +++ b/ballerina/CompilerPlugin.toml @@ -3,7 +3,7 @@ id = "persist.sql-compiler-plugin" class = "io.ballerina.stdlib.persist.sql.compiler.PersistSqlCompilerPlugin" [[dependency]] -path = "../compiler-plugin/build/libs/persist.sql-compiler-plugin-1.4.0.jar" +path = "../compiler-plugin/build/libs/persist.sql-compiler-plugin-1.4.1-SNAPSHOT.jar" [[dependency]] path = "./lib/persist-native-1.4.0.jar" From f86ba30c337ebacdb5e2e07caf7a178bc0d9a3d1 Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Fri, 25 Oct 2024 17:32:23 +0530 Subject: [PATCH 02/24] [Automated] Update native jar versions in toml files --- ballerina/Dependencies.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index a237821..c184bd0 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -66,7 +66,7 @@ dependencies = [ [[package]] org = "ballerina" name = "http" -version = "2.12.0" +version = "2.12.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "auth"}, @@ -241,7 +241,7 @@ modules = [ [[package]] org = "ballerina" name = "mime" -version = "2.10.0" +version = "2.10.1" scope = "testOnly" dependencies = [ {org = "ballerina", name = "io"}, @@ -296,7 +296,7 @@ modules = [ [[package]] org = "ballerina" name = "sql" -version = "1.14.0" +version = "1.14.1" dependencies = [ {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, @@ -437,7 +437,7 @@ modules = [ [[package]] org = "ballerinax" name = "mysql" -version = "1.13.0" +version = "1.13.1" scope = "testOnly" dependencies = [ {org = "ballerina", name = "crypto"}, @@ -462,7 +462,7 @@ modules = [ [[package]] org = "ballerinax" name = "persist.sql" -version = "1.4.0" +version = "1.4.1" dependencies = [ {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "log"}, From fbdc343ce8af66db099135d2708dbcfe212929a5 Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Fri, 25 Oct 2024 17:52:10 +0530 Subject: [PATCH 03/24] [Automated] Update native jar versions in toml files --- ballerina/Dependencies.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index c184bd0..0969bec 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -100,6 +100,9 @@ dependencies = [ {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "lang.value"} ] +modules = [ + {org = "ballerina", packageName = "io", moduleName = "io"} +] [[package]] org = "ballerina" @@ -464,6 +467,7 @@ org = "ballerinax" name = "persist.sql" version = "1.4.1" dependencies = [ + {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "log"}, {org = "ballerina", name = "persist"}, From efc8ea6943d46ce7b4767f39764abd0f7b97664a Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Mon, 28 Oct 2024 13:21:45 +0530 Subject: [PATCH 04/24] Fix the issue when there is no associated enties for the given record --- ballerina/sql_client.bal | 56 ++++++ ballerina/stream_types.bal | 2 + ballerina/tests/api_subscription_types.bal | 73 ++++++++ ballerina/tests/h2-optional-assoc-tests.bal | 118 ++++++++++++ .../tests/h2_api_subscription_client.bal | 173 ++++++++++++++++++ ballerina/tests/init-tests.bal | 44 +++++ .../tests/mssql-optional-assoc-tests.bal | 117 ++++++++++++ .../tests/mssql_api_subscription_client.bal | 163 +++++++++++++++++ .../tests/mysql-optional-assoc-tests.bal | 117 ++++++++++++ .../tests/mysql_api_subscription_client.bal | 162 ++++++++++++++++ .../tests/mysql_hospital_persist_client.bal | 2 +- ballerina/tests/persist/api_subs.bal | 31 ++++ .../tests/postgresql-optional-assoc-tests.bal | 117 ++++++++++++ .../postgresql_api_subscription_client.bal | 163 +++++++++++++++++ ballerina/tests/resources/mysql/init.sql | 18 ++ ballerina/tests/resources/postgresql/init.sql | 18 ++ 16 files changed, 1373 insertions(+), 1 deletion(-) create mode 100644 ballerina/tests/api_subscription_types.bal create mode 100644 ballerina/tests/h2-optional-assoc-tests.bal create mode 100644 ballerina/tests/h2_api_subscription_client.bal create mode 100644 ballerina/tests/mssql-optional-assoc-tests.bal create mode 100644 ballerina/tests/mssql_api_subscription_client.bal create mode 100644 ballerina/tests/mysql-optional-assoc-tests.bal create mode 100644 ballerina/tests/mysql_api_subscription_client.bal create mode 100644 ballerina/tests/persist/api_subs.bal create mode 100644 ballerina/tests/postgresql-optional-assoc-tests.bal create mode 100644 ballerina/tests/postgresql_api_subscription_client.bal diff --git a/ballerina/sql_client.bal b/ballerina/sql_client.bal index 9fc736c..c71ba2b 100644 --- a/ballerina/sql_client.bal +++ b/ballerina/sql_client.bal @@ -229,6 +229,62 @@ public isolated client class SQLClient { } } + # Check whether associated entries exist for the given record. + # + # + 'object - The record to which the retrieved records should be appended + # + fields - The fields to be retrieved + # + include - The relations to be retrieved (SQL `JOINs` to be performed) + # + return - `()` if the operation is performed successfully or a `persist:Error` if the operation fails + public isolated function verifyEntityAssociation(anydata 'object, string[] fields, string[] include) returns persist:Error? { + if !('object is record {}) { + return error("The 'object' parameter should be a record"); + } + + do { + // check the values of included entities are () + foreach string joinKey in self.getJoinFields(include) { + JoinMetadata joinMetadata = self.joinMetadata.get(joinKey); + anydata associatedEntity = 'object.get(joinMetadata.fieldName); + if associatedEntity is record {} { + // check if the fields are empty in the associated record. + map nonEmptyAssocEntity = associatedEntity.filter(value => value != ()); + // If the associated entity has non-empty fields, then the association is already verified. + if (nonEmptyAssocEntity.length() > 0) { + continue; + } + + // check if the associated record values contain the foreign fields, if so, we can skip the query. + boolean hasKeys = true; + foreach string refColumn in joinMetadata.refColumns { + if !associatedEntity.hasKey(refColumn) { + hasKeys = false; + break; + } + } + if (hasKeys) { + 'object[joinMetadata.fieldName] = (); + continue; + } + // construct the query to check whether the associated entries are exists + sql:ParameterizedQuery query = ``; + map whereFilter = check self.getManyRelationWhereFilter('object, joinMetadata); + query = sql:queryConcat( + ` SELECT COUNT(*) AS count`, + ` FROM `, stringToParameterizedQuery(self.escape(joinMetadata.refTable)), + ` WHERE`, check self.getWhereClauses(whereFilter, true) + ); + // execute the query and check the count of the associated entries + int count = check self.dbClient->queryRow(query); + if (count == 0) { + 'object[joinMetadata.fieldName] = (); + } + } + } + } on fail error err { + return error persist:Error(err.message(), err); + } + } + public isolated function getKeyFields() returns string[] { return self.keyFields; } diff --git a/ballerina/stream_types.bal b/ballerina/stream_types.bal index bd09899..cfdd900 100644 --- a/ballerina/stream_types.bal +++ b/ballerina/stream_types.bal @@ -60,6 +60,8 @@ public class PersistSQLStream { return error(value.message()); } check (self.persistClient).getManyRelations(value, self.fields, self.include, self.typeDescriptions); + // verifies the entity association whether associated entity is available in the database. + check (self.persistClient).verifyEntityAssociation(value, self.fields, self.include); string[] keyFields = (self.persistClient).getKeyFields(); foreach string keyField in keyFields { diff --git a/ballerina/tests/api_subscription_types.bal b/ballerina/tests/api_subscription_types.bal new file mode 100644 index 0000000..1ba8862 --- /dev/null +++ b/ballerina/tests/api_subscription_types.bal @@ -0,0 +1,73 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +public type Subscription record {| + readonly string subscriptionId; + string userName; + string apimetadataApiId; + string apimetadataOrgId; +|}; + +public type SubscriptionOptionalized record {| + string subscriptionId?; + string userName?; + string apimetadataApiId?; + string apimetadataOrgId?; +|}; + +public type SubscriptionWithRelations record {| + *SubscriptionOptionalized; + ApiMetadataOptionalized apimetadata?; +|}; + +public type SubscriptionTargetType typedesc; + +public type SubscriptionInsert Subscription; + +public type SubscriptionUpdate record {| + string userName?; + string apimetadataApiId?; + string apimetadataOrgId?; +|}; + +public type ApiMetadata record {| + readonly string apiId; + readonly string orgId; + string apiName; + string metadata; + +|}; + +public type ApiMetadataOptionalized record {| + string apiId?; + string orgId?; + string apiName?; + string metadata?; +|}; + +public type ApiMetadataWithRelations record {| + *ApiMetadataOptionalized; + SubscriptionOptionalized subscription?; +|}; + +public type ApiMetadataTargetType typedesc; + +public type ApiMetadataInsert ApiMetadata; + +public type ApiMetadataUpdate record {| + string apiName?; + string metadata?; +|}; diff --git a/ballerina/tests/h2-optional-assoc-tests.bal b/ballerina/tests/h2-optional-assoc-tests.bal new file mode 100644 index 0000000..8d6d10c --- /dev/null +++ b/ballerina/tests/h2-optional-assoc-tests.bal @@ -0,0 +1,118 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/test; +import ballerina/persist; + +@test:Config { + groups: ["assoc", "h2"] +} +function h2APMNoRelationsTest() returns error? { + H2ApimClient apimClient = check new (); + + [string, string][] metaId = check apimClient->/apimetadata.post([{ + apiId: "123457", + orgId: "wso2", + apiName: "abc", + metadata: "metadata" + }]); + test:assertEquals(metaId, [["123457", "wso2"]]); + + stream streamResult = apimClient->/apimetadata(); + H2ApimWithSubscriptions[] apimResults = check from H2ApimWithSubscriptions apiMetadata in streamResult + select apiMetadata; + + test:assertEquals(apimResults.length(), 1); + test:assertEquals(apimResults[0].subscription, ()); + check apimClient.close(); +} + +@test:Config { + groups: ["assoc", "h2"], + dependsOn: [mssqlAPMNoRelationsTest, mssqlAPMWithRelationsTest] +} +function h2APMWithoutRelationsTest() returns error? { + H2ApimClient apimClient = check new (); + + stream streamResult = apimClient->/apimetadata(); + H2ApimWithoutSubscriptions[] apimResults = check from H2ApimWithoutSubscriptions apiMetadata in streamResult + select apiMetadata; + + test:assertEquals(apimResults.length(), 2); + test:assertEquals(apimResults[0], { + apiId: "123457", + orgId: "wso2", + apiName: "abc", + metadata: "metadata" +}); + check apimClient.close(); +} + +@test:Config { + groups: ["assoc", "h2"], + dependsOn: [h2APMNoRelationsTest] +} +function h2APMWithRelationsTest() returns error? { + H2ApimClient apimClient = check new (); + + [string, string][] metaId = check apimClient->/apimetadata.post([{ + apiId: "123458", + orgId: "wso2", + apiName: "abc", + metadata: "metadata" + }]); + test:assertEquals(metaId, [["123458", "wso2"]]); + + string[] subId = check apimClient->/subscriptions.post([{ + subscriptionId: "123", + userName: "ballerina", + apimetadataApiId: "123458", + apimetadataOrgId: "wso2" + }]); + test:assertEquals(subId, ["123"]); + + stream streamResult = apimClient->/apimetadata(); + H2ApimWithSubscriptions[] apimResults = check from H2ApimWithSubscriptions apiMetadata in streamResult + select apiMetadata; + + test:assertEquals(apimResults.length(), 2); + + test:assertEquals(apimResults[0].subscription, ()); + test:assertEquals(apimResults[1].subscription, { + subscriptionId: "123", + apimetadataApiId: "123458" + }); + check apimClient.close(); +} + +type H2ApimWithSubscriptions record {| + string apiId; + string orgId; + string apiName; + string metadata; + record {| + string subscriptionId; + string apimetadataApiId; + |} subscription?; +|}; + + +type H2ApimWithoutSubscriptions record {| + string apiId; + string orgId; + string apiName; + string metadata; +|}; \ No newline at end of file diff --git a/ballerina/tests/h2_api_subscription_client.bal b/ballerina/tests/h2_api_subscription_client.bal new file mode 100644 index 0000000..c707aa5 --- /dev/null +++ b/ballerina/tests/h2_api_subscription_client.bal @@ -0,0 +1,173 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/jballerina.java; +import ballerina/persist; +import ballerina/sql; +import ballerinax/h2.driver as _; +import ballerinax/java.jdbc; + +const SUBSCRIPTION = "subscriptions"; +const API_METADATA = "apimetadata"; + +public isolated client class H2ApimClient { + *persist:AbstractPersistClient; + + private final jdbc:Client dbClient; + + private final map persistClients; + + private final record {|SQLMetadata...;|} & readonly metadata = { + [SUBSCRIPTION]: { + entityName: "Subscription", + tableName: "Subscription", + fieldMetadata: { + subscriptionId: {columnName: "subscriptionId"}, + userName: {columnName: "userName"}, + apimetadataApiId: {columnName: "apimetadataApiId"}, + apimetadataOrgId: {columnName: "apimetadataOrgId"}, + "apimetadata.apiId": {relation: {entityName: "apimetadata", refField: "apiId"}}, + "apimetadata.orgId": {relation: {entityName: "apimetadata", refField: "orgId"}}, + "apimetadata.apiName": {relation: {entityName: "apimetadata", refField: "apiName"}}, + "apimetadata.metadata": {relation: {entityName: "apimetadata", refField: "metadata"}} + }, + keyFields: ["subscriptionId"], + joinMetadata: {apimetadata: {entity: ApiMetadata, fieldName: "apimetadata", refTable: "ApiMetadata", refColumns: ["apiId", "orgId"], joinColumns: ["apimetadataApiId", "apimetadataOrgId"], 'type: ONE_TO_ONE}} + }, + [API_METADATA]: { + entityName: "ApiMetadata", + tableName: "ApiMetadata", + fieldMetadata: { + apiId: {columnName: "apiId"}, + orgId: {columnName: "orgId"}, + apiName: {columnName: "apiName"}, + metadata: {columnName: "metadata"}, + "subscription.subscriptionId": {relation: {entityName: "subscription", refField: "subscriptionId"}}, + "subscription.userName": {relation: {entityName: "subscription", refField: "userName"}}, + "subscription.apimetadataApiId": {relation: {entityName: "subscription", refField: "apimetadataApiId"}}, + "subscription.apimetadataOrgId": {relation: {entityName: "subscription", refField: "apimetadataOrgId"}} + }, + keyFields: ["apiId", "orgId"], + joinMetadata: {subscription: {entity: Subscription, fieldName: "subscription", refTable: "Subscription", refColumns: ["apimetadataApiId", "apimetadataOrgId"], joinColumns: ["apiId", "orgId"], 'type: ONE_TO_ONE}} + } + }; + + public isolated function init() returns persist:Error? { + jdbc:Client|error dbClient = new (url = h2.url, user = h2.user, password = h2.password, options = {...h2.connectionOptions}); + if dbClient is error { + return error(dbClient.message()); + } + self.dbClient = dbClient; + self.persistClients = { + [SUBSCRIPTION]: check new (dbClient, self.metadata.get(SUBSCRIPTION), H2_SPECIFICS), + [API_METADATA]: check new (dbClient, self.metadata.get(API_METADATA), H2_SPECIFICS) + }; + } + + isolated resource function get subscriptions(SubscriptionTargetType targetType = <>, sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``, sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.H2Processor", + name: "query" + } external; + + isolated resource function get subscriptions/[string subscriptionId](SubscriptionTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.H2Processor", + name: "queryOne" + } external; + + isolated resource function post subscriptions(SubscriptionInsert[] data) returns string[]|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(SUBSCRIPTION); + } + _ = check sqlClient.runBatchInsertQuery(data); + return from SubscriptionInsert inserted in data + select inserted.subscriptionId; + } + + isolated resource function put subscriptions/[string subscriptionId](SubscriptionUpdate value) returns Subscription|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(SUBSCRIPTION); + } + _ = check sqlClient.runUpdateQuery(subscriptionId, value); + return self->/subscriptions/[subscriptionId].get(); + } + + isolated resource function delete subscriptions/[string subscriptionId]() returns Subscription|persist:Error { + Subscription result = check self->/subscriptions/[subscriptionId].get(); + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(SUBSCRIPTION); + } + _ = check sqlClient.runDeleteQuery(subscriptionId); + return result; + } + + isolated resource function get apimetadata(ApiMetadataTargetType targetType = <>, sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``, sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.H2Processor", + name: "query" + } external; + + isolated resource function get apimetadata/[string apiId]/[string orgId](ApiMetadataTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.H2Processor", + name: "queryOne" + } external; + + isolated resource function post apimetadata(ApiMetadataInsert[] data) returns [string, string][]|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(API_METADATA); + } + _ = check sqlClient.runBatchInsertQuery(data); + return from ApiMetadataInsert inserted in data + select [inserted.apiId, inserted.orgId]; + } + + isolated resource function put apimetadata/[string apiId]/[string orgId](ApiMetadataUpdate value) returns ApiMetadata|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(API_METADATA); + } + _ = check sqlClient.runUpdateQuery({"apiId": apiId, "orgId": orgId}, value); + return self->/apimetadata/[apiId]/[orgId].get(); + } + + isolated resource function delete apimetadata/[string apiId]/[string orgId]() returns ApiMetadata|persist:Error { + ApiMetadata result = check self->/apimetadata/[apiId]/[orgId].get(); + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(API_METADATA); + } + _ = check sqlClient.runDeleteQuery({"apiId": apiId, "orgId": orgId}); + return result; + } + + remote isolated function queryNativeSQL(sql:ParameterizedQuery sqlQuery, typedesc rowType = <>) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.H2Processor" + } external; + + remote isolated function executeNativeSQL(sql:ParameterizedQuery sqlQuery) returns ExecutionResult|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.H2Processor" + } external; + + public isolated function close() returns persist:Error? { + error? result = self.dbClient.close(); + if result is error { + return error(result.message()); + } + return result; + } +} diff --git a/ballerina/tests/init-tests.bal b/ballerina/tests/init-tests.bal index 1c93395..cc769c7 100644 --- a/ballerina/tests/init-tests.bal +++ b/ballerina/tests/init-tests.bal @@ -86,6 +86,8 @@ function initMySqlTests() returns error? { _ = check mysqlDbClient->execute(`TRUNCATE Doctor`); _ = check mysqlDbClient->execute(`TRUNCATE appointment`); _ = check mysqlDbClient->execute(`TRUNCATE patients`); + _ = check mysqlDbClient->execute(`TRUNCATE Subscription`); + _ = check mysqlDbClient->execute(`TRUNCATE ApiMetadata`); _ = check mysqlDbClient->execute(`SET FOREIGN_KEY_CHECKS = 1`); check mysqlDbClient.close(); } @@ -275,6 +277,26 @@ function initMsSqlTests() returns error? { PRIMARY KEY([id]) ); `); + _ = check mssqlDbClient->execute(` + CREATE TABLE [ApiMetadata] ( + [apiId] VARCHAR(191) NOT NULL, + [orgId] VARCHAR(191) NOT NULL, + [apiName] VARCHAR(191) NOT NULL, + [metadata] VARCHAR(191) NOT NULL, + PRIMARY KEY([apiId],[orgId]) + ); + `); + _ = check mssqlDbClient->execute(` + CREATE TABLE [Subscription] ( + [subscriptionId] VARCHAR(191) NOT NULL, + [userName] VARCHAR(191) NOT NULL, + [apimetadataApiId] VARCHAR(191) NOT NULL, + [apimetadataOrgId] VARCHAR(191) NOT NULL, + UNIQUE ([apimetadataApiId], [apimetadataOrgId]), + FOREIGN KEY([apimetadataApiId], [apimetadataOrgId]) REFERENCES [ApiMetadata]([apiId], [orgId]), + PRIMARY KEY([subscriptionId]) + ); + `); } function initPostgreSqlTests() returns error? { @@ -292,6 +314,8 @@ function initPostgreSqlTests() returns error? { _ = check postgresqlDbClient->execute(`TRUNCATE "IntIdRecord" CASCADE`); _ = check postgresqlDbClient->execute(`TRUNCATE "AllTypesIdRecord" CASCADE`); _ = check postgresqlDbClient->execute(`TRUNCATE "CompositeAssociationRecord" CASCADE`); + _ = check postgresqlDbClient->execute(`TRUNCATE "Subscription" CASCADE`); + _ = check postgresqlDbClient->execute(`TRUNCATE "ApiMetadata" CASCADE`); check postgresqlDbClient.close(); } @@ -492,6 +516,26 @@ function initH2Tests() returns error? { PRIMARY KEY("id") )`); + _ = check h2DbClient->execute(`DROP TABLE IF EXISTS "Subscription"`); + _ = check h2DbClient->execute(`DROP TABLE IF EXISTS "ApiMetadata"`); + _ = check h2DbClient->execute(`CREATE TABLE "ApiMetadata" ( + "apiId" VARCHAR(191) NOT NULL, + "orgId" VARCHAR(191) NOT NULL, + "apiName" VARCHAR(191) NOT NULL, + "metadata" VARCHAR(191) NOT NULL, + PRIMARY KEY("apiId","orgId") + )`); + + _ = check h2DbClient->execute(`CREATE TABLE "Subscription" ( + "subscriptionId" VARCHAR(191) NOT NULL, + "userName" VARCHAR(191) NOT NULL, + "apimetadataApiId" VARCHAR(191) NOT NULL, + "apimetadataOrgId" VARCHAR(191) NOT NULL, + UNIQUE ("apimetadataApiId", "apimetadataOrgId"), + FOREIGN KEY("apimetadataApiId", "apimetadataOrgId") REFERENCES "ApiMetadata"("apiId", "orgId"), + PRIMARY KEY("subscriptionId") + )`); + check h2DbClient.close(); } diff --git a/ballerina/tests/mssql-optional-assoc-tests.bal b/ballerina/tests/mssql-optional-assoc-tests.bal new file mode 100644 index 0000000..ee6ba03 --- /dev/null +++ b/ballerina/tests/mssql-optional-assoc-tests.bal @@ -0,0 +1,117 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/test; +import ballerina/persist; + +@test:Config { + groups: ["assoc", "mssql"] +} +function mssqlAPMNoRelationsTest() returns error? { + MsSqlApimClient apimClient = check new (); + + [string, string][] metaId = check apimClient->/apimetadata.post([{ + apiId: "123457", + orgId: "wso2", + apiName: "abc", + metadata: "metadata" + }]); + test:assertEquals(metaId, [["123457", "wso2"]]); + + stream streamResult = apimClient->/apimetadata(); + MsSqlAPIMWithSubscriptions[] apimResults = check from MsSqlAPIMWithSubscriptions apiMetadata in streamResult + select apiMetadata; + + test:assertEquals(apimResults.length(), 1); + test:assertEquals(apimResults[0].subscription, ()); + check apimClient.close(); +} + +@test:Config { + groups: ["assoc", "mssql"], + dependsOn: [mssqlAPMNoRelationsTest, mssqlAPMWithRelationsTest] +} +function mssqlAPMWithoutRelationsTest() returns error? { + MsSqlApimClient apimClient = check new (); + + stream streamResult = apimClient->/apimetadata(); + MsSqlApimWithoutSubscriptions[] apimResults = check from MsSqlApimWithoutSubscriptions apiMetadata in streamResult + select apiMetadata; + + test:assertEquals(apimResults.length(), 2); + test:assertEquals(apimResults[0], { + apiId: "123457", + orgId: "wso2", + apiName: "abc", + metadata: "metadata" +}); + check apimClient.close(); +} + +@test:Config { + groups: ["assoc", "mssql"], + dependsOn: [mssqlAPMNoRelationsTest] +} +function mssqlAPMWithRelationsTest() returns error? { + MsSqlApimClient apimClient = check new (); + + [string, string][] metaId = check apimClient->/apimetadata.post([{ + apiId: "123458", + orgId: "wso2", + apiName: "abc", + metadata: "metadata" + }]); + test:assertEquals(metaId, [["123458", "wso2"]]); + + string[] subId = check apimClient->/subscriptions.post([{ + subscriptionId: "123", + userName: "ballerina", + apimetadataApiId: "123458", + apimetadataOrgId: "wso2" + }]); + test:assertEquals(subId, ["123"]); + + stream streamResult = apimClient->/apimetadata(); + MsSqlAPIMWithSubscriptions[] apimResults = check from MsSqlAPIMWithSubscriptions apiMetadata in streamResult + select apiMetadata; + + test:assertEquals(apimResults.length(), 2); + + test:assertEquals(apimResults[0].subscription, ()); + test:assertEquals(apimResults[1].subscription, { + subscriptionId: "123", + apimetadataApiId: "123458" + }); + check apimClient.close(); +} + +type MsSqlAPIMWithSubscriptions record {| + string apiId; + string orgId; + string apiName; + string metadata; + record {| + string subscriptionId; + string apimetadataApiId; + |} subscription?; +|}; + +type MsSqlApimWithoutSubscriptions record {| + string apiId; + string orgId; + string apiName; + string metadata; +|}; \ No newline at end of file diff --git a/ballerina/tests/mssql_api_subscription_client.bal b/ballerina/tests/mssql_api_subscription_client.bal new file mode 100644 index 0000000..2841932 --- /dev/null +++ b/ballerina/tests/mssql_api_subscription_client.bal @@ -0,0 +1,163 @@ +// AUTO-GENERATED FILE. DO NOT MODIFY. + +// This file is an auto-generated file by Ballerina persistence layer for model. +// It should not be modified by hand. + +import ballerina/jballerina.java; +import ballerina/persist; +import ballerina/sql; +import ballerinax/mssql; +import ballerinax/mssql.driver as _; + +const SUBSCRIPTION = "subscriptions"; +const API_METADATA = "apimetadata"; + +public isolated client class MsSqlApimClient { + *persist:AbstractPersistClient; + + private final mssql:Client dbClient; + + private final map persistClients; + + private final record {|SQLMetadata...;|} & readonly metadata = { + [SUBSCRIPTION]: { + entityName: "Subscription", + tableName: "Subscription", + fieldMetadata: { + subscriptionId: {columnName: "subscriptionId"}, + userName: {columnName: "userName"}, + apimetadataApiId: {columnName: "apimetadataApiId"}, + apimetadataOrgId: {columnName: "apimetadataOrgId"}, + "apimetadata.apiId": {relation: {entityName: "apimetadata", refField: "apiId"}}, + "apimetadata.orgId": {relation: {entityName: "apimetadata", refField: "orgId"}}, + "apimetadata.apiName": {relation: {entityName: "apimetadata", refField: "apiName"}}, + "apimetadata.metadata": {relation: {entityName: "apimetadata", refField: "metadata"}} + }, + keyFields: ["subscriptionId"], + joinMetadata: {apimetadata: {entity: ApiMetadata, fieldName: "apimetadata", refTable: "ApiMetadata", refColumns: ["apiId", "orgId"], joinColumns: ["apimetadataApiId", "apimetadataOrgId"], 'type: ONE_TO_ONE}} + }, + [API_METADATA]: { + entityName: "ApiMetadata", + tableName: "ApiMetadata", + fieldMetadata: { + apiId: {columnName: "apiId"}, + orgId: {columnName: "orgId"}, + apiName: {columnName: "apiName"}, + metadata: {columnName: "metadata"}, + "subscription.subscriptionId": {relation: {entityName: "subscription", refField: "subscriptionId"}}, + "subscription.userName": {relation: {entityName: "subscription", refField: "userName"}}, + "subscription.apimetadataApiId": {relation: {entityName: "subscription", refField: "apimetadataApiId"}}, + "subscription.apimetadataOrgId": {relation: {entityName: "subscription", refField: "apimetadataOrgId"}} + }, + keyFields: ["apiId", "orgId"], + joinMetadata: {subscription: {entity: Subscription, fieldName: "subscription", refTable: "Subscription", refColumns: ["apimetadataApiId", "apimetadataOrgId"], joinColumns: ["apiId", "orgId"], 'type: ONE_TO_ONE}} + } + }; + + public isolated function init() returns persist:Error? { + mssql:Client|error dbClient = new (host = mssql.host, user = mssql.user, password = mssql.password, database = mssql.database, port = mssql.port); + if dbClient is error { + return error(dbClient.message()); + } + self.dbClient = dbClient; + self.persistClients = { + [SUBSCRIPTION]: check new (dbClient, self.metadata.get(SUBSCRIPTION), MSSQL_SPECIFICS), + [API_METADATA]: check new (dbClient, self.metadata.get(API_METADATA), MSSQL_SPECIFICS) + }; + } + + isolated resource function get subscriptions(SubscriptionTargetType targetType = <>, sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``, sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MSSQLProcessor", + name: "query" + } external; + + isolated resource function get subscriptions/[string subscriptionId](SubscriptionTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MSSQLProcessor", + name: "queryOne" + } external; + + isolated resource function post subscriptions(SubscriptionInsert[] data) returns string[]|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(SUBSCRIPTION); + } + _ = check sqlClient.runBatchInsertQuery(data); + return from SubscriptionInsert inserted in data + select inserted.subscriptionId; + } + + isolated resource function put subscriptions/[string subscriptionId](SubscriptionUpdate value) returns Subscription|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(SUBSCRIPTION); + } + _ = check sqlClient.runUpdateQuery(subscriptionId, value); + return self->/subscriptions/[subscriptionId].get(); + } + + isolated resource function delete subscriptions/[string subscriptionId]() returns Subscription|persist:Error { + Subscription result = check self->/subscriptions/[subscriptionId].get(); + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(SUBSCRIPTION); + } + _ = check sqlClient.runDeleteQuery(subscriptionId); + return result; + } + + isolated resource function get apimetadata(ApiMetadataTargetType targetType = <>, sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``, sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MSSQLProcessor", + name: "query" + } external; + + isolated resource function get apimetadata/[string apiId]/[string orgId](ApiMetadataTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MSSQLProcessor", + name: "queryOne" + } external; + + isolated resource function post apimetadata(ApiMetadataInsert[] data) returns [string, string][]|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(API_METADATA); + } + _ = check sqlClient.runBatchInsertQuery(data); + return from ApiMetadataInsert inserted in data + select [inserted.apiId, inserted.orgId]; + } + + isolated resource function put apimetadata/[string apiId]/[string orgId](ApiMetadataUpdate value) returns ApiMetadata|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(API_METADATA); + } + _ = check sqlClient.runUpdateQuery({"apiId": apiId, "orgId": orgId}, value); + return self->/apimetadata/[apiId]/[orgId].get(); + } + + isolated resource function delete apimetadata/[string apiId]/[string orgId]() returns ApiMetadata|persist:Error { + ApiMetadata result = check self->/apimetadata/[apiId]/[orgId].get(); + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(API_METADATA); + } + _ = check sqlClient.runDeleteQuery({"apiId": apiId, "orgId": orgId}); + return result; + } + + remote isolated function queryNativeSQL(sql:ParameterizedQuery sqlQuery, typedesc rowType = <>) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MSSQLProcessor" + } external; + + remote isolated function executeNativeSQL(sql:ParameterizedQuery sqlQuery) returns ExecutionResult|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MSSQLProcessor" + } external; + + public isolated function close() returns persist:Error? { + error? result = self.dbClient.close(); + if result is error { + return error(result.message()); + } + return result; + } +} + diff --git a/ballerina/tests/mysql-optional-assoc-tests.bal b/ballerina/tests/mysql-optional-assoc-tests.bal new file mode 100644 index 0000000..565e2f4 --- /dev/null +++ b/ballerina/tests/mysql-optional-assoc-tests.bal @@ -0,0 +1,117 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/test; +import ballerina/persist; + +@test:Config { + groups: ["assoc", "mysql"] +} +function MySqlAPMNoRelationsTest() returns error? { + MySqlApimClient apimClient = check new (); + + [string, string][] metaId = check apimClient->/apimetadata.post([{ + apiId: "123457", + orgId: "wso2", + apiName: "abc", + metadata: "metadata" + }]); + test:assertEquals(metaId, [["123457", "wso2"]]); + + stream streamResult = apimClient->/apimetadata(); + MySqlAPIMWithSubscriptions[] apimResults = check from MySqlAPIMWithSubscriptions apiMetadata in streamResult + select apiMetadata; + + test:assertEquals(apimResults.length(), 1); + test:assertEquals(apimResults[0].subscription, ()); + check apimClient.close(); +} + +@test:Config { + groups: ["assoc", "mysql"], + dependsOn: [MySqlAPMNoRelationsTest, MySqlAPMWithRelationsTest] +} +function MySqlAPMWithoutRelationsTest() returns error? { + MySqlApimClient apimClient = check new (); + + stream streamResult = apimClient->/apimetadata(); + MySqlApimWithoutSubscriptions[] apimResults = check from MySqlApimWithoutSubscriptions apiMetadata in streamResult + select apiMetadata; + + test:assertEquals(apimResults.length(), 2); + test:assertEquals(apimResults[0], { + apiId: "123457", + orgId: "wso2", + apiName: "abc", + metadata: "metadata" +}); + check apimClient.close(); +} + +@test:Config { + groups: ["assoc", "mysql"], + dependsOn: [MySqlAPMNoRelationsTest] +} +function MySqlAPMWithRelationsTest() returns error? { + MySqlApimClient apimClient = check new (); + + [string, string][] metaId = check apimClient->/apimetadata.post([{ + apiId: "123458", + orgId: "wso2", + apiName: "abc", + metadata: "metadata" + }]); + test:assertEquals(metaId, [["123458", "wso2"]]); + + string[] subId = check apimClient->/subscriptions.post([{ + subscriptionId: "123", + userName: "ballerina", + apimetadataApiId: "123458", + apimetadataOrgId: "wso2" + }]); + test:assertEquals(subId, ["123"]); + + stream streamResult = apimClient->/apimetadata(); + MySqlAPIMWithSubscriptions[] apimResults = check from MySqlAPIMWithSubscriptions apiMetadata in streamResult + select apiMetadata; + + test:assertEquals(apimResults.length(), 2); + + + test:assertEquals(apimResults[1].subscription, { + subscriptionId: "123", + apimetadataApiId: "123458" + }); + check apimClient.close(); +} + +type MySqlAPIMWithSubscriptions record {| + string apiId; + string orgId; + string apiName; + string metadata; + record {| + string subscriptionId; + string apimetadataApiId; + |} subscription?; +|}; + +type MySqlApimWithoutSubscriptions record {| + string apiId; + string orgId; + string apiName; + string metadata; +|}; \ No newline at end of file diff --git a/ballerina/tests/mysql_api_subscription_client.bal b/ballerina/tests/mysql_api_subscription_client.bal new file mode 100644 index 0000000..3f5af2e --- /dev/null +++ b/ballerina/tests/mysql_api_subscription_client.bal @@ -0,0 +1,162 @@ +// AUTO-GENERATED FILE. DO NOT MODIFY. + +// This file is an auto-generated file by Ballerina persistence layer for model. +// It should not be modified by hand. + +import ballerina/jballerina.java; +import ballerina/persist; +import ballerina/sql; +import ballerinax/mysql; +import ballerinax/mysql.driver as _; + +const SUBSCRIPTION = "subscriptions"; +const API_METADATA = "apimetadata"; + +public isolated client class MySqlApimClient { + *persist:AbstractPersistClient; + + private final mysql:Client dbClient; + + private final map persistClients; + + private final record {|SQLMetadata...;|} & readonly metadata = { + [SUBSCRIPTION]: { + entityName: "Subscription", + tableName: "Subscription", + fieldMetadata: { + subscriptionId: {columnName: "subscriptionId"}, + userName: {columnName: "userName"}, + apimetadataApiId: {columnName: "apimetadataApiId"}, + apimetadataOrgId: {columnName: "apimetadataOrgId"}, + "apimetadata.apiId": {relation: {entityName: "apimetadata", refField: "apiId"}}, + "apimetadata.orgId": {relation: {entityName: "apimetadata", refField: "orgId"}}, + "apimetadata.apiName": {relation: {entityName: "apimetadata", refField: "apiName"}}, + "apimetadata.metadata": {relation: {entityName: "apimetadata", refField: "metadata"}} + }, + keyFields: ["subscriptionId"], + joinMetadata: {apimetadata: {entity: ApiMetadata, fieldName: "apimetadata", refTable: "ApiMetadata", refColumns: ["apiId", "orgId"], joinColumns: ["apimetadataApiId", "apimetadataOrgId"], 'type: ONE_TO_ONE}} + }, + [API_METADATA]: { + entityName: "ApiMetadata", + tableName: "ApiMetadata", + fieldMetadata: { + apiId: {columnName: "apiId"}, + orgId: {columnName: "orgId"}, + apiName: {columnName: "apiName"}, + metadata: {columnName: "metadata"}, + "subscription.subscriptionId": {relation: {entityName: "subscription", refField: "subscriptionId"}}, + "subscription.userName": {relation: {entityName: "subscription", refField: "userName"}}, + "subscription.apimetadataApiId": {relation: {entityName: "subscription", refField: "apimetadataApiId"}}, + "subscription.apimetadataOrgId": {relation: {entityName: "subscription", refField: "apimetadataOrgId"}} + }, + keyFields: ["apiId", "orgId"], + joinMetadata: {subscription: {entity: Subscription, fieldName: "subscription", refTable: "Subscription", refColumns: ["apimetadataApiId", "apimetadataOrgId"], joinColumns: ["apiId", "orgId"], 'type: ONE_TO_ONE}} + } + }; + + public isolated function init() returns persist:Error? { + mysql:Client|error dbClient = new (host = mysql.host, user = mysql.user, password = mysql.password, database = mysql.database, port = mysql.port); + if dbClient is error { + return error(dbClient.message()); + } + self.dbClient = dbClient; + self.persistClients = { + [SUBSCRIPTION]: check new (dbClient, self.metadata.get(SUBSCRIPTION), MYSQL_SPECIFICS), + [API_METADATA]: check new (dbClient, self.metadata.get(API_METADATA), MYSQL_SPECIFICS) + }; + } + + isolated resource function get subscriptions(SubscriptionTargetType targetType = <>, sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``, sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor", + name: "query" + } external; + + isolated resource function get subscriptions/[string subscriptionId](SubscriptionTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor", + name: "queryOne" + } external; + + isolated resource function post subscriptions(SubscriptionInsert[] data) returns string[]|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(SUBSCRIPTION); + } + _ = check sqlClient.runBatchInsertQuery(data); + return from SubscriptionInsert inserted in data + select inserted.subscriptionId; + } + + isolated resource function put subscriptions/[string subscriptionId](SubscriptionUpdate value) returns Subscription|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(SUBSCRIPTION); + } + _ = check sqlClient.runUpdateQuery(subscriptionId, value); + return self->/subscriptions/[subscriptionId].get(); + } + + isolated resource function delete subscriptions/[string subscriptionId]() returns Subscription|persist:Error { + Subscription result = check self->/subscriptions/[subscriptionId].get(); + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(SUBSCRIPTION); + } + _ = check sqlClient.runDeleteQuery(subscriptionId); + return result; + } + + isolated resource function get apimetadata(ApiMetadataTargetType targetType = <>, sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``, sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor", + name: "query" + } external; + + isolated resource function get apimetadata/[string apiId]/[string orgId](ApiMetadataTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor", + name: "queryOne" + } external; + + isolated resource function post apimetadata(ApiMetadataInsert[] data) returns [string, string][]|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(API_METADATA); + } + _ = check sqlClient.runBatchInsertQuery(data); + return from ApiMetadataInsert inserted in data + select [inserted.apiId, inserted.orgId]; + } + + isolated resource function put apimetadata/[string apiId]/[string orgId](ApiMetadataUpdate value) returns ApiMetadata|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(API_METADATA); + } + _ = check sqlClient.runUpdateQuery({"apiId": apiId, "orgId": orgId}, value); + return self->/apimetadata/[apiId]/[orgId].get(); + } + + isolated resource function delete apimetadata/[string apiId]/[string orgId]() returns ApiMetadata|persist:Error { + ApiMetadata result = check self->/apimetadata/[apiId]/[orgId].get(); + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(API_METADATA); + } + _ = check sqlClient.runDeleteQuery({"apiId": apiId, "orgId": orgId}); + return result; + } + + remote isolated function queryNativeSQL(sql:ParameterizedQuery sqlQuery, typedesc rowType = <>) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor" + } external; + + remote isolated function executeNativeSQL(sql:ParameterizedQuery sqlQuery) returns ExecutionResult|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.MySQLProcessor" + } external; + + public isolated function close() returns persist:Error? { + error? result = self.dbClient.close(); + if result is error { + return error(result.message()); + } + return result; + } +} diff --git a/ballerina/tests/mysql_hospital_persist_client.bal b/ballerina/tests/mysql_hospital_persist_client.bal index 9e0f80c..45f2bcb 100644 --- a/ballerina/tests/mysql_hospital_persist_client.bal +++ b/ballerina/tests/mysql_hospital_persist_client.bal @@ -105,7 +105,7 @@ public isolated client class MySqlHospitalClient { }; public isolated function init() returns persist:Error? { - mysql:Client|error dbClient = new (host = "localhost", user = "root", password = "Test123#", database = "test", port = 3305); + mysql:Client|error dbClient = new (host = mysql.host, user = mysql.user, password = mysql.password, database = mysql.database, port = mysql.port); if dbClient is error { return error(dbClient.message()); } diff --git a/ballerina/tests/persist/api_subs.bal b/ballerina/tests/persist/api_subs.bal new file mode 100644 index 0000000..246b98c --- /dev/null +++ b/ballerina/tests/persist/api_subs.bal @@ -0,0 +1,31 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/persist as _; + +public type Subscription record {| + readonly string subscriptionId; + string userName; + ApiMetadata apimetadata; +|}; + +public type ApiMetadata record {| + readonly string apiId; + readonly string orgId; + string apiName; + string metadata; + Subscription? subscription; +|}; \ No newline at end of file diff --git a/ballerina/tests/postgresql-optional-assoc-tests.bal b/ballerina/tests/postgresql-optional-assoc-tests.bal new file mode 100644 index 0000000..8a0a443 --- /dev/null +++ b/ballerina/tests/postgresql-optional-assoc-tests.bal @@ -0,0 +1,117 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/test; +import ballerina/persist; + +@test:Config { + groups: ["assoc", "postgresql"] +} +function PostgreSqlAPMNoRelationsTest() returns error? { + PostgreSqlApimClient apimClient = check new (); + + [string, string][] metaId = check apimClient->/apimetadata.post([{ + apiId: "123457", + orgId: "wso2", + apiName: "abc", + metadata: "metadata" + }]); + test:assertEquals(metaId, [["123457", "wso2"]]); + + stream streamResult = apimClient->/apimetadata(); + PostgreSqlAPIMWithSubscriptions[] apimResults = check from PostgreSqlAPIMWithSubscriptions apiMetadata in streamResult + select apiMetadata; + + test:assertEquals(apimResults.length(), 1); + test:assertEquals(apimResults[0].subscription, ()); + check apimClient.close(); +} + +@test:Config { + groups: ["assoc", "postgresql"], + dependsOn: [PostgreSqlAPMNoRelationsTest, PostgreSqlAPMWithRelationsTest] +} +function PostgreSqlAPMWithoutRelationsTest() returns error? { + PostgreSqlApimClient apimClient = check new (); + + stream streamResult = apimClient->/apimetadata(); + PostgreSqlApimWithoutSubscriptions[] apimResults = check from PostgreSqlApimWithoutSubscriptions apiMetadata in streamResult + select apiMetadata; + + test:assertEquals(apimResults.length(), 2); + test:assertEquals(apimResults[0], { + apiId: "123457", + orgId: "wso2", + apiName: "abc", + metadata: "metadata" +}); + check apimClient.close(); +} + +@test:Config { + groups: ["assoc", "postgresql"], + dependsOn: [PostgreSqlAPMNoRelationsTest] +} +function PostgreSqlAPMWithRelationsTest() returns error? { + PostgreSqlApimClient apimClient = check new (); + + [string, string][] metaId = check apimClient->/apimetadata.post([{ + apiId: "123458", + orgId: "wso2", + apiName: "abc", + metadata: "metadata" + }]); + test:assertEquals(metaId, [["123458", "wso2"]]); + + string[] subId = check apimClient->/subscriptions.post([{ + subscriptionId: "123", + userName: "ballerina", + apimetadataApiId: "123458", + apimetadataOrgId: "wso2" + }]); + test:assertEquals(subId, ["123"]); + + stream streamResult = apimClient->/apimetadata(); + PostgreSqlAPIMWithSubscriptions[] apimResults = check from PostgreSqlAPIMWithSubscriptions apiMetadata in streamResult + select apiMetadata; + + test:assertEquals(apimResults.length(), 2); + + + test:assertEquals(apimResults[1].subscription, { + subscriptionId: "123", + apimetadataApiId: "123458" + }); + check apimClient.close(); +} + +type PostgreSqlAPIMWithSubscriptions record {| + string apiId; + string orgId; + string apiName; + string metadata; + record {| + string subscriptionId; + string apimetadataApiId; + |} subscription?; +|}; + +type PostgreSqlApimWithoutSubscriptions record {| + string apiId; + string orgId; + string apiName; + string metadata; +|}; \ No newline at end of file diff --git a/ballerina/tests/postgresql_api_subscription_client.bal b/ballerina/tests/postgresql_api_subscription_client.bal new file mode 100644 index 0000000..8c610d4 --- /dev/null +++ b/ballerina/tests/postgresql_api_subscription_client.bal @@ -0,0 +1,163 @@ +// AUTO-GENERATED FILE. DO NOT MODIFY. + +// This file is an auto-generated file by Ballerina persistence layer for model. +// It should not be modified by hand. + +import ballerina/jballerina.java; +import ballerina/persist; +import ballerina/sql; +import ballerinax/postgresql; +import ballerinax/postgresql.driver as _; + +const SUBSCRIPTION = "subscriptions"; +const API_METADATA = "apimetadata"; + +public isolated client class PostgreSqlApimClient { + *persist:AbstractPersistClient; + + private final postgresql:Client dbClient; + + private final map persistClients; + + private final record {|SQLMetadata...;|} & readonly metadata = { + [SUBSCRIPTION]: { + entityName: "Subscription", + tableName: "Subscription", + fieldMetadata: { + subscriptionId: {columnName: "subscriptionId"}, + userName: {columnName: "userName"}, + apimetadataApiId: {columnName: "apimetadataApiId"}, + apimetadataOrgId: {columnName: "apimetadataOrgId"}, + "apimetadata.apiId": {relation: {entityName: "apimetadata", refField: "apiId"}}, + "apimetadata.orgId": {relation: {entityName: "apimetadata", refField: "orgId"}}, + "apimetadata.apiName": {relation: {entityName: "apimetadata", refField: "apiName"}}, + "apimetadata.metadata": {relation: {entityName: "apimetadata", refField: "metadata"}} + }, + keyFields: ["subscriptionId"], + joinMetadata: {apimetadata: {entity: ApiMetadata, fieldName: "apimetadata", refTable: "ApiMetadata", refColumns: ["apiId", "orgId"], joinColumns: ["apimetadataApiId", "apimetadataOrgId"], 'type: ONE_TO_ONE}} + }, + [API_METADATA]: { + entityName: "ApiMetadata", + tableName: "ApiMetadata", + fieldMetadata: { + apiId: {columnName: "apiId"}, + orgId: {columnName: "orgId"}, + apiName: {columnName: "apiName"}, + metadata: {columnName: "metadata"}, + "subscription.subscriptionId": {relation: {entityName: "subscription", refField: "subscriptionId"}}, + "subscription.userName": {relation: {entityName: "subscription", refField: "userName"}}, + "subscription.apimetadataApiId": {relation: {entityName: "subscription", refField: "apimetadataApiId"}}, + "subscription.apimetadataOrgId": {relation: {entityName: "subscription", refField: "apimetadataOrgId"}} + }, + keyFields: ["apiId", "orgId"], + joinMetadata: {subscription: {entity: Subscription, fieldName: "subscription", refTable: "Subscription", refColumns: ["apimetadataApiId", "apimetadataOrgId"], joinColumns: ["apiId", "orgId"], 'type: ONE_TO_ONE}} + } + }; + + public isolated function init() returns persist:Error? { + postgresql:Client|error dbClient = new (host = postgresql.host, username = postgresql.user, password = postgresql.password, database = postgresql.database, port = postgresql.port); + if dbClient is error { + return error(dbClient.message()); + } + self.dbClient = dbClient; + self.persistClients = { + [SUBSCRIPTION]: check new (dbClient, self.metadata.get(SUBSCRIPTION), POSTGRESQL_SPECIFICS), + [API_METADATA]: check new (dbClient, self.metadata.get(API_METADATA), POSTGRESQL_SPECIFICS) + }; + } + + isolated resource function get subscriptions(SubscriptionTargetType targetType = <>, sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``, sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.PostgreSQLProcessor", + name: "query" + } external; + + isolated resource function get subscriptions/[string subscriptionId](SubscriptionTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.PostgreSQLProcessor", + name: "queryOne" + } external; + + isolated resource function post subscriptions(SubscriptionInsert[] data) returns string[]|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(SUBSCRIPTION); + } + _ = check sqlClient.runBatchInsertQuery(data); + return from SubscriptionInsert inserted in data + select inserted.subscriptionId; + } + + isolated resource function put subscriptions/[string subscriptionId](SubscriptionUpdate value) returns Subscription|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(SUBSCRIPTION); + } + _ = check sqlClient.runUpdateQuery(subscriptionId, value); + return self->/subscriptions/[subscriptionId].get(); + } + + isolated resource function delete subscriptions/[string subscriptionId]() returns Subscription|persist:Error { + Subscription result = check self->/subscriptions/[subscriptionId].get(); + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(SUBSCRIPTION); + } + _ = check sqlClient.runDeleteQuery(subscriptionId); + return result; + } + + isolated resource function get apimetadata(ApiMetadataTargetType targetType = <>, sql:ParameterizedQuery whereClause = ``, sql:ParameterizedQuery orderByClause = ``, sql:ParameterizedQuery limitClause = ``, sql:ParameterizedQuery groupByClause = ``) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.PostgreSQLProcessor", + name: "query" + } external; + + isolated resource function get apimetadata/[string apiId]/[string orgId](ApiMetadataTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.PostgreSQLProcessor", + name: "queryOne" + } external; + + isolated resource function post apimetadata(ApiMetadataInsert[] data) returns [string, string][]|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(API_METADATA); + } + _ = check sqlClient.runBatchInsertQuery(data); + return from ApiMetadataInsert inserted in data + select [inserted.apiId, inserted.orgId]; + } + + isolated resource function put apimetadata/[string apiId]/[string orgId](ApiMetadataUpdate value) returns ApiMetadata|persist:Error { + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(API_METADATA); + } + _ = check sqlClient.runUpdateQuery({"apiId": apiId, "orgId": orgId}, value); + return self->/apimetadata/[apiId]/[orgId].get(); + } + + isolated resource function delete apimetadata/[string apiId]/[string orgId]() returns ApiMetadata|persist:Error { + ApiMetadata result = check self->/apimetadata/[apiId]/[orgId].get(); + SQLClient sqlClient; + lock { + sqlClient = self.persistClients.get(API_METADATA); + } + _ = check sqlClient.runDeleteQuery({"apiId": apiId, "orgId": orgId}); + return result; + } + + remote isolated function queryNativeSQL(sql:ParameterizedQuery sqlQuery, typedesc rowType = <>) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.PostgreSQLProcessor" + } external; + + remote isolated function executeNativeSQL(sql:ParameterizedQuery sqlQuery) returns ExecutionResult|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.sql.datastore.PostgreSQLProcessor" + } external; + + public isolated function close() returns persist:Error? { + error? result = self.dbClient.close(); + if result is error { + return error(result.message()); + } + return result; + } +} + diff --git a/ballerina/tests/resources/mysql/init.sql b/ballerina/tests/resources/mysql/init.sql index da9af0d..a78f071 100644 --- a/ballerina/tests/resources/mysql/init.sql +++ b/ballerina/tests/resources/mysql/init.sql @@ -164,3 +164,21 @@ CREATE TABLE test.appointment ( FOREIGN KEY(doctorId) REFERENCES Doctor(id), PRIMARY KEY(id) ); + +CREATE TABLE test.ApiMetadata ( + apiId VARCHAR(191) NOT NULL, + orgId VARCHAR(191) NOT NULL, + apiName VARCHAR(191) NOT NULL, + metadata VARCHAR(191) NOT NULL, + PRIMARY KEY(apiId,orgId) +); + +CREATE TABLE test.Subscription ( + subscriptionId VARCHAR(191) NOT NULL, + userName VARCHAR(191) NOT NULL, + apimetadataApiId VARCHAR(191) NOT NULL, + apimetadataOrgId VARCHAR(191) NOT NULL, + UNIQUE (apimetadataApiId, apimetadataOrgId), + FOREIGN KEY(apimetadataApiId, apimetadataOrgId) REFERENCES ApiMetadata(apiId, orgId), + PRIMARY KEY(subscriptionId) +); \ No newline at end of file diff --git a/ballerina/tests/resources/postgresql/init.sql b/ballerina/tests/resources/postgresql/init.sql index 17a7c34..3b78041 100644 --- a/ballerina/tests/resources/postgresql/init.sql +++ b/ballerina/tests/resources/postgresql/init.sql @@ -151,3 +151,21 @@ CREATE TABLE "appointment" ( FOREIGN KEY("doctorId") REFERENCES "Doctor"("id"), PRIMARY KEY("id") ); + +CREATE TABLE "ApiMetadata" ( + "apiId" VARCHAR(191) NOT NULL, + "orgId" VARCHAR(191) NOT NULL, + "apiName" VARCHAR(191) NOT NULL, + "metadata" VARCHAR(191) NOT NULL, + PRIMARY KEY("apiId","orgId") +); + +CREATE TABLE "Subscription" ( + "subscriptionId" VARCHAR(191) NOT NULL, + "userName" VARCHAR(191) NOT NULL, + "apimetadataApiId" VARCHAR(191) NOT NULL, + "apimetadataOrgId" VARCHAR(191) NOT NULL, + UNIQUE ("apimetadataApiId", "apimetadataOrgId"), + FOREIGN KEY("apimetadataApiId", "apimetadataOrgId") REFERENCES "ApiMetadata"("apiId", "orgId"), + PRIMARY KEY("subscriptionId") +); From 14129eff36cdb135afbe5cb1c608c25a66bd64ec Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Mon, 28 Oct 2024 13:51:50 +0530 Subject: [PATCH 05/24] update the change log --- changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog.md b/changelog.md index 883790f..384d179 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- [[Bug] Bal Persist fails when tries to retrieve record with a nonexistent associations](https://github.com/ballerina-platform/ballerina-library/issues/7304) + +## [1.4.0] - 2024-08-20 + ### Added - [Added support for H2 DB as a datasource](https://github.com/ballerina-platform/ballerina-library/issues/5715) From 0e023b62038b0571be316173ddfd58c758dca17f Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Mon, 28 Oct 2024 13:54:43 +0530 Subject: [PATCH 06/24] Add new line at EOF --- ballerina/tests/h2-optional-assoc-tests.bal | 2 +- ballerina/tests/mssql-optional-assoc-tests.bal | 2 +- ballerina/tests/mysql-optional-assoc-tests.bal | 2 +- ballerina/tests/persist/api_subs.bal | 2 +- ballerina/tests/postgresql-optional-assoc-tests.bal | 2 +- ballerina/tests/resources/mysql/init.sql | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ballerina/tests/h2-optional-assoc-tests.bal b/ballerina/tests/h2-optional-assoc-tests.bal index 8d6d10c..4b44c8b 100644 --- a/ballerina/tests/h2-optional-assoc-tests.bal +++ b/ballerina/tests/h2-optional-assoc-tests.bal @@ -115,4 +115,4 @@ type H2ApimWithoutSubscriptions record {| string orgId; string apiName; string metadata; -|}; \ No newline at end of file +|}; diff --git a/ballerina/tests/mssql-optional-assoc-tests.bal b/ballerina/tests/mssql-optional-assoc-tests.bal index ee6ba03..584f7db 100644 --- a/ballerina/tests/mssql-optional-assoc-tests.bal +++ b/ballerina/tests/mssql-optional-assoc-tests.bal @@ -114,4 +114,4 @@ type MsSqlApimWithoutSubscriptions record {| string orgId; string apiName; string metadata; -|}; \ No newline at end of file +|}; diff --git a/ballerina/tests/mysql-optional-assoc-tests.bal b/ballerina/tests/mysql-optional-assoc-tests.bal index 565e2f4..5f89885 100644 --- a/ballerina/tests/mysql-optional-assoc-tests.bal +++ b/ballerina/tests/mysql-optional-assoc-tests.bal @@ -114,4 +114,4 @@ type MySqlApimWithoutSubscriptions record {| string orgId; string apiName; string metadata; -|}; \ No newline at end of file +|}; diff --git a/ballerina/tests/persist/api_subs.bal b/ballerina/tests/persist/api_subs.bal index 246b98c..98978e4 100644 --- a/ballerina/tests/persist/api_subs.bal +++ b/ballerina/tests/persist/api_subs.bal @@ -28,4 +28,4 @@ public type ApiMetadata record {| string apiName; string metadata; Subscription? subscription; -|}; \ No newline at end of file +|}; diff --git a/ballerina/tests/postgresql-optional-assoc-tests.bal b/ballerina/tests/postgresql-optional-assoc-tests.bal index 8a0a443..5411f6d 100644 --- a/ballerina/tests/postgresql-optional-assoc-tests.bal +++ b/ballerina/tests/postgresql-optional-assoc-tests.bal @@ -114,4 +114,4 @@ type PostgreSqlApimWithoutSubscriptions record {| string orgId; string apiName; string metadata; -|}; \ No newline at end of file +|}; diff --git a/ballerina/tests/resources/mysql/init.sql b/ballerina/tests/resources/mysql/init.sql index a78f071..3783822 100644 --- a/ballerina/tests/resources/mysql/init.sql +++ b/ballerina/tests/resources/mysql/init.sql @@ -181,4 +181,4 @@ CREATE TABLE test.Subscription ( UNIQUE (apimetadataApiId, apimetadataOrgId), FOREIGN KEY(apimetadataApiId, apimetadataOrgId) REFERENCES ApiMetadata(apiId, orgId), PRIMARY KEY(subscriptionId) -); \ No newline at end of file +); From 963f3466b12ba228aa5138030a0130640c85db63 Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Mon, 28 Oct 2024 14:03:39 +0530 Subject: [PATCH 07/24] Update ballerina/sql_client.bal Co-authored-by: Nuvindu Nirmana <63797478+Nuvindu@users.noreply.github.com> --- ballerina/sql_client.bal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ballerina/sql_client.bal b/ballerina/sql_client.bal index c71ba2b..e7d12ed 100644 --- a/ballerina/sql_client.bal +++ b/ballerina/sql_client.bal @@ -236,8 +236,8 @@ public isolated client class SQLClient { # + include - The relations to be retrieved (SQL `JOINs` to be performed) # + return - `()` if the operation is performed successfully or a `persist:Error` if the operation fails public isolated function verifyEntityAssociation(anydata 'object, string[] fields, string[] include) returns persist:Error? { - if !('object is record {}) { - return error("The 'object' parameter should be a record"); + if 'object !is record {} { + return error persist:Error("The 'object' parameter should be a record"); } do { From 63bd5ae896612ea061b27f0a80406f2e41a0d7ec Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Mon, 28 Oct 2024 14:07:40 +0530 Subject: [PATCH 08/24] update the license headers --- .../tests/mssql_api_subscription_client.bal | 19 +++++++++++++++---- .../tests/mysql_api_subscription_client.bal | 19 +++++++++++++++---- .../postgresql_api_subscription_client.bal | 19 +++++++++++++++---- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/ballerina/tests/mssql_api_subscription_client.bal b/ballerina/tests/mssql_api_subscription_client.bal index 2841932..f799693 100644 --- a/ballerina/tests/mssql_api_subscription_client.bal +++ b/ballerina/tests/mssql_api_subscription_client.bal @@ -1,7 +1,18 @@ -// AUTO-GENERATED FILE. DO NOT MODIFY. - -// This file is an auto-generated file by Ballerina persistence layer for model. -// It should not be modified by hand. +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. import ballerina/jballerina.java; import ballerina/persist; diff --git a/ballerina/tests/mysql_api_subscription_client.bal b/ballerina/tests/mysql_api_subscription_client.bal index 3f5af2e..6cbf5a7 100644 --- a/ballerina/tests/mysql_api_subscription_client.bal +++ b/ballerina/tests/mysql_api_subscription_client.bal @@ -1,7 +1,18 @@ -// AUTO-GENERATED FILE. DO NOT MODIFY. - -// This file is an auto-generated file by Ballerina persistence layer for model. -// It should not be modified by hand. +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. import ballerina/jballerina.java; import ballerina/persist; diff --git a/ballerina/tests/postgresql_api_subscription_client.bal b/ballerina/tests/postgresql_api_subscription_client.bal index 8c610d4..0e1ba02 100644 --- a/ballerina/tests/postgresql_api_subscription_client.bal +++ b/ballerina/tests/postgresql_api_subscription_client.bal @@ -1,7 +1,18 @@ -// AUTO-GENERATED FILE. DO NOT MODIFY. - -// This file is an auto-generated file by Ballerina persistence layer for model. -// It should not be modified by hand. +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. import ballerina/jballerina.java; import ballerina/persist; From 2cfaa5810805e2cbbb0336854ebf731f443b4fec Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Mon, 28 Oct 2024 23:56:13 +0530 Subject: [PATCH 09/24] Apply suggestions from code review Co-authored-by: Nuvindu Nirmana <63797478+Nuvindu@users.noreply.github.com> --- ballerina/sql_client.bal | 6 +++--- ballerina/tests/mssql_api_subscription_client.bal | 2 +- ballerina/tests/mysql_api_subscription_client.bal | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ballerina/sql_client.bal b/ballerina/sql_client.bal index e7d12ed..57474f4 100644 --- a/ballerina/sql_client.bal +++ b/ballerina/sql_client.bal @@ -249,7 +249,7 @@ public isolated client class SQLClient { // check if the fields are empty in the associated record. map nonEmptyAssocEntity = associatedEntity.filter(value => value != ()); // If the associated entity has non-empty fields, then the association is already verified. - if (nonEmptyAssocEntity.length() > 0) { + if nonEmptyAssocEntity.length() > 0 { continue; } @@ -261,7 +261,7 @@ public isolated client class SQLClient { break; } } - if (hasKeys) { + if hasKeys { 'object[joinMetadata.fieldName] = (); continue; } @@ -275,7 +275,7 @@ public isolated client class SQLClient { ); // execute the query and check the count of the associated entries int count = check self.dbClient->queryRow(query); - if (count == 0) { + if count == 0 { 'object[joinMetadata.fieldName] = (); } } diff --git a/ballerina/tests/mssql_api_subscription_client.bal b/ballerina/tests/mssql_api_subscription_client.bal index f799693..b16d2ab 100644 --- a/ballerina/tests/mssql_api_subscription_client.bal +++ b/ballerina/tests/mssql_api_subscription_client.bal @@ -68,7 +68,7 @@ public isolated client class MsSqlApimClient { public isolated function init() returns persist:Error? { mssql:Client|error dbClient = new (host = mssql.host, user = mssql.user, password = mssql.password, database = mssql.database, port = mssql.port); if dbClient is error { - return error(dbClient.message()); + return error persist:Error(dbClient.message()); } self.dbClient = dbClient; self.persistClients = { diff --git a/ballerina/tests/mysql_api_subscription_client.bal b/ballerina/tests/mysql_api_subscription_client.bal index 6cbf5a7..0793ecc 100644 --- a/ballerina/tests/mysql_api_subscription_client.bal +++ b/ballerina/tests/mysql_api_subscription_client.bal @@ -68,7 +68,7 @@ public isolated client class MySqlApimClient { public isolated function init() returns persist:Error? { mysql:Client|error dbClient = new (host = mysql.host, user = mysql.user, password = mysql.password, database = mysql.database, port = mysql.port); if dbClient is error { - return error(dbClient.message()); + return error persist:Error(dbClient.message()); } self.dbClient = dbClient; self.persistClients = { From 256cd034c0608eb79f2ac9202cb171903ae383fd Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Tue, 29 Oct 2024 11:05:42 +0530 Subject: [PATCH 10/24] update the github workflows --- .github/workflows/build-timestamped-master.yml | 8 ++++---- .github/workflows/publish-release.yml | 4 ++-- .github/workflows/publish-snapshot-nexus.yml | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-timestamped-master.yml b/.github/workflows/build-timestamped-master.yml index ae7e73a..82fa97d 100644 --- a/.github/workflows/build-timestamped-master.yml +++ b/.github/workflows/build-timestamped-master.yml @@ -16,9 +16,9 @@ jobs: runs-on: ubuntu-latest if: github.repository_owner == 'ballerina-platform' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up JDK 17 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: 17.0.7 @@ -44,9 +44,9 @@ jobs: run: | ./gradlew clean build publishAllPublicationsToGitHubPackagesRepository --scan --no-daemon - name: Generate CodeCov Report - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 - name: Upload Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: ballerina-runtime path: target/ballerina-runtime/ diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index ad204e6..054ecc6 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -10,9 +10,9 @@ jobs: runs-on: ubuntu-latest if: github.repository_owner == 'ballerina-platform' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up JDK 17 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: 17.0.7 diff --git a/.github/workflows/publish-snapshot-nexus.yml b/.github/workflows/publish-snapshot-nexus.yml index b13aaea..4e2930f 100644 --- a/.github/workflows/publish-snapshot-nexus.yml +++ b/.github/workflows/publish-snapshot-nexus.yml @@ -8,9 +8,9 @@ jobs: runs-on: ubuntu-latest if: github.repository_owner == 'ballerina-platform' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up JDK 17 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 17.0.7 From 94a2eb58f25bb760951d7806964aa27283f09c4a Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Tue, 29 Oct 2024 18:41:35 +0530 Subject: [PATCH 11/24] [Automated] Update native jar versions in toml files --- ballerina/Dependencies.toml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 0969bec..4d42431 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -100,9 +100,6 @@ dependencies = [ {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "lang.value"} ] -modules = [ - {org = "ballerina", packageName = "io", moduleName = "io"} -] [[package]] org = "ballerina" @@ -400,7 +397,7 @@ modules = [ [[package]] org = "ballerinax" name = "java.jdbc" -version = "1.12.0" +version = "1.12.1" scope = "testOnly" dependencies = [ {org = "ballerina", name = "io"}, @@ -467,7 +464,6 @@ org = "ballerinax" name = "persist.sql" version = "1.4.1" dependencies = [ - {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "log"}, {org = "ballerina", name = "persist"}, @@ -491,7 +487,7 @@ modules = [ [[package]] org = "ballerinax" name = "postgresql" -version = "1.13.0" +version = "1.13.1" scope = "testOnly" dependencies = [ {org = "ballerina", name = "crypto"}, From 6278b7e63ddfd1e64282e29ed5b6ff87c7356c31 Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Tue, 29 Oct 2024 22:25:08 +0530 Subject: [PATCH 12/24] Add env to fix the TOOMANYREQUESTS issues --- .github/workflows/trivy-scan.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 458aab5..a3bf024 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -8,6 +8,9 @@ on: jobs: call_workflow: name: Run Trivy Scan Workflow + env: + TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db + TRIVY_JAVA_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db if: ${{ github.repository_owner == 'ballerina-platform' }} uses: ballerina-platform/ballerina-library/.github/workflows/trivy-scan-template.yml@main secrets: inherit From 1aa698c2aded652acf7d3311db141c93a083f1d0 Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Wed, 30 Oct 2024 09:56:01 +0530 Subject: [PATCH 13/24] Add env to fix the TOOMANYREQUESTS issues --- .github/workflows/publish-release.yml | 3 +++ .github/workflows/trivy-scan.yml | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 054ecc6..5b3d638 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -27,6 +27,9 @@ jobs: - name: Create lib directory if not exists run: mkdir -p ballerina/lib - name: Run Trivy vulnerability scanner + env: + TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db + TRIVY_JAVA_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db uses: aquasecurity/trivy-action@master with: scan-type: 'rootfs' diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index a3bf024..458aab5 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -8,9 +8,6 @@ on: jobs: call_workflow: name: Run Trivy Scan Workflow - env: - TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db - TRIVY_JAVA_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db if: ${{ github.repository_owner == 'ballerina-platform' }} uses: ballerina-platform/ballerina-library/.github/workflows/trivy-scan-template.yml@main secrets: inherit From 3a88eb03716762084d55d48c17df090cc485d0ae Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Wed, 30 Oct 2024 10:53:39 +0530 Subject: [PATCH 14/24] Fix the error lstat /github/workspace/ballerina/lib: no such file or directory --- .github/workflows/publish-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 5b3d638..b4b595a 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -33,7 +33,7 @@ jobs: uses: aquasecurity/trivy-action@master with: scan-type: 'rootfs' - scan-ref: '/github/workspace/ballerina/lib' + scan-ref: '${{ github.workspace.ballerina.lib }}/' format: 'table' timeout: '10m0s' exit-code: '1' From 640625694cd35547ce237c185dc86291112bd393 Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Wed, 30 Oct 2024 11:18:53 +0530 Subject: [PATCH 15/24] Downgrade the trivy to 0.23.0 --- .github/workflows/publish-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index b4b595a..63650d2 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -30,10 +30,10 @@ jobs: env: TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db TRIVY_JAVA_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db - uses: aquasecurity/trivy-action@master + uses: aquasecurity/trivy-action@0.23.0 with: scan-type: 'rootfs' - scan-ref: '${{ github.workspace.ballerina.lib }}/' + scan-ref: '/github/workspace/ballerina/lib' format: 'table' timeout: '10m0s' exit-code: '1' From 2cf98721ca04cd374b82c8eeb5d583348a14aa6a Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Wed, 30 Oct 2024 20:36:13 +0530 Subject: [PATCH 16/24] Move the trivy plugin to the latest with scan-ref change --- .github/workflows/publish-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 63650d2..b4b595a 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -30,10 +30,10 @@ jobs: env: TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db TRIVY_JAVA_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db - uses: aquasecurity/trivy-action@0.23.0 + uses: aquasecurity/trivy-action@master with: scan-type: 'rootfs' - scan-ref: '/github/workspace/ballerina/lib' + scan-ref: '${{ github.workspace.ballerina.lib }}/' format: 'table' timeout: '10m0s' exit-code: '1' From b46322a9d6d33e41b495669c92cba864fda45a5c Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Wed, 30 Oct 2024 21:49:58 +0530 Subject: [PATCH 17/24] Set scanner to scan only vulnerabilities --- .github/workflows/publish-release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index b4b595a..d1f5c55 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -37,6 +37,7 @@ jobs: format: 'table' timeout: '10m0s' exit-code: '1' + scanners: "vuln" - name: Set version env variable run: echo "VERSION=$((grep -w 'version' | cut -d= -f2) < gradle.properties | rev | cut --complement -d- -f1 | rev)" >> $GITHUB_ENV - name: Pre release dependency version update From bf71c1b839dc731b6f3c401cfce4b85518f3568f Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Thu, 31 Oct 2024 10:04:53 +0530 Subject: [PATCH 18/24] revert back to the base configs --- .github/workflows/publish-release.yml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index d1f5c55..cc436dc 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -27,17 +27,13 @@ jobs: - name: Create lib directory if not exists run: mkdir -p ballerina/lib - name: Run Trivy vulnerability scanner - env: - TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db - TRIVY_JAVA_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db - uses: aquasecurity/trivy-action@master + uses: aquasecurity/trivy-action@0.23.0 with: - scan-type: 'rootfs' - scan-ref: '${{ github.workspace.ballerina.lib }}/' - format: 'table' - timeout: '10m0s' - exit-code: '1' - scanners: "vuln" + scan-type: "rootfs" + scan-ref: "/github/workspace/ballerina/lib" + format: "table" + timeout: "10m0s" + exit-code: "1" - name: Set version env variable run: echo "VERSION=$((grep -w 'version' | cut -d= -f2) < gradle.properties | rev | cut --complement -d- -f1 | rev)" >> $GITHUB_ENV - name: Pre release dependency version update From 1df712b285513b89adfea71fe864d8bb8c33c0a9 Mon Sep 17 00:00:00 2001 From: ballerina-bot Date: Thu, 31 Oct 2024 06:33:04 +0000 Subject: [PATCH 19/24] [Automated] Update native jar versions in toml files --- ballerina/Dependencies.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 4d42431..7b8b005 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -66,7 +66,7 @@ dependencies = [ [[package]] org = "ballerina" name = "http" -version = "2.12.2" +version = "2.12.0" scope = "testOnly" dependencies = [ {org = "ballerina", name = "auth"}, @@ -241,7 +241,7 @@ modules = [ [[package]] org = "ballerina" name = "mime" -version = "2.10.1" +version = "2.10.0" scope = "testOnly" dependencies = [ {org = "ballerina", name = "io"}, @@ -296,7 +296,7 @@ modules = [ [[package]] org = "ballerina" name = "sql" -version = "1.14.1" +version = "1.14.0" dependencies = [ {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, @@ -397,7 +397,7 @@ modules = [ [[package]] org = "ballerinax" name = "java.jdbc" -version = "1.12.1" +version = "1.12.0" scope = "testOnly" dependencies = [ {org = "ballerina", name = "io"}, @@ -437,7 +437,7 @@ modules = [ [[package]] org = "ballerinax" name = "mysql" -version = "1.13.1" +version = "1.13.0" scope = "testOnly" dependencies = [ {org = "ballerina", name = "crypto"}, @@ -487,7 +487,7 @@ modules = [ [[package]] org = "ballerinax" name = "postgresql" -version = "1.13.1" +version = "1.13.0" scope = "testOnly" dependencies = [ {org = "ballerina", name = "crypto"}, From 87d75d65bf610e0043723342f96037f27c2a2dd7 Mon Sep 17 00:00:00 2001 From: ballerina-bot Date: Thu, 31 Oct 2024 06:36:07 +0000 Subject: [PATCH 20/24] [Automated] Update native jar versions in toml files --- ballerina/Ballerina.toml | 2 +- ballerina/CompilerPlugin.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 8a7de28..6a51f11 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -16,7 +16,7 @@ graalvmCompatible = true groupId = "io.ballerina.stdlib" artifactId = "persist.sql-native" version = "1.4.1" -path = "../native/build/libs/persist.sql-native-1.4.1-SNAPSHOT.jar" +path = "../native/build/libs/persist.sql-native-1.4.1.jar" [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml index 6bd6fbc..348324e 100644 --- a/ballerina/CompilerPlugin.toml +++ b/ballerina/CompilerPlugin.toml @@ -3,7 +3,7 @@ id = "persist.sql-compiler-plugin" class = "io.ballerina.stdlib.persist.sql.compiler.PersistSqlCompilerPlugin" [[dependency]] -path = "../compiler-plugin/build/libs/persist.sql-compiler-plugin-1.4.1-SNAPSHOT.jar" +path = "../compiler-plugin/build/libs/persist.sql-compiler-plugin-1.4.1.jar" [[dependency]] path = "./lib/persist-native-1.4.0.jar" From 5c8ad6ebf38c7d3965cb4f473118e71e30e40d75 Mon Sep 17 00:00:00 2001 From: ballerina-bot Date: Thu, 31 Oct 2024 06:37:22 +0000 Subject: [PATCH 21/24] [Gradle Release Plugin] - pre tag commit: 'v1.4.1'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5cfa3f7..4180a90 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=io.ballerina.stdlib -version=1.4.1-SNAPSHOT +version=1.4.1 puppycrawlCheckstyleVersion=10.12.1 checkstyleToolVersion=10.12.1 From e3e42bba3f86a230e57df3412a809174309828b2 Mon Sep 17 00:00:00 2001 From: ballerina-bot Date: Thu, 31 Oct 2024 06:37:23 +0000 Subject: [PATCH 22/24] [Gradle Release Plugin] - new version commit: 'v1.4.2-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 4180a90..30829f1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=io.ballerina.stdlib -version=1.4.1 +version=1.4.2-SNAPSHOT puppycrawlCheckstyleVersion=10.12.1 checkstyleToolVersion=10.12.1 From 2b01be28f443765fe264d1db7774f3983e345004 Mon Sep 17 00:00:00 2001 From: warunalakshitha Date: Sat, 16 Nov 2024 23:16:38 +0530 Subject: [PATCH 23/24] Migrate to new Runtime APIs --- gradle.properties | 60 +++++++++---------- .../ballerina/stdlib/persist/sql/Utils.java | 2 +- .../persist/sql/datastore/SQLProcessor.java | 2 +- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/gradle.properties b/gradle.properties index 30829f1..1a9b965 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,63 +12,63 @@ gsonVersion=2.10 ballerinaGradlePluginVersion=2.0.1 jacocoVersion=0.8.10 -ballerinaLangVersion=2201.10.0 +ballerinaLangVersion=2201.11.0-20241112-214900-6b80ab87 # Direct Dependencies # Level 01 -stdlibIoVersion=1.6.1 -stdlibTimeVersion=2.4.0 -stdlibUrlVersion=2.4.0 +stdlibIoVersion=1.6.2-20241112-233100-995cf5f +stdlibTimeVersion=2.6.0-20241113-073800-201b904 +stdlibUrlVersion=2.4.1-20241113-073900-335ff51 # Level 02 -stdlibLogVersion=2.10.0 -stdlibOsVersion=1.8.0 -stdlibPersistVersion=1.4.0 +stdlibLogVersion=2.10.1-20241113-120000-4577868 +stdlibOsVersion=1.8.1-20241113-122000-cca973b +stdlibPersistVersion=1.4.1-20241113-122000-306cc63 # Level 03 -stdlibFileVersion=1.10.0 +stdlibFileVersion=1.10.1-20241113-151700-e1a2e38 # Level 05 -stdlibHttpVersion=2.12.0 +stdlibHttpVersion=2.13.0-20241114-182900-7e9f66a # Level 07 -stdlibSqlVersion=1.14.0 +stdlibSqlVersion=1.15.0-20241116-124800-0bc6a40 # Ballerina external dependency -stdlibH2DriverVersion=1.1.0 -stdlibMysqlDriverVersion=1.6.0 -stdlibMssqlDriverVersion=1.6.0 -stdlibPostgresqlDriverVersion=1.5.1 +stdlibH2DriverVersion=1.1.1-20241116-205700-cf03680 +stdlibMysqlDriverVersion=1.7.1-20241116-205100-72cd8ab +stdlibMssqlDriverVersion=1.6.1-20241116-205000-d3351e2 +stdlibPostgresqlDriverVersion=1.5.2-20241116-204700-582e4be # Ballerinax Observer -observeVersion=1.3.0 -observeInternalVersion=1.3.0 +observeVersion=1.4.0-20241113-092000-b83ae74 +observeInternalVersion=1.3.1-20241113-101700-265054d # Transitive Dependencies #Level 02 -stdlibConstraintVersion=1.5.0 -stdlibCryptoVersion=2.7.2 -stdlibTaskVersion=2.5.0 +stdlibConstraintVersion=1.6.0-20241113-090900-d276ad5 +stdlibCryptoVersion=2.7.3-20241113-081400-d015a39 +stdlibTaskVersion=2.5.1-20241113-123500-f905281 # Level 03 -stdlibCacheVersion=3.8.0 -stdlibMimeVersion=2.10.0 -stdlibUuidVersion=1.8.0 +stdlibCacheVersion=3.8.1-20241113-125700-b75a1bf +stdlibMimeVersion=2.10.2-20241113-154200-d953747 +stdlibUuidVersion=1.8.1-20241113-154400-443c67b # Level 04 -stdlibAuthVersion=2.12.0 -stdlibJwtVersion=2.13.0 -stdlibOAuth2Version=2.12.0 +stdlibAuthVersion=2.12.1-20241113-162300-ded40eb +stdlibJwtVersion=2.13.1-20241113-162400-b59ccfa +stdlibOAuth2Version=2.12.1-20241113-162400-4c6ddfe # Level 06 -stdlibTransactionVersion=1.10.0 +stdlibTransactionVersion=1.10.1-20241116-112500-189a4e5 # Test Dependencies -stdlibMysqlVersion=1.13.0 -stdlibMssqlVersion=1.13.0 -stdlibPostgresqlVersion=1.13.0 -stdlibJdbcVersion=1.12.0 +stdlibMysqlVersion=1.13.2-20241116-211000-539dff6 +stdlibMssqlVersion=1.13.2-20241116-222300-cb5ec9c +stdlibPostgresqlVersion=1.13.3-20241116-222400-f6d72ae +stdlibJdbcVersion=1.12.2-20241116-224900-893e2b1 # Enabled publishing insecure checksums, due to fail to publish to maven central # Refer https://github.com/gradle/gradle/issues/11308 diff --git a/native/src/main/java/io/ballerina/stdlib/persist/sql/Utils.java b/native/src/main/java/io/ballerina/stdlib/persist/sql/Utils.java index ad750dd..6202b14 100644 --- a/native/src/main/java/io/ballerina/stdlib/persist/sql/Utils.java +++ b/native/src/main/java/io/ballerina/stdlib/persist/sql/Utils.java @@ -18,9 +18,9 @@ package io.ballerina.stdlib.persist.sql; -import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; diff --git a/native/src/main/java/io/ballerina/stdlib/persist/sql/datastore/SQLProcessor.java b/native/src/main/java/io/ballerina/stdlib/persist/sql/datastore/SQLProcessor.java index 942441e..699e5b6 100644 --- a/native/src/main/java/io/ballerina/stdlib/persist/sql/datastore/SQLProcessor.java +++ b/native/src/main/java/io/ballerina/stdlib/persist/sql/datastore/SQLProcessor.java @@ -20,12 +20,12 @@ import io.ballerina.runtime.api.Environment; import io.ballerina.runtime.api.Future; -import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.async.Callback; import io.ballerina.runtime.api.constants.RuntimeConstants; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.ErrorType; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.StreamType; import io.ballerina.runtime.api.types.Type; From fd229a58cae7c9fe2d0ed63288d3c6e9e7e9f825 Mon Sep 17 00:00:00 2001 From: warunalakshitha Date: Sun, 17 Nov 2024 01:28:25 +0530 Subject: [PATCH 24/24] Fix issue with yields --- .../ballerina/stdlib/persist/sql/Handler.java | 9 -- .../persist/sql/datastore/SQLProcessor.java | 142 +++++------------- 2 files changed, 37 insertions(+), 114 deletions(-) delete mode 100644 native/src/main/java/io/ballerina/stdlib/persist/sql/Handler.java diff --git a/native/src/main/java/io/ballerina/stdlib/persist/sql/Handler.java b/native/src/main/java/io/ballerina/stdlib/persist/sql/Handler.java deleted file mode 100644 index cd1357c..0000000 --- a/native/src/main/java/io/ballerina/stdlib/persist/sql/Handler.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.ballerina.stdlib.persist.sql; - -import io.ballerina.runtime.api.values.BError; - -public interface Handler { - void notifySuccess(Object result); - - void notifyFailure(BError bError); -} diff --git a/native/src/main/java/io/ballerina/stdlib/persist/sql/datastore/SQLProcessor.java b/native/src/main/java/io/ballerina/stdlib/persist/sql/datastore/SQLProcessor.java index a458351..9662b99 100644 --- a/native/src/main/java/io/ballerina/stdlib/persist/sql/datastore/SQLProcessor.java +++ b/native/src/main/java/io/ballerina/stdlib/persist/sql/datastore/SQLProcessor.java @@ -36,11 +36,9 @@ import io.ballerina.runtime.transactions.TransactionLocalContext; import io.ballerina.runtime.transactions.TransactionResourceManager; import io.ballerina.stdlib.persist.Constants; -import io.ballerina.stdlib.persist.sql.Handler; import io.ballerina.stdlib.persist.sql.Utils; import java.util.Map; -import java.util.concurrent.CompletableFuture; import static io.ballerina.stdlib.persist.Constants.KEY_FIELDS; import static io.ballerina.stdlib.persist.ErrorGenerator.wrapError; @@ -51,10 +49,10 @@ import static io.ballerina.stdlib.persist.Utils.getRecordTypeWithKeyFields; import static io.ballerina.stdlib.persist.Utils.getTransactionContextProperties; import static io.ballerina.stdlib.persist.sql.Constants.DB_CLIENT; +import static io.ballerina.stdlib.persist.sql.Constants.PERSIST_EXECUTION_RESULT; import static io.ballerina.stdlib.persist.sql.Constants.SQL_EXECUTE_METHOD; import static io.ballerina.stdlib.persist.sql.Constants.SQL_QUERY_METHOD; import static io.ballerina.stdlib.persist.sql.ModuleUtils.getModule; -import static io.ballerina.stdlib.persist.sql.ModuleUtils.getResult; import static io.ballerina.stdlib.persist.sql.Utils.createPersistNativeSQLStream; import static io.ballerina.stdlib.persist.sql.Utils.wrapSQLError; @@ -85,28 +83,7 @@ static BStream query(Environment env, BObject client, BTypedesc targetType, BObj BArray fields = metadata[0]; BArray includes = metadata[1]; BArray typeDescriptions = metadata[2]; - - CompletableFuture balFuture = new CompletableFuture<>(); - Thread.startVirtualThread(() -> { - Handler handler = new Handler() { - @Override - public void notifySuccess(Object o) { - if (o instanceof BStream) { // stream - BStream sqlStream = (BStream) o; - balFuture.complete(Utils.createPersistSQLStreamValue(sqlStream, targetType, fields, - includes, typeDescriptions, persistClient, null)); - } else { // persist:Error - balFuture.complete(Utils.createPersistSQLStreamValue(null, targetType, fields, includes, - typeDescriptions, persistClient, (BError) o)); - } - } - - @Override - public void notifyFailure(BError bError) { - balFuture.complete(Utils.createPersistSQLStreamValue(null, targetType, fields, includes, - typeDescriptions, persistClient, wrapError(bError))); - } - }; + return env.yieldAndRun(() -> { try { Object result = env.getRuntime().callMethod( // Call `SQLClient.runReadQuery( @@ -116,12 +93,18 @@ public void notifyFailure(BError bError) { persistClient, Constants.RUN_READ_QUERY_METHOD, new StrandMetadata(false, trxContextProperties), targetTypeWithIdFields, fields, includes, whereClause, orderByClause, limitClause, groupByClause); - handler.notifySuccess(result); + if (result instanceof BStream bStream) { // stream + return Utils.createPersistSQLStreamValue(bStream, targetType, fields, includes, typeDescriptions, + persistClient, null); + } + // persist:Error + return Utils.createPersistSQLStreamValue(null, targetType, fields, includes, typeDescriptions, + persistClient, (BError) result); } catch (BError bError) { - handler.notifyFailure(bError); + return Utils.createPersistSQLStreamValue(null, targetType, fields, includes, typeDescriptions, + persistClient, wrapError(bError)); } }); - return (BStream) getResult(balFuture); } static Object queryOne(Environment env, BObject client, BArray path, BTypedesc targetType) { @@ -143,22 +126,9 @@ static Object queryOne(Environment env, BObject client, BArray path, BTypedesc t BArray typeDescriptions = metadata[2]; Object key = getKey(env, path); - - CompletableFuture balFuture = new CompletableFuture<>(); - Thread.startVirtualThread(() -> { - Handler handler = new Handler() { - @Override - public void notifySuccess(Object o) { - balFuture.complete(o); - } - - @Override - public void notifyFailure(BError bError) { - balFuture.complete(wrapError(bError)); - } - }; + return env.yieldAndRun(() -> { try { - Object result = env.getRuntime().callMethod( + return env.getRuntime().callMethod( // Call `SQLClient.runReadByKeyQuery( // typedesc rowType, typedesc rowTypeWithIdFields, anydata key, // string[] fields = [], string[] include = [], typedesc[] typeDescriptions = [] @@ -167,12 +137,10 @@ public void notifyFailure(BError bError) { getPersistClient(client, entity), Constants.RUN_READ_BY_KEY_QUERY_METHOD, new StrandMetadata(false, trxContextProperties), targetType, targetTypeWithIdFields, key, fields, includes, typeDescriptions); - handler.notifySuccess(result); } catch (BError bError) { - handler.notifyFailure(bError); + return wrapError(bError); } }); - return getResult(balFuture); } static BStream queryNativeSQL(Environment env, BObject client, BObject paramSQLString, @@ -194,45 +162,28 @@ private static BStream queryNativeSQLBal(Environment env, BObject client, BObjec TransactionResourceManager trxResourceManager = TransactionResourceManager.getInstance(); TransactionLocalContext currentTrxContext = trxResourceManager.getCurrentTransactionContext(); - CompletableFuture balFuture = new CompletableFuture<>(); - Thread.startVirtualThread(() -> { - Map properties = null; - if (currentTrxContext != null) { - properties = Map.of(RuntimeConstants.CURRENT_TRANSACTION_CONTEXT_PROPERTY, currentTrxContext); - } - Handler handler = new Handler() { - @Override - public void notifySuccess(Object o) { - // returned type is `stream` - BStream sqlStream = (BStream) o; - BObject persistNativeStream = createPersistNativeSQLStream(sqlStream, null); - RecordType streamConstraint = - (RecordType) TypeUtils.getReferredType(targetType.getDescribingType()); - balFuture.complete( - ValueCreator.createStreamValue(TypeCreator.createStreamType(streamConstraint, - PredefinedTypes.TYPE_NULL), persistNativeStream) - ); - } - - @Override - public void notifyFailure(BError bError) { // can only be hit on a panic - BObject errorStream = Utils.createPersistNativeSQLStream(null, bError); - balFuture.complete(errorStream); - } - }; + return (BStream) env.yieldAndRun(() -> { try { + Map properties = null; + if (currentTrxContext != null) { + properties = Map.of(RuntimeConstants.CURRENT_TRANSACTION_CONTEXT_PROPERTY, currentTrxContext); + } Object result = env.getRuntime().callMethod( // Call `sqlClient.query(paramSQLString, targetType)` which returns // `stream` - - dbClient, SQL_QUERY_METHOD, null, new StrandMetadata(false, properties), paramSQLString, + dbClient, SQL_QUERY_METHOD, new StrandMetadata(false, properties), paramSQLString, targetType); - handler.notifySuccess(result); + // returned type is `stream` + BStream sqlStream = (BStream) result; + BObject persistNativeStream = createPersistNativeSQLStream(sqlStream, null); + RecordType streamConstraint = + (RecordType) TypeUtils.getReferredType(targetType.getDescribingType()); + return ValueCreator.createStreamValue(TypeCreator.createStreamType(streamConstraint, + PredefinedTypes.TYPE_NULL), persistNativeStream); } catch (BError bError) { - handler.notifyFailure(bError); + return Utils.createPersistNativeSQLStream(null, bError); } }); - return (BStream) getResult(balFuture); } private static Object executeNativeSQLBal(Environment env, BObject client, BObject paramSQLString) { @@ -240,42 +191,23 @@ private static Object executeNativeSQLBal(Environment env, BObject client, BObje TransactionResourceManager trxResourceManager = TransactionResourceManager.getInstance(); TransactionLocalContext currentTrxContext = trxResourceManager.getCurrentTransactionContext(); - CompletableFuture balFuture = new CompletableFuture<>(); - Thread.startVirtualThread(() -> { + return env.yieldAndRun(() -> { Map properties = null; if (currentTrxContext != null) { properties = Map.of(RuntimeConstants.CURRENT_TRANSACTION_CONTEXT_PROPERTY, currentTrxContext); } - Handler handler = new Handler() { - @Override - public void notifySuccess(Object o) { - if (o instanceof BMap) { // returned type is `sql:ExecutionResult` - BMap persistExecutionResult = - ValueCreator.createRecordValue(getModule(), - io.ballerina.stdlib.persist.sql.Constants.PERSIST_EXECUTION_RESULT, - (BMap) o); - balFuture.complete(persistExecutionResult); - } else if (o instanceof BError) { // returned type is `sql:Error` - BError persistError = wrapSQLError((BError) o); - balFuture.complete(persistError); - } - } - - @Override - public void notifyFailure(BError bError) { // can only be hit on a panic - BError persistError = wrapError(bError); - balFuture.complete(persistError); - } - }; try { - env.getRuntime().callMethod( + Object result = env.getRuntime().callMethod( // Call `sqlClient.execute(paramSQLString)` which returns `sql:ExecutionResult|sql:Error` - dbClient, SQL_EXECUTE_METHOD, null, new StrandMetadata(false, properties), paramSQLString); - handler.notifySuccess(balFuture); + dbClient, SQL_EXECUTE_METHOD, new StrandMetadata(false, properties), paramSQLString); + if (result instanceof BMap map) { // returned type is `sql:ExecutionResult` + return ValueCreator.createRecordValue(getModule(), PERSIST_EXECUTION_RESULT, (BMap) map); + } + return wrapSQLError((BError) result); } catch (BError bError) { - handler.notifyFailure(bError); + return bError; } }); - return getResult(balFuture); } }