mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
Merged feature/webhooks into integration
This commit is contained in:
@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2025. 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.actions.customizers;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.QueryOrGetInputInterface;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.code.InitializableViaCodeReference;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReferenceWithProperties;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Implementation of TableCustomizerInterface that runs multiple other customizers
|
||||||
|
*******************************************************************************/
|
||||||
|
public class MultiCustomizer implements InitializableViaCodeReference, TableCustomizerInterface
|
||||||
|
{
|
||||||
|
private static final String KEY_CODE_REFERENCES = "codeReferences";
|
||||||
|
|
||||||
|
private List<TableCustomizerInterface> customizers = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Factory method that builds a {@link QCodeReferenceWithProperties} that will
|
||||||
|
* allow this multi-customizer to be assigned to a table, and to track
|
||||||
|
* in that code ref's properties, the "sub" QCodeReferences to be used.
|
||||||
|
*
|
||||||
|
* Added to a table as in:
|
||||||
|
* <pre>
|
||||||
|
* table.withCustomizer(TableCustomizers.POST_INSERT_RECORD,
|
||||||
|
* MultiCustomizer.of(QCodeReference(x), QCodeReference(y)));
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param codeReferences
|
||||||
|
* one or more {@link QCodeReference objects} to run when this customizer
|
||||||
|
* runs. note that they will run in the order provided in this list.
|
||||||
|
***************************************************************************/
|
||||||
|
public static QCodeReferenceWithProperties of(QCodeReference... codeReferences)
|
||||||
|
{
|
||||||
|
ArrayList<QCodeReference> list = new ArrayList<>(Arrays.stream(codeReferences).toList());
|
||||||
|
return (new QCodeReferenceWithProperties(MultiCustomizer.class, MapBuilder.of(KEY_CODE_REFERENCES, list)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Add an additional table customizer code reference to an existing
|
||||||
|
* codeReference, e.g., constructed by the `of` factory method.
|
||||||
|
*
|
||||||
|
* @see #of(QCodeReference...)
|
||||||
|
***************************************************************************/
|
||||||
|
public static void addTableCustomizer(QCodeReferenceWithProperties existingMultiCustomizerCodeReference, QCodeReference codeReference)
|
||||||
|
{
|
||||||
|
ArrayList<QCodeReference> list = (ArrayList<QCodeReference>) existingMultiCustomizerCodeReference.getProperties().computeIfAbsent(KEY_CODE_REFERENCES, key -> new ArrayList<>());
|
||||||
|
list.add(codeReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* When this class is instantiated by the QCodeLoader, initialize the
|
||||||
|
* sub-customizer objects.
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void initialize(QCodeReference codeReference)
|
||||||
|
{
|
||||||
|
if(codeReference instanceof QCodeReferenceWithProperties codeReferenceWithProperties)
|
||||||
|
{
|
||||||
|
Serializable codeReferencesPropertyValue = codeReferenceWithProperties.getProperties().get(KEY_CODE_REFERENCES);
|
||||||
|
if(codeReferencesPropertyValue instanceof List<?> list)
|
||||||
|
{
|
||||||
|
for(Object o : list)
|
||||||
|
{
|
||||||
|
if(o instanceof QCodeReference reference)
|
||||||
|
{
|
||||||
|
TableCustomizerInterface customizer = QCodeLoader.getAdHoc(TableCustomizerInterface.class, reference);
|
||||||
|
customizers.add(customizer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.warn("Property KEY_CODE_REFERENCES [" + KEY_CODE_REFERENCES + "] must be a List<QCodeReference>.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(customizers.isEmpty())
|
||||||
|
{
|
||||||
|
LOG.info("No TableCustomizers were specified for MultiCustomizer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* run postQuery method over all sub-customizers
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
public List<QRecord> postQuery(QueryOrGetInputInterface queryInput, List<QRecord> records) throws QException
|
||||||
|
{
|
||||||
|
for(TableCustomizerInterface customizer : customizers)
|
||||||
|
{
|
||||||
|
records = customizer.postQuery(queryInput, records);
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* run preInsert method over all sub-customizers
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
public List<QRecord> preInsert(InsertInput insertInput, List<QRecord> records, boolean isPreview) throws QException
|
||||||
|
{
|
||||||
|
for(TableCustomizerInterface customizer : customizers)
|
||||||
|
{
|
||||||
|
records = customizer.preInsert(insertInput, records, isPreview);
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* run postInsert method over all sub-customizers
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
public List<QRecord> postInsert(InsertInput insertInput, List<QRecord> records) throws QException
|
||||||
|
{
|
||||||
|
for(TableCustomizerInterface customizer : customizers)
|
||||||
|
{
|
||||||
|
records = customizer.postInsert(insertInput, records);
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* run preUpdate method over all sub-customizers
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
public List<QRecord> preUpdate(UpdateInput updateInput, List<QRecord> records, boolean isPreview, Optional<List<QRecord>> oldRecordList) throws QException
|
||||||
|
{
|
||||||
|
for(TableCustomizerInterface customizer : customizers)
|
||||||
|
{
|
||||||
|
records = customizer.preUpdate(updateInput, records, isPreview, oldRecordList);
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* run postUpdate method over all sub-customizers
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
public List<QRecord> postUpdate(UpdateInput updateInput, List<QRecord> records, Optional<List<QRecord>> oldRecordList) throws QException
|
||||||
|
{
|
||||||
|
for(TableCustomizerInterface customizer : customizers)
|
||||||
|
{
|
||||||
|
records = customizer.postUpdate(updateInput, records, oldRecordList);
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* run preDelete method over all sub-customizers
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
public List<QRecord> preDelete(DeleteInput deleteInput, List<QRecord> records, boolean isPreview) throws QException
|
||||||
|
{
|
||||||
|
for(TableCustomizerInterface customizer : customizers)
|
||||||
|
{
|
||||||
|
records = customizer.preDelete(deleteInput, records, isPreview);
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* run postDelete method over all sub-customizers
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
public List<QRecord> postDelete(DeleteInput deleteInput, List<QRecord> records) throws QException
|
||||||
|
{
|
||||||
|
for(TableCustomizerInterface customizer : customizers)
|
||||||
|
{
|
||||||
|
records = customizer.postDelete(deleteInput, records);
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -401,6 +401,7 @@ public class DeleteAction
|
|||||||
if(CollectionUtils.nullSafeHasContents(associatedKeys))
|
if(CollectionUtils.nullSafeHasContents(associatedKeys))
|
||||||
{
|
{
|
||||||
DeleteInput nextLevelDeleteInput = new DeleteInput();
|
DeleteInput nextLevelDeleteInput = new DeleteInput();
|
||||||
|
nextLevelDeleteInput.setFlags(deleteInput.getFlags());
|
||||||
nextLevelDeleteInput.setTransaction(deleteInput.getTransaction());
|
nextLevelDeleteInput.setTransaction(deleteInput.getTransaction());
|
||||||
nextLevelDeleteInput.setTableName(association.getAssociatedTableName());
|
nextLevelDeleteInput.setTableName(association.getAssociatedTableName());
|
||||||
nextLevelDeleteInput.setPrimaryKeys(associatedKeys);
|
nextLevelDeleteInput.setPrimaryKeys(associatedKeys);
|
||||||
|
@ -34,7 +34,6 @@ import java.util.Set;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import com.kingsrook.qqq.backend.core.actions.AbstractQActionFunction;
|
import com.kingsrook.qqq.backend.core.actions.AbstractQActionFunction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.audits.DMLAuditAction;
|
import com.kingsrook.qqq.backend.core.actions.audits.DMLAuditAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
|
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
|
||||||
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationStatusUpdater;
|
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationStatusUpdater;
|
||||||
@ -158,7 +157,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
|||||||
//////////////////////////////////////////////////
|
//////////////////////////////////////////////////
|
||||||
// insert any associations in the input records //
|
// insert any associations in the input records //
|
||||||
//////////////////////////////////////////////////
|
//////////////////////////////////////////////////
|
||||||
manageAssociations(table, insertOutput.getRecords(), insertInput.getTransaction());
|
manageAssociations(table, insertOutput.getRecords(), insertInput);
|
||||||
|
|
||||||
//////////////////
|
//////////////////
|
||||||
// do the audit //
|
// do the audit //
|
||||||
@ -386,7 +385,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void manageAssociations(QTableMetaData table, List<QRecord> insertedRecords, QBackendTransaction transaction) throws QException
|
private void manageAssociations(QTableMetaData table, List<QRecord> insertedRecords, InsertInput insertInput) throws QException
|
||||||
{
|
{
|
||||||
for(Association association : CollectionUtils.nonNullList(table.getAssociations()))
|
for(Association association : CollectionUtils.nonNullList(table.getAssociations()))
|
||||||
{
|
{
|
||||||
@ -419,7 +418,8 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
|||||||
if(CollectionUtils.nullSafeHasContents(nextLevelInserts))
|
if(CollectionUtils.nullSafeHasContents(nextLevelInserts))
|
||||||
{
|
{
|
||||||
InsertInput nextLevelInsertInput = new InsertInput();
|
InsertInput nextLevelInsertInput = new InsertInput();
|
||||||
nextLevelInsertInput.setTransaction(transaction);
|
nextLevelInsertInput.withFlags(insertInput.getFlags());
|
||||||
|
nextLevelInsertInput.setTransaction(insertInput.getTransaction());
|
||||||
nextLevelInsertInput.setTableName(association.getAssociatedTableName());
|
nextLevelInsertInput.setTableName(association.getAssociatedTableName());
|
||||||
nextLevelInsertInput.setRecords(nextLevelInserts);
|
nextLevelInsertInput.setRecords(nextLevelInserts);
|
||||||
InsertOutput nextLevelInsertOutput = new InsertAction().execute(nextLevelInsertInput);
|
InsertOutput nextLevelInsertOutput = new InsertAction().execute(nextLevelInsertInput);
|
||||||
|
@ -126,6 +126,7 @@ public class ReplaceAction extends AbstractQActionFunction<ReplaceInput, Replace
|
|||||||
InsertInput insertInput = new InsertInput();
|
InsertInput insertInput = new InsertInput();
|
||||||
insertInput.setTableName(table.getName());
|
insertInput.setTableName(table.getName());
|
||||||
insertInput.setRecords(insertList);
|
insertInput.setRecords(insertList);
|
||||||
|
insertInput.withFlags(input.getFlags());
|
||||||
insertInput.setTransaction(transaction);
|
insertInput.setTransaction(transaction);
|
||||||
insertInput.setOmitDmlAudit(input.getOmitDmlAudit());
|
insertInput.setOmitDmlAudit(input.getOmitDmlAudit());
|
||||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||||
@ -135,6 +136,7 @@ public class ReplaceAction extends AbstractQActionFunction<ReplaceInput, Replace
|
|||||||
UpdateInput updateInput = new UpdateInput();
|
UpdateInput updateInput = new UpdateInput();
|
||||||
updateInput.setTableName(table.getName());
|
updateInput.setTableName(table.getName());
|
||||||
updateInput.setRecords(updateList);
|
updateInput.setRecords(updateList);
|
||||||
|
updateInput.withFlags(input.getFlags());
|
||||||
updateInput.setTransaction(transaction);
|
updateInput.setTransaction(transaction);
|
||||||
updateInput.setOmitDmlAudit(input.getOmitDmlAudit());
|
updateInput.setOmitDmlAudit(input.getOmitDmlAudit());
|
||||||
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
|
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
|
||||||
@ -151,6 +153,7 @@ public class ReplaceAction extends AbstractQActionFunction<ReplaceInput, Replace
|
|||||||
DeleteInput deleteInput = new DeleteInput();
|
DeleteInput deleteInput = new DeleteInput();
|
||||||
deleteInput.setTableName(table.getName());
|
deleteInput.setTableName(table.getName());
|
||||||
deleteInput.setQueryFilter(deleteFilter);
|
deleteInput.setQueryFilter(deleteFilter);
|
||||||
|
deleteInput.withFlags(input.getFlags());
|
||||||
deleteInput.setTransaction(transaction);
|
deleteInput.setTransaction(transaction);
|
||||||
deleteInput.setOmitDmlAudit(input.getOmitDmlAudit());
|
deleteInput.setOmitDmlAudit(input.getOmitDmlAudit());
|
||||||
DeleteOutput deleteOutput = new DeleteAction().execute(deleteInput);
|
DeleteOutput deleteOutput = new DeleteAction().execute(deleteInput);
|
||||||
|
@ -605,6 +605,7 @@ public class UpdateAction
|
|||||||
{
|
{
|
||||||
LOG.debug("Deleting associatedRecords", logPair("associatedTable", associatedTable.getName()), logPair("noOfRecords", queryOutput.getRecords().size()));
|
LOG.debug("Deleting associatedRecords", logPair("associatedTable", associatedTable.getName()), logPair("noOfRecords", queryOutput.getRecords().size()));
|
||||||
DeleteInput deleteInput = new DeleteInput();
|
DeleteInput deleteInput = new DeleteInput();
|
||||||
|
deleteInput.setFlags(updateInput.getFlags());
|
||||||
deleteInput.setTransaction(updateInput.getTransaction());
|
deleteInput.setTransaction(updateInput.getTransaction());
|
||||||
deleteInput.setTableName(association.getAssociatedTableName());
|
deleteInput.setTableName(association.getAssociatedTableName());
|
||||||
deleteInput.setPrimaryKeys(queryOutput.getRecords().stream().map(r -> r.getValue(associatedTable.getPrimaryKeyField())).collect(Collectors.toList()));
|
deleteInput.setPrimaryKeys(queryOutput.getRecords().stream().map(r -> r.getValue(associatedTable.getPrimaryKeyField())).collect(Collectors.toList()));
|
||||||
@ -617,6 +618,7 @@ public class UpdateAction
|
|||||||
LOG.debug("Updating associatedRecords", logPair("associatedTable", associatedTable.getName()), logPair("noOfRecords", nextLevelUpdates.size()));
|
LOG.debug("Updating associatedRecords", logPair("associatedTable", associatedTable.getName()), logPair("noOfRecords", nextLevelUpdates.size()));
|
||||||
UpdateInput nextLevelUpdateInput = new UpdateInput();
|
UpdateInput nextLevelUpdateInput = new UpdateInput();
|
||||||
nextLevelUpdateInput.setTransaction(updateInput.getTransaction());
|
nextLevelUpdateInput.setTransaction(updateInput.getTransaction());
|
||||||
|
nextLevelUpdateInput.setFlags(updateInput.getFlags());
|
||||||
nextLevelUpdateInput.setTableName(association.getAssociatedTableName());
|
nextLevelUpdateInput.setTableName(association.getAssociatedTableName());
|
||||||
nextLevelUpdateInput.setRecords(nextLevelUpdates);
|
nextLevelUpdateInput.setRecords(nextLevelUpdates);
|
||||||
UpdateOutput nextLevelUpdateOutput = new UpdateAction().execute(nextLevelUpdateInput);
|
UpdateOutput nextLevelUpdateOutput = new UpdateAction().execute(nextLevelUpdateInput);
|
||||||
@ -627,6 +629,7 @@ public class UpdateAction
|
|||||||
LOG.debug("Inserting associatedRecords", logPair("associatedTable", associatedTable.getName()), logPair("noOfRecords", nextLevelUpdates.size()));
|
LOG.debug("Inserting associatedRecords", logPair("associatedTable", associatedTable.getName()), logPair("noOfRecords", nextLevelUpdates.size()));
|
||||||
InsertInput nextLevelInsertInput = new InsertInput();
|
InsertInput nextLevelInsertInput = new InsertInput();
|
||||||
nextLevelInsertInput.setTransaction(updateInput.getTransaction());
|
nextLevelInsertInput.setTransaction(updateInput.getTransaction());
|
||||||
|
nextLevelInsertInput.setFlags(updateInput.getFlags());
|
||||||
nextLevelInsertInput.setTableName(association.getAssociatedTableName());
|
nextLevelInsertInput.setTableName(association.getAssociatedTableName());
|
||||||
nextLevelInsertInput.setRecords(nextLevelInserts);
|
nextLevelInsertInput.setRecords(nextLevelInserts);
|
||||||
InsertOutput nextLevelInsertOutput = new InsertAction().execute(nextLevelInsertInput);
|
InsertOutput nextLevelInsertOutput = new InsertAction().execute(nextLevelInsertInput);
|
||||||
|
@ -47,12 +47,12 @@ public abstract class BasicCustomPossibleValueProvider<S, ID extends Serializabl
|
|||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
**
|
**
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
protected abstract S getSourceObject(Serializable id);
|
protected abstract S getSourceObject(Serializable id) throws QException;
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
**
|
**
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
protected abstract List<S> getAllSourceObjects();
|
protected abstract List<S> getAllSourceObjects() throws QException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ public abstract class BasicCustomPossibleValueProvider<S, ID extends Serializabl
|
|||||||
**
|
**
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
@Override
|
@Override
|
||||||
public QPossibleValue<ID> getPossibleValue(Serializable idValue)
|
public QPossibleValue<ID> getPossibleValue(Serializable idValue) throws QException
|
||||||
{
|
{
|
||||||
S sourceObject = getSourceObject(idValue);
|
S sourceObject = getSourceObject(idValue);
|
||||||
if(sourceObject == null)
|
if(sourceObject == null)
|
||||||
|
@ -45,7 +45,7 @@ public interface QCustomPossibleValueProvider<T extends Serializable>
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
QPossibleValue<T> getPossibleValue(Serializable idValue);
|
QPossibleValue<T> getPossibleValue(Serializable idValue) throws QException;
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2025. 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.model.actions.tables;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** interface to mark enums (presumably classes too, but the original intent is
|
||||||
|
** enums) that can be added to insert/update/delete action inputs to flag behaviors
|
||||||
|
*******************************************************************************/
|
||||||
|
public interface ActionFlag extends Serializable
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -24,9 +24,12 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.delete;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.ActionFlag;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
@ -47,6 +50,8 @@ public class DeleteInput extends AbstractTableActionInput
|
|||||||
private boolean omitDmlAudit = false;
|
private boolean omitDmlAudit = false;
|
||||||
private String auditContext = null;
|
private String auditContext = null;
|
||||||
|
|
||||||
|
private Set<ActionFlag> flags;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -295,4 +300,65 @@ public class DeleteInput extends AbstractTableActionInput
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for flags
|
||||||
|
*******************************************************************************/
|
||||||
|
public Set<ActionFlag> getFlags()
|
||||||
|
{
|
||||||
|
return (this.flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for flags
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setFlags(Set<ActionFlag> flags)
|
||||||
|
{
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for flags
|
||||||
|
*******************************************************************************/
|
||||||
|
public DeleteInput withFlags(Set<ActionFlag> flags)
|
||||||
|
{
|
||||||
|
this.flags = flags;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public DeleteInput withFlag(ActionFlag flag)
|
||||||
|
{
|
||||||
|
if(this.flags == null)
|
||||||
|
{
|
||||||
|
this.flags = new HashSet<>();
|
||||||
|
}
|
||||||
|
this.flags.add(flag);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public boolean hasFlag(ActionFlag flag)
|
||||||
|
{
|
||||||
|
if(this.flags == null)
|
||||||
|
{
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this.flags.contains(flag));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,12 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.insert;
|
|||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.ActionFlag;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
@ -48,6 +51,8 @@ public class InsertInput extends AbstractTableActionInput
|
|||||||
private boolean omitDmlAudit = false;
|
private boolean omitDmlAudit = false;
|
||||||
private String auditContext = null;
|
private String auditContext = null;
|
||||||
|
|
||||||
|
private Set<ActionFlag> flags;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -316,4 +321,65 @@ public class InsertInput extends AbstractTableActionInput
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for flags
|
||||||
|
*******************************************************************************/
|
||||||
|
public Set<ActionFlag> getFlags()
|
||||||
|
{
|
||||||
|
return (this.flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for flags
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setFlags(Set<ActionFlag> flags)
|
||||||
|
{
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for flags
|
||||||
|
*******************************************************************************/
|
||||||
|
public InsertInput withFlags(Set<ActionFlag> flags)
|
||||||
|
{
|
||||||
|
this.flags = flags;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public InsertInput withFlag(ActionFlag flag)
|
||||||
|
{
|
||||||
|
if(this.flags == null)
|
||||||
|
{
|
||||||
|
this.flags = new HashSet<>();
|
||||||
|
}
|
||||||
|
this.flags.add(flag);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public boolean hasFlag(ActionFlag flag)
|
||||||
|
{
|
||||||
|
if(this.flags == null)
|
||||||
|
{
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this.flags.contains(flag));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,12 @@
|
|||||||
package com.kingsrook.qqq.backend.core.model.actions.tables.replace;
|
package com.kingsrook.qqq.backend.core.model.actions.tables.replace;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.ActionFlag;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||||
@ -45,6 +48,8 @@ public class ReplaceInput extends AbstractTableActionInput
|
|||||||
|
|
||||||
private boolean omitDmlAudit = false;
|
private boolean omitDmlAudit = false;
|
||||||
|
|
||||||
|
private Set<ActionFlag> flags;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -303,4 +308,65 @@ public class ReplaceInput extends AbstractTableActionInput
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for flags
|
||||||
|
*******************************************************************************/
|
||||||
|
public Set<ActionFlag> getFlags()
|
||||||
|
{
|
||||||
|
return (this.flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for flags
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setFlags(Set<ActionFlag> flags)
|
||||||
|
{
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for flags
|
||||||
|
*******************************************************************************/
|
||||||
|
public ReplaceInput withFlags(Set<ActionFlag> flags)
|
||||||
|
{
|
||||||
|
this.flags = flags;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public ReplaceInput withFlag(ActionFlag flag)
|
||||||
|
{
|
||||||
|
if(this.flags == null)
|
||||||
|
{
|
||||||
|
this.flags = new HashSet<>();
|
||||||
|
}
|
||||||
|
this.flags.add(flag);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public boolean hasFlag(ActionFlag flag)
|
||||||
|
{
|
||||||
|
if(this.flags == null)
|
||||||
|
{
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this.flags.contains(flag));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,12 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.update;
|
|||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.ActionFlag;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
@ -56,6 +59,8 @@ public class UpdateInput extends AbstractTableActionInput
|
|||||||
private boolean omitModifyDateUpdate = false;
|
private boolean omitModifyDateUpdate = false;
|
||||||
private String auditContext = null;
|
private String auditContext = null;
|
||||||
|
|
||||||
|
private Set<ActionFlag> flags;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -385,4 +390,65 @@ public class UpdateInput extends AbstractTableActionInput
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for flags
|
||||||
|
*******************************************************************************/
|
||||||
|
public Set<ActionFlag> getFlags()
|
||||||
|
{
|
||||||
|
return (this.flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for flags
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setFlags(Set<ActionFlag> flags)
|
||||||
|
{
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for flags
|
||||||
|
*******************************************************************************/
|
||||||
|
public UpdateInput withFlags(Set<ActionFlag> flags)
|
||||||
|
{
|
||||||
|
this.flags = flags;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public UpdateInput withFlag(ActionFlag flag)
|
||||||
|
{
|
||||||
|
if(this.flags == null)
|
||||||
|
{
|
||||||
|
this.flags = new HashSet<>();
|
||||||
|
}
|
||||||
|
this.flags.add(flag);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public boolean hasFlag(ActionFlag flag)
|
||||||
|
{
|
||||||
|
if(this.flags == null)
|
||||||
|
{
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this.flags.contains(flag));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2025. 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.model.tables;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionCheckResult;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.values.BasicCustomPossibleValueProvider;
|
||||||
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** possible-value source provider for the `QQQ Table` PVS - a list of all tables
|
||||||
|
** in an application/qInstance (that you have permission to see)
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QQQTableCustomPossibleValueProvider extends BasicCustomPossibleValueProvider<QRecord, Integer>
|
||||||
|
{
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
protected QPossibleValue<Integer> makePossibleValue(QRecord sourceObject)
|
||||||
|
{
|
||||||
|
return (new QPossibleValue<>(sourceObject.getValueInteger("id"), sourceObject.getValueString("label")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
protected QRecord getSourceObject(Serializable id) throws QException
|
||||||
|
{
|
||||||
|
QRecord qqqTableRecord = GetAction.execute(QQQTable.TABLE_NAME, id);
|
||||||
|
if(qqqTableRecord == null)
|
||||||
|
{
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTableMetaData table = QContext.getQInstance().getTable(qqqTableRecord.getValueString("name"));
|
||||||
|
return isTableAllowed(table) ? qqqTableRecord : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
protected List<QRecord> getAllSourceObjects() throws QException
|
||||||
|
{
|
||||||
|
List<QRecord> records = QueryAction.execute(QQQTable.TABLE_NAME, null);
|
||||||
|
ArrayList<QRecord> rs = new ArrayList<>();
|
||||||
|
for(QRecord record : records)
|
||||||
|
{
|
||||||
|
QTableMetaData table = QContext.getQInstance().getTable(record.getValueString("name"));
|
||||||
|
if(isTableAllowed(table))
|
||||||
|
{
|
||||||
|
rs.add(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
private boolean isTableAllowed(QTableMetaData table)
|
||||||
|
{
|
||||||
|
if(table == null)
|
||||||
|
{
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(table.getIsHidden())
|
||||||
|
{
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
PermissionCheckResult permissionCheckResult = PermissionsHelper.getPermissionCheckResult(new QueryInput(table.getName()), table);
|
||||||
|
if(!PermissionCheckResult.ALLOW.equals(permissionCheckResult))
|
||||||
|
{
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -27,6 +27,9 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.audits.AuditLevel;
|
import com.kingsrook.qqq.backend.core.model.metadata.audits.AuditLevel;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
|
import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PVSValueFormatAndFields;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||||
@ -125,10 +128,11 @@ public class QQQTablesMetaDataProvider
|
|||||||
public QPossibleValueSource defineQQQTablePossibleValueSource()
|
public QPossibleValueSource defineQQQTablePossibleValueSource()
|
||||||
{
|
{
|
||||||
return (new QPossibleValueSource()
|
return (new QPossibleValueSource()
|
||||||
.withType(QPossibleValueSourceType.TABLE)
|
|
||||||
.withName(QQQTable.TABLE_NAME)
|
.withName(QQQTable.TABLE_NAME)
|
||||||
.withTableName(QQQTable.TABLE_NAME))
|
.withType(QPossibleValueSourceType.CUSTOM)
|
||||||
.withOrderByField("label");
|
.withIdType(QFieldType.INTEGER)
|
||||||
|
.withCustomCodeReference(new QCodeReference(QQQTableCustomPossibleValueProvider.class))
|
||||||
|
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ import com.kingsrook.qqq.backend.core.context.QContext;
|
|||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.ActionFlag;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
@ -52,10 +53,10 @@ import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Base class to manage creating scheduled jobs based on records in another table
|
* Base class to manage creating scheduled jobs based on records in another table
|
||||||
**
|
*
|
||||||
** Expected to be used via BaseSyncToScheduledJobTableCustomizer - see its javadoc.
|
* Expected to be used via BaseSyncToScheduledJobTableCustomizer - see its javadoc.
|
||||||
**
|
* @see BaseSyncToScheduledJobTableCustomizer
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public abstract class AbstractRecordSyncToScheduledJobProcess extends AbstractTableSyncTransformStep implements MetaDataProducerInterface<QProcessMetaData>
|
public abstract class AbstractRecordSyncToScheduledJobProcess extends AbstractTableSyncTransformStep implements MetaDataProducerInterface<QProcessMetaData>
|
||||||
{
|
{
|
||||||
@ -65,6 +66,20 @@ public abstract class AbstractRecordSyncToScheduledJobProcess extends AbstractTa
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* action flags that can be put in an insert/update/delete input to control
|
||||||
|
* behavior of this process.
|
||||||
|
***************************************************************************/
|
||||||
|
public enum ActionFlags implements ActionFlag
|
||||||
|
{
|
||||||
|
/***************************************************************************
|
||||||
|
* tell this process not to run upon such an action taken on the source table.
|
||||||
|
***************************************************************************/
|
||||||
|
DO_NOT_SYNC
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -89,7 +104,6 @@ public abstract class AbstractRecordSyncToScheduledJobProcess extends AbstractTa
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -40,9 +40,11 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.InitializableViaCodeReference;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.InitializableViaCodeReference;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||||
@ -84,16 +86,44 @@ public class BaseSyncToScheduledJobTableCustomizer implements TableCustomizerInt
|
|||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
**
|
* Create a {@link QCodeReferenceWithProperties} that can be used to add this
|
||||||
|
* class to a table.
|
||||||
|
*
|
||||||
|
* If this is the only customizer for the post insert/update/delete events
|
||||||
|
* on your table, you can instead call setTableCustomizers. But if you want,
|
||||||
|
* for example, a sync-scheduled-job (what this customizer does) plus some other
|
||||||
|
* customizers, then you can call this method to get a code reference that you
|
||||||
|
* can add, for example, to {@link com.kingsrook.qqq.backend.core.actions.customizers.MultiCustomizer}
|
||||||
|
*
|
||||||
|
* @param tableMetaData the table that the customizer will be used on.
|
||||||
|
* @param syncProcess instance of the subclass of AbstractRecordSyncToScheduledJobProcess
|
||||||
|
* that should run in the table's post insert/update/delete
|
||||||
|
* events.
|
||||||
|
* @see #setTableCustomizers(QTableMetaData, AbstractRecordSyncToScheduledJobProcess)
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
public static void setTableCustomizers(QTableMetaData tableMetaData, AbstractRecordSyncToScheduledJobProcess syncProcess)
|
public static QCodeReferenceWithProperties makeCodeReference(QTableMetaData tableMetaData, AbstractRecordSyncToScheduledJobProcess syncProcess)
|
||||||
{
|
{
|
||||||
QCodeReference codeReference = new QCodeReferenceWithProperties(BaseSyncToScheduledJobTableCustomizer.class, Map.of(
|
return new QCodeReferenceWithProperties(BaseSyncToScheduledJobTableCustomizer.class, Map.of(
|
||||||
KEY_TABLE_NAME, tableMetaData.getName(),
|
KEY_TABLE_NAME, tableMetaData.getName(),
|
||||||
KEY_SYNC_PROCESS_NAME, syncProcess.getClass().getSimpleName(),
|
KEY_SYNC_PROCESS_NAME, syncProcess.getClass().getSimpleName(),
|
||||||
KEY_SCHEDULED_JOB_FOREIGN_KEY_TYPE, syncProcess.getScheduledJobForeignKeyType()
|
KEY_SCHEDULED_JOB_FOREIGN_KEY_TYPE, syncProcess.getScheduledJobForeignKeyType()
|
||||||
));
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Add post insert/update/delete customizers to a table, that will run a
|
||||||
|
* sync-scheduled-job process.
|
||||||
|
*
|
||||||
|
* @param tableMetaData the table that the customizer will be used on.
|
||||||
|
* @param syncProcess instance of the subclass of AbstractRecordSyncToScheduledJobProcess
|
||||||
|
* that should run in the table's post insert/update/delete
|
||||||
|
* events.
|
||||||
|
***************************************************************************/
|
||||||
|
public static void setTableCustomizers(QTableMetaData tableMetaData, AbstractRecordSyncToScheduledJobProcess syncProcess)
|
||||||
|
{
|
||||||
|
QCodeReference codeReference = makeCodeReference(tableMetaData, syncProcess);
|
||||||
tableMetaData.withCustomizer(TableCustomizers.POST_INSERT_RECORD, codeReference);
|
tableMetaData.withCustomizer(TableCustomizers.POST_INSERT_RECORD, codeReference);
|
||||||
tableMetaData.withCustomizer(TableCustomizers.POST_UPDATE_RECORD, codeReference);
|
tableMetaData.withCustomizer(TableCustomizers.POST_UPDATE_RECORD, codeReference);
|
||||||
tableMetaData.withCustomizer(TableCustomizers.POST_DELETE_RECORD, codeReference);
|
tableMetaData.withCustomizer(TableCustomizers.POST_DELETE_RECORD, codeReference);
|
||||||
@ -138,6 +168,16 @@ public class BaseSyncToScheduledJobTableCustomizer implements TableCustomizerInt
|
|||||||
@Override
|
@Override
|
||||||
public List<QRecord> postInsertOrUpdate(AbstractActionInput input, List<QRecord> records, Optional<List<QRecord>> oldRecordList) throws QException
|
public List<QRecord> postInsertOrUpdate(AbstractActionInput input, List<QRecord> records, Optional<List<QRecord>> oldRecordList) throws QException
|
||||||
{
|
{
|
||||||
|
if(input instanceof UpdateInput updateInput && updateInput.hasFlag(AbstractRecordSyncToScheduledJobProcess.ActionFlags.DO_NOT_SYNC))
|
||||||
|
{
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(input instanceof InsertInput insertInput && insertInput.hasFlag(AbstractRecordSyncToScheduledJobProcess.ActionFlags.DO_NOT_SYNC))
|
||||||
|
{
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
runSyncProcessForRecordList(records, syncProcessName);
|
runSyncProcessForRecordList(records, syncProcessName);
|
||||||
return records;
|
return records;
|
||||||
}
|
}
|
||||||
@ -157,7 +197,17 @@ public class BaseSyncToScheduledJobTableCustomizer implements TableCustomizerInt
|
|||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
**
|
* Run the named process over a set of records (e.g., that were inserted or
|
||||||
|
* updated).
|
||||||
|
*
|
||||||
|
* This method is normally called from within this class, in postInsertOrUpdate.
|
||||||
|
*
|
||||||
|
* Note that if the {@link ScheduledJob} table isn't defined in the QInstance,
|
||||||
|
* that the process will not be called.
|
||||||
|
*
|
||||||
|
* @param records list of records to use as source records in the table-sync
|
||||||
|
* to the scheduledJob table.
|
||||||
|
* @param processName name of the sync-process to run.
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
public void runSyncProcessForRecordList(List<QRecord> records, String processName)
|
public void runSyncProcessForRecordList(List<QRecord> records, String processName)
|
||||||
{
|
{
|
||||||
@ -199,7 +249,15 @@ public class BaseSyncToScheduledJobTableCustomizer implements TableCustomizerInt
|
|||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
**
|
* Delete scheduled job records for source-table records that have been deleted.
|
||||||
|
*
|
||||||
|
* This method is normally called from within this class, in postDelete.
|
||||||
|
*
|
||||||
|
* Note that if the {@link ScheduledJob} table isn't defined in the QInstance,
|
||||||
|
* that the process will not be called.
|
||||||
|
*
|
||||||
|
* @param records list of records to use as foreign-key sources to identify
|
||||||
|
* scheduledJob records to delete
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
public void deleteScheduledJobsForRecordList(List<QRecord> records)
|
public void deleteScheduledJobsForRecordList(List<QRecord> records)
|
||||||
{
|
{
|
||||||
@ -296,15 +354,6 @@ public class BaseSyncToScheduledJobTableCustomizer implements TableCustomizerInt
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for KEY_SCHEDULED_JOB_FOREIGN_KEY_TYPE
|
|
||||||
*******************************************************************************/
|
|
||||||
public String getKEY_SCHEDULED_JOB_FOREIGN_KEY_TYPE()
|
|
||||||
{
|
|
||||||
return (BaseSyncToScheduledJobTableCustomizer.KEY_SCHEDULED_JOB_FOREIGN_KEY_TYPE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for scheduledJobForeignKeyType
|
** Getter for scheduledJobForeignKeyType
|
||||||
@ -335,5 +384,4 @@ public class BaseSyncToScheduledJobTableCustomizer implements TableCustomizerInt
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2025. 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.actions.customizers;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReferenceWithProperties;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for MultiCustomizer
|
||||||
|
*******************************************************************************/
|
||||||
|
class MultiCustomizerTest extends BaseTest
|
||||||
|
{
|
||||||
|
private static List<String> events = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@BeforeEach
|
||||||
|
@AfterEach
|
||||||
|
void beforeAndAfterEach()
|
||||||
|
{
|
||||||
|
events.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = TestUtils.defineInstance();
|
||||||
|
qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||||
|
.withCustomizer(TableCustomizers.PRE_INSERT_RECORD, MultiCustomizer.of(
|
||||||
|
new QCodeReference(CustomizerA.class),
|
||||||
|
new QCodeReference(CustomizerB.class)
|
||||||
|
));
|
||||||
|
reInitInstanceInContext(qInstance);
|
||||||
|
|
||||||
|
new InsertAction().execute(new InsertInput(TestUtils.TABLE_NAME_PERSON_MEMORY).withRecord(new QRecord()));
|
||||||
|
assertThat(events).hasSize(2)
|
||||||
|
.contains("CustomizerA.preInsert")
|
||||||
|
.contains("CustomizerB.preInsert");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testAddingMore() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = TestUtils.defineInstance();
|
||||||
|
|
||||||
|
QCodeReferenceWithProperties multiCustomizer = MultiCustomizer.of(new QCodeReference(CustomizerA.class));
|
||||||
|
MultiCustomizer.addTableCustomizer(multiCustomizer, new QCodeReference(CustomizerB.class));
|
||||||
|
|
||||||
|
qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).withCustomizer(TableCustomizers.PRE_INSERT_RECORD, multiCustomizer);
|
||||||
|
reInitInstanceInContext(qInstance);
|
||||||
|
|
||||||
|
new InsertAction().execute(new InsertInput(TestUtils.TABLE_NAME_PERSON_MEMORY).withRecord(new QRecord()));
|
||||||
|
assertThat(events).hasSize(2)
|
||||||
|
.contains("CustomizerA.preInsert")
|
||||||
|
.contains("CustomizerB.preInsert");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public static class CustomizerA implements TableCustomizerInterface
|
||||||
|
{
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
public List<QRecord> preInsert(InsertInput insertInput, List<QRecord> records, boolean isPreview) throws QException
|
||||||
|
{
|
||||||
|
events.add("CustomizerA.preInsert");
|
||||||
|
return (records);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public static class CustomizerB implements TableCustomizerInterface
|
||||||
|
{
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
public List<QRecord> preInsert(InsertInput insertInput, List<QRecord> records, boolean isPreview) throws QException
|
||||||
|
{
|
||||||
|
events.add("CustomizerB.preInsert");
|
||||||
|
return (records);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -80,7 +80,7 @@ class TablesCustomPossibleValueProviderTest extends BaseTest
|
|||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
void testGetPossibleValue()
|
void testGetPossibleValue() throws QException
|
||||||
{
|
{
|
||||||
TablesCustomPossibleValueProvider provider = new TablesCustomPossibleValueProvider();
|
TablesCustomPossibleValueProvider provider = new TablesCustomPossibleValueProvider();
|
||||||
|
|
||||||
|
@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2025. 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.model.tables;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.values.SearchPossibleValueSourceInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
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.permissions.PermissionLevel;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for QQQTableCustomPossibleValueProvider
|
||||||
|
*******************************************************************************/
|
||||||
|
class QQQTableCustomPossibleValueProviderTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@BeforeEach
|
||||||
|
void beforeEach() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = TestUtils.defineInstance();
|
||||||
|
|
||||||
|
qInstance.addTable(new QTableMetaData()
|
||||||
|
.withName("hidden")
|
||||||
|
.withIsHidden(true)
|
||||||
|
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER)));
|
||||||
|
|
||||||
|
qInstance.addTable(new QTableMetaData()
|
||||||
|
.withName("restricted")
|
||||||
|
.withPermissionRules(new QPermissionRules().withLevel(PermissionLevel.HAS_ACCESS_PERMISSION))
|
||||||
|
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER)));
|
||||||
|
|
||||||
|
new QQQTablesMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||||
|
|
||||||
|
QContext.init(qInstance, newSession());
|
||||||
|
|
||||||
|
for(String tableName : qInstance.getTables().keySet())
|
||||||
|
{
|
||||||
|
QQQTableTableManager.getQQQTableId(qInstance, tableName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testGetPossibleValue() throws QException
|
||||||
|
{
|
||||||
|
Integer personTableId = QQQTableTableManager.getQQQTableId(QContext.getQInstance(), TestUtils.TABLE_NAME_PERSON);
|
||||||
|
QQQTableCustomPossibleValueProvider provider = new QQQTableCustomPossibleValueProvider();
|
||||||
|
|
||||||
|
QPossibleValue<Integer> possibleValue = provider.getPossibleValue(personTableId);
|
||||||
|
assertEquals(personTableId, possibleValue.getId());
|
||||||
|
assertEquals("Person", possibleValue.getLabel());
|
||||||
|
|
||||||
|
assertNull(provider.getPossibleValue(-1));
|
||||||
|
|
||||||
|
Integer hiddenTableId = QQQTableTableManager.getQQQTableId(QContext.getQInstance(), "hidden");
|
||||||
|
assertNull(provider.getPossibleValue(hiddenTableId));
|
||||||
|
|
||||||
|
Integer restrictedTableId = QQQTableTableManager.getQQQTableId(QContext.getQInstance(), "restricted");
|
||||||
|
assertNull(provider.getPossibleValue(restrictedTableId));
|
||||||
|
|
||||||
|
QContext.getQSession().withPermission("restricted.hasAccess");
|
||||||
|
assertNotNull(provider.getPossibleValue(restrictedTableId));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testSearchPossibleValue() throws QException
|
||||||
|
{
|
||||||
|
Integer personTableId = QQQTableTableManager.getQQQTableId(QContext.getQInstance(), TestUtils.TABLE_NAME_PERSON);
|
||||||
|
Integer shapeTableId = QQQTableTableManager.getQQQTableId(QContext.getQInstance(), TestUtils.TABLE_NAME_SHAPE);
|
||||||
|
Integer hiddenTableId = QQQTableTableManager.getQQQTableId(QContext.getQInstance(), "hidden");
|
||||||
|
Integer restrictedTableId = QQQTableTableManager.getQQQTableId(QContext.getQInstance(), "restricted");
|
||||||
|
|
||||||
|
QQQTableCustomPossibleValueProvider provider = new QQQTableCustomPossibleValueProvider();
|
||||||
|
|
||||||
|
List<QPossibleValue<Integer>> list = provider.search(new SearchPossibleValueSourceInput()
|
||||||
|
.withPossibleValueSourceName(QQQTable.TABLE_NAME));
|
||||||
|
assertThat(list).anyMatch(p -> p.getId().equals(personTableId));
|
||||||
|
assertThat(list).noneMatch(p -> p.getId().equals(-1));
|
||||||
|
assertThat(list).noneMatch(p -> p.getId().equals(hiddenTableId));
|
||||||
|
assertThat(list).noneMatch(p -> p.getId().equals(restrictedTableId));
|
||||||
|
assertNull(provider.getPossibleValue("restricted"));
|
||||||
|
|
||||||
|
list = provider.search(new SearchPossibleValueSourceInput()
|
||||||
|
.withPossibleValueSourceName(QQQTable.TABLE_NAME)
|
||||||
|
.withIdList(List.of(personTableId, shapeTableId, hiddenTableId)));
|
||||||
|
assertEquals(2, list.size());
|
||||||
|
assertThat(list).anyMatch(p -> p.getId().equals(personTableId));
|
||||||
|
assertThat(list).anyMatch(p -> p.getId().equals(shapeTableId));
|
||||||
|
assertThat(list).noneMatch(p -> p.getId().equals(hiddenTableId));
|
||||||
|
|
||||||
|
list = provider.search(new SearchPossibleValueSourceInput()
|
||||||
|
.withPossibleValueSourceName(QQQTable.TABLE_NAME)
|
||||||
|
.withLabelList(List.of("Person", "Shape", "Restricted")));
|
||||||
|
assertEquals(2, list.size());
|
||||||
|
assertThat(list).anyMatch(p -> p.getId().equals(personTableId));
|
||||||
|
assertThat(list).anyMatch(p -> p.getId().equals(shapeTableId));
|
||||||
|
assertThat(list).noneMatch(p -> p.getId().equals(restrictedTableId));
|
||||||
|
|
||||||
|
list = provider.search(new SearchPossibleValueSourceInput()
|
||||||
|
.withPossibleValueSourceName(QQQTable.TABLE_NAME)
|
||||||
|
.withSearchTerm("restricted"));
|
||||||
|
assertEquals(0, list.size());
|
||||||
|
|
||||||
|
/////////////////////////////////////////
|
||||||
|
// add permission for restricted table //
|
||||||
|
/////////////////////////////////////////
|
||||||
|
QContext.getQSession().withPermission("restricted.hasAccess");
|
||||||
|
list = provider.search(new SearchPossibleValueSourceInput()
|
||||||
|
.withPossibleValueSourceName(QQQTable.TABLE_NAME)
|
||||||
|
.withSearchTerm("restricted"));
|
||||||
|
assertEquals(1, list.size());
|
||||||
|
|
||||||
|
list = provider.search(new SearchPossibleValueSourceInput()
|
||||||
|
.withPossibleValueSourceName(QQQTable.TABLE_NAME)
|
||||||
|
.withLabelList(List.of("Person", "Shape", "Restricted")));
|
||||||
|
assertEquals(3, list.size());
|
||||||
|
assertThat(list).anyMatch(p -> p.getId().equals(personTableId));
|
||||||
|
assertThat(list).anyMatch(p -> p.getId().equals(shapeTableId));
|
||||||
|
assertThat(list).anyMatch(p -> p.getId().equals(restrictedTableId));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user