mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-17 20:50:44 +00:00
Merged feature/CE-1113-single-carton-reqs into dev
This commit is contained in:
@ -218,6 +218,8 @@ public class MetaDataAction
|
|||||||
|
|
||||||
metaDataOutput.setEnvironmentValues(metaDataInput.getInstance().getEnvironmentValues());
|
metaDataOutput.setEnvironmentValues(metaDataInput.getInstance().getEnvironmentValues());
|
||||||
|
|
||||||
|
metaDataOutput.setHelpContents(metaDataInput.getInstance().getHelpContent());
|
||||||
|
|
||||||
// todo post-customization - can do whatever w/ the result if you want?
|
// todo post-customization - can do whatever w/ the result if you want?
|
||||||
|
|
||||||
return metaDataOutput;
|
return metaDataOutput;
|
||||||
|
@ -105,14 +105,21 @@ public class QInstanceHelpContentManager
|
|||||||
for(String part : key.split(";"))
|
for(String part : key.split(";"))
|
||||||
{
|
{
|
||||||
String[] parts = part.split(":");
|
String[] parts = part.split(":");
|
||||||
nameValuePairs.put(parts[0], parts[1]);
|
if(parts.length > 1)
|
||||||
|
{
|
||||||
|
nameValuePairs.put(parts[0], parts[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.info("Discarding help content with key that does not contain name:value format", logPair("key", key), logPair("id", record.getValue("id")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String tableName = nameValuePairs.get("table");
|
String tableName = nameValuePairs.get("table");
|
||||||
String processName = nameValuePairs.get("process");
|
String processName = nameValuePairs.get("process");
|
||||||
String fieldName = nameValuePairs.get("field");
|
String fieldName = nameValuePairs.get("field");
|
||||||
String sectionName = nameValuePairs.get("section");
|
String sectionName = nameValuePairs.get("section");
|
||||||
String stepName = nameValuePairs.get("step");
|
String stepName = nameValuePairs.get("step");
|
||||||
String widgetName = nameValuePairs.get("widget");
|
String widgetName = nameValuePairs.get("widget");
|
||||||
String slotName = nameValuePairs.get("slot");
|
String slotName = nameValuePairs.get("slot");
|
||||||
|
|
||||||
@ -143,7 +150,7 @@ public class QInstanceHelpContentManager
|
|||||||
///////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
if(StringUtils.hasContent(tableName))
|
if(StringUtils.hasContent(tableName))
|
||||||
{
|
{
|
||||||
processHelpContentForTable(key, tableName, sectionName, fieldName, roles, helpContent);
|
processHelpContentForTable(key, tableName, sectionName, fieldName, slotName, roles, helpContent);
|
||||||
}
|
}
|
||||||
else if(StringUtils.hasContent(processName))
|
else if(StringUtils.hasContent(processName))
|
||||||
{
|
{
|
||||||
@ -153,6 +160,10 @@ public class QInstanceHelpContentManager
|
|||||||
{
|
{
|
||||||
processHelpContentForWidget(key, widgetName, slotName, roles, helpContent);
|
processHelpContentForWidget(key, widgetName, slotName, roles, helpContent);
|
||||||
}
|
}
|
||||||
|
else if(nameValuePairs.containsKey("instanceLevel"))
|
||||||
|
{
|
||||||
|
processHelpContentForInstance(key, slotName, roles, helpContent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
@ -165,7 +176,7 @@ public class QInstanceHelpContentManager
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private static void processHelpContentForTable(String key, String tableName, String sectionName, String fieldName, Set<HelpRole> roles, QHelpContent helpContent)
|
private static void processHelpContentForTable(String key, String tableName, String sectionName, String fieldName, String slotName, Set<HelpRole> roles, QHelpContent helpContent)
|
||||||
{
|
{
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
||||||
if(table == null)
|
if(table == null)
|
||||||
@ -212,7 +223,21 @@ public class QInstanceHelpContentManager
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG.info("Unrecognized key format for table help content", logPair("key", key));
|
if(!StringUtils.hasContent(slotName))
|
||||||
|
{
|
||||||
|
LOG.info("Missing slot name in table-level help content", logPair("key", key));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(helpContent != null)
|
||||||
|
{
|
||||||
|
table.withHelpContent(slotName, helpContent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
table.removeHelpContent(slotName, roles);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,6 +332,30 @@ public class QInstanceHelpContentManager
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static void processHelpContentForInstance(String key, String slotName, Set<HelpRole> roles, QHelpContent helpContent)
|
||||||
|
{
|
||||||
|
if(!StringUtils.hasContent(slotName))
|
||||||
|
{
|
||||||
|
LOG.info("Missing slot name in instance-level help content", logPair("key", key));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(helpContent != null)
|
||||||
|
{
|
||||||
|
QContext.getQInstance().withHelpContent(slotName, helpContent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QContext.getQInstance().removeHelpContent(slotName, roles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** add a help content object to a list - replacing an entry in the list with the
|
** add a help content object to a list - replacing an entry in the list with the
|
||||||
** same roles if one is found.
|
** same roles if one is found.
|
||||||
|
@ -32,6 +32,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendProcessMe
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendReportMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendReportMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendWidgetMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendWidgetMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -47,8 +48,9 @@ public class MetaDataOutput extends AbstractActionOutput
|
|||||||
private Map<String, QFrontendWidgetMetaData> widgets;
|
private Map<String, QFrontendWidgetMetaData> widgets;
|
||||||
private Map<String, String> environmentValues;
|
private Map<String, String> environmentValues;
|
||||||
|
|
||||||
private List<AppTreeNode> appTree;
|
private List<AppTreeNode> appTree;
|
||||||
private QBrandingMetaData branding;
|
private QBrandingMetaData branding;
|
||||||
|
private Map<String, List<QHelpContent>> helpContents;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -226,4 +228,25 @@ public class MetaDataOutput extends AbstractActionOutput
|
|||||||
this.environmentValues = environmentValues;
|
this.environmentValues = environmentValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for helpContents
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setHelpContents(Map<String, List<QHelpContent>> helpContents)
|
||||||
|
{
|
||||||
|
this.helpContents = helpContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for helpContents
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Map<String, List<QHelpContent>> getHelpContents()
|
||||||
|
{
|
||||||
|
return helpContents;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.metadata.JoinGraph;
|
import com.kingsrook.qqq.backend.core.actions.metadata.JoinGraph;
|
||||||
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataAction;
|
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataAction;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.instances.QInstanceHelpContentManager;
|
||||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidationKey;
|
import com.kingsrook.qqq.backend.core.instances.QInstanceValidationKey;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
|
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
|
||||||
@ -44,6 +45,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.branding.QBrandingMetaData;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
|
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode;
|
import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNodeType;
|
import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNodeType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.help.HelpRole;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.messaging.QMessagingProviderMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.messaging.QMessagingProviderMetaData;
|
||||||
@ -79,7 +82,7 @@ public class QInstance
|
|||||||
private QAuthenticationMetaData authentication = null;
|
private QAuthenticationMetaData authentication = null;
|
||||||
private QBrandingMetaData branding = null;
|
private QBrandingMetaData branding = null;
|
||||||
private Map<String, QAutomationProviderMetaData> automationProviders = new HashMap<>();
|
private Map<String, QAutomationProviderMetaData> automationProviders = new HashMap<>();
|
||||||
private Map<String, QMessagingProviderMetaData> messagingProviders = new HashMap<>();
|
private Map<String, QMessagingProviderMetaData> messagingProviders = new HashMap<>();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Important to use LinkedHashmap here, to preserve the order in which entries are added. //
|
// Important to use LinkedHashmap here, to preserve the order in which entries are added. //
|
||||||
@ -100,6 +103,8 @@ public class QInstance
|
|||||||
|
|
||||||
private Map<String, QSupplementalInstanceMetaData> supplementalMetaData = new LinkedHashMap<>();
|
private Map<String, QSupplementalInstanceMetaData> supplementalMetaData = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
protected Map<String, List<QHelpContent>> helpContent;
|
||||||
|
|
||||||
private String deploymentMode;
|
private String deploymentMode;
|
||||||
private Map<String, String> environmentValues = new LinkedHashMap<>();
|
private Map<String, String> environmentValues = new LinkedHashMap<>();
|
||||||
private String defaultTimeZoneId = "UTC";
|
private String defaultTimeZoneId = "UTC";
|
||||||
@ -1380,4 +1385,74 @@ public class QInstance
|
|||||||
this.schedulableTypes = schedulableTypes;
|
this.schedulableTypes = schedulableTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for helpContent
|
||||||
|
*******************************************************************************/
|
||||||
|
public Map<String, List<QHelpContent>> getHelpContent()
|
||||||
|
{
|
||||||
|
return (this.helpContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for helpContent
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setHelpContent(Map<String, List<QHelpContent>> helpContent)
|
||||||
|
{
|
||||||
|
this.helpContent = helpContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for helpContent
|
||||||
|
*******************************************************************************/
|
||||||
|
public QInstance withHelpContent(Map<String, List<QHelpContent>> helpContent)
|
||||||
|
{
|
||||||
|
this.helpContent = helpContent;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for adding 1 helpContent (for a slot)
|
||||||
|
*******************************************************************************/
|
||||||
|
public QInstance withHelpContent(String slot, QHelpContent helpContent)
|
||||||
|
{
|
||||||
|
if(this.helpContent == null)
|
||||||
|
{
|
||||||
|
this.helpContent = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<QHelpContent> listForSlot = this.helpContent.computeIfAbsent(slot, (k) -> new ArrayList<>());
|
||||||
|
QInstanceHelpContentManager.putHelpContentInList(helpContent, listForSlot);
|
||||||
|
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** remove a helpContent for a slot based on its set of roles
|
||||||
|
*******************************************************************************/
|
||||||
|
public void removeHelpContent(String slot, Set<HelpRole> roles)
|
||||||
|
{
|
||||||
|
if(this.helpContent == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<QHelpContent> listForSlot = this.helpContent.get(slot);
|
||||||
|
if(listForSlot == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, listForSlot);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareableTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareableTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
|
||||||
@ -76,7 +77,8 @@ public class QFrontendTableMetaData
|
|||||||
private boolean usesVariants;
|
private boolean usesVariants;
|
||||||
private String variantTableLabel;
|
private String variantTableLabel;
|
||||||
|
|
||||||
private ShareableTableMetaData shareableTableMetaData;
|
private ShareableTableMetaData shareableTableMetaData;
|
||||||
|
private Map<String, List<QHelpContent>> helpContents;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
// do not add setters. take values from the source-object in the constructor!! //
|
// do not add setters. take values from the source-object in the constructor!! //
|
||||||
@ -172,6 +174,8 @@ public class QFrontendTableMetaData
|
|||||||
usesVariants = true;
|
usesVariants = true;
|
||||||
variantTableLabel = actionInput.getInstance().getTable(backend.getVariantOptionsTableName()).getLabel();
|
variantTableLabel = actionInput.getInstance().getTable(backend.getVariantOptionsTableName()).getLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.helpContents = tableMetaData.getHelpContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -382,4 +386,15 @@ public class QFrontendTableMetaData
|
|||||||
{
|
{
|
||||||
return shareableTableMetaData;
|
return shareableTableMetaData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for helpContents
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Map<String, List<QHelpContent>> getHelpContents()
|
||||||
|
{
|
||||||
|
return helpContents;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ import java.util.Optional;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
|
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.instances.QInstanceHelpContentManager;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntityField;
|
import com.kingsrook.qqq.backend.core.model.data.QRecordEntityField;
|
||||||
@ -43,6 +44,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
|
import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.help.HelpRole;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||||
@ -110,6 +113,8 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
|
|
||||||
private ShareableTableMetaData shareableTableMetaData;
|
private ShareableTableMetaData shareableTableMetaData;
|
||||||
|
|
||||||
|
protected Map<String, List<QHelpContent>> helpContent;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -1446,4 +1451,74 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for helpContent
|
||||||
|
*******************************************************************************/
|
||||||
|
public Map<String, List<QHelpContent>> getHelpContent()
|
||||||
|
{
|
||||||
|
return (this.helpContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for helpContent
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setHelpContent(Map<String, List<QHelpContent>> helpContent)
|
||||||
|
{
|
||||||
|
this.helpContent = helpContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for helpContent
|
||||||
|
*******************************************************************************/
|
||||||
|
public QTableMetaData withHelpContent(Map<String, List<QHelpContent>> helpContent)
|
||||||
|
{
|
||||||
|
this.helpContent = helpContent;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for adding 1 helpContent (for a slot)
|
||||||
|
*******************************************************************************/
|
||||||
|
public QTableMetaData withHelpContent(String slot, QHelpContent helpContent)
|
||||||
|
{
|
||||||
|
if(this.helpContent == null)
|
||||||
|
{
|
||||||
|
this.helpContent = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<QHelpContent> listForSlot = this.helpContent.computeIfAbsent(slot, (k) -> new ArrayList<>());
|
||||||
|
QInstanceHelpContentManager.putHelpContentInList(helpContent, listForSlot);
|
||||||
|
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** remove a helpContent for a slot based on its set of roles
|
||||||
|
*******************************************************************************/
|
||||||
|
public void removeHelpContent(String slot, Set<HelpRole> roles)
|
||||||
|
{
|
||||||
|
if(this.helpContent == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<QHelpContent> listForSlot = this.helpContent.get(slot);
|
||||||
|
if(listForSlot == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, listForSlot);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,9 @@ package com.kingsrook.qqq.backend.core.instances;
|
|||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
import com.kingsrook.qqq.backend.core.actions.dashboard.PersonsByCreateDateBarChart;
|
import com.kingsrook.qqq.backend.core.actions.dashboard.PersonsByCreateDateBarChart;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
||||||
@ -31,9 +33,12 @@ import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.logging.QCollectingLogger;
|
||||||
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
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.helpcontent.HelpContent;
|
import com.kingsrook.qqq.backend.core.model.helpcontent.HelpContent;
|
||||||
import com.kingsrook.qqq.backend.core.model.helpcontent.HelpContentMetaDataProvider;
|
import com.kingsrook.qqq.backend.core.model.helpcontent.HelpContentMetaDataProvider;
|
||||||
import com.kingsrook.qqq.backend.core.model.helpcontent.HelpContentRole;
|
import com.kingsrook.qqq.backend.core.model.helpcontent.HelpContentRole;
|
||||||
@ -155,6 +160,108 @@ class QInstanceHelpContentManagerTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testTable() throws QException
|
||||||
|
{
|
||||||
|
/////////////////////////////////////
|
||||||
|
// get the instance from base test //
|
||||||
|
/////////////////////////////////////
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
new HelpContentMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
// first, assert there's no help content on the table //
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
assertThat(qInstance.getTable(TestUtils.TABLE_NAME_PERSON).getHelpContent()).isNullOrEmpty();
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
// insert a record missing its slot //
|
||||||
|
//////////////////////////////////////
|
||||||
|
new InsertAction().execute(new InsertInput(HelpContent.TABLE_NAME).withRecordEntity(new HelpContent()
|
||||||
|
.withId(1)
|
||||||
|
.withKey("table:person")
|
||||||
|
.withContent("content")
|
||||||
|
.withRole(HelpContentRole.ALL_SCREENS.getId())));
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// assert still no help content //
|
||||||
|
//////////////////////////////////
|
||||||
|
assertThat(qInstance.getTable(TestUtils.TABLE_NAME_PERSON).getHelpContent()).isNullOrEmpty();
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// insert a good record //
|
||||||
|
//////////////////////////
|
||||||
|
new InsertAction().execute(new InsertInput(HelpContent.TABLE_NAME).withRecordEntity(new HelpContent()
|
||||||
|
.withId(1)
|
||||||
|
.withKey("table:person;slot:someSlot")
|
||||||
|
.withContent("content")
|
||||||
|
.withRole(HelpContentRole.ALL_SCREENS.getId())));
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// now - post-insert customizer should have automatically added help content to the instance //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Map<String, List<QHelpContent>> helpContent = qInstance.getTable(TestUtils.TABLE_NAME_PERSON).getHelpContent();
|
||||||
|
assertEquals(1, helpContent.size());
|
||||||
|
assertEquals(1, helpContent.get("someSlot").size());
|
||||||
|
assertEquals("content", helpContent.get("someSlot").get(0).getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testInstance() throws QException
|
||||||
|
{
|
||||||
|
/////////////////////////////////////
|
||||||
|
// get the instance from base test //
|
||||||
|
/////////////////////////////////////
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
new HelpContentMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
// first, assert there's no help content on the instance //
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
assertThat(qInstance.getHelpContent()).isNullOrEmpty();
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
// insert a record missing its slot //
|
||||||
|
//////////////////////////////////////
|
||||||
|
new InsertAction().execute(new InsertInput(HelpContent.TABLE_NAME).withRecordEntity(new HelpContent()
|
||||||
|
.withId(1)
|
||||||
|
.withKey("instanceLevel:true")
|
||||||
|
.withContent("content")
|
||||||
|
.withRole(HelpContentRole.ALL_SCREENS.getId())));
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// assert still no help content //
|
||||||
|
//////////////////////////////////
|
||||||
|
assertThat(qInstance.getHelpContent()).isNullOrEmpty();
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// insert a good record //
|
||||||
|
//////////////////////////
|
||||||
|
new InsertAction().execute(new InsertInput(HelpContent.TABLE_NAME).withRecordEntity(new HelpContent()
|
||||||
|
.withId(1)
|
||||||
|
.withKey("instanceLevel:true;slot:someSlot")
|
||||||
|
.withContent("content")
|
||||||
|
.withRole(HelpContentRole.ALL_SCREENS.getId())));
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// now - post-insert customizer should have automatically added help content to the instance //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Map<String, List<QHelpContent>> helpContent = qInstance.getHelpContent();
|
||||||
|
assertEquals(1, helpContent.size());
|
||||||
|
assertEquals(1, helpContent.get("someSlot").size());
|
||||||
|
assertEquals("content", helpContent.get("someSlot").get(0).getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -285,6 +392,46 @@ class QInstanceHelpContentManagerTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testMalformedKeys() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
new HelpContentMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||||
|
|
||||||
|
QCollectingLogger collectingLogger = QLogger.activateCollectingLoggerForClass(QInstanceHelpContentManager.class);
|
||||||
|
|
||||||
|
Function<String, QRecord> helpContentCreator = (String key) -> new HelpContent()
|
||||||
|
.withId(1)
|
||||||
|
.withKey(key)
|
||||||
|
.withContent("v1")
|
||||||
|
.withRole(HelpContentRole.INSERT_SCREEN.getId()).toQRecord();
|
||||||
|
|
||||||
|
QInstanceHelpContentManager.processHelpContentRecord(qInstance, helpContentCreator.apply("foo;bar:baz"));
|
||||||
|
assertThat(collectingLogger.getCollectedMessages()).hasSize(1);
|
||||||
|
assertThat(collectingLogger.getCollectedMessages().get(0).getMessage()).contains("Discarding help content with key that does not contain name:value format");
|
||||||
|
collectingLogger.clear();
|
||||||
|
|
||||||
|
QInstanceHelpContentManager.processHelpContentRecord(qInstance, helpContentCreator.apply(null));
|
||||||
|
assertThat(collectingLogger.getCollectedMessages()).hasSize(1);
|
||||||
|
assertThat(collectingLogger.getCollectedMessages().get(0).getMessage()).contains("Error processing a helpContent record");
|
||||||
|
collectingLogger.clear();
|
||||||
|
|
||||||
|
QInstanceHelpContentManager.processHelpContentRecord(qInstance, helpContentCreator.apply("table:notATable;slot:foo"));
|
||||||
|
assertThat(collectingLogger.getCollectedMessages()).hasSize(1);
|
||||||
|
assertThat(collectingLogger.getCollectedMessages().get(0).getMessage()).contains("Unrecognized table in help content");
|
||||||
|
collectingLogger.clear();
|
||||||
|
|
||||||
|
QInstanceHelpContentManager.processHelpContentRecord(qInstance, helpContentCreator.apply("table:" + TestUtils.TABLE_NAME_PERSON));
|
||||||
|
assertThat(collectingLogger.getCollectedMessages()).hasSize(1);
|
||||||
|
assertThat(collectingLogger.getCollectedMessages().get(0).getMessage()).contains("Missing slot name");
|
||||||
|
collectingLogger.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -382,11 +382,7 @@ public class ApiImplementation
|
|||||||
// map record fields for api //
|
// map record fields for api //
|
||||||
// note - don't put them in the output until after the count, just because that looks a little nicer, i think //
|
// note - don't put them in the output until after the count, just because that looks a little nicer, i think //
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
ArrayList<Map<String, Serializable>> records = new ArrayList<>();
|
ArrayList<Map<String, Serializable>> records = QRecordApiAdapter.qRecordsToApiMapList(queryOutput.getRecords(), tableName, apiName, version);
|
||||||
for(QRecord record : queryOutput.getRecords())
|
|
||||||
{
|
|
||||||
records.add(QRecordApiAdapter.qRecordToApiMap(record, tableName, apiName, version));
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
// optionally do the count //
|
// optionally do the count //
|
||||||
@ -619,7 +615,7 @@ public class ApiImplementation
|
|||||||
+ table.getFields().get(table.getPrimaryKeyField()).getLabel() + " of " + primaryKey));
|
+ table.getFields().get(table.getPrimaryKeyField()).getLabel() + " of " + primaryKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Serializable> outputRecord = QRecordApiAdapter.qRecordToApiMap(record, tableName, apiInstanceMetaData.getName(), version);
|
Map<String, Serializable> outputRecord = QRecordApiAdapter.qRecordsToApiMapList(List.of(record), tableName, apiInstanceMetaData.getName(), version).get(0);
|
||||||
return (outputRecord);
|
return (outputRecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ import com.kingsrook.qqq.api.javalin.QBadRequestException;
|
|||||||
import com.kingsrook.qqq.api.model.APIVersion;
|
import com.kingsrook.qqq.api.model.APIVersion;
|
||||||
import com.kingsrook.qqq.api.model.APIVersionRange;
|
import com.kingsrook.qqq.api.model.APIVersionRange;
|
||||||
import com.kingsrook.qqq.api.model.actions.ApiFieldCustomValueMapper;
|
import com.kingsrook.qqq.api.model.actions.ApiFieldCustomValueMapper;
|
||||||
|
import com.kingsrook.qqq.api.model.actions.ApiFieldCustomValueMapperBulkSupportInterface;
|
||||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
||||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
||||||
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
|
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
|
||||||
@ -52,6 +53,7 @@ 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.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
|
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
||||||
import org.apache.commons.lang.BooleanUtils;
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@ -67,9 +69,61 @@ public class QRecordApiAdapter
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Convert a QRecord to a map for the API
|
** Simple/short form of convert a QRecord to a map for the API - e.g., meant for
|
||||||
|
** public consumption.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static Map<String, Serializable> qRecordToApiMap(QRecord record, String tableName, String apiName, String apiVersion) throws QException
|
public static Map<String, Serializable> qRecordToApiMap(QRecord record, String tableName, String apiName, String apiVersion) throws QException
|
||||||
|
{
|
||||||
|
return qRecordsToApiMapList(ListBuilder.of(record), tableName, apiName, apiVersion).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** bulk-version of the qRecordToApiMap - will use
|
||||||
|
** ApiFieldCustomValueMapperBulkSupportInterface's in the bulky way.
|
||||||
|
*******************************************************************************/
|
||||||
|
public static ArrayList<Map<String, Serializable>> qRecordsToApiMapList(List<QRecord> records, String tableName, String apiName, String apiVersion) throws QException
|
||||||
|
{
|
||||||
|
Map<String, ApiFieldCustomValueMapper> fieldValueMappers = new HashMap<>();
|
||||||
|
|
||||||
|
List<QFieldMetaData> tableApiFields = GetTableApiFieldsAction.getTableApiFieldList(new GetTableApiFieldsAction.ApiNameVersionAndTableName(apiName, apiVersion, tableName));
|
||||||
|
for(QFieldMetaData field : tableApiFields)
|
||||||
|
{
|
||||||
|
ApiFieldMetaData apiFieldMetaData = ObjectUtils.tryAndRequireNonNullElse(() -> ApiFieldMetaDataContainer.of(field).getApiFieldMetaData(apiName), new ApiFieldMetaData());
|
||||||
|
String apiFieldName = ApiFieldMetaData.getEffectiveApiFieldName(apiName, field);
|
||||||
|
|
||||||
|
if(apiFieldMetaData.getCustomValueMapper() != null)
|
||||||
|
{
|
||||||
|
if(!fieldValueMappers.containsKey(apiFieldMetaData.getCustomValueMapper().getName()))
|
||||||
|
{
|
||||||
|
ApiFieldCustomValueMapper customValueMapper = QCodeLoader.getAdHoc(ApiFieldCustomValueMapper.class, apiFieldMetaData.getCustomValueMapper());
|
||||||
|
fieldValueMappers.put(apiFieldMetaData.getCustomValueMapper().getName(), customValueMapper);
|
||||||
|
|
||||||
|
if(customValueMapper instanceof ApiFieldCustomValueMapperBulkSupportInterface bulkMapper)
|
||||||
|
{
|
||||||
|
bulkMapper.prepareToProduceApiValues(records);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<Map<String, Serializable>> rs = new ArrayList<>();
|
||||||
|
for(QRecord record : records)
|
||||||
|
{
|
||||||
|
rs.add(QRecordApiAdapter.qRecordToApiMap(record, tableName, apiName, apiVersion, fieldValueMappers));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** private version of convert a QRecord to a map for the API - takes params to
|
||||||
|
** support working in bulk w/ customizers much better.
|
||||||
|
*******************************************************************************/
|
||||||
|
private static Map<String, Serializable> qRecordToApiMap(QRecord record, String tableName, String apiName, String apiVersion, Map<String, ApiFieldCustomValueMapper> fieldValueMappers) throws QException
|
||||||
{
|
{
|
||||||
if(record == null)
|
if(record == null)
|
||||||
{
|
{
|
||||||
@ -87,14 +141,25 @@ public class QRecordApiAdapter
|
|||||||
ApiFieldMetaData apiFieldMetaData = ObjectUtils.tryAndRequireNonNullElse(() -> ApiFieldMetaDataContainer.of(field).getApiFieldMetaData(apiName), new ApiFieldMetaData());
|
ApiFieldMetaData apiFieldMetaData = ObjectUtils.tryAndRequireNonNullElse(() -> ApiFieldMetaDataContainer.of(field).getApiFieldMetaData(apiName), new ApiFieldMetaData());
|
||||||
String apiFieldName = ApiFieldMetaData.getEffectiveApiFieldName(apiName, field);
|
String apiFieldName = ApiFieldMetaData.getEffectiveApiFieldName(apiName, field);
|
||||||
|
|
||||||
Serializable value = null;
|
Serializable value;
|
||||||
if(StringUtils.hasContent(apiFieldMetaData.getReplacedByFieldName()))
|
if(StringUtils.hasContent(apiFieldMetaData.getReplacedByFieldName()))
|
||||||
{
|
{
|
||||||
value = record.getValue(apiFieldMetaData.getReplacedByFieldName());
|
value = record.getValue(apiFieldMetaData.getReplacedByFieldName());
|
||||||
}
|
}
|
||||||
else if(apiFieldMetaData.getCustomValueMapper() != null)
|
else if(apiFieldMetaData.getCustomValueMapper() != null)
|
||||||
{
|
{
|
||||||
ApiFieldCustomValueMapper customValueMapper = QCodeLoader.getAdHoc(ApiFieldCustomValueMapper.class, apiFieldMetaData.getCustomValueMapper());
|
if(fieldValueMappers == null)
|
||||||
|
{
|
||||||
|
fieldValueMappers = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
String customValueMapperName = apiFieldMetaData.getCustomValueMapper().getName();
|
||||||
|
if(!fieldValueMappers.containsKey(customValueMapperName))
|
||||||
|
{
|
||||||
|
fieldValueMappers.put(customValueMapperName, QCodeLoader.getAdHoc(ApiFieldCustomValueMapper.class, apiFieldMetaData.getCustomValueMapper()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiFieldCustomValueMapper customValueMapper = fieldValueMappers.get(customValueMapperName);
|
||||||
value = customValueMapper.produceApiValue(record, apiFieldName);
|
value = customValueMapper.produceApiValue(record, apiFieldName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -331,5 +396,4 @@ public class QRecordApiAdapter
|
|||||||
|
|
||||||
return (null);
|
return (null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -750,7 +750,7 @@ public class QJavalinApiHandler
|
|||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
html = html.replace("{spec-url}", apiInstanceMetaData.getPath() + version + "/openapi.json");
|
html = html.replace("{spec-url}", apiInstanceMetaData.getPath() + version + "/openapi.json");
|
||||||
html = html.replace("{version}", version);
|
html = html.replace("{version}", version);
|
||||||
html = html.replace("{primaryColor}", branding == null ? "#FF791A" : branding.getAccentColor());
|
html = html.replace("{primaryColor}", (branding == null || branding.getAccentColor() == null) ? "#FF791A" : branding.getAccentColor());
|
||||||
|
|
||||||
if(branding != null && StringUtils.hasContent(branding.getLogo()))
|
if(branding != null && StringUtils.hasContent(branding.getLogo()))
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.model.actions;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** optional interface that can be added to an ApiFieldCustomValueMapper to
|
||||||
|
** signal that the customizer knows how to work in bulk.
|
||||||
|
**
|
||||||
|
** e.g., given a list of records, do a bulk query for data; memoize that data;
|
||||||
|
** then use it in multiple calls to produceApiValue, without, e.g., going back to
|
||||||
|
** a backend to fetch data.
|
||||||
|
*******************************************************************************/
|
||||||
|
public interface ApiFieldCustomValueMapperBulkSupportInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
void prepareToProduceApiValues(List<QRecord> records);
|
||||||
|
|
||||||
|
}
|
@ -26,12 +26,14 @@ import java.io.Serializable;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.Month;
|
import java.time.Month;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.kingsrook.qqq.api.BaseTest;
|
import com.kingsrook.qqq.api.BaseTest;
|
||||||
import com.kingsrook.qqq.api.TestUtils;
|
import com.kingsrook.qqq.api.TestUtils;
|
||||||
import com.kingsrook.qqq.api.javalin.QBadRequestException;
|
import com.kingsrook.qqq.api.javalin.QBadRequestException;
|
||||||
import com.kingsrook.qqq.api.model.actions.ApiFieldCustomValueMapper;
|
import com.kingsrook.qqq.api.model.actions.ApiFieldCustomValueMapper;
|
||||||
|
import com.kingsrook.qqq.api.model.actions.ApiFieldCustomValueMapperBulkSupportInterface;
|
||||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
||||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
||||||
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
|
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
|
||||||
@ -197,6 +199,43 @@ class ApiImplementationTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testBulkValueCustomizer() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
ApiInstanceMetaData apiInstanceMetaData = ApiInstanceMetaDataContainer.of(qInstance).getApiInstanceMetaData(TestUtils.API_NAME);
|
||||||
|
TestUtils.insertSimpsons();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// set up a custom value mapper on lastName field of person table //
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_PERSON);
|
||||||
|
QFieldMetaData field = table.getField("lastName");
|
||||||
|
field.withSupplementalMetaData(new ApiFieldMetaDataContainer()
|
||||||
|
.withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData()
|
||||||
|
.withInitialVersion(TestUtils.V2022_Q4)
|
||||||
|
.withCustomValueMapper(new QCodeReference(PersonLastNameBulkApiValueCustomizer.class))));
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// get a person - make sure custom method ran //
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
Map<String, Serializable> person = ApiImplementation.get(apiInstanceMetaData, TestUtils.CURRENT_API_VERSION, "person", "1");
|
||||||
|
assertEquals("value from prepareToProduceApiValues", person.get("lastName"));
|
||||||
|
assertEquals(1, PersonLastNameBulkApiValueCustomizer.prepareWasCalledWithThisNoOfRecords);
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
// query for persons - make sure custom method ran //
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
Map<String, Serializable> queryResult = ApiImplementation.query(apiInstanceMetaData, TestUtils.CURRENT_API_VERSION, "person", Collections.emptyMap());
|
||||||
|
assertEquals("value from prepareToProduceApiValues", ((List<Map<String, Object>>) queryResult.get("records")).get(0).get("lastName"));
|
||||||
|
assertEquals(queryResult.get("count"), PersonLastNameBulkApiValueCustomizer.prepareWasCalledWithThisNoOfRecords);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -264,4 +303,39 @@ class ApiImplementationTest extends BaseTest
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static class PersonLastNameBulkApiValueCustomizer extends ApiFieldCustomValueMapper implements ApiFieldCustomValueMapperBulkSupportInterface
|
||||||
|
{
|
||||||
|
static Integer prepareWasCalledWithThisNoOfRecords = null;
|
||||||
|
|
||||||
|
private String valueToPutInRecords = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public Serializable produceApiValue(QRecord record, String apiFieldName)
|
||||||
|
{
|
||||||
|
return (valueToPutInRecords);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void prepareToProduceApiValues(List<QRecord> records)
|
||||||
|
{
|
||||||
|
prepareWasCalledWithThisNoOfRecords = records.size();
|
||||||
|
valueToPutInRecords = "value from prepareToProduceApiValues";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user