Merge pull request #166 from Kingsrook/feature/banners

Initial checkin of Banners under QBrandingMetaData
This commit is contained in:
2025-03-27 12:03:15 -05:00
committed by GitHub
12 changed files with 787 additions and 58 deletions

View File

@ -33,6 +33,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
/*******************************************************************************
** a default implementation of MetaDataFilterInterface, that allows all the things
*******************************************************************************/
@Deprecated(since = "migrated to metaDataCustomizer")
public class AllowAllMetaDataFilter implements MetaDataFilterInterface
{

View File

@ -0,0 +1,92 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.metadata;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
/*******************************************************************************
** a default implementation of MetaDataFilterInterface, that is all noop.
*******************************************************************************/
public class DefaultNoopMetaDataActionCustomizer implements MetaDataActionCustomizerInterface
{
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowTable(MetaDataInput input, QTableMetaData table)
{
return (true);
}
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowProcess(MetaDataInput input, QProcessMetaData process)
{
return (true);
}
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowReport(MetaDataInput input, QReportMetaData report)
{
return (true);
}
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowApp(MetaDataInput input, QAppMetaData app)
{
return (true);
}
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowWidget(MetaDataInput input, QWidgetMetaDataInterface widget)
{
return (true);
}
}

View File

@ -23,10 +23,12 @@ package com.kingsrook.qqq.backend.core.actions.metadata;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionCheckResult;
@ -34,6 +36,7 @@ import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
@ -65,7 +68,7 @@ public class MetaDataAction
{
private static final QLogger LOG = QLogger.getLogger(MetaDataAction.class);
private static Memoization<QInstance, MetaDataFilterInterface> metaDataFilterMemoization = new Memoization<>();
private static Memoization<QInstance, MetaDataActionCustomizerInterface> metaDataActionCustomizerMemoization = new Memoization<>();
@ -79,7 +82,7 @@ public class MetaDataAction
MetaDataOutput metaDataOutput = new MetaDataOutput();
Map<String, AppTreeNode> treeNodes = new LinkedHashMap<>();
MetaDataFilterInterface filter = getMetaDataFilter();
MetaDataActionCustomizerInterface customizer = getMetaDataActionCustomizer();
/////////////////////////////////////
// map tables to frontend metadata //
@ -90,7 +93,7 @@ public class MetaDataAction
String tableName = entry.getKey();
QTableMetaData table = entry.getValue();
if(!filter.allowTable(metaDataInput, table))
if(!customizer.allowTable(metaDataInput, table))
{
continue;
}
@ -119,7 +122,7 @@ public class MetaDataAction
String processName = entry.getKey();
QProcessMetaData process = entry.getValue();
if(!filter.allowProcess(metaDataInput, process))
if(!customizer.allowProcess(metaDataInput, process))
{
continue;
}
@ -144,7 +147,7 @@ public class MetaDataAction
String reportName = entry.getKey();
QReportMetaData report = entry.getValue();
if(!filter.allowReport(metaDataInput, report))
if(!customizer.allowReport(metaDataInput, report))
{
continue;
}
@ -169,7 +172,7 @@ public class MetaDataAction
String widgetName = entry.getKey();
QWidgetMetaDataInterface widget = entry.getValue();
if(!filter.allowWidget(metaDataInput, widget))
if(!customizer.allowWidget(metaDataInput, widget))
{
continue;
}
@ -206,7 +209,7 @@ public class MetaDataAction
continue;
}
if(!filter.allowApp(metaDataInput, app))
if(!customizer.allowApp(metaDataInput, app))
{
continue;
}
@ -292,11 +295,22 @@ public class MetaDataAction
metaDataOutput.setBranding(QContext.getQInstance().getBranding());
}
metaDataOutput.setEnvironmentValues(QContext.getQInstance().getEnvironmentValues());
metaDataOutput.setEnvironmentValues(Objects.requireNonNullElse(QContext.getQInstance().getEnvironmentValues(), Collections.emptyMap()));
metaDataOutput.setHelpContents(QContext.getQInstance().getHelpContent());
metaDataOutput.setHelpContents(Objects.requireNonNullElse(QContext.getQInstance().getHelpContent(), Collections.emptyMap()));
// todo post-customization - can do whatever w/ the result if you want?
try
{
customizer.postProcess(metaDataOutput);
}
catch(QUserFacingException e)
{
LOG.debug("User-facing exception thrown in meta-data customizer post-processing", e);
}
catch(Exception e)
{
LOG.warn("Unexpected error thrown in meta-data customizer post-processing", e);
}
return metaDataOutput;
}
@ -306,26 +320,36 @@ public class MetaDataAction
/***************************************************************************
**
***************************************************************************/
private MetaDataFilterInterface getMetaDataFilter()
private MetaDataActionCustomizerInterface getMetaDataActionCustomizer()
{
return metaDataFilterMemoization.getResult(QContext.getQInstance(), i ->
return metaDataActionCustomizerMemoization.getResult(QContext.getQInstance(), i ->
{
MetaDataActionCustomizerInterface actionCustomizer = null;
QCodeReference metaDataActionCustomizerReference = QContext.getQInstance().getMetaDataActionCustomizer();
if(metaDataActionCustomizerReference != null)
{
actionCustomizer = QCodeLoader.getAdHoc(MetaDataActionCustomizerInterface.class, metaDataActionCustomizerReference);
LOG.debug("Using new meta-data actionCustomizer of type: " + actionCustomizer.getClass().getSimpleName());
}
if(actionCustomizer == null)
{
MetaDataFilterInterface filter = null;
QCodeReference metaDataFilterReference = QContext.getQInstance().getMetaDataFilter();
if(metaDataFilterReference != null)
{
filter = QCodeLoader.getAdHoc(MetaDataFilterInterface.class, metaDataFilterReference);
LOG.debug("Using new meta-data filter of type: " + filter.getClass().getSimpleName());
actionCustomizer = QCodeLoader.getAdHoc(MetaDataActionCustomizerInterface.class, metaDataFilterReference);
LOG.debug("Using new meta-data actionCustomizer (via metaDataFilter reference) of type: " + actionCustomizer.getClass().getSimpleName());
}
}
if(filter == null)
if(actionCustomizer == null)
{
filter = new AllowAllMetaDataFilter();
LOG.debug("Using new default (allow-all) meta-data filter");
actionCustomizer = new DefaultNoopMetaDataActionCustomizer();
LOG.debug("Using new default (allow-all) meta-data actionCustomizer");
}
return (filter);
}).orElseThrow(() -> new QRuntimeException("Error getting metaDataFilter"));
return (actionCustomizer);
}).orElseThrow(() -> new QRuntimeException("Error getting MetaDataActionCustomizer"));
}

View File

@ -0,0 +1,78 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.metadata;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
/*******************************************************************************
** Interface for customizations that can be injected by an application into
** the MetaDataAction - e.g., loading applicable meta-data for a user into a
** frontend.
*******************************************************************************/
public interface MetaDataActionCustomizerInterface
{
/***************************************************************************
**
***************************************************************************/
boolean allowTable(MetaDataInput input, QTableMetaData table);
/***************************************************************************
**
***************************************************************************/
boolean allowProcess(MetaDataInput input, QProcessMetaData process);
/***************************************************************************
**
***************************************************************************/
boolean allowReport(MetaDataInput input, QReportMetaData report);
/***************************************************************************
**
***************************************************************************/
boolean allowApp(MetaDataInput input, QAppMetaData app);
/***************************************************************************
**
***************************************************************************/
boolean allowWidget(MetaDataInput input, QWidgetMetaDataInterface widget);
/***************************************************************************
**
***************************************************************************/
default void postProcess(MetaDataOutput metaDataOutput) throws QException
{
/////////////////////
// noop by default //
/////////////////////
}
}

View File

@ -22,43 +22,11 @@
package com.kingsrook.qqq.backend.core.actions.metadata;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
/*******************************************************************************
**
*******************************************************************************/
public interface MetaDataFilterInterface
@Deprecated(since = "migrated to metaDataCustomizer")
public interface MetaDataFilterInterface extends MetaDataActionCustomizerInterface
{
/***************************************************************************
**
***************************************************************************/
boolean allowTable(MetaDataInput input, QTableMetaData table);
/***************************************************************************
**
***************************************************************************/
boolean allowProcess(MetaDataInput input, QProcessMetaData process);
/***************************************************************************
**
***************************************************************************/
boolean allowReport(MetaDataInput input, QReportMetaData report);
/***************************************************************************
**
***************************************************************************/
boolean allowApp(MetaDataInput input, QAppMetaData app);
/***************************************************************************
**
***************************************************************************/
boolean allowWidget(MetaDataInput input, QWidgetMetaDataInterface widget);
}

View File

@ -45,6 +45,7 @@ import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRenderer;
import com.kingsrook.qqq.backend.core.actions.metadata.JoinGraph;
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataActionCustomizerInterface;
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataFilterInterface;
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
import com.kingsrook.qqq.backend.core.actions.reporting.customizers.ReportCustomRecordSourceInterface;
@ -245,6 +246,11 @@ public class QInstanceValidator
{
validateSimpleCodeReference("Instance metaDataFilter ", qInstance.getMetaDataFilter(), MetaDataFilterInterface.class);
}
if(qInstance.getMetaDataActionCustomizer() != null)
{
validateSimpleCodeReference("Instance metaDataActionCustomizer ", qInstance.getMetaDataActionCustomizer(), MetaDataActionCustomizerInterface.class);
}
}

View File

@ -116,8 +116,11 @@ public class QInstance
private QPermissionRules defaultPermissionRules = QPermissionRules.defaultInstance();
private QAuditRules defaultAuditRules = QAuditRules.defaultInstanceLevelNone();
@Deprecated(since = "migrated to metaDataCustomizer")
private QCodeReference metaDataFilter = null;
private QCodeReference metaDataActionCustomizer = null;
//////////////////////////////////////////////////////////////////////////////////////
// todo - lock down the object (no more changes allowed) after it's been validated? //
// if doing so, may need to copy all of the collections into read-only versions... //
@ -1495,6 +1498,7 @@ public class QInstance
/*******************************************************************************
** Getter for metaDataFilter
*******************************************************************************/
@Deprecated(since = "migrated to metaDataCustomizer")
public QCodeReference getMetaDataFilter()
{
return (this.metaDataFilter);
@ -1505,6 +1509,7 @@ public class QInstance
/*******************************************************************************
** Setter for metaDataFilter
*******************************************************************************/
@Deprecated(since = "migrated to metaDataCustomizer")
public void setMetaDataFilter(QCodeReference metaDataFilter)
{
this.metaDataFilter = metaDataFilter;
@ -1515,6 +1520,7 @@ public class QInstance
/*******************************************************************************
** Fluent setter for metaDataFilter
*******************************************************************************/
@Deprecated(since = "migrated to metaDataCustomizer")
public QInstance withMetaDataFilter(QCodeReference metaDataFilter)
{
this.metaDataFilter = metaDataFilter;
@ -1586,4 +1592,35 @@ public class QInstance
return (this);
}
/*******************************************************************************
** Getter for metaDataActionCustomizer
*******************************************************************************/
public QCodeReference getMetaDataActionCustomizer()
{
return (this.metaDataActionCustomizer);
}
/*******************************************************************************
** Setter for metaDataActionCustomizer
*******************************************************************************/
public void setMetaDataActionCustomizer(QCodeReference metaDataActionCustomizer)
{
this.metaDataActionCustomizer = metaDataActionCustomizer;
}
/*******************************************************************************
** Fluent setter for metaDataActionCustomizer
*******************************************************************************/
public QInstance withMetaDataActionCustomizer(QCodeReference metaDataActionCustomizer)
{
this.metaDataActionCustomizer = metaDataActionCustomizer;
return (this);
}
}

View File

@ -0,0 +1,269 @@
/*
* 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.metadata.branding;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
/*******************************************************************************
** element of BrandingMetaData - content to send to a frontend for showing a
** user across the whole UI - e.g., what environment you're in, or a message
** about your account - site announcements, etc.
*******************************************************************************/
public class Banner implements Serializable, Cloneable
{
private Severity severity;
private String textColor;
private String backgroundColor;
private String messageText;
private String messageHTML;
private Map<String, Serializable> additionalStyles;
/***************************************************************************
**
***************************************************************************/
public enum Severity
{
INFO, WARNING, ERROR, SUCCESS
}
/***************************************************************************
**
***************************************************************************/
@Override
public Banner clone()
{
try
{
Banner clone = (Banner) super.clone();
//////////////////////////////////////////////////////////////////////////////////////
// copy mutable state here, so the clone can't change the internals of the original //
//////////////////////////////////////////////////////////////////////////////////////
if(additionalStyles != null)
{
clone.setAdditionalStyles(new LinkedHashMap<>(additionalStyles));
}
return clone;
}
catch(CloneNotSupportedException e)
{
throw new AssertionError();
}
}
/*******************************************************************************
** Getter for textColor
*******************************************************************************/
public String getTextColor()
{
return (this.textColor);
}
/*******************************************************************************
** Setter for textColor
*******************************************************************************/
public void setTextColor(String textColor)
{
this.textColor = textColor;
}
/*******************************************************************************
** Fluent setter for textColor
*******************************************************************************/
public Banner withTextColor(String textColor)
{
this.textColor = textColor;
return (this);
}
/*******************************************************************************
** Getter for backgroundColor
*******************************************************************************/
public String getBackgroundColor()
{
return (this.backgroundColor);
}
/*******************************************************************************
** Setter for backgroundColor
*******************************************************************************/
public void setBackgroundColor(String backgroundColor)
{
this.backgroundColor = backgroundColor;
}
/*******************************************************************************
** Fluent setter for backgroundColor
*******************************************************************************/
public Banner withBackgroundColor(String backgroundColor)
{
this.backgroundColor = backgroundColor;
return (this);
}
/*******************************************************************************
** Getter for additionalStyles
*******************************************************************************/
public Map<String, Serializable> getAdditionalStyles()
{
return (this.additionalStyles);
}
/*******************************************************************************
** Setter for additionalStyles
*******************************************************************************/
public void setAdditionalStyles(Map<String, Serializable> additionalStyles)
{
this.additionalStyles = additionalStyles;
}
/*******************************************************************************
** Fluent setter for additionalStyles
*******************************************************************************/
public Banner withAdditionalStyles(Map<String, Serializable> additionalStyles)
{
this.additionalStyles = additionalStyles;
return (this);
}
/*******************************************************************************
** Getter for messageText
*******************************************************************************/
public String getMessageText()
{
return (this.messageText);
}
/*******************************************************************************
** Setter for messageText
*******************************************************************************/
public void setMessageText(String messageText)
{
this.messageText = messageText;
}
/*******************************************************************************
** Fluent setter for messageText
*******************************************************************************/
public Banner withMessageText(String messageText)
{
this.messageText = messageText;
return (this);
}
/*******************************************************************************
** Getter for messageHTML
*******************************************************************************/
public String getMessageHTML()
{
return (this.messageHTML);
}
/*******************************************************************************
** Setter for messageHTML
*******************************************************************************/
public void setMessageHTML(String messageHTML)
{
this.messageHTML = messageHTML;
}
/*******************************************************************************
** Fluent setter for messageHTML
*******************************************************************************/
public Banner withMessageHTML(String messageHTML)
{
this.messageHTML = messageHTML;
return (this);
}
/*******************************************************************************
** Getter for severity
*******************************************************************************/
public Severity getSeverity()
{
return (this.severity);
}
/*******************************************************************************
** Setter for severity
*******************************************************************************/
public void setSeverity(Severity severity)
{
this.severity = severity;
}
/*******************************************************************************
** Fluent setter for severity
*******************************************************************************/
public Banner withSeverity(Severity severity)
{
this.severity = severity;
return (this);
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.metadata.branding;
/*******************************************************************************
** interface to define keys for where banners should be displayed.
** expect frontends to implement this interface with enums of known possible values
*******************************************************************************/
public interface BannerSlot
{
}

View File

@ -22,6 +22,9 @@
package com.kingsrook.qqq.backend.core.model.metadata.branding;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
@ -30,7 +33,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
** Meta-Data to define branding in a QQQ instance.
**
*******************************************************************************/
public class QBrandingMetaData implements TopLevelMetaDataInterface
public class QBrandingMetaData implements TopLevelMetaDataInterface, Cloneable, Serializable
{
private String companyName;
private String companyUrl;
@ -39,9 +42,45 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface
private String icon;
private String accentColor;
@Deprecated(since = "migrate to use banners map instead")
private String environmentBannerText;
@Deprecated(since = "migrate to use banners map instead")
private String environmentBannerColor;
private Map<BannerSlot, Banner> banners;
/***************************************************************************
**
***************************************************************************/
@Override
public QBrandingMetaData clone()
{
try
{
QBrandingMetaData clone = (QBrandingMetaData) super.clone();
//////////////////////////////////////////////////////////////////////////////////////
// copy mutable state here, so the clone can't change the internals of the original //
//////////////////////////////////////////////////////////////////////////////////////
if(banners != null)
{
clone.banners = new LinkedHashMap<>();
for(Map.Entry<BannerSlot, Banner> entry : this.banners.entrySet())
{
clone.banners.put(entry.getKey(), entry.getValue().clone());
}
}
return clone;
}
catch(CloneNotSupportedException e)
{
throw new AssertionError();
}
}
/*******************************************************************************
@ -267,6 +306,7 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface
/*******************************************************************************
** Getter for environmentBannerText
*******************************************************************************/
@Deprecated(since = "migrate to use banners map instead")
public String getEnvironmentBannerText()
{
return (this.environmentBannerText);
@ -277,6 +317,7 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface
/*******************************************************************************
** Setter for environmentBannerText
*******************************************************************************/
@Deprecated(since = "migrate to use banners map instead")
public void setEnvironmentBannerText(String environmentBannerText)
{
this.environmentBannerText = environmentBannerText;
@ -287,6 +328,7 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface
/*******************************************************************************
** Fluent setter for environmentBannerText
*******************************************************************************/
@Deprecated(since = "migrate to use banners map instead")
public QBrandingMetaData withEnvironmentBannerText(String environmentBannerText)
{
this.environmentBannerText = environmentBannerText;
@ -298,6 +340,7 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface
/*******************************************************************************
** Getter for environmentBannerColor
*******************************************************************************/
@Deprecated(since = "migrate to use banners map instead")
public String getEnvironmentBannerColor()
{
return (this.environmentBannerColor);
@ -308,6 +351,7 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface
/*******************************************************************************
** Setter for environmentBannerColor
*******************************************************************************/
@Deprecated(since = "migrate to use banners map instead")
public void setEnvironmentBannerColor(String environmentBannerColor)
{
this.environmentBannerColor = environmentBannerColor;
@ -318,6 +362,7 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface
/*******************************************************************************
** Fluent setter for environmentBannerColor
*******************************************************************************/
@Deprecated(since = "migrate to use banners map instead")
public QBrandingMetaData withEnvironmentBannerColor(String environmentBannerColor)
{
this.environmentBannerColor = environmentBannerColor;
@ -334,4 +379,52 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface
{
qInstance.setBranding(this);
}
/*******************************************************************************
** Getter for banners
*******************************************************************************/
public Map<BannerSlot, Banner> getBanners()
{
return (this.banners);
}
/*******************************************************************************
** Setter for banners
*******************************************************************************/
public void setBanners(Map<BannerSlot, Banner> banners)
{
this.banners = banners;
}
/*******************************************************************************
** Fluent setter for banners
*******************************************************************************/
public QBrandingMetaData withBanners(Map<BannerSlot, Banner> banners)
{
this.banners = banners;
return (this);
}
/***************************************************************************
**
***************************************************************************/
public QBrandingMetaData withBanner(BannerSlot slot, Banner banner)
{
if(this.banners == null)
{
this.banners = new LinkedHashMap<>();
}
this.banners.put(slot, banner);
return (this);
}
}

View File

@ -364,6 +364,7 @@ class MetaDataActionTest extends BaseTest
**
*******************************************************************************/
@Test
@Deprecated(since = "migrated to metaDataCustomizer")
void testFilter() throws QException
{
QCollectingLogger collectingLogger = QLogger.activateCollectingLoggerForClass(MetaDataAction.class);
@ -397,7 +398,7 @@ class MetaDataActionTest extends BaseTest
// run again (with the same instance as before) to assert about memoization of the filter based on the QInstance //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
new MetaDataAction().execute(new MetaDataInput());
assertThat(collectingLogger.getCollectedMessages()).filteredOn(clm -> clm.getMessage().contains("filter of type: DenyAllFilter")).hasSize(1);
assertThat(collectingLogger.getCollectedMessages()).filteredOn(clm -> clm.getMessage().contains("actionCustomizer (via metaDataFilter reference) of type: DenyAllFilter")).hasSize(1);
QLogger.deactivateCollectingLoggerForClass(MetaDataAction.class);
@ -413,6 +414,59 @@ class MetaDataActionTest extends BaseTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testCustomizer() throws QException
{
QCollectingLogger collectingLogger = QLogger.activateCollectingLoggerForClass(MetaDataAction.class);
//////////////////////////////////////////////////////
// run default version, and assert tables are found //
//////////////////////////////////////////////////////
MetaDataOutput result = new MetaDataAction().execute(new MetaDataInput());
assertFalse(result.getTables().isEmpty(), "should be some tables");
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// run again (with the same instance as before) to assert about memoization of the filter based on the QInstance //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
new MetaDataAction().execute(new MetaDataInput());
assertThat(collectingLogger.getCollectedMessages()).filteredOn(clm -> clm.getMessage().contains("Using new default")).hasSize(1);
/////////////////////////////////////////////////////////////
// set up new instance to use a custom filter, to deny all //
/////////////////////////////////////////////////////////////
QInstance instance = TestUtils.defineInstance();
instance.setMetaDataActionCustomizer(new QCodeReference(DenyAllFilteringCustomizer.class));
reInitInstanceInContext(instance);
/////////////////////////////////////////////////////
// re-run, and assert all tables are filtered away //
/////////////////////////////////////////////////////
result = new MetaDataAction().execute(new MetaDataInput());
assertTrue(result.getTables().isEmpty(), "should be no tables");
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// run again (with the same instance as before) to assert about memoization of the filter based on the QInstance //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
new MetaDataAction().execute(new MetaDataInput());
assertThat(collectingLogger.getCollectedMessages()).filteredOn(clm -> clm.getMessage().contains("meta-data actionCustomizer of type: DenyAllFilteringCustomizer")).hasSize(1);
QLogger.deactivateCollectingLoggerForClass(MetaDataAction.class);
/////////////////////////////////////////////////////////////////////////////////
// run now with the DefaultNoopMetaDataActionCustomizer, confirm we get tables //
/////////////////////////////////////////////////////////////////////////////////
instance = TestUtils.defineInstance();
instance.setMetaDataActionCustomizer(new QCodeReference(DefaultNoopMetaDataActionCustomizer.class));
reInitInstanceInContext(instance);
result = new MetaDataAction().execute(new MetaDataInput());
assertFalse(result.getTables().isEmpty(), "should be some tables");
}
/***************************************************************************
**
***************************************************************************/
@ -462,6 +516,67 @@ class MetaDataActionTest extends BaseTest
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowWidget(MetaDataInput input, QWidgetMetaDataInterface widget)
{
return false;
}
}
/***************************************************************************
**
***************************************************************************/
public static class DenyAllFilteringCustomizer implements MetaDataActionCustomizerInterface
{
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowTable(MetaDataInput input, QTableMetaData table)
{
return false;
}
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowProcess(MetaDataInput input, QProcessMetaData process)
{
return false;
}
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowReport(MetaDataInput input, QReportMetaData report)
{
return false;
}
/***************************************************************************
**
***************************************************************************/
@Override
public boolean allowApp(MetaDataInput input, QAppMetaData app)
{
return false;
}
/***************************************************************************
**
***************************************************************************/

View File

@ -40,6 +40,7 @@ import com.kingsrook.qqq.backend.core.actions.dashboard.PersonsByCreateDateBarCh
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRenderer;
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.ParentWidgetRenderer;
import com.kingsrook.qqq.backend.core.actions.metadata.AllowAllMetaDataFilter;
import com.kingsrook.qqq.backend.core.actions.metadata.DefaultNoopMetaDataActionCustomizer;
import com.kingsrook.qqq.backend.core.actions.processes.CancelProcessActionTest;
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
import com.kingsrook.qqq.backend.core.actions.reporting.customizers.ReportCustomRecordSourceInterface;
@ -160,6 +161,20 @@ public class QInstanceValidatorTest extends BaseTest
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testMetaDataActionCustomizer()
{
assertValidationFailureReasons((qInstance) -> qInstance.setMetaDataActionCustomizer(new QCodeReference(QInstanceValidator.class)),
"Instance metaDataActionCustomizer CodeReference is not of the expected type");
assertValidationSuccess((qInstance) -> qInstance.setMetaDataActionCustomizer(new QCodeReference(DefaultNoopMetaDataActionCustomizer.class)));
assertValidationSuccess((qInstance) -> qInstance.setMetaDataActionCustomizer(null));
}
/*******************************************************************************
** Test an instance with null backends - should throw.