Merge pull request #51 from Kingsrook/feature/CE-752-add-information-to-order

Feature/ce 752 add information to order
This commit is contained in:
2023-12-14 13:13:54 -06:00
committed by GitHub
18 changed files with 1942 additions and 1 deletions

View File

@ -0,0 +1,269 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.instances;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
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.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.help.HelpFormat;
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.help.QHelpRole;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
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.StringUtils;
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
/*******************************************************************************
** Utility methods for working with (dynamic, from a table) HelpContent - and
** putting it into meta-data in a QInstance.
*******************************************************************************/
public class QInstanceHelpContentManager
{
private static final QLogger LOG = QLogger.getLogger(QInstanceHelpContentManager.class);
/*******************************************************************************
**
*******************************************************************************/
public static void loadHelpContent(QInstance qInstance)
{
try
{
if(qInstance.getTable(HelpContent.TABLE_NAME) == null)
{
return;
}
QueryInput queryInput = new QueryInput();
queryInput.setTableName(HelpContent.TABLE_NAME);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
for(QRecord record : queryOutput.getRecords())
{
processHelpContentRecord(qInstance, record);
}
}
catch(Exception e)
{
LOG.error("Error loading help content", e);
}
}
/*******************************************************************************
**
*******************************************************************************/
public static void processHelpContentRecord(QInstance qInstance, QRecord record)
{
try
{
/////////////////////////////////////////////////
// parse the key into its parts that we expect //
/////////////////////////////////////////////////
String key = record.getValueString("key");
Map<String, String> nameValuePairs = new HashMap<>();
for(String part : key.split(";"))
{
String[] parts = part.split(":");
nameValuePairs.put(parts[0], parts[1]);
}
String tableName = nameValuePairs.get("table");
String processName = nameValuePairs.get("process");
String fieldName = nameValuePairs.get("field");
String sectionName = nameValuePairs.get("section");
///////////////////////////////////////////////////////////
// build a help content meta-data object from the record //
///////////////////////////////////////////////////////////
QHelpContent helpContent = new QHelpContent()
.withContent(record.getValueString("content"))
.withRole(QHelpRole.valueOf(record.getValueString("role"))); // mmm, we could fall down a bit here w/ other app-defined roles...
if(StringUtils.hasContent(record.getValueString("format")))
{
helpContent.setFormat(HelpFormat.valueOf(record.getValueString("format")));
}
Set<HelpRole> roles = helpContent.getRoles();
///////////////////////////////////////////////////////////////////////////////////////////////////
// check - if there are no contents, then let's remove this help content from the container //
// (note pre-delete customizer will take advantage of this, passing in empty content on purpose) //
///////////////////////////////////////////////////////////////////////////////////////////////////
if(!StringUtils.hasContent(helpContent.getContent()))
{
helpContent = null;
}
///////////////////////////////////////////////////////////////////////////////////
// look at what parts of the key we got, and find the meta-data object to update //
///////////////////////////////////////////////////////////////////////////////////
if(StringUtils.hasContent(tableName))
{
QTableMetaData table = qInstance.getTable(tableName);
if(table == null)
{
LOG.info("Unrecognized table in help content", logPair("key", key));
return;
}
if(StringUtils.hasContent(fieldName))
{
//////////////////////////
// handle a table field //
//////////////////////////
QFieldMetaData field = table.getFields().get(fieldName);
if(field == null)
{
LOG.info("Unrecognized table field in help content", logPair("key", key));
return;
}
if(helpContent != null)
{
field.withHelpContent(helpContent);
}
else
{
field.removeHelpContent(roles);
}
}
else if(StringUtils.hasContent(sectionName))
{
////////////////////////////
// handle a table section //
////////////////////////////
Optional<QFieldSection> optionalSection = table.getSections().stream().filter(s -> sectionName.equals(s.getName())).findFirst();
if(optionalSection.isEmpty())
{
LOG.info("Unrecognized table section in help content", logPair("key", key));
return;
}
if(helpContent != null)
{
optionalSection.get().withHelpContent(helpContent);
}
else
{
optionalSection.get().removeHelpContent(roles);
}
}
}
else if(StringUtils.hasContent(processName))
{
QProcessMetaData process = qInstance.getProcess(processName);
if(process == null)
{
LOG.info("Unrecognized process in help content", logPair("key", key));
return;
}
if(StringUtils.hasContent(fieldName))
{
////////////////////////////
// handle a process field //
////////////////////////////
Optional<QFieldMetaData> optionalField = CollectionUtils.mergeLists(process.getInputFields(), process.getOutputFields())
.stream().filter(f -> fieldName.equals(f.getName()))
.findFirst();
if(optionalField.isEmpty())
{
LOG.info("Unrecognized process field in help content", logPair("key", key));
return;
}
if(helpContent != null)
{
optionalField.get().withHelpContent(helpContent);
}
else
{
optionalField.get().removeHelpContent(roles);
}
}
}
}
catch(Exception e)
{
LOG.warn("Error processing a helpContent record", e, logPair("id", record.getValue("id")));
}
}
/*******************************************************************************
** add a help content object to a list - replacing an entry in the list with the
** same roles if one is found.
*******************************************************************************/
public static void putHelpContentInList(QHelpContent helpContent, List<QHelpContent> helpContents)
{
ListIterator<QHelpContent> iterator = helpContents.listIterator();
while(iterator.hasNext())
{
QHelpContent existingContent = iterator.next();
if(Objects.equals(existingContent.getRoles(), helpContent.getRoles()))
{
iterator.set(helpContent);
return;
}
}
helpContents.add(helpContent);
}
/*******************************************************************************
** Remove any helpContent entries in a list if they have a set of roles that matches
** the input set.
*******************************************************************************/
public static void removeHelpContentByRoleSetFromList(Set<HelpRole> roles, List<QHelpContent> helpContents)
{
if(helpContents == null)
{
return;
}
helpContents.removeIf(existingContent -> Objects.equals(existingContent.getRoles(), roles));
}
}

View File

@ -0,0 +1,296 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.helpcontent;
import java.time.Instant;
import com.kingsrook.qqq.backend.core.model.data.QField;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
/*******************************************************************************
** QRecord Entity for HelpContent table
*******************************************************************************/
public class HelpContent extends QRecordEntity
{
public static final String TABLE_NAME = "helpContent";
@QField(isEditable = false)
private Integer id;
@QField(isEditable = false)
private Instant createDate;
@QField(isEditable = false)
private Instant modifyDate;
@QField(isRequired = true, maxLength = 250, valueTooLongBehavior = ValueTooLongBehavior.ERROR)
private String key;
@QField()
private String content;
@QField(possibleValueSourceName = HelpContentFormat.NAME)
private String format;
@QField(possibleValueSourceName = HelpContentRole.NAME, isRequired = true)
private String role;
/*******************************************************************************
** Default constructor
*******************************************************************************/
public HelpContent()
{
}
/*******************************************************************************
** Constructor that takes a QRecord
*******************************************************************************/
public HelpContent(QRecord record)
{
populateFromQRecord(record);
}
/*******************************************************************************
** Getter for id
*******************************************************************************/
public Integer getId()
{
return (this.id);
}
/*******************************************************************************
** Setter for id
*******************************************************************************/
public void setId(Integer id)
{
this.id = id;
}
/*******************************************************************************
** Fluent setter for id
*******************************************************************************/
public HelpContent withId(Integer id)
{
this.id = id;
return (this);
}
/*******************************************************************************
** Getter for createDate
*******************************************************************************/
public Instant getCreateDate()
{
return (this.createDate);
}
/*******************************************************************************
** Setter for createDate
*******************************************************************************/
public void setCreateDate(Instant createDate)
{
this.createDate = createDate;
}
/*******************************************************************************
** Fluent setter for createDate
*******************************************************************************/
public HelpContent withCreateDate(Instant createDate)
{
this.createDate = createDate;
return (this);
}
/*******************************************************************************
** Getter for modifyDate
*******************************************************************************/
public Instant getModifyDate()
{
return (this.modifyDate);
}
/*******************************************************************************
** Setter for modifyDate
*******************************************************************************/
public void setModifyDate(Instant modifyDate)
{
this.modifyDate = modifyDate;
}
/*******************************************************************************
** Fluent setter for modifyDate
*******************************************************************************/
public HelpContent withModifyDate(Instant modifyDate)
{
this.modifyDate = modifyDate;
return (this);
}
/*******************************************************************************
** Getter for key
*******************************************************************************/
public String getKey()
{
return (this.key);
}
/*******************************************************************************
** Setter for key
*******************************************************************************/
public void setKey(String key)
{
this.key = key;
}
/*******************************************************************************
** Fluent setter for key
*******************************************************************************/
public HelpContent withKey(String key)
{
this.key = key;
return (this);
}
/*******************************************************************************
** Getter for content
*******************************************************************************/
public String getContent()
{
return (this.content);
}
/*******************************************************************************
** Setter for content
*******************************************************************************/
public void setContent(String content)
{
this.content = content;
}
/*******************************************************************************
** Fluent setter for content
*******************************************************************************/
public HelpContent withContent(String content)
{
this.content = content;
return (this);
}
/*******************************************************************************
** Getter for format
*******************************************************************************/
public String getFormat()
{
return (this.format);
}
/*******************************************************************************
** Setter for format
*******************************************************************************/
public void setFormat(String format)
{
this.format = format;
}
/*******************************************************************************
** Fluent setter for format
*******************************************************************************/
public HelpContent withFormat(String format)
{
this.format = format;
return (this);
}
/*******************************************************************************
** Getter for role
*******************************************************************************/
public String getRole()
{
return (this.role);
}
/*******************************************************************************
** Setter for role
*******************************************************************************/
public void setRole(String role)
{
this.role = role;
}
/*******************************************************************************
** Fluent setter for role
*******************************************************************************/
public HelpContent withRole(String role)
{
this.role = role;
return (this);
}
}

View File

@ -0,0 +1,121 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.helpcontent;
import java.util.Objects;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PossibleValueEnum;
/*******************************************************************************
** HelpContentFormat - possible value enum
*******************************************************************************/
public enum HelpContentFormat implements PossibleValueEnum<String>
{
TEXT("TEXT", "Plain Text"),
HTML("HTML", "HTML"),
MARKDOWN("MARKDOWN", "Markdown");
private final String id;
private final String label;
public static final String NAME = "helpContentFormat";
/*******************************************************************************
**
*******************************************************************************/
HelpContentFormat(String id, String label)
{
this.id = id;
this.label = label;
}
/*******************************************************************************
** Get instance by id
**
*******************************************************************************/
public static HelpContentFormat getById(String id)
{
if(id == null)
{
return (null);
}
for(HelpContentFormat value : HelpContentFormat.values())
{
if(Objects.equals(value.id, id))
{
return (value);
}
}
return (null);
}
/*******************************************************************************
** Getter for id
**
*******************************************************************************/
public String getId()
{
return id;
}
/*******************************************************************************
** Getter for label
**
*******************************************************************************/
public String getLabel()
{
return label;
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public String getPossibleValueId()
{
return (getId());
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public String getPossibleValueLabel()
{
return (getLabel());
}
}

View File

@ -0,0 +1,94 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.helpcontent;
import java.util.List;
import java.util.function.Consumer;
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
/*******************************************************************************
** Meta-data provider for table & PVS's for defining help-content for other
** meta-data objects within a QQQ app
*******************************************************************************/
public class HelpContentMetaDataProvider
{
/*******************************************************************************
**
*******************************************************************************/
public void defineAll(QInstance instance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
{
defineHelpContentTable(instance, backendName, backendDetailEnricher);
instance.addPossibleValueSource(QPossibleValueSource.newForEnum(HelpContentFormat.NAME, HelpContentFormat.values()));
instance.addPossibleValueSource(QPossibleValueSource.newForEnum(HelpContentRole.NAME, HelpContentRole.values()));
}
/*******************************************************************************
**
*******************************************************************************/
private void defineHelpContentTable(QInstance instance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
{
QTableMetaData table = new QTableMetaData()
.withName(HelpContent.TABLE_NAME)
.withBackendName(backendName)
.withRecordLabelFormat("%s %s")
.withRecordLabelFields("key", "role")
.withPrimaryKeyField("id")
.withUniqueKey(new UniqueKey("key", "role"))
.withFieldsFromEntity(HelpContent.class)
.withSection(new QFieldSection("identity", new QIcon("badge"), Tier.T1, List.of("id", "key", "role")))
.withSection(new QFieldSection("content", new QIcon("dataset"), Tier.T2, List.of("format", "content")))
.withSection(new QFieldSection("dates", new QIcon("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")))
.withCustomizer(TableCustomizers.POST_INSERT_RECORD, new QCodeReference(HelpContentPostInsertCustomizer.class))
.withCustomizer(TableCustomizers.POST_UPDATE_RECORD, new QCodeReference(HelpContentPostUpdateCustomizer.class))
.withCustomizer(TableCustomizers.PRE_UPDATE_RECORD, new QCodeReference(HelpContentPreUpdateCustomizer.class))
.withCustomizer(TableCustomizers.PRE_DELETE_RECORD, new QCodeReference(HelpContentPreDeleteCustomizer.class));
table.getField("format").withFieldAdornment(AdornmentType.Size.SMALL.toAdornment());
table.getField("key").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment());
table.getField("content").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment());
table.getField("content").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("html")));
if(backendDetailEnricher != null)
{
backendDetailEnricher.accept(table);
}
instance.addTable(table);
}
}

View File

@ -0,0 +1,66 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.helpcontent;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPostInsertCustomizer;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.instances.QInstanceHelpContentManager;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
/*******************************************************************************
** after records are inserted, put their help content in meta-data
*******************************************************************************/
public class HelpContentPostInsertCustomizer extends AbstractPostInsertCustomizer
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public List<QRecord> apply(List<QRecord> records) throws QException
{
return insertRecordsIntoMetaData(records);
}
/*******************************************************************************
**
*******************************************************************************/
static List<QRecord> insertRecordsIntoMetaData(List<QRecord> records)
{
if(records != null)
{
for(QRecord record : records)
{
QInstanceHelpContentManager.processHelpContentRecord(QContext.getQInstance(), record);
}
}
return records;
}
}

View File

@ -0,0 +1,46 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.helpcontent;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPostUpdateCustomizer;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
/*******************************************************************************
** after records are updated, put their help content in meta-data
*******************************************************************************/
public class HelpContentPostUpdateCustomizer extends AbstractPostUpdateCustomizer
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public List<QRecord> apply(List<QRecord> records) throws QException
{
return HelpContentPostInsertCustomizer.insertRecordsIntoMetaData(records);
}
}

View File

@ -0,0 +1,73 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.helpcontent;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPreDeleteCustomizer;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.instances.QInstanceHelpContentManager;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
/*******************************************************************************
** remove existing helpContent from meta-data when a record is deleted
*******************************************************************************/
public class HelpContentPreDeleteCustomizer extends AbstractPreDeleteCustomizer
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public List<QRecord> apply(List<QRecord> records) throws QException
{
if(records != null)
{
for(QRecord record : records)
{
removeOldRecordFromMetaData(record);
}
}
return (records);
}
/*******************************************************************************
**
*******************************************************************************/
static void removeOldRecordFromMetaData(QRecord oldRecord)
{
////////////////////////////////////////////////////////////////////////////
// this (clearing the content) will remove the helpContent under this key //
////////////////////////////////////////////////////////////////////////////
if(oldRecord != null)
{
QRecord recordWithoutContent = new QRecord(oldRecord);
recordWithoutContent.setValue("content", null);
QInstanceHelpContentManager.processHelpContentRecord(QContext.getQInstance(), recordWithoutContent);
}
}
}

View File

@ -0,0 +1,55 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.helpcontent;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPreUpdateCustomizer;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
/*******************************************************************************
** in case a row's Key or Role was changed, remove existing helpContent from that key.
*******************************************************************************/
public class HelpContentPreUpdateCustomizer extends AbstractPreUpdateCustomizer
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public List<QRecord> apply(List<QRecord> records) throws QException
{
if(records != null)
{
for(QRecord record : records)
{
QRecord oldRecord = getOldRecordMap().get(record.getValueInteger("id"));
HelpContentPreDeleteCustomizer.removeOldRecordFromMetaData(oldRecord);
}
}
return (records);
}
}

View File

@ -0,0 +1,128 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.helpcontent;
import java.util.Objects;
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpRole;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PossibleValueEnum;
/*******************************************************************************
** HelpContentRole - possible value enum
*******************************************************************************/
public enum HelpContentRole implements PossibleValueEnum<String>
{
ALL_SCREENS(QHelpRole.ALL_SCREENS.name(), "All Screens"),
READ_SCREENS(QHelpRole.READ_SCREENS.name(), "Query & View Screens"),
WRITE_SCREENS(QHelpRole.WRITE_SCREENS.name(), "Insert & Edit Screens"),
QUERY_SCREEN(QHelpRole.QUERY_SCREEN.name(), "Query Screen Only"),
VIEW_SCREEN(QHelpRole.VIEW_SCREEN.name(), "View Screen Only"),
EDIT_SCREEN(QHelpRole.EDIT_SCREEN.name(), "Edit Screen Only"),
INSERT_SCREEN(QHelpRole.INSERT_SCREEN.name(), "Insert Screen Only"),
PROCESS_SCREEN(QHelpRole.PROCESS_SCREEN.name(), "Process Screens");
private final String id;
private final String label;
public static final String NAME = "HelpContentRole";
/*******************************************************************************
**
*******************************************************************************/
HelpContentRole(String id, String label)
{
this.id = id;
this.label = label;
}
/*******************************************************************************
** Get instance by id
**
*******************************************************************************/
public static HelpContentRole getById(String id)
{
if(id == null)
{
return (null);
}
for(HelpContentRole value : HelpContentRole.values())
{
if(Objects.equals(value.id, id))
{
return (value);
}
}
return (null);
}
/*******************************************************************************
** Getter for id
**
*******************************************************************************/
public String getId()
{
return id;
}
/*******************************************************************************
** Getter for label
**
*******************************************************************************/
public String getLabel()
{
return label;
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public String getPossibleValueId()
{
return (getId());
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public String getPossibleValueLabel()
{
return (getLabel());
}
}

View File

@ -34,10 +34,13 @@ import java.util.Set;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.github.hervian.reflection.Fun;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.instances.QInstanceHelpContentManager;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.data.QField;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
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.security.FieldSecurityLock;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
@ -65,7 +68,7 @@ public class QFieldMetaData implements Cloneable
// propose doing that in a secondary field, e.g., "onlyEditableOn=insert|update" //
///////////////////////////////////////////////////////////////////////////////////
private String displayFormat = "%s";
private String displayFormat = "%s";
private Serializable defaultValue;
private String possibleValueSourceName;
private QQueryFilter possibleValueSourceFilter;
@ -84,6 +87,7 @@ public class QFieldMetaData implements Cloneable
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private List<FieldAdornment> adornments;
private List<QHelpContent> helpContents;
private Map<String, QSupplementalFieldMetaData> supplementalMetaData;
@ -928,4 +932,61 @@ public class QFieldMetaData implements Cloneable
return (this);
}
/*******************************************************************************
** Getter for helpContents
*******************************************************************************/
public List<QHelpContent> getHelpContents()
{
return (this.helpContents);
}
/*******************************************************************************
** Setter for helpContents
*******************************************************************************/
public void setHelpContents(List<QHelpContent> helpContents)
{
this.helpContents = helpContents;
}
/*******************************************************************************
** Fluent setter for helpContents
*******************************************************************************/
public QFieldMetaData withHelpContents(List<QHelpContent> helpContents)
{
this.helpContents = helpContents;
return (this);
}
/*******************************************************************************
** Fluent setter for adding 1 helpContent
*******************************************************************************/
public QFieldMetaData withHelpContent(QHelpContent helpContent)
{
if(this.helpContents == null)
{
this.helpContents = new ArrayList<>();
}
QInstanceHelpContentManager.putHelpContentInList(helpContent, this.helpContents);
return (this);
}
/*******************************************************************************
** remove a single helpContent based on its set of roles
*******************************************************************************/
public void removeHelpContent(Set<HelpRole> roles)
{
QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, this.helpContents);
}
}

View File

@ -29,6 +29,7 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
/*******************************************************************************
@ -50,6 +51,7 @@ public class QFrontendFieldMetaData
private Serializable defaultValue;
private List<FieldAdornment> adornments;
private List<QHelpContent> helpContents;
//////////////////////////////////////////////////////////////////////////////////
// do not add setters. take values from the source-object in the constructor!! //
@ -72,6 +74,7 @@ public class QFrontendFieldMetaData
this.displayFormat = fieldMetaData.getDisplayFormat();
this.adornments = fieldMetaData.getAdornments();
this.defaultValue = fieldMetaData.getDefaultValue();
this.helpContents = fieldMetaData.getHelpContents();
}
@ -183,4 +186,16 @@ public class QFrontendFieldMetaData
{
return defaultValue;
}
/*******************************************************************************
** Getter for helpContents
**
*******************************************************************************/
public List<QHelpContent> getHelpContents()
{
return helpContents;
}
}

View File

@ -0,0 +1,33 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.help;
/*******************************************************************************
** How a piece of help content is formatted.
*******************************************************************************/
public enum HelpFormat
{
TEXT,
HTML,
MARKDOWN
}

View File

@ -0,0 +1,35 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.help;
/*******************************************************************************
** Interface to be associated with a HelpContent, to identify where the content
** is meant to be used (e.g., only on "write" screens, vs. on app home pages, etc).
**
** Defined in HelpContext to be this interface, so alternate frontends can
** specify their own particular values - but a standard set of values is provided
** by QQQ in QHelpRole.
*******************************************************************************/
public interface HelpRole
{
}

View File

@ -0,0 +1,232 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.help;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/*******************************************************************************
** meta-data defintion of "Help Content" to show to a user - for use in
** a specific "role" (e.g., insert screens but not view screens), and in a
** particular "format" (e.g., plain text, html, markdown).
**
** Meant to be assigned to several different pieces of QQQ meta data (fields,
** tables, processes, etc), and used as-needed by various frontends.
**
** May evolve something like a "Presentation" attribute in the future - e.g.,
** to say "present this one as a tooltip" vs. "present this one as inline text"
**
** May be dynamically added to meta-data via (non-meta-) data - see
** HelpContentMetaDataProvider and QInstanceHelpContentManager
*******************************************************************************/
public class QHelpContent
{
private String content;
private HelpFormat format;
private Set<HelpRole> roles;
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public QHelpContent()
{
}
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public QHelpContent(String content)
{
setContent(content);
}
/*******************************************************************************
** Getter for content
*******************************************************************************/
public String getContent()
{
return (this.content);
}
/*******************************************************************************
** Setter for content
*******************************************************************************/
public void setContent(String content)
{
this.content = content;
}
/*******************************************************************************
** Fluent setter for content
*******************************************************************************/
public QHelpContent withContent(String content)
{
this.content = content;
return (this);
}
/*******************************************************************************
** Fluent setter for content that also sets format as HTML
*******************************************************************************/
public QHelpContent withContentAsHTML(String content)
{
this.content = content;
this.format = HelpFormat.HTML;
return (this);
}
/*******************************************************************************
** Fluent setter for content that also sets format as TEXT
*******************************************************************************/
public QHelpContent withContentAsText(String content)
{
this.content = content;
this.format = HelpFormat.TEXT;
return (this);
}
/*******************************************************************************
** Fluent setter for content that also sets format as Markdown
*******************************************************************************/
public QHelpContent withContentAsMarkdown(String content)
{
this.content = content;
this.format = HelpFormat.MARKDOWN;
return (this);
}
/*******************************************************************************
** Getter for format
*******************************************************************************/
public HelpFormat getFormat()
{
return (this.format);
}
/*******************************************************************************
** Setter for format
*******************************************************************************/
public void setFormat(HelpFormat format)
{
this.format = format;
}
/*******************************************************************************
** Fluent setter for format
*******************************************************************************/
public QHelpContent withFormat(HelpFormat format)
{
this.format = format;
return (this);
}
/*******************************************************************************
** Getter for roles
*******************************************************************************/
public Set<HelpRole> getRoles()
{
return (this.roles);
}
/*******************************************************************************
** Setter for roles
*******************************************************************************/
public void setRoles(Set<HelpRole> roles)
{
this.roles = roles;
}
/*******************************************************************************
** Fluent setter for roles
*******************************************************************************/
public QHelpContent withRoles(Set<HelpRole> roles)
{
this.roles = roles;
return (this);
}
/*******************************************************************************
** Fluent method to add a role
*******************************************************************************/
public QHelpContent withRole(HelpRole role)
{
return (withRoles(role));
}
/*******************************************************************************
** Fluent method to add a role
*******************************************************************************/
public QHelpContent withRoles(HelpRole... roles)
{
if(roles == null || roles.length == 0)
{
return (this);
}
if(this.roles == null)
{
this.roles = new HashSet<>();
}
Collections.addAll(this.roles, roles);
return (this);
}
}

View File

@ -0,0 +1,40 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.help;
/*******************************************************************************
** QQQ default or standard HelpRoles.
*******************************************************************************/
public enum QHelpRole implements HelpRole
{
ALL_SCREENS,
READ_SCREENS,
WRITE_SCREENS,
QUERY_SCREEN,
VIEW_SCREEN,
EDIT_SCREEN,
INSERT_SCREEN,
PROCESS_SCREEN,
APP_SCREEN,
TABLE_ACTION_MENU
}

View File

@ -22,7 +22,12 @@
package com.kingsrook.qqq.backend.core.model.metadata.tables;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.kingsrook.qqq.backend.core.instances.QInstanceHelpContentManager;
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.QIcon;
import com.kingsrook.qqq.backend.core.utils.collections.MutableList;
@ -44,6 +49,8 @@ public class QFieldSection
private boolean isHidden = false;
private Integer gridColumns;
private List<QHelpContent> helpContents;
/*******************************************************************************
@ -364,4 +371,61 @@ public class QFieldSection
return (this);
}
/*******************************************************************************
** Getter for helpContents
*******************************************************************************/
public List<QHelpContent> getHelpContents()
{
return (this.helpContents);
}
/*******************************************************************************
** Setter for helpContents
*******************************************************************************/
public void setHelpContents(List<QHelpContent> helpContents)
{
this.helpContents = helpContents;
}
/*******************************************************************************
** Fluent setter for helpContents
*******************************************************************************/
public QFieldSection withHelpContents(List<QHelpContent> helpContents)
{
this.helpContents = helpContents;
return (this);
}
/*******************************************************************************
** Fluent setter for adding 1 helpContent
*******************************************************************************/
public QFieldSection withHelpContent(QHelpContent helpContent)
{
if(this.helpContents == null)
{
this.helpContents = new ArrayList<>();
}
QInstanceHelpContentManager.putHelpContentInList(helpContent, this.helpContents);
return (this);
}
/*******************************************************************************
** remove a single helpContent based on its set of roles
*******************************************************************************/
public void removeHelpContent(Set<HelpRole> roles)
{
QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, this.helpContents);
}
}

View File

@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.processes.implementations.general;
import java.util.ArrayList;
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
import com.kingsrook.qqq.backend.core.model.actions.processes.Status;
import static com.kingsrook.qqq.backend.core.model.actions.processes.Status.ERROR;
import static com.kingsrook.qqq.backend.core.model.actions.processes.Status.OK;
@ -80,6 +81,20 @@ public class StandardProcessSummaryLineProducer
/*******************************************************************************
**
*******************************************************************************/
public static ProcessSummaryLine getNoDifferencesNoUpdateLine()
{
return new ProcessSummaryLine(Status.INFO)
.withSingularFutureMessage("has no differences and will not be updated")
.withPluralFutureMessage("have no differences and will not be updated")
.withSingularPastMessage("has no differences and was not updated")
.withPluralPastMessage("have no differences and were not updated");
}
/*******************************************************************************
** Make a line that'll say " had an error"
*******************************************************************************/

View File

@ -0,0 +1,298 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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.instances;
import java.util.List;
import java.util.Set;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
import com.kingsrook.qqq.backend.core.model.helpcontent.HelpContent;
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.metadata.QInstance;
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.help.QHelpRole;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
/*******************************************************************************
** Unit test for QInstanceHelpContentManager
*******************************************************************************/
class QInstanceHelpContentManagerTest extends BaseTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void testTableField() 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 person.id //
////////////////////////////////////////////////////////
assertNoPersonIdHelp(qInstance);
HelpContent recordEntity = new HelpContent()
.withId(1)
.withKey("table:person;field:id")
.withContent("v1")
.withRole(HelpContentRole.INSERT_SCREEN.getId());
new InsertAction().execute(new InsertInput(HelpContent.TABLE_NAME).withRecordEntity(recordEntity));
///////////////////////////////////////////////////////////////////////////////////////////////
// now - post-insert customizer should have automatically added help content to the instance //
///////////////////////////////////////////////////////////////////////////////////////////////
assertOnePersonIdHelp(qInstance, "v1", Set.of(QHelpRole.INSERT_SCREEN));
///////////////////////////////////////////////////
// define a new instance - assert is empty again //
///////////////////////////////////////////////////
QInstance newInstance = TestUtils.defineInstance();
QContext.setQInstance(newInstance);
new HelpContentMetaDataProvider().defineAll(newInstance, TestUtils.MEMORY_BACKEND_NAME, null);
assertNoPersonIdHelp(newInstance);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// now run the method that start-up (or hotswap) will run, to look up existing records and translate to meta-data //
// then re-assert that the help is back //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
QInstanceHelpContentManager.loadHelpContent(newInstance);
assertOnePersonIdHelp(newInstance, "v1", Set.of(QHelpRole.INSERT_SCREEN));
////////////////////////////////////////////////////////////////////
// update the record's content - the meta-data should get updated //
////////////////////////////////////////////////////////////////////
recordEntity.setContent("v2");
new UpdateAction().execute(new UpdateInput(HelpContent.TABLE_NAME).withRecordEntity(recordEntity));
assertOnePersonIdHelp(newInstance, "v2", Set.of(QHelpRole.INSERT_SCREEN));
////////////////////////////////////////////////////////////////////////////
// now update the role and assert it "moves" in the meta-data as expected //
////////////////////////////////////////////////////////////////////////////
recordEntity.setRole(HelpContentRole.WRITE_SCREENS.getId());
new UpdateAction().execute(new UpdateInput(HelpContent.TABLE_NAME).withRecordEntity(recordEntity));
assertOnePersonIdHelp(newInstance, "v2", Set.of(QHelpRole.WRITE_SCREENS));
//////////////////////////////////////////////////////////////////////////////////////
// now delete the record - the pre-insert should remove the help from the meta-data //
//////////////////////////////////////////////////////////////////////////////////////
new DeleteAction().execute(new DeleteInput(HelpContent.TABLE_NAME).withPrimaryKeys(List.of(1)));
assertNoPersonIdHelp(newInstance);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testTableSection() 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 section //
//////////////////////////////////////////////////////////
assertNoPersonSectionHelp(qInstance);
HelpContent recordEntity = new HelpContent()
.withId(1)
.withKey("table:person;section:identity")
.withContent("v1")
.withRole(HelpContentRole.INSERT_SCREEN.getId());
new InsertAction().execute(new InsertInput(HelpContent.TABLE_NAME).withRecordEntity(recordEntity));
///////////////////////////////////////////////////////////////////////////////////////////////
// now - post-insert customizer should have automatically added help content to the instance //
///////////////////////////////////////////////////////////////////////////////////////////////
assertOnePersonSectionHelp(qInstance, "v1", Set.of(QHelpRole.INSERT_SCREEN));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testProcessField() 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 section //
//////////////////////////////////////////////////////////
assertNoGreetPersonFieldHelp(qInstance);
HelpContent recordEntity = new HelpContent()
.withId(1)
.withKey("process:" + TestUtils.PROCESS_NAME_GREET_PEOPLE + ";field:greetingPrefix")
.withContent("v1")
.withRole(HelpContentRole.INSERT_SCREEN.getId());
new InsertAction().execute(new InsertInput(HelpContent.TABLE_NAME).withRecordEntity(recordEntity));
///////////////////////////////////////////////////////////////////////////////////////////////
// now - post-insert customizer should have automatically added help content to the instance //
///////////////////////////////////////////////////////////////////////////////////////////////
assertOneGreetPersonFieldHelp(qInstance, "v1", Set.of(QHelpRole.INSERT_SCREEN));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testInsertedRecordReplacesHelpContentFromMetaData() throws QException
{
/////////////////////////////////////
// get the instance from base test //
/////////////////////////////////////
QInstance qInstance = QContext.getQInstance();
qInstance.getTable(TestUtils.TABLE_NAME_PERSON).getField("id")
.withHelpContent(new QHelpContent().withContent("v0").withRole(QHelpRole.INSERT_SCREEN));
new HelpContentMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
/////////////////////////////////////////////
// assert the help from meta-data is there //
/////////////////////////////////////////////
assertOnePersonIdHelp(qInstance, "v0", Set.of(QHelpRole.INSERT_SCREEN));
HelpContent recordEntity = new HelpContent()
.withId(1)
.withKey("table:person;field:id")
.withContent("v1")
.withRole(HelpContentRole.INSERT_SCREEN.getId());
new InsertAction().execute(new InsertInput(HelpContent.TABLE_NAME).withRecordEntity(recordEntity));
///////////////////////////////////////////////////////////////////////////////////////////////
// now - post-insert customizer should have automatically added help content to the instance //
///////////////////////////////////////////////////////////////////////////////////////////////
assertOnePersonIdHelp(qInstance, "v1", Set.of(QHelpRole.INSERT_SCREEN));
}
/*******************************************************************************
**
*******************************************************************************/
void assertNoPersonIdHelp(QInstance qInstance)
{
List<QHelpContent> helpContents = qInstance.getTable(TestUtils.TABLE_NAME_PERSON).getField("id").getHelpContents();
assertThat(helpContents).isNullOrEmpty();
}
/*******************************************************************************
**
*******************************************************************************/
void assertOnePersonIdHelp(QInstance qInstance, String content, Set<HelpRole> roles)
{
List<QHelpContent> helpContents = qInstance.getTable(TestUtils.TABLE_NAME_PERSON).getField("id").getHelpContents();
assertEquals(1, helpContents.size());
assertEquals(content, helpContents.get(0).getContent());
assertEquals(roles, helpContents.get(0).getRoles());
}
/*******************************************************************************
**
*******************************************************************************/
void assertNoPersonSectionHelp(QInstance qInstance)
{
List<QHelpContent> helpContents = qInstance.getTable(TestUtils.TABLE_NAME_PERSON).getSections()
.stream().filter(s -> s.getName().equals("identity")).findFirst()
.get().getHelpContents();
assertThat(helpContents).isNullOrEmpty();
}
/*******************************************************************************
**
*******************************************************************************/
void assertOnePersonSectionHelp(QInstance qInstance, String content, Set<HelpRole> roles)
{
List<QHelpContent> helpContents = qInstance.getTable(TestUtils.TABLE_NAME_PERSON).getSections()
.stream().filter(s -> s.getName().equals("identity")).findFirst()
.get().getHelpContents();
assertEquals(1, helpContents.size());
assertEquals(content, helpContents.get(0).getContent());
assertEquals(roles, helpContents.get(0).getRoles());
}
/*******************************************************************************
**
*******************************************************************************/
void assertNoGreetPersonFieldHelp(QInstance qInstance)
{
List<QHelpContent> helpContents = qInstance.getProcess(TestUtils.PROCESS_NAME_GREET_PEOPLE).getInputFields()
.stream().filter(f -> f.getName().equals("greetingPrefix")).findFirst()
.get().getHelpContents();
assertThat(helpContents).isNullOrEmpty();
}
/*******************************************************************************
**
*******************************************************************************/
void assertOneGreetPersonFieldHelp(QInstance qInstance, String content, Set<HelpRole> roles)
{
List<QHelpContent> helpContents = qInstance.getProcess(TestUtils.PROCESS_NAME_GREET_PEOPLE).getInputFields()
.stream().filter(f -> f.getName().equals("greetingPrefix")).findFirst()
.get().getHelpContents();
assertEquals(1, helpContents.size());
assertEquals(content, helpContents.get(0).getContent());
assertEquals(roles, helpContents.get(0).getRoles());
}
}