Add optional additional validation to widget meta datas; implemented at least in part for ChildRecordListWidget

This commit is contained in:
2025-02-08 20:54:38 -06:00
parent 33f3ebd4c6
commit e25ec61731
5 changed files with 121 additions and 3 deletions

View File

@ -37,6 +37,8 @@ import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
import com.kingsrook.qqq.backend.core.instances.validation.plugins.QInstanceValidatorPluginInterface;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
@ -51,12 +53,15 @@ import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.ChildRecordListData;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.WidgetType;
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.dashboard.AbstractWidgetMetaDataBuilder;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
@ -83,7 +88,9 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
.withIsCard(true)
.withCodeReference(new QCodeReference(ChildRecordListRenderer.class))
.withType(WidgetType.CHILD_RECORD_LIST.getType())
.withDefaultValue("joinName", join.getName())));
.withDefaultValue("joinName", join.getName())
.withValidatorPlugin(new ChildRecordListWidgetValidator())
));
}
@ -165,10 +172,10 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
*******************************************************************************/
public Builder withManageAssociationName(String manageAssociationName)
{
// todo - validate association name exists (on the table?)
widgetMetaData.withDefaultValue("manageAssociationName", manageAssociationName);
return (this);
}
}
@ -321,4 +328,68 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
}
}
/***************************************************************************
**
***************************************************************************/
private static class ChildRecordListWidgetValidator implements QInstanceValidatorPluginInterface<QWidgetMetaDataInterface>
{
/***************************************************************************
**
***************************************************************************/
@Override
public void validate(QWidgetMetaDataInterface widgetMetaData, QInstance qInstance, QInstanceValidator qInstanceValidator)
{
String prefix = "Widget " + widgetMetaData.getName() + ": ";
//////////////////////////////////
// make sure join name is given //
//////////////////////////////////
String joinName = ValueUtils.getValueAsString(CollectionUtils.nonNullMap(widgetMetaData.getDefaultValues()).get("joinName"));
if(qInstanceValidator.assertCondition(StringUtils.hasContent(joinName), prefix + "defaultValue for joinName must be given"))
{
///////////////////////////
// make sure join exists //
///////////////////////////
QJoinMetaData join = qInstance.getJoin(joinName);
if(qInstanceValidator.assertCondition(join != null, prefix + "No join named " + joinName + " exists in the instance"))
{
//////////////////////////////////////////////////////////////////////////////////
// if there's a manageAssociationName, make sure the table has that association //
//////////////////////////////////////////////////////////////////////////////////
String manageAssociationName = ValueUtils.getValueAsString(widgetMetaData.getDefaultValues().get("manageAssociationName"));
if(StringUtils.hasContent(manageAssociationName))
{
validateAssociationName(prefix, manageAssociationName, join, qInstance, qInstanceValidator);
}
}
}
}
/***************************************************************************
**
***************************************************************************/
private void validateAssociationName(String prefix, String manageAssociationName, QJoinMetaData join, QInstance qInstance, QInstanceValidator qInstanceValidator)
{
///////////////////////////////////
// make sure join's table exists //
///////////////////////////////////
QTableMetaData table = qInstance.getTable(join.getLeftTable());
if(table == null)
{
qInstanceValidator.getErrors().add(prefix + "Unable to validate manageAssociationName, as table [" + join.getLeftTable() + "] on left-side table of join [" + join.getName() + "] does not exist.");
}
else
{
if(CollectionUtils.nonNullList(table.getAssociations()).stream().noneMatch(a -> manageAssociationName.equals(a.getName())))
{
qInstanceValidator.getErrors().add(prefix + "an association named [" + manageAssociationName + "] does not exist on table [" + join.getLeftTable() + "]");
}
}
}
}
}

View File

@ -2008,6 +2008,11 @@ public class QInstanceValidator
}
}
if(widget.getValidatorPlugin() != null)
{
widget.getValidatorPlugin().validate(widget, qInstance, this);
}
runPlugins(QWidgetMetaDataInterface.class, widget, qInstance);
}
);

View File

@ -30,6 +30,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import com.kingsrook.qqq.backend.core.instances.QInstanceHelpContentManager;
import com.kingsrook.qqq.backend.core.instances.validation.plugins.QInstanceValidatorPluginInterface;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.WidgetType;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.help.HelpRole;
@ -69,6 +70,7 @@ public class QWidgetMetaData implements QWidgetMetaDataInterface
protected Map<String, Serializable> defaultValues = new LinkedHashMap<>();
protected QInstanceValidatorPluginInterface<QWidgetMetaDataInterface> validatorPlugin;
/*******************************************************************************
@ -764,4 +766,35 @@ public class QWidgetMetaData implements QWidgetMetaDataInterface
QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, listForSlot);
}
/*******************************************************************************
** Getter for validatorPlugin
*******************************************************************************/
public QInstanceValidatorPluginInterface<QWidgetMetaDataInterface> getValidatorPlugin()
{
return (this.validatorPlugin);
}
/*******************************************************************************
** Setter for validatorPlugin
*******************************************************************************/
public void setValidatorPlugin(QInstanceValidatorPluginInterface<QWidgetMetaDataInterface> validatorPlugin)
{
this.validatorPlugin = validatorPlugin;
}
/*******************************************************************************
** Fluent setter for validatorPlugin
*******************************************************************************/
public QWidgetMetaData withValidatorPlugin(QInstanceValidatorPluginInterface<QWidgetMetaDataInterface> validatorPlugin)
{
this.validatorPlugin = validatorPlugin;
return (this);
}
}

View File

@ -26,6 +26,7 @@ import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.kingsrook.qqq.backend.core.instances.validation.plugins.QInstanceValidatorPluginInterface;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
@ -277,5 +278,13 @@ public interface QWidgetMetaDataInterface extends MetaDataWithPermissionRules, T
qInstance.addWidget(this);
}
/***************************************************************************
** let the widget include an instance validator plugin
***************************************************************************/
default QInstanceValidatorPluginInterface<QWidgetMetaDataInterface> getValidatorPlugin()
{
return (null);
}
}

View File

@ -37,7 +37,7 @@ public @interface ChildRecordListWidget
{
boolean enabled();
String label() default "";
String label();
int maxRows() default 20;