diff --git a/ballerina/metadata_types.bal b/ballerina/metadata_types.bal index 4abd5d5..20b32c0 100644 --- a/ballerina/metadata_types.bal +++ b/ballerina/metadata_types.bal @@ -69,6 +69,7 @@ public type RelationMetadata record {| # + entity - The name of the entity that is being joined # + fieldName - The name of the field in the `entity` that is being joined # + refCollection - The name of the Redis collection to be joined +# + refMetaDataKey - The name of the Redis collection to be joined # + refFields - The names of the fields of the refered collection # + joinFields - The names of the join fields # + joinCollection - The name of the joining collection used for a many-to-many relation @@ -79,6 +80,7 @@ public type RefMetadata record {| typedesc entity; string fieldName; string refCollection; + string refMetaDataKey?; string[] refFields; string[] joinFields; string joinCollection?; diff --git a/ballerina/redis_client.bal b/ballerina/redis_client.bal index f6f6f57..557f555 100644 --- a/ballerina/redis_client.bal +++ b/ballerina/redis_client.bal @@ -214,12 +214,12 @@ public isolated client class RedisClient { if allRefFields.length() > 0 { map currentObject = check self.dbClient->hMGet(keyWithPrefix, allRefFields); self.logQuery(HMGET, {key: keyWithPrefix, fieldNames: allRefFields.toJsonString()}); - foreach RefMetadata refMedaData in self.refMetadata { - string refKey = refMedaData.refCollection; - foreach string refField in refMedaData.joinFields { + foreach RefMetadata refMetaData in self.refMetadata { + string refKey = refMetaData.refCollection; + foreach string refField in refMetaData.joinFields { refKey += string `${KEY_SEPERATOR}${currentObject[refField].toString()}`; } - string setKey = string `${refKey}${KEY_SEPERATOR}${self.collectionName}`; + string setKey = string `${refKey}${KEY_SEPERATOR}${refMetaData.refMetaDataKey ?: ""}`; _ = check self.dbClient->sRem(setKey, [keySuffix.substring(1)]); self.logQuery(SREM, {key: setKey, suffixes: [keySuffix].toJsonString()}); } @@ -339,7 +339,7 @@ public isolated client class RedisClient { } // Get keys of existing associations - string setKey = string `${newRelatedRecordKey}${KEY_SEPERATOR}${self.collectionName}`; + string setKey = string `${newRelatedRecordKey}${KEY_SEPERATOR}${refMetaData.refMetaDataKey ?: ""}`; int isKeyExists = check self.dbClient->exists([setKey]); self.logQuery(EXISTS, [setKey]); if isKeyExists != 0 { @@ -394,7 +394,7 @@ public isolated client class RedisClient { prevRelatedRecordKey += string `${KEY_SEPERATOR}${prevRecord[joinField].toString()}`; } // Attach to new association - string newSetKey = string `${newRelatedRecordKey}${KEY_SEPERATOR}${self.collectionName}`; + string newSetKey = string `${newRelatedRecordKey}${KEY_SEPERATOR}${refMetaData.refMetaDataKey ?: ""}`; int|error sAdd = self.dbClient->sAdd(newSetKey, [recordKeySuffix.substring(1)]); if sAdd is error { return error persist:Error(sAdd.message(), sAdd); @@ -402,7 +402,7 @@ public isolated client class RedisClient { self.logQuery(SADD, {key: newSetKey, suffixes: [recordKeySuffix].toJsonString()}); // Detach from previous association - string prevSetKey = string `${prevRelatedRecordKey}${KEY_SEPERATOR}${self.collectionName}`; + string prevSetKey = string `${prevRelatedRecordKey}${KEY_SEPERATOR}${refMetaData.refMetaDataKey ?: ""}`; int|error sRem = self.dbClient->sRem(prevSetKey, [recordKeySuffix.substring(1)]); if sRem is error { return error persist:Error(sRem.message(), sRem); @@ -521,7 +521,7 @@ public isolated client class RedisClient { if refMetaData.joinFields == self.keyFields { // Non-owner have direct access to the association set - string setKey = string `${self.getKeyFromObject('object)}${KEY_SEPERATOR}${refMetaData.refCollection}`; + string setKey = string `${self.getKeyFromObject('object)}${KEY_SEPERATOR}${refMetaData.fieldName}`; string[] keys = check self.dbClient->sMembers(setKey); self.logQuery(SMEMBERS, setKey); return from string key in keys @@ -666,7 +666,7 @@ public isolated client class RedisClient { } // Check the cardinality of refered entity record - string setKey = string `${refRecordKey}${KEY_SEPERATOR}${self.collectionName}`; + string setKey = string `${refRecordKey}${KEY_SEPERATOR}${refMetadataValue.refMetaDataKey ?: ""}`; int|error sCard = self.dbClient->sCard(setKey); self.logQuery(SCARD, setKey); if sCard is int && sCard > 0 && refMetadataValue.'type == ONE_TO_ONE { @@ -755,8 +755,7 @@ public isolated client class RedisClient { string[] timeValues = re `:`.split(timeValue); time:TimeOfDay output = { hour: check int:fromString(timeValues[0]), - minute: check int:fromString( - timeValues[1]), + minute: check int:fromString(timeValues[1]), second: check decimal:fromString(timeValues[2]) }; return output; diff --git a/ballerina/tests/init-test.bal b/ballerina/tests/init-test.bal index e5382c7..1381618 100644 --- a/ballerina/tests/init-test.bal +++ b/ballerina/tests/init-test.bal @@ -13,7 +13,6 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. - import ballerina/test; import ballerina/time; import ballerinax/redis; @@ -36,8 +35,16 @@ AllTypes allTypes1 = { dateType: {year: 1993, month: 11, day: 3}, timeOfDayType: {hour: 12, minute: 32, second: 34}, utcType: [1684493685, 0.998012], - civilType: {utcOffset: {hours: 5, minutes: 30, seconds: 0}, timeAbbrev: "Asia/Colombo", year: 2024, month: 2, - day: 27, hour: 10, minute: 30, second: 21}, + civilType: { + utcOffset: {hours: 5, minutes: 30, seconds: 0}, + timeAbbrev: "Asia/Colombo", + year: 2024, + month: 2, + day: 27, + hour: 10, + minute: 30, + second: 21 + }, booleanTypeOptional: false, intTypeOptional: 5, floatTypeOptional: 6.0, @@ -46,8 +53,16 @@ AllTypes allTypes1 = { dateTypeOptional: {year: 1993, month: 11, day: 3}, timeOfDayTypeOptional: {hour: 12, minute: 32, second: 34}, utcTypeOptional: [1684493685, 0.998012], - civilTypeOptional: {utcOffset: {hours: 5, minutes: 30, seconds: 0}, timeAbbrev: "Asia/Colombo", year: 2024, - month: 2, day: 27, hour: 10, minute: 30, second: 21}, + civilTypeOptional: { + utcOffset: {hours: 5, minutes: 30, seconds: 0}, + timeAbbrev: "Asia/Colombo", + year: 2024, + month: 2, + day: 27, + hour: 10, + minute: 30, + second: 21 + }, enumType: "TYPE_3", enumTypeOptional: "TYPE_2" }; @@ -86,8 +101,16 @@ AllTypes allTypes2 = { dateType: {year: 1996, month: 11, day: 3}, timeOfDayType: {hour: 17, minute: 32, second: 34}, utcType: [1684493685, 0.998012], - civilType: {utcOffset: {hours: 5, minutes: 30, seconds: 0}, timeAbbrev: "Asia/Colombo", year: 2024, month: 2, - day: 27, hour: 10, minute: 30, second: 21}, + civilType: { + utcOffset: {hours: 5, minutes: 30, seconds: 0}, + timeAbbrev: "Asia/Colombo", + year: 2024, + month: 2, + day: 27, + hour: 10, + minute: 30, + second: 21 + }, booleanTypeOptional: true, intTypeOptional: 6, floatTypeOptional: 66.0, @@ -96,8 +119,16 @@ AllTypes allTypes2 = { dateTypeOptional: {year: 1293, month: 11, day: 3}, timeOfDayTypeOptional: {hour: 19, minute: 32, second: 34}, utcTypeOptional: [1684493685, 0.998012], - civilTypeOptional: {utcOffset: {hours: 5, minutes: 30, seconds: 0}, timeAbbrev: "Asia/Colombo", year: 2024, - month: 2, day: 27, hour: 10, minute: 30, second: 21}, + civilTypeOptional: { + utcOffset: {hours: 5, minutes: 30, seconds: 0}, + timeAbbrev: "Asia/Colombo", + year: 2024, + month: 2, + day: 27, + hour: 10, + minute: 30, + second: 21 + }, enumType: "TYPE_1", enumTypeOptional: "TYPE_3" }; @@ -136,8 +167,16 @@ AllTypes allTypes3 = { dateType: {year: 1996, month: 11, day: 3}, timeOfDayType: {hour: 17, minute: 32, second: 34}, utcType: [1684493685, 0.998012], - civilType: {utcOffset: {hours: 5, minutes: 30, seconds: 0}, timeAbbrev: "Asia/Colombo", year: 2024, month: 2, - day: 27, hour: 10, minute: 30, second: 21}, + civilType: { + utcOffset: {hours: 5, minutes: 30, seconds: 0}, + timeAbbrev: "Asia/Colombo", + year: 2024, + month: 2, + day: 27, + hour: 10, + minute: 30, + second: 21 + }, enumType: "TYPE_1" }; @@ -165,8 +204,16 @@ AllTypes allTypes1Updated = { dateType: {year: 1996, month: 12, day: 13}, timeOfDayType: {hour: 16, minute: 12, second: 14}, utcType: [1686493685, 0.996012], - civilType: {utcOffset: {hours: 6, minutes: 0, seconds: 0}, timeAbbrev: "Asia/Colombo", year: 2022, month: 12, - day: 7, hour: 14, minute: 5, second: 43}, + civilType: { + utcOffset: {hours: 6, minutes: 0, seconds: 0}, + timeAbbrev: "Asia/Colombo", + year: 2022, + month: 12, + day: 7, + hour: 14, + minute: 5, + second: 43 + }, booleanTypeOptional: true, intTypeOptional: 53, floatTypeOptional: 26.0, @@ -175,8 +222,16 @@ AllTypes allTypes1Updated = { dateTypeOptional: {year: 1923, month: 11, day: 3}, timeOfDayTypeOptional: {hour: 18, minute: 32, second: 34}, utcTypeOptional: [1686493685, 0.996012], - civilTypeOptional: {utcOffset: {hours: 6, minutes: 0, seconds: 0}, timeAbbrev: "Asia/Colombo", year: 2022, - month: 12, day: 7, hour: 14, minute: 5, second: 43}, + civilTypeOptional: { + utcOffset: {hours: 6, minutes: 0, seconds: 0}, + timeAbbrev: "Asia/Colombo", + year: 2022, + month: 12, + day: 7, + hour: 14, + minute: 5, + second: 43 + }, enumType: "TYPE_4", enumTypeOptional: "TYPE_4" }; @@ -740,3 +795,74 @@ EmployeeInfo employeeInfoNative3 = { locationBuildingCode: "building-native-3" } }; + +public type PersonWithAssociations record {| + int id; + string name; + record {| + string code; + |}[] soldBuildings; + record {| + string code; + |}[] ownBuildings; +|}; + +Person person1 = { + id: 1, + name: "Jane" +}; + +Person person2 = { + id: 2, + name: "Mike" +}; + +PersonUpdate person1Update = { + name: "Mary" +}; + +Person person1Updated = { + id: 1, + name: "Mary" +}; + +Apartment apartment1 = { + code: "B001", + city: "New York", + state: "New York", + country: "USA", + postalCode: "10001", + 'type: "Studio", + soldpersonId: 1, + ownpersonId: 2 +}; + +ApartmentUpdate apartmentUpdate = { + postalCode: "00002" +}; + +Apartment apartment1Updated = { + code: "B001", + city: "New York", + state: "New York", + country: "USA", + postalCode: "00002", + 'type: "Studio", + soldpersonId: 1, + ownpersonId: 2 +}; + +PersonWithAssociations person1WithAssociations = { + id: 1, + name: "Jane", + soldBuildings: [ + { + code: "B001" + } + ], + ownBuildings: [ + { + code: "B002" + } + ] +}; diff --git a/ballerina/tests/persist/associations.bal b/ballerina/tests/persist/associations.bal new file mode 100644 index 0000000..341e513 --- /dev/null +++ b/ballerina/tests/persist/associations.bal @@ -0,0 +1,36 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +import ballerina/persist as _; + +type Person record {| + readonly int id; + string name; + + Apartment[] soldBuildings; + Apartment[] ownBuildings; +|}; + +type Apartment record {| + readonly string code; + string city; + string state; + string country; + string postalCode; + string 'type; + + Person soldPerson; + Person ownPerson; +|}; diff --git a/ballerina/tests/persist/rainier.bal b/ballerina/tests/persist/rainier.bal index 34b9a0e..e2e9d35 100644 --- a/ballerina/tests/persist/rainier.bal +++ b/ballerina/tests/persist/rainier.bal @@ -13,7 +13,6 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. - import ballerina/time; enum Gender { @@ -30,6 +29,7 @@ type Employee record {| time:Date hireDate; Department department; + Department? headOf; Workspace workspace; |}; @@ -57,6 +57,7 @@ type Department record {| string deptName; Employee[] employees; + Employee departmentHead; |}; type OrderItem record {| diff --git a/ballerina/tests/redis-person-tests.bal b/ballerina/tests/redis-person-tests.bal new file mode 100644 index 0000000..7069410 --- /dev/null +++ b/ballerina/tests/redis-person-tests.bal @@ -0,0 +1,316 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +import ballerina/persist; +import ballerina/test; + +@test:Config { + groups: ["person", "redis"], + dependsOn: [redisWorkspaceDeleteTestNegative, redisDepartmentDeleteTestNegative] +} +function redisPersonCreateTest() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + int[] ids = check manyAssociationsClient->/people.post([person1]); + test:assertEquals(ids, [person1.id]); + + Person personRetrieved = check manyAssociationsClient->/people/[person1.id]; + test:assertEquals(personRetrieved, person1); + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["person", "redis"], + dependsOn: [redisWorkspaceDeleteTestNegative, redisDepartmentDeleteTestNegative] +} +function redisPersonCreateTest2() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + int[] ids = check manyAssociationsClient->/people.post([person2]); + + test:assertEquals(ids, [person2.id]); + + Person personRetrieved = check manyAssociationsClient->/people/[person2.id]; + test:assertEquals(personRetrieved, person2); + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["person", "redis"], + dependsOn: [redisPersonCreateTest] +} +function redisPersonReadOneTest() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + Person personRetrieved = check manyAssociationsClient->/people/[person1.id]; + test:assertEquals(personRetrieved, person1); + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["person", "redis"], + dependsOn: [redisPersonCreateTest] +} +function redisPersonReadOneTestNegative() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + Person|error personRetrieved = manyAssociationsClient->/people/[23]; + if personRetrieved is persist:NotFoundError { + test:assertEquals(personRetrieved.message(), + "A record with the key 'Person:23' does not exist for the entity 'Person'."); + } else { + test:assertFail("NotFoundError expected."); + } + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["person", "redis"], + dependsOn: [redisPersonCreateTest, redisPersonCreateTest2] +} +function redisPersonReadManyTest() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + stream personStream = manyAssociationsClient->/people; + Person[] people = check from Person person in personStream + order by person.id ascending + select person; + + test:assertEquals(people, [person1, person2]); + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["dependent", "person"], + dependsOn: [redisPersonCreateTest, redisPersonCreateTest2, redisApartmentCreateTest] +} +function redisPersonReadManyDependentTest1() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + stream personStream = manyAssociationsClient->/people; + PersonWithAssociations[] people = check from PersonWithAssociations person in personStream + order by person.id ascending + select person; + + test:assertEquals(people, [ + { + id: 1, + name: "Jane", + soldBuildings: [{code: "B001"}], + ownBuildings: [] + }, + { + id: 2, + name: "Mike", + soldBuildings: [], + ownBuildings: [{code: "B001"}] + } + ]); + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["person", "redis"], + dependsOn: [redisPersonReadOneTest, redisPersonReadManyTest, redisPersonReadManyDependentTest1] +} +function redisPersonUpdateTest() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + Person person = check manyAssociationsClient->/people/[person1.id].put({ + name: "Mary" + }); + + test:assertEquals(person, person1Updated); + + Person personRetrieved = check manyAssociationsClient->/people/[person1.id]; + test:assertEquals(personRetrieved, person1Updated); + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["person", "redis"], + dependsOn: [redisPersonReadOneTest, redisPersonReadManyTest, redisPersonReadManyDependentTest1] +} +function redisPersonUpdateTestNegative1() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + Person|error person = manyAssociationsClient->/people/[23].put({ + name: "Jane" + }); + + if person is persist:NotFoundError { + test:assertEquals(person.message(), + "A record with the key 'Person:23' does not exist for the entity 'Person'."); + } else { + test:assertFail("NotFoundError expected."); + } + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["person", "redis"], + dependsOn: [redisPersonUpdateTest, redisApartmentDeleteTest] +} +function redisPersonDeleteTest() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + Person person = check manyAssociationsClient->/people/[person1.id].delete(); + test:assertEquals(person, person1Updated); + + stream personStream = manyAssociationsClient->/people; + Person[] people = check from Person person2 in personStream + order by person2.id ascending + select person2; + + test:assertEquals(people, [person2]); + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["person", "redis"], + dependsOn: [redisPersonDeleteTest] +} +function redisPersonDeleteTestNegative() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + Person|error person = manyAssociationsClient->/people/[person1.id].delete(); + + if person is persist:NotFoundError { + test:assertEquals(person.message(), + string `A record with the key 'Person:${person1.id}' does not exist for the entity 'Person'.`); + } else { + test:assertFail("NotFoundError expected."); + } + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["apartment", "redis"], + dependsOn: [redisPersonCreateTest, redisPersonCreateTest2] +} +function redisApartmentCreateTest() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + string[] apartmentCodes = check manyAssociationsClient->/apartments.post([apartment1]); + test:assertEquals(apartmentCodes, [apartment1.code]); + + Apartment apartmentRetrieved = check manyAssociationsClient->/apartments/[apartment1.code]; + test:assertEquals(apartmentRetrieved, apartment1); + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["apartment", "redis"], + dependsOn: [redisApartmentCreateTest] +} +function redisApartmentReadOneTest() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + Apartment apartmentRetrieved = check manyAssociationsClient->/apartments/[apartment1.code]; + test:assertEquals(apartmentRetrieved, apartment1); + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["apartment", "redis"], + dependsOn: [redisApartmentCreateTest] +} +function redisApartmentReadOneTestNegative() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + Apartment|error apartmentRetrieved = manyAssociationsClient->/apartments/["invalid-apartment-code"]; + if apartmentRetrieved is persist:NotFoundError { + test:assertEquals(apartmentRetrieved.message(), + "A record with the key 'Apartment:invalid-apartment-code' does not exist for the entity 'Apartment'."); + } else { + test:assertFail("persist:NotFoundError expected."); + } + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["apartment", "redis"], + dependsOn: [redisApartmentReadOneTest] +} +function redisApartmentUpdateTest() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + Apartment apartment = check manyAssociationsClient->/apartments/[apartment1.code].put({ + postalCode: "00002" + }); + + test:assertEquals(apartment, apartment1Updated); + + Apartment apartmentRetrieved = check manyAssociationsClient->/apartments/[apartment1.code]; + test:assertEquals(apartmentRetrieved, apartment1Updated); + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["apartment", "redis"], + dependsOn: [redisApartmentReadOneTest] +} +function redisApartmentUpdateTestNegative1() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + Apartment|error apartment = manyAssociationsClient->/apartments/["invalid-apartment-code"].put({ + postalCode: "00002" + }); + + if apartment is persist:NotFoundError { + test:assertEquals(apartment.message(), + "A record with the key 'Apartment:invalid-apartment-code' does not exist for the entity 'Apartment'."); + } else { + test:assertFail("persist:NotFoundError expected."); + } + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["apartment", "redis"], + dependsOn: [redisApartmentUpdateTest, redisPersonReadManyDependentTest1] +} +function redisApartmentDeleteTest() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + Apartment apartment = check manyAssociationsClient->/apartments/[apartment1.code].delete(); + test:assertEquals(apartment, apartment1Updated); + + stream apartmentStream = manyAssociationsClient->/apartments; + Apartment[] apartments = check from Apartment apartment2 in apartmentStream + order by apartment2.code ascending + select apartment2; + + test:assertEquals(apartments, []); + check manyAssociationsClient.close(); +} + +@test:Config { + groups: ["apartment", "redis"], + dependsOn: [redisApartmentDeleteTest] +} +function redisApartmentDeleteTestNegative() returns error? { + RedisManyAssociationsClient manyAssociationsClient = check new (); + + Apartment|error apartment = manyAssociationsClient->/apartments/[apartment1.code].delete(); + + if apartment is error { + test:assertEquals(apartment.message(), + string `A record with the key 'Apartment:${apartment1.code}' does not exist for the entity 'Apartment'.`); + } else { + test:assertFail("persist:NotFoundError expected."); + } + check manyAssociationsClient.close(); +} diff --git a/ballerina/tests/redis_rainier_generated_client.bal b/ballerina/tests/redis_rainier_generated_client.bal index d7547ad..26e7215 100644 --- a/ballerina/tests/redis_rainier_generated_client.bal +++ b/ballerina/tests/redis_rainier_generated_client.bal @@ -13,7 +13,6 @@ // 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 ballerinax/redis; @@ -32,7 +31,7 @@ public isolated client class RedisRainierClient { private final map persistClients; private final record {|RedisMetadata...;|} & readonly metadata = { - [EMPLOYEE] : { + [EMPLOYEE]: { entityName: "Employee", collectionName: "Employee", fieldMetadata: { @@ -44,64 +43,156 @@ public isolated client class RedisRainierClient { hireDate: {fieldName: "hireDate", fieldDataType: DATE}, departmentDeptNo: {fieldName: "departmentDeptNo", fieldDataType: STRING}, workspaceWorkspaceId: {fieldName: "workspaceWorkspaceId", fieldDataType: STRING}, - "department.deptNo": {relation: {entityName: "department", refField: "deptNo", - refFieldDataType: STRING}}, - "department.deptName": {relation: {entityName: "department", refField: "deptName", - refFieldDataType: STRING}}, - "workspace.workspaceId": {relation: {entityName: "workspace", refField: "workspaceId", - refFieldDataType: STRING}}, - "workspace.workspaceType": {relation: {entityName: "workspace", refField: "workspaceType", - refFieldDataType: STRING}}, - "workspace.locationBuildingCode": {relation: {entityName: "workspace", - refField: "locationBuildingCode", refFieldDataType: STRING}} + "department.deptNo": { + relation: { + entityName: "department", + refField: "deptNo", + refFieldDataType: STRING + } + }, + "department.deptName": { + relation: { + entityName: "department", + refField: "deptName", + refFieldDataType: STRING + } + }, + "workspace.workspaceId": { + relation: { + entityName: "workspace", + refField: "workspaceId", + refFieldDataType: STRING + } + }, + "workspace.workspaceType": { + relation: { + entityName: "workspace", + refField: "workspaceType", + refFieldDataType: STRING + } + }, + "workspace.locationBuildingCode": { + relation: { + entityName: "workspace", + refField: "locationBuildingCode", + refFieldDataType: STRING + } + } }, keyFields: ["empNo"], refMetadata: { - department: {entity: Department, fieldName: "department", refCollection: "Department", - refFields: ["deptNo"], joinFields: ["departmentDeptNo"], 'type: ONE_TO_MANY}, - workspace: {entity: Workspace, fieldName: "workspace", refCollection: "Workspace", - refFields: ["workspaceId"], joinFields: ["workspaceWorkspaceId"], 'type: ONE_TO_MANY} + department: { + entity: Department, + fieldName: "department", + refCollection: "Department", + refMetaDataKey: "employees", + refFields: ["deptNo"], + joinFields: ["departmentDeptNo"], + 'type: ONE_TO_MANY + }, + workspace: { + entity: Workspace, + fieldName: "workspace", + refCollection: "Workspace", + refMetaDataKey: "employees", + refFields: ["workspaceId"], + joinFields: ["workspaceWorkspaceId"], + 'type: ONE_TO_MANY + } } }, - [WORKSPACE] : { + [WORKSPACE]: { entityName: "Workspace", collectionName: "Workspace", fieldMetadata: { workspaceId: {fieldName: "workspaceId", fieldDataType: STRING}, workspaceType: {fieldName: "workspaceType", fieldDataType: STRING}, locationBuildingCode: {fieldName: "locationBuildingCode", fieldDataType: STRING}, - "location.buildingCode": {relation: {entityName: "location", refField: "buildingCode", - refFieldDataType: STRING}}, + "location.buildingCode": { + relation: { + entityName: "location", + refField: "buildingCode", + refFieldDataType: STRING + } + }, "location.city": {relation: {entityName: "location", refField: "city", refFieldDataType: STRING}}, "location.state": {relation: {entityName: "location", refField: "state", refFieldDataType: STRING}}, "location.country": {relation: {entityName: "location", refField: "country", refFieldDataType: STRING}}, - "location.postalCode": {relation: {entityName: "location", refField: "postalCode", - refFieldDataType: STRING}}, + "location.postalCode": { + relation: { + entityName: "location", + refField: "postalCode", + refFieldDataType: STRING + } + }, "location.type": {relation: {entityName: "location", refField: "type", refFieldDataType: STRING}}, "employees[].empNo": {relation: {entityName: "employees", refField: "empNo", refFieldDataType: STRING}}, - "employees[].firstName": {relation: {entityName: "employees", refField: "firstName", - refFieldDataType: STRING}}, - "employees[].lastName": {relation: {entityName: "employees", refField: "lastName", - refFieldDataType: STRING}}, - "employees[].birthDate": {relation: {entityName: "employees", refField: "birthDate", - refFieldDataType: DATE}}, + "employees[].firstName": { + relation: { + entityName: "employees", + refField: "firstName", + refFieldDataType: STRING + } + }, + "employees[].lastName": { + relation: { + entityName: "employees", + refField: "lastName", + refFieldDataType: STRING + } + }, + "employees[].birthDate": { + relation: { + entityName: "employees", + refField: "birthDate", + refFieldDataType: DATE + } + }, "employees[].gender": {relation: {entityName: "employees", refField: "gender", refFieldDataType: ENUM}}, - "employees[].hireDate": {relation: {entityName: "employees", refField: "hireDate", - refFieldDataType: DATE}}, - "employees[].departmentDeptNo": {relation: {entityName: "employees", refField: "departmentDeptNo", - refFieldDataType: STRING}}, - "employees[].workspaceWorkspaceId": {relation: {entityName: "employees", - refField: "workspaceWorkspaceId", refFieldDataType: STRING}} + "employees[].hireDate": { + relation: { + entityName: "employees", + refField: "hireDate", + refFieldDataType: DATE + } + }, + "employees[].departmentDeptNo": { + relation: { + entityName: "employees", + refField: "departmentDeptNo", + refFieldDataType: STRING + } + }, + "employees[].workspaceWorkspaceId": { + relation: { + entityName: "employees", + refField: "workspaceWorkspaceId", + refFieldDataType: STRING + } + } }, keyFields: ["workspaceId"], refMetadata: { - location: {entity: Building, fieldName: "location", refCollection: "Building", - refFields: ["buildingCode"], joinFields: ["locationBuildingCode"], 'type: ONE_TO_MANY}, - employees: {entity: Employee, fieldName: "employees", refCollection: "Employee", - refFields: ["workspaceWorkspaceId"], joinFields: ["workspaceId"], 'type: MANY_TO_ONE} + location: { + entity: Building, + fieldName: "location", + refCollection: "Building", + refMetaDataKey: "workspaces", + refFields: ["buildingCode"], + joinFields: ["locationBuildingCode"], + 'type: ONE_TO_MANY + }, + employees: { + entity: Employee, + fieldName: "employees", + refCollection: "Employee", + refFields: ["workspaceWorkspaceId"], + joinFields: ["workspaceId"], + 'type: MANY_TO_ONE + } } }, - [BUILDING] : { + [BUILDING]: { entityName: "Building", collectionName: "Building", fieldMetadata: { @@ -111,43 +202,104 @@ public isolated client class RedisRainierClient { country: {fieldName: "country", fieldDataType: STRING}, postalCode: {fieldName: "postalCode", fieldDataType: STRING}, 'type: {fieldName: "type", fieldDataType: STRING}, - "workspaces[].workspaceId": {relation: {entityName: "workspaces", refField: "workspaceId", - refFieldDataType: STRING}}, - "workspaces[].workspaceType": {relation: {entityName: "workspaces", refField: "workspaceType", - refFieldDataType: STRING}}, - "workspaces[].locationBuildingCode": {relation: {entityName: "workspaces", - refField: "locationBuildingCode", refFieldDataType: STRING}} + "workspaces[].workspaceId": { + relation: { + entityName: "workspaces", + refField: "workspaceId", + refFieldDataType: STRING + } + }, + "workspaces[].workspaceType": { + relation: { + entityName: "workspaces", + refField: "workspaceType", + refFieldDataType: STRING + } + }, + "workspaces[].locationBuildingCode": { + relation: { + entityName: "workspaces", + refField: "locationBuildingCode", + refFieldDataType: STRING + } + } }, keyFields: ["buildingCode"], - refMetadata: {workspaces: {entity: Workspace, fieldName: "workspaces", refCollection: "Workspace", - refFields: ["locationBuildingCode"], joinFields: ["buildingCode"], 'type: MANY_TO_ONE}} + refMetadata: { + workspaces: { + entity: Workspace, + fieldName: "workspaces", + refCollection: "Workspace", + refFields: ["locationBuildingCode"], + joinFields: ["buildingCode"], + 'type: MANY_TO_ONE + } + } }, - [DEPARTMENT] : { + [DEPARTMENT]: { entityName: "Department", collectionName: "Department", fieldMetadata: { deptNo: {fieldName: "deptNo", fieldDataType: STRING}, deptName: {fieldName: "deptName", fieldDataType: STRING}, "employees[].empNo": {relation: {entityName: "employees", refField: "empNo", refFieldDataType: STRING}}, - "employees[].firstName": {relation: {entityName: "employees", refField: "firstName", - refFieldDataType: STRING}}, - "employees[].lastName": {relation: {entityName: "employees", refField: "lastName", - refFieldDataType: STRING}}, - "employees[].birthDate": {relation: {entityName: "employees", refField: "birthDate", - refFieldDataType: DATE}}, + "employees[].firstName": { + relation: { + entityName: "employees", + refField: "firstName", + refFieldDataType: STRING + } + }, + "employees[].lastName": { + relation: { + entityName: "employees", + refField: "lastName", + refFieldDataType: STRING + } + }, + "employees[].birthDate": { + relation: { + entityName: "employees", + refField: "birthDate", + refFieldDataType: DATE + } + }, "employees[].gender": {relation: {entityName: "employees", refField: "gender", refFieldDataType: ENUM}}, - "employees[].hireDate": {relation: {entityName: "employees", refField: "hireDate", - refFieldDataType: DATE}}, - "employees[].departmentDeptNo": {relation: {entityName: "employees", refField: "departmentDeptNo", - refFieldDataType: STRING}}, - "employees[].workspaceWorkspaceId": {relation: {entityName: "employees", - refField: "workspaceWorkspaceId", refFieldDataType: STRING}} + "employees[].hireDate": { + relation: { + entityName: "employees", + refField: "hireDate", + refFieldDataType: DATE + } + }, + "employees[].departmentDeptNo": { + relation: { + entityName: "employees", + refField: "departmentDeptNo", + refFieldDataType: STRING + } + }, + "employees[].workspaceWorkspaceId": { + relation: { + entityName: "employees", + refField: "workspaceWorkspaceId", + refFieldDataType: STRING + } + } }, keyFields: ["deptNo"], - refMetadata: {employees: {entity: Employee, fieldName: "employees", refCollection: "Employee", - refFields: ["departmentDeptNo"], joinFields: ["deptNo"], 'type: MANY_TO_ONE}} + refMetadata: { + employees: { + entity: Employee, + fieldName: "employees", + refCollection: "Employee", + refFields: ["departmentDeptNo"], + joinFields: ["deptNo"], + 'type: MANY_TO_ONE + } + } }, - [ORDER_ITEM] : { + [ORDER_ITEM]: { entityName: "OrderItem", collectionName: "OrderItem", fieldMetadata: { @@ -167,21 +319,21 @@ public isolated client class RedisRainierClient { } self.dbClient = dbClient; self.persistClients = { - [EMPLOYEE] : check new (dbClient, self.metadata.get(EMPLOYEE)), - [WORKSPACE] : check new (dbClient, self.metadata.get(WORKSPACE)), - [BUILDING] : check new (dbClient, self.metadata.get(BUILDING)), - [DEPARTMENT] : check new (dbClient, self.metadata.get(DEPARTMENT)), - [ORDER_ITEM] : check new (dbClient, self.metadata.get(ORDER_ITEM)) + [EMPLOYEE]: check new (dbClient, self.metadata.get(EMPLOYEE)), + [WORKSPACE]: check new (dbClient, self.metadata.get(WORKSPACE)), + [BUILDING]: check new (dbClient, self.metadata.get(BUILDING)), + [DEPARTMENT]: check new (dbClient, self.metadata.get(DEPARTMENT)), + [ORDER_ITEM]: check new (dbClient, self.metadata.get(ORDER_ITEM)) }; } - isolated resource function get employees(EmployeeTargetType targetType = <>) returns stream) returns stream = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "query" } external; - isolated resource function get employees/[string empNo](EmployeeTargetType targetType = <>) + isolated resource function get employees/[string empNo](EmployeeTargetType targetType = <>) returns targetType|persist:Error = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "queryOne" @@ -216,13 +368,13 @@ public isolated client class RedisRainierClient { return result; } - isolated resource function get workspaces(WorkspaceTargetType targetType = <>) returns stream) returns stream = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "query" } external; - isolated resource function get workspaces/[string workspaceId](WorkspaceTargetType targetType = <>) + isolated resource function get workspaces/[string workspaceId](WorkspaceTargetType targetType = <>) returns targetType|persist:Error = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "queryOne" @@ -238,7 +390,7 @@ public isolated client class RedisRainierClient { select inserted.workspaceId; } - isolated resource function put workspaces/[string workspaceId](WorkspaceUpdate value) + isolated resource function put workspaces/[string workspaceId](WorkspaceUpdate value) returns Workspace|persist:Error { RedisClient redisClient; lock { @@ -258,13 +410,13 @@ public isolated client class RedisRainierClient { return result; } - isolated resource function get buildings(BuildingTargetType targetType = <>) returns stream) returns stream = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "query" } external; - isolated resource function get buildings/[string buildingCode](BuildingTargetType targetType = <>) + isolated resource function get buildings/[string buildingCode](BuildingTargetType targetType = <>) returns targetType|persist:Error = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "queryOne" @@ -280,7 +432,7 @@ public isolated client class RedisRainierClient { select inserted.buildingCode; } - isolated resource function put buildings/[string buildingCode](BuildingUpdate value) + isolated resource function put buildings/[string buildingCode](BuildingUpdate value) returns Building|persist:Error { RedisClient redisClient; lock { @@ -300,13 +452,13 @@ public isolated client class RedisRainierClient { return result; } - isolated resource function get departments(DepartmentTargetType targetType = <>) returns stream) returns stream = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "query" } external; - isolated resource function get departments/[string deptNo](DepartmentTargetType targetType = <>) + isolated resource function get departments/[string deptNo](DepartmentTargetType targetType = <>) returns targetType|persist:Error = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "queryOne" @@ -322,7 +474,7 @@ public isolated client class RedisRainierClient { select inserted.deptNo; } - isolated resource function put departments/[string deptNo](DepartmentUpdate value) + isolated resource function put departments/[string deptNo](DepartmentUpdate value) returns Department|persist:Error { RedisClient redisClient; lock { @@ -342,13 +494,13 @@ public isolated client class RedisRainierClient { return result; } - isolated resource function get orderitems(OrderItemTargetType targetType = <>) returns stream) returns stream = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "query" } external; - isolated resource function get orderitems/[string orderId]/[string itemId](OrderItemTargetType targetType = <>) + isolated resource function get orderitems/[string orderId]/[string itemId](OrderItemTargetType targetType = <>) returns targetType|persist:Error = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "queryOne" @@ -364,7 +516,7 @@ public isolated client class RedisRainierClient { select [inserted.orderId, inserted.itemId]; } - isolated resource function put orderitems/[string orderId]/[string itemId](OrderItemUpdate value) + isolated resource function put orderitems/[string orderId]/[string itemId](OrderItemUpdate value) returns OrderItem|persist:Error { RedisClient redisClient; lock { diff --git a/ballerina/tests/redis_test_entities_generated_client.bal b/ballerina/tests/redis_test_entities_generated_client.bal index 5bfafea..9ad105b 100644 --- a/ballerina/tests/redis_test_entities_generated_client.bal +++ b/ballerina/tests/redis_test_entities_generated_client.bal @@ -13,7 +13,6 @@ // 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 ballerinax/redis; @@ -35,7 +34,7 @@ public isolated client class RedisTestEntitiesClient { private final map persistClients; private final record {|RedisMetadata...;|} & readonly metadata = { - [ALL_TYPES] : { + [ALL_TYPES]: { entityName: "AllTypes", collectionName: "AllTypes", fieldMetadata: { @@ -63,7 +62,7 @@ public isolated client class RedisTestEntitiesClient { }, keyFields: ["id"] }, - [STRING_ID_RECORD] : { + [STRING_ID_RECORD]: { entityName: "StringIdRecord", collectionName: "StringIdRecord", fieldMetadata: { @@ -72,7 +71,7 @@ public isolated client class RedisTestEntitiesClient { }, keyFields: ["id"] }, - [INT_ID_RECORD] : { + [INT_ID_RECORD]: { entityName: "IntIdRecord", collectionName: "IntIdRecord", fieldMetadata: { @@ -81,7 +80,7 @@ public isolated client class RedisTestEntitiesClient { }, keyFields: ["id"] }, - [FLOAT_ID_RECORD] : { + [FLOAT_ID_RECORD]: { entityName: "FloatIdRecord", collectionName: "FloatIdRecord", fieldMetadata: { @@ -90,7 +89,7 @@ public isolated client class RedisTestEntitiesClient { }, keyFields: ["id"] }, - [DECIMAL_ID_RECORD] : { + [DECIMAL_ID_RECORD]: { entityName: "DecimalIdRecord", collectionName: "DecimalIdRecord", fieldMetadata: { @@ -99,7 +98,7 @@ public isolated client class RedisTestEntitiesClient { }, keyFields: ["id"] }, - [BOOLEAN_ID_RECORD] : { + [BOOLEAN_ID_RECORD]: { entityName: "BooleanIdRecord", collectionName: "BooleanIdRecord", fieldMetadata: { @@ -108,7 +107,7 @@ public isolated client class RedisTestEntitiesClient { }, keyFields: ["id"] }, - [COMPOSITE_ASSOCIATION_RECORD] : { + [COMPOSITE_ASSOCIATION_RECORD]: { entityName: "CompositeAssociationRecord", collectionName: "CompositeAssociationRecord", fieldMetadata: { @@ -119,27 +118,75 @@ public isolated client class RedisTestEntitiesClient { alltypesidrecordFloatType: {fieldName: "alltypesidrecordFloatType", fieldDataType: FLOAT}, alltypesidrecordDecimalType: {fieldName: "alltypesidrecordDecimalType", fieldDataType: DECIMAL}, alltypesidrecordStringType: {fieldName: "alltypesidrecordStringType", fieldDataType: STRING}, - "allTypesIdRecord.booleanType": {relation: {entityName: "allTypesIdRecord", refField: "booleanType", - refFieldDataType: BOOLEAN}}, - "allTypesIdRecord.intType": {relation: {entityName: "allTypesIdRecord", refField: "intType", - refFieldDataType: INT}}, - "allTypesIdRecord.floatType": {relation: {entityName: "allTypesIdRecord", refField: "floatType", - refFieldDataType: FLOAT}}, - "allTypesIdRecord.decimalType": {relation: {entityName: "allTypesIdRecord", refField: "decimalType", - refFieldDataType: DECIMAL}}, - "allTypesIdRecord.stringType": {relation: {entityName: "allTypesIdRecord", refField: "stringType", - refFieldDataType: STRING}}, - "allTypesIdRecord.randomField": {relation: {entityName: "allTypesIdRecord", refField: "randomField", - refFieldDataType: STRING}} + "allTypesIdRecord.booleanType": { + relation: { + entityName: "allTypesIdRecord", + refField: "booleanType", + refFieldDataType: BOOLEAN + } + }, + "allTypesIdRecord.intType": { + relation: { + entityName: "allTypesIdRecord", + refField: "intType", + refFieldDataType: INT + } + }, + "allTypesIdRecord.floatType": { + relation: { + entityName: "allTypesIdRecord", + refField: "floatType", + refFieldDataType: FLOAT + } + }, + "allTypesIdRecord.decimalType": { + relation: { + entityName: "allTypesIdRecord", + refField: "decimalType", + refFieldDataType: DECIMAL + } + }, + "allTypesIdRecord.stringType": { + relation: { + entityName: "allTypesIdRecord", + refField: "stringType", + refFieldDataType: STRING + } + }, + "allTypesIdRecord.randomField": { + relation: { + entityName: "allTypesIdRecord", + refField: "randomField", + refFieldDataType: STRING + } + } }, keyFields: ["id"], - refMetadata: {allTypesIdRecord: {entity: AllTypesIdRecord, fieldName: "allTypesIdRecord", - refCollection: "AllTypesIdRecord", refFields: ["booleanType", "intType", "floatType", "decimalType", - "stringType"], joinFields: ["alltypesidrecordBooleanType", "alltypesidrecordIntType", - "alltypesidrecordFloatType", "alltypesidrecordDecimalType", "alltypesidrecordStringType"], - 'type: ONE_TO_ONE}} + refMetadata: { + allTypesIdRecord: { + entity: AllTypesIdRecord, + fieldName: "allTypesIdRecord", + refCollection: "AllTypesIdRecord", + refMetaDataKey: "compositeAssociationRecord", + refFields: [ + "booleanType", + "intType", + "floatType", + "decimalType", + "stringType" + ], + joinFields: [ + "alltypesidrecordBooleanType", + "alltypesidrecordIntType", + "alltypesidrecordFloatType", + "alltypesidrecordDecimalType", + "alltypesidrecordStringType" + ], + 'type: ONE_TO_ONE + } + } }, - [ALL_TYPES_ID_RECORD] : { + [ALL_TYPES_ID_RECORD]: { entityName: "AllTypesIdRecord", collectionName: "AllTypesIdRecord", fieldMetadata: { @@ -149,32 +196,79 @@ public isolated client class RedisTestEntitiesClient { decimalType: {fieldName: "decimalType", fieldDataType: DECIMAL}, stringType: {fieldName: "stringType", fieldDataType: STRING}, randomField: {fieldName: "randomField", fieldDataType: STRING}, - "compositeAssociationRecord.id": {relation: {entityName: "compositeAssociationRecord", refField: "id", - refFieldDataType: STRING}}, - "compositeAssociationRecord.randomField": {relation: {entityName: "compositeAssociationRecord", - refField: "randomField", refFieldDataType: STRING}}, - "compositeAssociationRecord.alltypesidrecordBooleanType": {relation: { - entityName: "compositeAssociationRecord", refField: "alltypesidrecordBooleanType", - refFieldDataType: BOOLEAN}}, - "compositeAssociationRecord.alltypesidrecordIntType": {relation: { - entityName: "compositeAssociationRecord", refField: "alltypesidrecordIntType", - refFieldDataType: INT}}, - "compositeAssociationRecord.alltypesidrecordFloatType": {relation: { - entityName: "compositeAssociationRecord", refField: "alltypesidrecordFloatType", - refFieldDataType: FLOAT}}, - "compositeAssociationRecord.alltypesidrecordDecimalType": {relation: { - entityName: "compositeAssociationRecord", refField: "alltypesidrecordDecimalType", - refFieldDataType: DECIMAL}}, - "compositeAssociationRecord.alltypesidrecordStringType": {relation: { - entityName: "compositeAssociationRecord", refField: "alltypesidrecordStringType", - refFieldDataType: STRING}} + "compositeAssociationRecord.id": { + relation: { + entityName: "compositeAssociationRecord", + refField: "id", + refFieldDataType: STRING + } + }, + "compositeAssociationRecord.randomField": { + relation: { + entityName: "compositeAssociationRecord", + refField: "randomField", + refFieldDataType: STRING + } + }, + "compositeAssociationRecord.alltypesidrecordBooleanType": { + relation: { + entityName: "compositeAssociationRecord", + refField: "alltypesidrecordBooleanType", + refFieldDataType: BOOLEAN + } + }, + "compositeAssociationRecord.alltypesidrecordIntType": { + relation: { + entityName: "compositeAssociationRecord", + refField: "alltypesidrecordIntType", + refFieldDataType: INT + } + }, + "compositeAssociationRecord.alltypesidrecordFloatType": { + relation: { + entityName: "compositeAssociationRecord", + refField: "alltypesidrecordFloatType", + refFieldDataType: FLOAT + } + }, + "compositeAssociationRecord.alltypesidrecordDecimalType": { + relation: { + entityName: "compositeAssociationRecord", + refField: "alltypesidrecordDecimalType", + refFieldDataType: DECIMAL + } + }, + "compositeAssociationRecord.alltypesidrecordStringType": { + relation: { + entityName: "compositeAssociationRecord", + refField: "alltypesidrecordStringType", + refFieldDataType: STRING + } + } }, keyFields: ["booleanType", "intType", "floatType", "decimalType", "stringType"], - refMetadata: {compositeAssociationRecord: {entity: CompositeAssociationRecord, - fieldName: "compositeAssociationRecord", refCollection: "CompositeAssociationRecord", - refFields: ["alltypesidrecordBooleanType", "alltypesidrecordIntType", "alltypesidrecordFloatType", - "alltypesidrecordDecimalType", "alltypesidrecordStringType"], joinFields: ["booleanType", "intType", - "floatType", "decimalType", "stringType"], 'type: ONE_TO_ONE}} + refMetadata: { + compositeAssociationRecord: { + entity: CompositeAssociationRecord, + fieldName: "compositeAssociationRecord", + refCollection: "CompositeAssociationRecord", + refFields: [ + "alltypesidrecordBooleanType", + "alltypesidrecordIntType", + "alltypesidrecordFloatType", + "alltypesidrecordDecimalType", + "alltypesidrecordStringType" + ], + joinFields: [ + "booleanType", + "intType", + "floatType", + "decimalType", + "stringType" + ], + 'type: ONE_TO_ONE + } + } } }; @@ -185,24 +279,24 @@ public isolated client class RedisTestEntitiesClient { } self.dbClient = dbClient; self.persistClients = { - [ALL_TYPES] : check new (dbClient, self.metadata.get(ALL_TYPES)), - [STRING_ID_RECORD] : check new (dbClient, self.metadata.get(STRING_ID_RECORD)), - [INT_ID_RECORD] : check new (dbClient, self.metadata.get(INT_ID_RECORD)), - [FLOAT_ID_RECORD] : check new (dbClient, self.metadata.get(FLOAT_ID_RECORD)), - [DECIMAL_ID_RECORD] : check new (dbClient, self.metadata.get(DECIMAL_ID_RECORD)), - [BOOLEAN_ID_RECORD] : check new (dbClient, self.metadata.get(BOOLEAN_ID_RECORD)), - [COMPOSITE_ASSOCIATION_RECORD] : check new (dbClient, self.metadata.get(COMPOSITE_ASSOCIATION_RECORD)), - [ALL_TYPES_ID_RECORD] : check new (dbClient, self.metadata.get(ALL_TYPES_ID_RECORD)) + [ALL_TYPES]: check new (dbClient, self.metadata.get(ALL_TYPES)), + [STRING_ID_RECORD]: check new (dbClient, self.metadata.get(STRING_ID_RECORD)), + [INT_ID_RECORD]: check new (dbClient, self.metadata.get(INT_ID_RECORD)), + [FLOAT_ID_RECORD]: check new (dbClient, self.metadata.get(FLOAT_ID_RECORD)), + [DECIMAL_ID_RECORD]: check new (dbClient, self.metadata.get(DECIMAL_ID_RECORD)), + [BOOLEAN_ID_RECORD]: check new (dbClient, self.metadata.get(BOOLEAN_ID_RECORD)), + [COMPOSITE_ASSOCIATION_RECORD]: check new (dbClient, self.metadata.get(COMPOSITE_ASSOCIATION_RECORD)), + [ALL_TYPES_ID_RECORD]: check new (dbClient, self.metadata.get(ALL_TYPES_ID_RECORD)) }; } - isolated resource function get alltypes(AllTypesTargetType targetType = <>) + isolated resource function get alltypes(AllTypesTargetType targetType = <>) returns stream = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "query" } external; - isolated resource function get alltypes/[int id](AllTypesTargetType targetType = <>) + isolated resource function get alltypes/[int id](AllTypesTargetType targetType = <>) returns targetType|persist:Error = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "queryOne" @@ -237,13 +331,13 @@ public isolated client class RedisTestEntitiesClient { return result; } - isolated resource function get stringidrecords(StringIdRecordTargetType targetType = <>) + isolated resource function get stringidrecords(StringIdRecordTargetType targetType = <>) returns stream = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "query" } external; - isolated resource function get stringidrecords/[string id](StringIdRecordTargetType targetType = <>) + isolated resource function get stringidrecords/[string id](StringIdRecordTargetType targetType = <>) returns targetType|persist:Error = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "queryOne" @@ -259,7 +353,7 @@ public isolated client class RedisTestEntitiesClient { select inserted.id; } - isolated resource function put stringidrecords/[string id](StringIdRecordUpdate value) + isolated resource function put stringidrecords/[string id](StringIdRecordUpdate value) returns StringIdRecord|persist:Error { RedisClient redisClient; lock { @@ -279,13 +373,13 @@ public isolated client class RedisTestEntitiesClient { return result; } - isolated resource function get intidrecords(IntIdRecordTargetType targetType = <>) + isolated resource function get intidrecords(IntIdRecordTargetType targetType = <>) returns stream = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "query" } external; - isolated resource function get intidrecords/[int id](IntIdRecordTargetType targetType = <>) + isolated resource function get intidrecords/[int id](IntIdRecordTargetType targetType = <>) returns targetType|persist:Error = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "queryOne" @@ -320,13 +414,13 @@ public isolated client class RedisTestEntitiesClient { return result; } - isolated resource function get floatidrecords(FloatIdRecordTargetType targetType = <>) + isolated resource function get floatidrecords(FloatIdRecordTargetType targetType = <>) returns stream = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "query" } external; - isolated resource function get floatidrecords/[float id](FloatIdRecordTargetType targetType = <>) + isolated resource function get floatidrecords/[float id](FloatIdRecordTargetType targetType = <>) returns targetType|persist:Error = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "queryOne" @@ -342,7 +436,7 @@ public isolated client class RedisTestEntitiesClient { select inserted.id; } - isolated resource function put floatidrecords/[float id](FloatIdRecordUpdate value) + isolated resource function put floatidrecords/[float id](FloatIdRecordUpdate value) returns FloatIdRecord|persist:Error { RedisClient redisClient; lock { @@ -362,13 +456,13 @@ public isolated client class RedisTestEntitiesClient { return result; } - isolated resource function get decimalidrecords(DecimalIdRecordTargetType targetType = <>) + isolated resource function get decimalidrecords(DecimalIdRecordTargetType targetType = <>) returns stream = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "query" } external; - isolated resource function get decimalidrecords/[decimal id](DecimalIdRecordTargetType targetType = <>) + isolated resource function get decimalidrecords/[decimal id](DecimalIdRecordTargetType targetType = <>) returns targetType|persist:Error = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "queryOne" @@ -384,7 +478,7 @@ public isolated client class RedisTestEntitiesClient { select inserted.id; } - isolated resource function put decimalidrecords/[decimal id](DecimalIdRecordUpdate value) + isolated resource function put decimalidrecords/[decimal id](DecimalIdRecordUpdate value) returns DecimalIdRecord|persist:Error { RedisClient redisClient; lock { @@ -404,13 +498,13 @@ public isolated client class RedisTestEntitiesClient { return result; } - isolated resource function get booleanidrecords(BooleanIdRecordTargetType targetType = <>) + isolated resource function get booleanidrecords(BooleanIdRecordTargetType targetType = <>) returns stream = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "query" } external; - isolated resource function get booleanidrecords/[boolean id](BooleanIdRecordTargetType targetType = <>) + isolated resource function get booleanidrecords/[boolean id](BooleanIdRecordTargetType targetType = <>) returns targetType|persist:Error = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "queryOne" @@ -426,7 +520,7 @@ public isolated client class RedisTestEntitiesClient { select inserted.id; } - isolated resource function put booleanidrecords/[boolean id](BooleanIdRecordUpdate value) + isolated resource function put booleanidrecords/[boolean id](BooleanIdRecordUpdate value) returns BooleanIdRecord|persist:Error { RedisClient redisClient; lock { @@ -446,20 +540,20 @@ public isolated client class RedisTestEntitiesClient { return result; } - isolated resource function get compositeassociationrecords(CompositeAssociationRecordTargetType targetType = <>) + isolated resource function get compositeassociationrecords(CompositeAssociationRecordTargetType targetType = <>) returns stream = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "query" } external; - isolated resource function get - compositeassociationrecords/[string id](CompositeAssociationRecordTargetType targetType = <>) + isolated resource function get + compositeassociationrecords/[string id](CompositeAssociationRecordTargetType targetType = <>) returns targetType|persist:Error = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "queryOne" } external; - isolated resource function post compositeassociationrecords(CompositeAssociationRecordInsert[] data) + isolated resource function post compositeassociationrecords(CompositeAssociationRecordInsert[] data) returns string[]|persist:Error { RedisClient redisClient; lock { @@ -470,7 +564,7 @@ public isolated client class RedisTestEntitiesClient { select inserted.id; } - isolated resource function put compositeassociationrecords/[string id](CompositeAssociationRecordUpdate value) + isolated resource function put compositeassociationrecords/[string id](CompositeAssociationRecordUpdate value) returns CompositeAssociationRecord|persist:Error { RedisClient redisClient; lock { @@ -480,7 +574,7 @@ public isolated client class RedisTestEntitiesClient { return self->/compositeassociationrecords/[id]; } - isolated resource function delete compositeassociationrecords/[string id]() + isolated resource function delete compositeassociationrecords/[string id]() returns CompositeAssociationRecord|persist:Error { CompositeAssociationRecord result = check self->/compositeassociationrecords/[id]; RedisClient redisClient; @@ -491,20 +585,20 @@ public isolated client class RedisTestEntitiesClient { return result; } - isolated resource function get alltypesidrecords(AllTypesIdRecordTargetType targetType = <>) + isolated resource function get alltypesidrecords(AllTypesIdRecordTargetType targetType = <>) returns stream = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "query" } external; isolated resource function get alltypesidrecords/[boolean booleanType]/[int intType]/[float floatType] - /[decimal decimalType]/[string stringType](AllTypesIdRecordTargetType targetType = <>) + /[decimal decimalType]/[string stringType](AllTypesIdRecordTargetType targetType = <>) returns targetType|persist:Error = @java:Method { 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", name: "queryOne" } external; - isolated resource function post alltypesidrecords(AllTypesIdRecordInsert[] data) returns [boolean, int, float, + isolated resource function post alltypesidrecords(AllTypesIdRecordInsert[] data) returns [boolean, int, float, decimal, string][]|persist:Error { RedisClient redisClient; lock { @@ -512,8 +606,13 @@ public isolated client class RedisTestEntitiesClient { } _ = check redisClient.runBatchInsertQuery(data); return from AllTypesIdRecordInsert inserted in data - select [inserted.booleanType, inserted.intType, inserted.floatType, inserted.decimalType, - inserted.stringType]; + select [ + inserted.booleanType, + inserted.intType, + inserted.floatType, + inserted.decimalType, + inserted.stringType + ]; } isolated resource function put alltypesidrecords/[boolean booleanType]/[int intType]/[float floatType] @@ -522,22 +621,34 @@ public isolated client class RedisTestEntitiesClient { lock { redisClient = self.persistClients.get(ALL_TYPES_ID_RECORD); } - _ = check redisClient.runUpdateQuery({"booleanType": booleanType, "intType": intType, "floatType": - floatType, "decimalType": decimalType, "stringType": stringType}, value); + _ = check redisClient.runUpdateQuery({ + "booleanType": booleanType, + "intType": intType, + "floatType": + floatType, + "decimalType": decimalType, + "stringType": stringType + }, value); return self->/alltypesidrecords/[booleanType]/[intType]/[floatType]/[decimalType]/[stringType]; } - isolated resource function delete - alltypesidrecords/[boolean booleanType]/[int intType]/[float floatType]/[decimal decimalType]/[string stringType]() + isolated resource function delete + alltypesidrecords/[boolean booleanType]/[int intType]/[float floatType]/[decimal decimalType]/[string stringType]() returns AllTypesIdRecord|persist:Error { - AllTypesIdRecord result = + AllTypesIdRecord result = check self->/alltypesidrecords/[booleanType]/[intType]/[floatType]/[decimalType]/[stringType]; RedisClient redisClient; lock { redisClient = self.persistClients.get(ALL_TYPES_ID_RECORD); } - _ = check redisClient.runDeleteQuery({"booleanType": booleanType, "intType": intType, "floatType": - floatType, "decimalType": decimalType, "stringType": stringType}); + _ = check redisClient.runDeleteQuery({ + "booleanType": booleanType, + "intType": intType, + "floatType": + floatType, + "decimalType": decimalType, + "stringType": stringType + }); return result; } diff --git a/ballerina/tests/redis_test_multiple_associations_client.bal b/ballerina/tests/redis_test_multiple_associations_client.bal new file mode 100644 index 0000000..a4188bc --- /dev/null +++ b/ballerina/tests/redis_test_multiple_associations_client.bal @@ -0,0 +1,185 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// 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 ballerinax/redis; + +const PERSON = "people"; +const APARTMENT = "apartments"; + +public isolated client class RedisManyAssociationsClient { + *persist:AbstractPersistClient; + + private final redis:Client dbClient; + + private final map persistClients; + + private final record {|RedisMetadata...;|} & readonly metadata = { + [PERSON]: { + entityName: "Person", + collectionName: "Person", + fieldMetadata: { + id: {fieldName: "id", fieldDataType: INT}, + name: {fieldName: "name", fieldDataType: STRING}, + "soldBuildings[].code": {relation: {entityName: "soldBuildings", refField: "code", refFieldDataType: STRING}}, + "soldBuildings[].city": {relation: {entityName: "soldBuildings", refField: "city", refFieldDataType: STRING}}, + "soldBuildings[].state": {relation: {entityName: "soldBuildings", refField: "state", refFieldDataType: STRING}}, + "soldBuildings[].country": {relation: {entityName: "soldBuildings", refField: "country", refFieldDataType: STRING}}, + "soldBuildings[].postalCode": {relation: {entityName: "soldBuildings", refField: "postalCode", refFieldDataType: STRING}}, + "soldBuildings[].type": {relation: {entityName: "soldBuildings", refField: "type", refFieldDataType: STRING}}, + "soldBuildings[].soldpersonId": {relation: {entityName: "soldBuildings", refField: "soldpersonId", refFieldDataType: INT}}, + "soldBuildings[].ownpersonId": {relation: {entityName: "soldBuildings", refField: "ownpersonId", refFieldDataType: INT}}, + "ownBuildings[].code": {relation: {entityName: "ownBuildings", refField: "code", refFieldDataType: STRING}}, + "ownBuildings[].city": {relation: {entityName: "ownBuildings", refField: "city", refFieldDataType: STRING}}, + "ownBuildings[].state": {relation: {entityName: "ownBuildings", refField: "state", refFieldDataType: STRING}}, + "ownBuildings[].country": {relation: {entityName: "ownBuildings", refField: "country", refFieldDataType: STRING}}, + "ownBuildings[].postalCode": {relation: {entityName: "ownBuildings", refField: "postalCode", refFieldDataType: STRING}}, + "ownBuildings[].type": {relation: {entityName: "ownBuildings", refField: "type", refFieldDataType: STRING}}, + "ownBuildings[].soldpersonId": {relation: {entityName: "ownBuildings", refField: "soldpersonId", refFieldDataType: INT}}, + "ownBuildings[].ownpersonId": {relation: {entityName: "ownBuildings", refField: "ownpersonId", refFieldDataType: INT}} + }, + keyFields: ["id"], + refMetadata: { + soldBuildings: {entity: Apartment, fieldName: "soldBuildings", refCollection: "Apartment", refFields: ["soldpersonId"], joinFields: ["id"], 'type: MANY_TO_ONE}, + ownBuildings: {entity: Apartment, fieldName: "ownBuildings", refCollection: "Apartment", refFields: ["ownpersonId"], joinFields: ["id"], 'type: MANY_TO_ONE} + } + }, + [APARTMENT]: { + entityName: "Apartment", + collectionName: "Apartment", + fieldMetadata: { + code: {fieldName: "code", fieldDataType: STRING}, + city: {fieldName: "city", fieldDataType: STRING}, + state: {fieldName: "state", fieldDataType: STRING}, + country: {fieldName: "country", fieldDataType: STRING}, + postalCode: {fieldName: "postalCode", fieldDataType: STRING}, + 'type: {fieldName: "type", fieldDataType: STRING}, + soldpersonId: {fieldName: "soldpersonId", fieldDataType: INT}, + ownpersonId: {fieldName: "ownpersonId", fieldDataType: INT}, + "soldPerson.id": {relation: {entityName: "soldPerson", refField: "id", refFieldDataType: INT}}, + "soldPerson.name": {relation: {entityName: "soldPerson", refField: "name", refFieldDataType: STRING}}, + "ownPerson.id": {relation: {entityName: "ownPerson", refField: "id", refFieldDataType: INT}}, + "ownPerson.name": {relation: {entityName: "ownPerson", refField: "name", refFieldDataType: STRING}} + }, + keyFields: ["code"], + refMetadata: { + soldPerson: {entity: Person, fieldName: "soldPerson", refCollection: "Person", refMetaDataKey: "soldBuildings", refFields: ["id"], joinFields: ["soldpersonId"], 'type: ONE_TO_MANY}, + ownPerson: {entity: Person, fieldName: "ownPerson", refCollection: "Person", refMetaDataKey: "ownBuildings", refFields: ["id"], joinFields: ["ownpersonId"], 'type: ONE_TO_MANY} + } + } + }; + + public isolated function init() returns persist:Error? { + redis:Client|error dbClient = new (redis); + if dbClient is error { + return error(dbClient.message()); + } + self.dbClient = dbClient; + self.persistClients = { + [PERSON]: check new (dbClient, self.metadata.get(PERSON)), + [APARTMENT]: check new (dbClient, self.metadata.get(APARTMENT)) + }; + } + + isolated resource function get people(PersonTargetType targetType = <>) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", + name: "query" + } external; + + isolated resource function get people/[int id](PersonTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", + name: "queryOne" + } external; + + isolated resource function post people(PersonInsert[] data) returns int[]|persist:Error { + RedisClient redisClient; + lock { + redisClient = self.persistClients.get(PERSON); + } + _ = check redisClient.runBatchInsertQuery(data); + return from PersonInsert inserted in data + select inserted.id; + } + + isolated resource function put people/[int id](PersonUpdate value) returns Person|persist:Error { + RedisClient redisClient; + lock { + redisClient = self.persistClients.get(PERSON); + } + _ = check redisClient.runUpdateQuery(id, value); + return self->/people/[id].get(); + } + + isolated resource function delete people/[int id]() returns Person|persist:Error { + Person result = check self->/people/[id].get(); + RedisClient redisClient; + lock { + redisClient = self.persistClients.get(PERSON); + } + _ = check redisClient.runDeleteQuery(id); + return result; + } + + isolated resource function get apartments(ApartmentTargetType targetType = <>) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", + name: "query" + } external; + + isolated resource function get apartments/[string code](ApartmentTargetType targetType = <>) returns targetType|persist:Error = @java:Method { + 'class: "io.ballerina.stdlib.persist.redis.datastore.RedisProcessor", + name: "queryOne" + } external; + + isolated resource function post apartments(ApartmentInsert[] data) returns string[]|persist:Error { + RedisClient redisClient; + lock { + redisClient = self.persistClients.get(APARTMENT); + } + _ = check redisClient.runBatchInsertQuery(data); + return from ApartmentInsert inserted in data + select inserted.code; + } + + isolated resource function put apartments/[string code](ApartmentUpdate value) returns Apartment|persist:Error { + RedisClient redisClient; + lock { + redisClient = self.persistClients.get(APARTMENT); + } + _ = check redisClient.runUpdateQuery(code, value); + return self->/apartments/[code].get(); + } + + isolated resource function delete apartments/[string code]() returns Apartment|persist:Error { + Apartment result = check self->/apartments/[code].get(); + RedisClient redisClient; + lock { + redisClient = self.persistClients.get(APARTMENT); + } + _ = check redisClient.runDeleteQuery(code); + return result; + } + + 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/test_entities_generated_types.bal b/ballerina/tests/test_entities_generated_types.bal index 0588800..fb4dd24 100644 --- a/ballerina/tests/test_entities_generated_types.bal +++ b/ballerina/tests/test_entities_generated_types.bal @@ -13,7 +13,6 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. - import ballerina/time; public enum EnumType { @@ -299,3 +298,70 @@ public type OrderItemExtendedUpdate record {| time:TimeOfDay arivalTimeTimeOfDay?; OrderType orderType?; |}; + +public type Person record {| + readonly int id; + string name; + +|}; + +public type PersonOptionalized record {| + int id?; + string name?; +|}; + +public type PersonWithRelations record {| + *PersonOptionalized; + ApartmentOptionalized[] soldBuildings?; + ApartmentOptionalized[] ownBuildings?; +|}; + +public type PersonTargetType typedesc; + +public type PersonInsert Person; + +public type PersonUpdate record {| + string name?; +|}; + +public type Apartment record {| + readonly string code; + string city; + string state; + string country; + string postalCode; + string 'type; + int soldpersonId; + int ownpersonId; +|}; + +public type ApartmentOptionalized record {| + string code?; + string city?; + string state?; + string country?; + string postalCode?; + string 'type?; + int soldpersonId?; + int ownpersonId?; +|}; + +public type ApartmentWithRelations record {| + *ApartmentOptionalized; + PersonOptionalized soldPerson?; + PersonOptionalized ownPerson?; +|}; + +public type ApartmentTargetType typedesc; + +public type ApartmentInsert Apartment; + +public type ApartmentUpdate record {| + string city?; + string state?; + string country?; + string postalCode?; + string 'type?; + int soldpersonId?; + int ownpersonId?; +|};