diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java index e62725ec..3697108b 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java @@ -624,6 +624,11 @@ public class QInstanceValidator supplementalTableMetaData.validate(qInstance, table, this); } + if(table.getShareableTableMetaData() != null) + { + table.getShareableTableMetaData().validate(qInstance, table, this); + } + runPlugins(QTableMetaData.class, table, qInstance); }); } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/sharing/ShareScopePossibleValueMetaDataProducer.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/sharing/ShareScopePossibleValueMetaDataProducer.java new file mode 100644 index 00000000..49e5bb64 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/sharing/ShareScopePossibleValueMetaDataProducer.java @@ -0,0 +1,48 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2024. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.core.model.metadata.sharing; + + +import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; +import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource; +import com.kingsrook.qqq.backend.core.processes.implementations.sharing.ShareScope; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class ShareScopePossibleValueMetaDataProducer implements MetaDataProducerInterface +{ + public static final String NAME = "shareScope"; + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public QPossibleValueSource produce(QInstance qInstance) throws QException + { + return QPossibleValueSource.newForEnum(NAME, ShareScope.values()); + } + +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/sharing/ShareableAudienceType.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/sharing/ShareableAudienceType.java new file mode 100644 index 00000000..2ab36ff3 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/sharing/ShareableAudienceType.java @@ -0,0 +1,186 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2024. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.core.model.metadata.sharing; + + +import java.io.Serializable; + + +/******************************************************************************* + ** As a component of a ShareableTableMetaData instance, define details about + ** one particular audience type. + ** + ** e.g., if a table can be shared to users and groups, there'd be 2 instances of + ** this object - one like: + ** - name: user + ** - fieldName: userId + ** - sourceTableName: User.TABLE_NAME + ** - sourceTableKeyFieldName: email (e.g., can be a UK, not just the PKey) + ** + ** and another similar, w/ the group-type details. + *******************************************************************************/ +public class ShareableAudienceType implements Serializable +{ + private String name; + private String fieldName; + private String sourceTableName; + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // maybe normally the primary key in the source table, but could be a unique-key instead sometimes // + ///////////////////////////////////////////////////////////////////////////////////////////////////// + private String sourceTableKeyFieldName; + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public ShareableAudienceType() + { + } + + + + /******************************************************************************* + ** Getter for name + *******************************************************************************/ + public String getName() + { + return (this.name); + } + + + + /******************************************************************************* + ** Setter for name + *******************************************************************************/ + public void setName(String name) + { + this.name = name; + } + + + + /******************************************************************************* + ** Fluent setter for name + *******************************************************************************/ + public ShareableAudienceType withName(String name) + { + this.name = name; + return (this); + } + + + + /******************************************************************************* + ** Getter for fieldName + *******************************************************************************/ + public String getFieldName() + { + return (this.fieldName); + } + + + + /******************************************************************************* + ** Setter for fieldName + *******************************************************************************/ + public void setFieldName(String fieldName) + { + this.fieldName = fieldName; + } + + + + /******************************************************************************* + ** Fluent setter for fieldName + *******************************************************************************/ + public ShareableAudienceType withFieldName(String fieldName) + { + this.fieldName = fieldName; + return (this); + } + + + + /******************************************************************************* + ** Getter for sourceTableName + *******************************************************************************/ + public String getSourceTableName() + { + return (this.sourceTableName); + } + + + + /******************************************************************************* + ** Setter for sourceTableName + *******************************************************************************/ + public void setSourceTableName(String sourceTableName) + { + this.sourceTableName = sourceTableName; + } + + + + /******************************************************************************* + ** Fluent setter for sourceTableName + *******************************************************************************/ + public ShareableAudienceType withSourceTableName(String sourceTableName) + { + this.sourceTableName = sourceTableName; + return (this); + } + + + + /******************************************************************************* + ** Getter for sourceTableKeyFieldName + *******************************************************************************/ + public String getSourceTableKeyFieldName() + { + return (this.sourceTableKeyFieldName); + } + + + + /******************************************************************************* + ** Setter for sourceTableKeyFieldName + *******************************************************************************/ + public void setSourceTableKeyFieldName(String sourceTableKeyFieldName) + { + this.sourceTableKeyFieldName = sourceTableKeyFieldName; + } + + + + /******************************************************************************* + ** Fluent setter for sourceTableKeyFieldName + *******************************************************************************/ + public ShareableAudienceType withSourceTableKeyFieldName(String sourceTableKeyFieldName) + { + this.sourceTableKeyFieldName = sourceTableKeyFieldName; + return (this); + } + +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/sharing/ShareableTableMetaData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/sharing/ShareableTableMetaData.java new file mode 100644 index 00000000..a0193b63 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/sharing/ShareableTableMetaData.java @@ -0,0 +1,356 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2024. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.core.model.metadata.sharing; + + +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import com.kingsrook.qqq.backend.core.instances.QInstanceValidator; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; +import com.kingsrook.qqq.backend.core.utils.CollectionUtils; +import com.kingsrook.qqq.backend.core.utils.StringUtils; + + +/******************************************************************************* + ** meta data to attach to a table, to describe that its records are shareable. + *******************************************************************************/ +public class ShareableTableMetaData implements Serializable +{ + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // this is the name of the table that is a many-to-one join to the table whose records are being shared. // + // not the table whose records are shared (the asset table) // + // for example: given that we want to share "savedReports", the value here could be "sharedSavedReports" // + // and this object will be attached to the savedReports table. // + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + private String sharedRecordTableName; + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + // name of the field in the sharedRecordTable that has a foreign key pointing at the asset table // + /////////////////////////////////////////////////////////////////////////////////////////////////////// + private String assetIdFieldName; + + ////////////////////////////////////////////////////// + // name of the scope field in the sharedRecordTable // + ////////////////////////////////////////////////////// + private String scopeFieldName; + + /////////////////////////////////////////////////////////// + // map of audienceTypes names to type definition objects // + /////////////////////////////////////////////////////////// + private Map audienceTypes; + + ///////////////////////////////////////////////// + // PVS that lists the available audience types // + ///////////////////////////////////////////////// + private String audienceTypesPossibleValueSourceName; + + ////////////////////////////////////////////////////////////// + // name of a field in "this" table, that has the owner's id // + ////////////////////////////////////////////////////////////// + private String thisTableOwnerIdFieldName; + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public ShareableTableMetaData() + { + } + + + + /******************************************************************************* + ** Getter for sharedRecordTableName + *******************************************************************************/ + public String getSharedRecordTableName() + { + return (this.sharedRecordTableName); + } + + + + /******************************************************************************* + ** Setter for sharedRecordTableName + *******************************************************************************/ + public void setSharedRecordTableName(String sharedRecordTableName) + { + this.sharedRecordTableName = sharedRecordTableName; + } + + + + /******************************************************************************* + ** Fluent setter for sharedRecordTableName + *******************************************************************************/ + public ShareableTableMetaData withSharedRecordTableName(String sharedRecordTableName) + { + this.sharedRecordTableName = sharedRecordTableName; + return (this); + } + + + + /******************************************************************************* + ** Getter for assetIdFieldName + *******************************************************************************/ + public String getAssetIdFieldName() + { + return (this.assetIdFieldName); + } + + + + /******************************************************************************* + ** Setter for assetIdFieldName + *******************************************************************************/ + public void setAssetIdFieldName(String assetIdFieldName) + { + this.assetIdFieldName = assetIdFieldName; + } + + + + /******************************************************************************* + ** Fluent setter for assetIdFieldName + *******************************************************************************/ + public ShareableTableMetaData withAssetIdFieldName(String assetIdFieldName) + { + this.assetIdFieldName = assetIdFieldName; + return (this); + } + + + + /******************************************************************************* + ** Getter for scopeFieldName + *******************************************************************************/ + public String getScopeFieldName() + { + return (this.scopeFieldName); + } + + + + /******************************************************************************* + ** Setter for scopeFieldName + *******************************************************************************/ + public void setScopeFieldName(String scopeFieldName) + { + this.scopeFieldName = scopeFieldName; + } + + + + /******************************************************************************* + ** Fluent setter for scopeFieldName + *******************************************************************************/ + public ShareableTableMetaData withScopeFieldName(String scopeFieldName) + { + this.scopeFieldName = scopeFieldName; + return (this); + } + + + + /******************************************************************************* + ** Getter for audienceTypes + *******************************************************************************/ + public Map getAudienceTypes() + { + return (this.audienceTypes); + } + + + + /******************************************************************************* + ** Setter for audienceTypes + *******************************************************************************/ + public void setAudienceTypes(Map audienceTypes) + { + this.audienceTypes = audienceTypes; + } + + + + /******************************************************************************* + ** Fluent setter for audienceTypes + *******************************************************************************/ + public ShareableTableMetaData withAudienceTypes(Map audienceTypes) + { + this.audienceTypes = audienceTypes; + return (this); + } + + + + /******************************************************************************* + ** Fluent setter for audienceTypes + *******************************************************************************/ + public ShareableTableMetaData withAudienceType(ShareableAudienceType audienceType) + { + if(this.audienceTypes == null) + { + this.audienceTypes = new LinkedHashMap<>(); + } + + if(audienceType.getName() == null) + { + throw (new IllegalArgumentException("Attempt to add an audience type without a name")); + } + + if(this.audienceTypes.containsKey(audienceType.getName())) + { + throw (new IllegalArgumentException("Attempt to add more than 1 audience type with the same name [" + audienceType.getName() + "]")); + } + + this.audienceTypes.put(audienceType.getName(), audienceType); + + return (this); + } + + + + /******************************************************************************* + ** Getter for audienceTypesPossibleValueSourceName + *******************************************************************************/ + public String getAudienceTypesPossibleValueSourceName() + { + return (this.audienceTypesPossibleValueSourceName); + } + + + + /******************************************************************************* + ** Setter for audienceTypesPossibleValueSourceName + *******************************************************************************/ + public void setAudienceTypesPossibleValueSourceName(String audienceTypesPossibleValueSourceName) + { + this.audienceTypesPossibleValueSourceName = audienceTypesPossibleValueSourceName; + } + + + + /******************************************************************************* + ** Fluent setter for audienceTypesPossibleValueSourceName + *******************************************************************************/ + public ShareableTableMetaData withAudienceTypesPossibleValueSourceName(String audienceTypesPossibleValueSourceName) + { + this.audienceTypesPossibleValueSourceName = audienceTypesPossibleValueSourceName; + return (this); + } + + + + /******************************************************************************* + ** Getter for thisTableOwnerIdFieldName + *******************************************************************************/ + public String getThisTableOwnerIdFieldName() + { + return (this.thisTableOwnerIdFieldName); + } + + + + /******************************************************************************* + ** Setter for thisTableOwnerIdFieldName + *******************************************************************************/ + public void setThisTableOwnerIdFieldName(String thisTableOwnerIdFieldName) + { + this.thisTableOwnerIdFieldName = thisTableOwnerIdFieldName; + } + + + + /******************************************************************************* + ** Fluent setter for thisTableOwnerIdFieldName + *******************************************************************************/ + public ShareableTableMetaData withThisTableOwnerIdFieldName(String thisTableOwnerIdFieldName) + { + this.thisTableOwnerIdFieldName = thisTableOwnerIdFieldName; + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void validate(QInstance qInstance, QTableMetaData tableMetaData, QInstanceValidator qInstanceValidator) + { + String prefix = "ShareableTableMetaData for table [" + tableMetaData.getName() + "]: "; + if(qInstanceValidator.assertCondition(StringUtils.hasContent(sharedRecordTableName), prefix + "missing sharedRecordTableName.")) + { + boolean hasAssetIdFieldName = qInstanceValidator.assertCondition(StringUtils.hasContent(assetIdFieldName), prefix + "missing assetIdFieldName"); + boolean hasScopeFieldName = qInstanceValidator.assertCondition(StringUtils.hasContent(scopeFieldName), prefix + "missing scopeFieldName"); + + QTableMetaData sharedRecordTable = qInstance.getTable(sharedRecordTableName); + boolean hasValidSharedRecordTable = qInstanceValidator.assertCondition(sharedRecordTable != null, prefix + "unrecognized sharedRecordTableName [" + sharedRecordTableName + "]"); + + if(hasValidSharedRecordTable && hasAssetIdFieldName) + { + qInstanceValidator.assertCondition(sharedRecordTable.getFields().containsKey(assetIdFieldName), prefix + "unrecognized assertIdFieldName [" + assetIdFieldName + "] in sharedRecordTable [" + sharedRecordTableName + "]"); + } + + if(hasValidSharedRecordTable && hasScopeFieldName) + { + qInstanceValidator.assertCondition(sharedRecordTable.getFields().containsKey(scopeFieldName), prefix + "unrecognized scopeFieldName [" + scopeFieldName + "] in sharedRecordTable [" + sharedRecordTableName + "]"); + } + + if(qInstanceValidator.assertCondition(CollectionUtils.nullSafeHasContents(audienceTypes), prefix + "missing audienceTypes")) + { + for(Map.Entry entry : audienceTypes.entrySet()) + { + ShareableAudienceType audienceType = entry.getValue(); + qInstanceValidator.assertCondition(Objects.equals(entry.getKey(), audienceType.getName()), prefix + "inconsistent naming for shareableAudienceType [" + entry.getKey() + "] != [" + audienceType.getName() + "]"); + if(qInstanceValidator.assertCondition(StringUtils.hasContent(audienceType.getFieldName()), prefix + "missing fieldName for shareableAudienceType [" + entry.getKey() + "]") && hasValidSharedRecordTable) + { + qInstanceValidator.assertCondition(sharedRecordTable.getFields().containsKey(audienceType.getFieldName()), prefix + "unrecognized fieldName [" + audienceType.getFieldName() + "] for shareableAudienceType [" + entry.getKey() + "] in sharedRecordTable [" + sharedRecordTableName + "]"); + } + + /* todo - make these optional i guess, because i didn't put user table in qqq + boolean hasSourceTableKeyFieldName = qInstanceValidator.assertCondition(StringUtils.hasContent(audienceType.getSourceTableKeyFieldName()), prefix + "missing sourceTableKeyFieldName for shareableAudienceType [" + entry.getKey() + "]"); + if(qInstanceValidator.assertCondition(qInstance.getTable(audienceType.getSourceTableName()) != null, prefix + "unrecognized sourceTableName [" + audienceType.getSourceTableName() + "] for shareableAudienceType [" + entry.getKey() + "] in sharedRecordTable [" + sharedRecordTableName + "]") && hasSourceTableKeyFieldName) + { + qInstanceValidator.assertCondition(qInstance.getTable(audienceType.getSourceTableName()).getFields().containsKey(audienceType.getSourceTableKeyFieldName()), prefix + "unrecognized sourceTableKeyFieldName [" + audienceType.getSourceTableKeyFieldName() + "] for shareableAudienceType [" + entry.getKey() + "] in sharedRecordTable [" + sharedRecordTableName + "]"); + } + */ + } + } + } + + if(StringUtils.hasContent(thisTableOwnerIdFieldName)) + { + qInstanceValidator.assertCondition(tableMetaData.getFields().containsKey(thisTableOwnerIdFieldName), prefix + "unrecognized thisTableOwnerIdFieldName [" + thisTableOwnerIdFieldName + "]"); + } + + if(StringUtils.hasContent(audienceTypesPossibleValueSourceName)) + { + qInstanceValidator.assertCondition(qInstance.getPossibleValueSource(audienceTypesPossibleValueSourceName) != null, prefix + "unrecognized audienceTypesPossibleValueSourceName [" + audienceTypesPossibleValueSourceName + "]"); + } + } + +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/tables/QTableMetaData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/tables/QTableMetaData.java index fac05aaa..a14fc566 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/tables/QTableMetaData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/tables/QTableMetaData.java @@ -48,6 +48,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon; import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules; import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules; import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock; +import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareableTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.QTableAutomationDetails; import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheOf; import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; @@ -107,6 +108,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData private List exposedJoins; + private ShareableTableMetaData shareableTableMetaData; /******************************************************************************* @@ -1385,4 +1387,35 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData return (this); } + + /******************************************************************************* + ** Getter for shareableTableMetaData + *******************************************************************************/ + public ShareableTableMetaData getShareableTableMetaData() + { + return (this.shareableTableMetaData); + } + + + + /******************************************************************************* + ** Setter for shareableTableMetaData + *******************************************************************************/ + public void setShareableTableMetaData(ShareableTableMetaData shareableTableMetaData) + { + this.shareableTableMetaData = shareableTableMetaData; + } + + + + /******************************************************************************* + ** Fluent setter for shareableTableMetaData + *******************************************************************************/ + public QTableMetaData withShareableTableMetaData(ShareableTableMetaData shareableTableMetaData) + { + this.shareableTableMetaData = shareableTableMetaData; + return (this); + } + + } diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidatorTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidatorTest.java index a9b490ad..1c6947a3 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidatorTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidatorTest.java @@ -78,6 +78,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.scheduleing.QScheduleMetaDa import com.kingsrook.qqq.backend.core.model.metadata.security.FieldSecurityLock; import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType; import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock; +import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareableTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.tables.Association; import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin; import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection; @@ -2004,7 +2005,21 @@ public class QInstanceValidatorTest extends BaseTest qInstance.addTable(newTable("B", "id", "aId")); qInstance.addJoin(new QJoinMetaData().withLeftTable("A").withRightTable("B").withName("AB").withType(JoinType.ONE_TO_ONE).withJoinOn(new JoinOn("id", "aId"))); }); + } + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testShareableTableMetaData() + { + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // just make sure we call this class's validator - the rest of its conditions are covered in its own test // + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + assertValidationFailureReasonsAllowingExtraReasons(qInstance -> qInstance.addTable(newTable("A", "id").withShareableTableMetaData(new ShareableTableMetaData())), + "missing sharedRecordTableName"); } @@ -2113,7 +2128,7 @@ public class QInstanceValidatorTest extends BaseTest /******************************************************************************* ** *******************************************************************************/ - private QTableMetaData newTable(String tableName, String... fieldNames) + protected QTableMetaData newTable(String tableName, String... fieldNames) { QTableMetaData tableMetaData = new QTableMetaData() .withName(tableName) @@ -2207,7 +2222,7 @@ public class QInstanceValidatorTest extends BaseTest /******************************************************************************* ** Assert that an instance is valid! *******************************************************************************/ - private void assertValidationSuccess(Consumer setup) + public static void assertValidationSuccess(Consumer setup) { try { diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/metadata/sharing/ShareableTableMetaDataTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/metadata/sharing/ShareableTableMetaDataTest.java new file mode 100644 index 00000000..23e5c4a7 --- /dev/null +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/metadata/sharing/ShareableTableMetaDataTest.java @@ -0,0 +1,129 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2024. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.core.model.metadata.sharing; + + +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; +import com.kingsrook.qqq.backend.core.utils.TestUtils; +import org.junit.jupiter.api.Test; +import static com.kingsrook.qqq.backend.core.instances.QInstanceValidatorTest.assertValidationFailureReasons; +import static com.kingsrook.qqq.backend.core.instances.QInstanceValidatorTest.assertValidationFailureReasonsAllowingExtraReasons; +import static com.kingsrook.qqq.backend.core.instances.QInstanceValidatorTest.assertValidationSuccess; + + +/******************************************************************************* + ** Unit test for ShareableTableMetaData + *******************************************************************************/ +class ShareableTableMetaDataTest +{ + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testValidation() + { + assertValidationFailureReasonsAllowingExtraReasons(qInstance -> qInstance.addTable(newTable().withShareableTableMetaData(new ShareableTableMetaData())), + "missing sharedRecordTableName"); + + assertValidationFailureReasonsAllowingExtraReasons(qInstance -> qInstance.addTable(newTable().withShareableTableMetaData(new ShareableTableMetaData() + .withSharedRecordTableName("notATable") + )), "unrecognized sharedRecordTableName"); + + assertValidationFailureReasonsAllowingExtraReasons(qInstance -> qInstance.addTable(newTable().withShareableTableMetaData(new ShareableTableMetaData() + .withAudienceTypesPossibleValueSourceName("notAPVS") + )), "unrecognized audienceTypesPossibleValueSourceName"); + + assertValidationFailureReasons(qInstance -> qInstance.addTable(newTable().withShareableTableMetaData(new ShareableTableMetaData() + .withSharedRecordTableName(TestUtils.TABLE_NAME_PERSON_MEMORY) + )), "missing assetIdFieldName", + "missing scopeFieldName", + "missing audienceTypes"); + + assertValidationFailureReasonsAllowingExtraReasons(qInstance -> qInstance.addTable(newTable().withShareableTableMetaData(new ShareableTableMetaData() + .withSharedRecordTableName(TestUtils.TABLE_NAME_PERSON_MEMORY) + .withAssetIdFieldName("notAField") + )), "unrecognized assertIdFieldName"); + + assertValidationFailureReasonsAllowingExtraReasons(qInstance -> qInstance.addTable(newTable().withShareableTableMetaData(new ShareableTableMetaData() + .withSharedRecordTableName(TestUtils.TABLE_NAME_PERSON_MEMORY) + .withScopeFieldName("notAField") + )), "unrecognized scopeFieldName"); + + assertValidationFailureReasonsAllowingExtraReasons(qInstance -> qInstance.addTable(newTable().withShareableTableMetaData(new ShareableTableMetaData() + .withSharedRecordTableName(TestUtils.TABLE_NAME_PERSON_MEMORY) + .withAudienceType(new ShareableAudienceType().withName("myType")) + )), "missing fieldName for shareableAudienceType"); + + assertValidationFailureReasonsAllowingExtraReasons(qInstance -> qInstance.addTable(newTable().withShareableTableMetaData(new ShareableTableMetaData() + .withSharedRecordTableName(TestUtils.TABLE_NAME_PERSON_MEMORY) + .withAudienceType(new ShareableAudienceType().withName("myType").withFieldName("notAField")) + )), "unrecognized fieldName"); + + /* todo - corresponding todo in main class + assertValidationFailureReasonsAllowingExtraReasons(qInstance -> qInstance.addTable(newTable().withShareableTableMetaData(new ShareableTableMetaData() + .withSharedRecordTableName(TestUtils.TABLE_NAME_PERSON_MEMORY) + .withAudienceType(new ShareableAudienceType().withName("myType").withFieldName("firstName").withSourceTableName("notATable")) + )), "unrecognized sourceTableName"); + + assertValidationFailureReasonsAllowingExtraReasons(qInstance -> qInstance.addTable(newTable().withShareableTableMetaData(new ShareableTableMetaData() + .withSharedRecordTableName(TestUtils.TABLE_NAME_PERSON_MEMORY) + .withAudienceType(new ShareableAudienceType().withName("myType").withFieldName("firstName").withSourceTableName(TestUtils.TABLE_NAME_SHAPE).withSourceTableKeyFieldName("notAField")) + )), "unrecognized sourceTableKeyFieldName"); + */ + + assertValidationFailureReasonsAllowingExtraReasons(qInstance -> qInstance.addTable(newTable().withShareableTableMetaData(new ShareableTableMetaData() + .withThisTableOwnerIdFieldName("notAField") + )), "unrecognized thisTableOwnerIdFieldName"); + + assertValidationSuccess(qInstance -> qInstance.addTable(newTable() + .withField(new QFieldMetaData("userId", QFieldType.INTEGER)) + .withShareableTableMetaData(new ShareableTableMetaData() + .withSharedRecordTableName(TestUtils.TABLE_NAME_PERSON_MEMORY) + .withAssetIdFieldName("firstName") + .withScopeFieldName("firstName") + .withThisTableOwnerIdFieldName("userId") + .withAudienceTypesPossibleValueSourceName(TestUtils.POSSIBLE_VALUE_SOURCE_STATE) + .withAudienceType(new ShareableAudienceType().withName("myType").withFieldName("lastName").withSourceTableName(TestUtils.TABLE_NAME_SHAPE).withSourceTableKeyFieldName("id")) + ))); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + protected QTableMetaData newTable() + { + QTableMetaData tableMetaData = new QTableMetaData() + .withName("A") + .withBackendName(TestUtils.DEFAULT_BACKEND_NAME) + .withPrimaryKeyField("id"); + + tableMetaData.addField(new QFieldMetaData("id", QFieldType.INTEGER)); + + return (tableMetaData); + } + +} \ No newline at end of file