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))
|
||||
{
|
||||
DeleteInput nextLevelDeleteInput = new DeleteInput();
|
||||
nextLevelDeleteInput.setFlags(deleteInput.getFlags());
|
||||
nextLevelDeleteInput.setTransaction(deleteInput.getTransaction());
|
||||
nextLevelDeleteInput.setTableName(association.getAssociatedTableName());
|
||||
nextLevelDeleteInput.setPrimaryKeys(associatedKeys);
|
||||
|
@ -34,7 +34,6 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.backend.core.actions.AbstractQActionFunction;
|
||||
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.automation.AutomationStatus;
|
||||
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 //
|
||||
//////////////////////////////////////////////////
|
||||
manageAssociations(table, insertOutput.getRecords(), insertInput.getTransaction());
|
||||
manageAssociations(table, insertOutput.getRecords(), insertInput);
|
||||
|
||||
//////////////////
|
||||
// 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()))
|
||||
{
|
||||
@ -419,7 +418,8 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
||||
if(CollectionUtils.nullSafeHasContents(nextLevelInserts))
|
||||
{
|
||||
InsertInput nextLevelInsertInput = new InsertInput();
|
||||
nextLevelInsertInput.setTransaction(transaction);
|
||||
nextLevelInsertInput.withFlags(insertInput.getFlags());
|
||||
nextLevelInsertInput.setTransaction(insertInput.getTransaction());
|
||||
nextLevelInsertInput.setTableName(association.getAssociatedTableName());
|
||||
nextLevelInsertInput.setRecords(nextLevelInserts);
|
||||
InsertOutput nextLevelInsertOutput = new InsertAction().execute(nextLevelInsertInput);
|
||||
|
@ -126,6 +126,7 @@ public class ReplaceAction extends AbstractQActionFunction<ReplaceInput, Replace
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(table.getName());
|
||||
insertInput.setRecords(insertList);
|
||||
insertInput.withFlags(input.getFlags());
|
||||
insertInput.setTransaction(transaction);
|
||||
insertInput.setOmitDmlAudit(input.getOmitDmlAudit());
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
@ -135,6 +136,7 @@ public class ReplaceAction extends AbstractQActionFunction<ReplaceInput, Replace
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(table.getName());
|
||||
updateInput.setRecords(updateList);
|
||||
updateInput.withFlags(input.getFlags());
|
||||
updateInput.setTransaction(transaction);
|
||||
updateInput.setOmitDmlAudit(input.getOmitDmlAudit());
|
||||
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
|
||||
@ -151,6 +153,7 @@ public class ReplaceAction extends AbstractQActionFunction<ReplaceInput, Replace
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setTableName(table.getName());
|
||||
deleteInput.setQueryFilter(deleteFilter);
|
||||
deleteInput.withFlags(input.getFlags());
|
||||
deleteInput.setTransaction(transaction);
|
||||
deleteInput.setOmitDmlAudit(input.getOmitDmlAudit());
|
||||
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()));
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setFlags(updateInput.getFlags());
|
||||
deleteInput.setTransaction(updateInput.getTransaction());
|
||||
deleteInput.setTableName(association.getAssociatedTableName());
|
||||
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()));
|
||||
UpdateInput nextLevelUpdateInput = new UpdateInput();
|
||||
nextLevelUpdateInput.setTransaction(updateInput.getTransaction());
|
||||
nextLevelUpdateInput.setFlags(updateInput.getFlags());
|
||||
nextLevelUpdateInput.setTableName(association.getAssociatedTableName());
|
||||
nextLevelUpdateInput.setRecords(nextLevelUpdates);
|
||||
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()));
|
||||
InsertInput nextLevelInsertInput = new InsertInput();
|
||||
nextLevelInsertInput.setTransaction(updateInput.getTransaction());
|
||||
nextLevelInsertInput.setFlags(updateInput.getFlags());
|
||||
nextLevelInsertInput.setTableName(association.getAssociatedTableName());
|
||||
nextLevelInsertInput.setRecords(nextLevelInserts);
|
||||
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
|
||||
public QPossibleValue<ID> getPossibleValue(Serializable idValue)
|
||||
public QPossibleValue<ID> getPossibleValue(Serializable idValue) throws QException
|
||||
{
|
||||
S sourceObject = getSourceObject(idValue);
|
||||
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.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
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.tables.ActionFlag;
|
||||
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.query.QQueryFilter;
|
||||
@ -47,6 +50,8 @@ public class DeleteInput extends AbstractTableActionInput
|
||||
private boolean omitDmlAudit = false;
|
||||
private String auditContext = null;
|
||||
|
||||
private Set<ActionFlag> flags;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -295,4 +300,65 @@ public class DeleteInput extends AbstractTableActionInput
|
||||
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.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
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.tables.ActionFlag;
|
||||
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.data.QRecord;
|
||||
@ -48,6 +51,8 @@ public class InsertInput extends AbstractTableActionInput
|
||||
private boolean omitDmlAudit = false;
|
||||
private String auditContext = null;
|
||||
|
||||
private Set<ActionFlag> flags;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -316,4 +321,65 @@ public class InsertInput extends AbstractTableActionInput
|
||||
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;
|
||||
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
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.tables.ActionFlag;
|
||||
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.metadata.tables.UniqueKey;
|
||||
@ -45,6 +48,8 @@ public class ReplaceInput extends AbstractTableActionInput
|
||||
|
||||
private boolean omitDmlAudit = false;
|
||||
|
||||
private Set<ActionFlag> flags;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -303,4 +308,65 @@ public class ReplaceInput extends AbstractTableActionInput
|
||||
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.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
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.tables.ActionFlag;
|
||||
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.data.QRecord;
|
||||
@ -56,6 +59,8 @@ public class UpdateInput extends AbstractTableActionInput
|
||||
private boolean omitModifyDateUpdate = false;
|
||||
private String auditContext = null;
|
||||
|
||||
private Set<ActionFlag> flags;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -385,4 +390,65 @@ public class UpdateInput extends AbstractTableActionInput
|
||||
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.audits.AuditLevel;
|
||||
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.QPossibleValueSourceType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||
@ -125,10 +128,11 @@ public class QQQTablesMetaDataProvider
|
||||
public QPossibleValueSource defineQQQTablePossibleValueSource()
|
||||
{
|
||||
return (new QPossibleValueSource()
|
||||
.withType(QPossibleValueSourceType.TABLE)
|
||||
.withName(QQQTable.TABLE_NAME)
|
||||
.withTableName(QQQTable.TABLE_NAME))
|
||||
.withOrderByField("label");
|
||||
.withType(QPossibleValueSourceType.CUSTOM)
|
||||
.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.logging.QLogger;
|
||||
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.QFilterCriteria;
|
||||
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
|
||||
**
|
||||
** Expected to be used via BaseSyncToScheduledJobTableCustomizer - see its javadoc.
|
||||
**
|
||||
* Base class to manage creating scheduled jobs based on records in another table
|
||||
*
|
||||
* Expected to be used via BaseSyncToScheduledJobTableCustomizer - see its javadoc.
|
||||
* @see BaseSyncToScheduledJobTableCustomizer
|
||||
*******************************************************************************/
|
||||
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.RunProcessOutput;
|
||||
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.QFilterCriteria;
|
||||
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.metadata.code.InitializableViaCodeReference;
|
||||
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_SYNC_PROCESS_NAME, syncProcess.getClass().getSimpleName(),
|
||||
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_UPDATE_RECORD, codeReference);
|
||||
tableMetaData.withCustomizer(TableCustomizers.POST_DELETE_RECORD, codeReference);
|
||||
@ -138,6 +168,16 @@ public class BaseSyncToScheduledJobTableCustomizer implements TableCustomizerInt
|
||||
@Override
|
||||
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);
|
||||
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)
|
||||
{
|
||||
@ -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)
|
||||
{
|
||||
@ -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
|
||||
@ -335,5 +384,4 @@ public class BaseSyncToScheduledJobTableCustomizer implements TableCustomizerInt
|
||||
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
|
||||
void testGetPossibleValue()
|
||||
void testGetPossibleValue() throws QException
|
||||
{
|
||||
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