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