From 9d1266036c4cd443fadf24fc75683c73ab46e0ae Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 23 Jun 2023 16:38:00 -0500 Subject: [PATCH] Adding scriptType fileModes, with multi-files under a script Revision. --- .../RunRecordScriptAutomationHandler.java | 3 +- .../scripts/RunAdHocRecordScriptAction.java | 3 +- .../templates/ConvertHtmlToPdfAction.java | 4 +- .../templates/RenderTemplateAction.java | 11 +- .../templates/ConvertHtmlToPdfInput.java | 4 +- .../templates/ConvertHtmlToPdfOutput.java | 4 +- .../templates/RenderTemplateInput.java | 11 +- .../templates/RenderTemplateOutput.java | 4 +- .../core/model/data/QRecordEntity.java | 34 ++- .../dashboard/nocode/WidgetHtmlLine.java | 4 +- .../model/scripts/ScriptRevisionFile.java | 265 ++++++++++++++++++ .../core/model/scripts/ScriptType.java | 34 +++ .../model/scripts/ScriptTypeFileMode.java | 96 +++++++ .../model/scripts/ScriptTypeFileSchema.java | 265 ++++++++++++++++++ .../scripts/ScriptsMetaDataProvider.java | 91 +++++- .../StoreScriptRevisionProcessStep.java | 196 ++++++++----- .../backend/core/utils/CollectionUtils.java | 67 +++++ .../templates/ConvertHtmlToPdfActionTest.java | 6 +- .../templates/RenderTemplateActionTest.java | 4 +- .../core/model/data/QRecordEntityTest.java | 23 ++ .../StoreScriptRevisionProcessStepTest.java | 95 ++++++- .../core/utils/CollectionUtilsTest.java | 46 +++ 22 files changed, 1155 insertions(+), 115 deletions(-) rename qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/{ => actions}/templates/ConvertHtmlToPdfInput.java (98%) rename qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/{ => actions}/templates/ConvertHtmlToPdfOutput.java (95%) rename qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/{ => actions}/templates/RenderTemplateInput.java (93%) rename qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/{ => actions}/templates/RenderTemplateOutput.java (95%) create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptRevisionFile.java create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptTypeFileMode.java create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptTypeFileSchema.java diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RunRecordScriptAutomationHandler.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RunRecordScriptAutomationHandler.java index 0ea035fd..1b4d997d 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RunRecordScriptAutomationHandler.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RunRecordScriptAutomationHandler.java @@ -42,6 +42,7 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.metadata.code.AdHocScriptCodeReference; import com.kingsrook.qqq.backend.core.model.scripts.Script; import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevision; +import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider; import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.ValueUtils; import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; @@ -74,7 +75,7 @@ public class RunRecordScriptAutomationHandler extends RecordAutomationHandler QueryInput queryInput = new QueryInput(); queryInput.setTableName(ScriptRevision.TABLE_NAME); queryInput.setFilter(new QQueryFilter(new QFilterCriteria("scriptId", QCriteriaOperator.EQUALS, scriptId))); - queryInput.withQueryJoin(new QueryJoin(Script.TABLE_NAME).withBaseTableOrAlias(ScriptRevision.TABLE_NAME).withJoinMetaData(QContext.getQInstance().getJoin("currentScriptRevision"))); + queryInput.withQueryJoin(new QueryJoin(Script.TABLE_NAME).withBaseTableOrAlias(ScriptRevision.TABLE_NAME).withJoinMetaData(QContext.getQInstance().getJoin(ScriptsMetaDataProvider.CURRENT_SCRIPT_REVISION_JOIN_NAME))); QueryOutput queryOutput = new QueryAction().execute(queryInput); if(CollectionUtils.nullSafeIsEmpty(queryOutput.getRecords())) { diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/scripts/RunAdHocRecordScriptAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/scripts/RunAdHocRecordScriptAction.java index f440e16f..adad50a6 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/scripts/RunAdHocRecordScriptAction.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/scripts/RunAdHocRecordScriptAction.java @@ -51,6 +51,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.AdHocScriptCodeReferen import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.model.scripts.Script; import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevision; +import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider; import com.kingsrook.qqq.backend.core.utils.CollectionUtils; @@ -197,7 +198,7 @@ public class RunAdHocRecordScriptAction QueryInput queryInput = new QueryInput(); queryInput.setTableName(ScriptRevision.TABLE_NAME); queryInput.setFilter(new QQueryFilter(new QFilterCriteria("script.id", QCriteriaOperator.EQUALS, codeReference.getScriptId()))); - queryInput.withQueryJoin(new QueryJoin(Script.TABLE_NAME).withBaseTableOrAlias(ScriptRevision.TABLE_NAME).withJoinMetaData(QContext.getQInstance().getJoin("currentScriptRevision"))); + queryInput.withQueryJoin(new QueryJoin(Script.TABLE_NAME).withBaseTableOrAlias(ScriptRevision.TABLE_NAME).withJoinMetaData(QContext.getQInstance().getJoin(ScriptsMetaDataProvider.CURRENT_SCRIPT_REVISION_JOIN_NAME))); QueryOutput queryOutput = new QueryAction().execute(queryInput); if(CollectionUtils.nullSafeHasContents(queryOutput.getRecords())) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/templates/ConvertHtmlToPdfAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/templates/ConvertHtmlToPdfAction.java index 38780bd0..a7f9096f 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/templates/ConvertHtmlToPdfAction.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/templates/ConvertHtmlToPdfAction.java @@ -26,8 +26,8 @@ import java.nio.file.Path; import java.util.Map; import com.kingsrook.qqq.backend.core.actions.AbstractQActionFunction; import com.kingsrook.qqq.backend.core.exceptions.QException; -import com.kingsrook.qqq.backend.core.model.templates.ConvertHtmlToPdfInput; -import com.kingsrook.qqq.backend.core.model.templates.ConvertHtmlToPdfOutput; +import com.kingsrook.qqq.backend.core.model.actions.templates.ConvertHtmlToPdfInput; +import com.kingsrook.qqq.backend.core.model.actions.templates.ConvertHtmlToPdfOutput; import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/templates/RenderTemplateAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/templates/RenderTemplateAction.java index 1bb6e3ce..e8a19fc2 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/templates/RenderTemplateAction.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/templates/RenderTemplateAction.java @@ -28,8 +28,8 @@ import com.kingsrook.qqq.backend.core.actions.AbstractQActionFunction; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.logging.QLogger; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput; -import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateInput; -import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateOutput; +import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateInput; +import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateOutput; import com.kingsrook.qqq.backend.core.model.templates.TemplateType; import com.kingsrook.qqq.backend.core.utils.StringUtils; import org.apache.velocity.VelocityContext; @@ -62,7 +62,12 @@ public class RenderTemplateAction extends AbstractQActionFunction entry : input.getContext().entrySet()) + { + context.put(entry.getKey(), entry.getValue()); + } setupEventHandlers(context); diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/templates/ConvertHtmlToPdfInput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/templates/ConvertHtmlToPdfInput.java similarity index 98% rename from qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/templates/ConvertHtmlToPdfInput.java rename to qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/templates/ConvertHtmlToPdfInput.java index 75ea07e4..9dd2eea1 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/templates/ConvertHtmlToPdfInput.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/templates/ConvertHtmlToPdfInput.java @@ -1,6 +1,6 @@ /* * QQQ - Low-code Application Framework for Engineers. - * Copyright (C) 2021-2022. Kingsrook, LLC + * 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/ @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -package com.kingsrook.qqq.backend.core.model.templates; +package com.kingsrook.qqq.backend.core.model.actions.templates; import java.io.OutputStream; diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/templates/ConvertHtmlToPdfOutput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/templates/ConvertHtmlToPdfOutput.java similarity index 95% rename from qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/templates/ConvertHtmlToPdfOutput.java rename to qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/templates/ConvertHtmlToPdfOutput.java index 95f71773..b4d4e147 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/templates/ConvertHtmlToPdfOutput.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/templates/ConvertHtmlToPdfOutput.java @@ -1,6 +1,6 @@ /* * QQQ - Low-code Application Framework for Engineers. - * Copyright (C) 2021-2022. Kingsrook, LLC + * 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/ @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -package com.kingsrook.qqq.backend.core.model.templates; +package com.kingsrook.qqq.backend.core.model.actions.templates; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput; diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/templates/RenderTemplateInput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/templates/RenderTemplateInput.java similarity index 93% rename from qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/templates/RenderTemplateInput.java rename to qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/templates/RenderTemplateInput.java index 87302d40..ee85265e 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/templates/RenderTemplateInput.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/templates/RenderTemplateInput.java @@ -1,6 +1,6 @@ /* * QQQ - Low-code Application Framework for Engineers. - * Copyright (C) 2021-2022. Kingsrook, LLC + * 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/ @@ -19,11 +19,12 @@ * along with this program. If not, see . */ -package com.kingsrook.qqq.backend.core.model.templates; +package com.kingsrook.qqq.backend.core.model.actions.templates; import java.util.Map; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput; +import com.kingsrook.qqq.backend.core.model.templates.TemplateType; /******************************************************************************* @@ -35,7 +36,7 @@ public class RenderTemplateInput extends AbstractActionInput private String code; // todo - TemplateReference, like CodeReference?? private TemplateType templateType; - private Map context; + private Map context; @@ -120,7 +121,7 @@ public class RenderTemplateInput extends AbstractActionInput ** Getter for context ** *******************************************************************************/ - public Map getContext() + public Map getContext() { return context; } @@ -131,7 +132,7 @@ public class RenderTemplateInput extends AbstractActionInput ** Setter for context ** *******************************************************************************/ - public void setContext(Map context) + public void setContext(Map context) { this.context = context; } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/templates/RenderTemplateOutput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/templates/RenderTemplateOutput.java similarity index 95% rename from qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/templates/RenderTemplateOutput.java rename to qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/templates/RenderTemplateOutput.java index 40d0e0f1..0e8e30d6 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/templates/RenderTemplateOutput.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/templates/RenderTemplateOutput.java @@ -1,6 +1,6 @@ /* * QQQ - Low-code Application Framework for Engineers. - * Copyright (C) 2021-2022. Kingsrook, LLC + * 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/ @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -package com.kingsrook.qqq.backend.core.model.templates; +package com.kingsrook.qqq.backend.core.model.actions.templates; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput; diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QRecordEntity.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QRecordEntity.java index 8bc11e70..1d48fb46 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QRecordEntity.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QRecordEntity.java @@ -61,11 +61,23 @@ public abstract class QRecordEntity ** *******************************************************************************/ public static T fromQRecord(Class c, QRecord qRecord) throws QException + { + return (QRecordEntity.fromQRecord(c, qRecord, "")); + } + + + + /******************************************************************************* + ** Build an entity of this QRecord type from a QRecord - where the fields for + ** this entity have the given prefix - e.g., if they were selected as part of a join. + ** + *******************************************************************************/ + public static T fromQRecord(Class c, QRecord qRecord, String fieldNamePrefix) throws QException { try { T entity = c.getConstructor().newInstance(); - entity.populateFromQRecord(qRecord); + entity.populateFromQRecord(qRecord, fieldNamePrefix); return (entity); } catch(Exception e) @@ -81,14 +93,32 @@ public abstract class QRecordEntity ** *******************************************************************************/ protected void populateFromQRecord(QRecord qRecord) throws QRuntimeException + { + populateFromQRecord(qRecord, ""); + } + + + + /******************************************************************************* + ** Build an entity of this QRecord type from a QRecord - where the fields for + ** this entity have the given prefix - e.g., if they were selected as part of a join. + ** + *******************************************************************************/ + protected void populateFromQRecord(QRecord qRecord, String fieldNamePrefix) throws QRuntimeException { try { List fieldList = getFieldList(this.getClass()); originalRecordValues = new HashMap<>(); + + if(fieldNamePrefix == null) + { + fieldNamePrefix = ""; + } + for(QRecordEntityField qRecordEntityField : fieldList) { - Serializable value = qRecord.getValue(qRecordEntityField.getFieldName()); + Serializable value = qRecord.getValue(fieldNamePrefix + qRecordEntityField.getFieldName()); Object typedValue = qRecordEntityField.convertValueType(value); qRecordEntityField.getSetter().invoke(this, typedValue); originalRecordValues.put(qRecordEntityField.getFieldName(), value); diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/nocode/WidgetHtmlLine.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/nocode/WidgetHtmlLine.java index 8da31154..6bfc98e5 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/nocode/WidgetHtmlLine.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/nocode/WidgetHtmlLine.java @@ -28,8 +28,8 @@ import java.util.Map; import com.kingsrook.qqq.backend.core.actions.templates.RenderTemplateAction; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; -import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateInput; -import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateOutput; +import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateInput; +import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateOutput; import com.kingsrook.qqq.backend.core.model.templates.TemplateType; diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptRevisionFile.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptRevisionFile.java new file mode 100644 index 00000000..f6a3d7f4 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptRevisionFile.java @@ -0,0 +1,265 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. 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 . + */ + +package com.kingsrook.qqq.backend.core.model.scripts; + + +import java.time.Instant; +import com.kingsrook.qqq.backend.core.exceptions.QException; +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; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class ScriptRevisionFile extends QRecordEntity +{ + public static final String TABLE_NAME = "scriptRevisionFile"; + + @QField(isEditable = false) + private Integer id; + + @QField(isEditable = false) + private Instant createDate; + + @QField(isEditable = false) + private Instant modifyDate; + + @QField(possibleValueSourceName = "scriptRevision") + private Integer scriptRevisionId; + + @QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.ERROR) + private String fileName; + + @QField() + private String contents; + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public ScriptRevisionFile() + { + } + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public ScriptRevisionFile(QRecord qRecord) throws QException + { + populateFromQRecord(qRecord); + } + + + + /******************************************************************************* + ** 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 ScriptRevisionFile 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 ScriptRevisionFile 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 ScriptRevisionFile withModifyDate(Instant modifyDate) + { + this.modifyDate = modifyDate; + return (this); + } + + + + /******************************************************************************* + ** Getter for scriptRevisionId + *******************************************************************************/ + public Integer getScriptRevisionId() + { + return (this.scriptRevisionId); + } + + + + /******************************************************************************* + ** Setter for scriptRevisionId + *******************************************************************************/ + public void setScriptRevisionId(Integer scriptRevisionId) + { + this.scriptRevisionId = scriptRevisionId; + } + + + + /******************************************************************************* + ** Fluent setter for scriptRevisionId + *******************************************************************************/ + public ScriptRevisionFile withScriptRevisionId(Integer scriptRevisionId) + { + this.scriptRevisionId = scriptRevisionId; + return (this); + } + + + + /******************************************************************************* + ** Getter for fileName + *******************************************************************************/ + public String getFileName() + { + return (this.fileName); + } + + + + /******************************************************************************* + ** Setter for fileName + *******************************************************************************/ + public void setFileName(String fileName) + { + this.fileName = fileName; + } + + + + /******************************************************************************* + ** Fluent setter for fileName + *******************************************************************************/ + public ScriptRevisionFile withFileName(String fileName) + { + this.fileName = fileName; + return (this); + } + + + + /******************************************************************************* + ** Getter for contents + *******************************************************************************/ + public String getContents() + { + return (this.contents); + } + + + + /******************************************************************************* + ** Setter for contents + *******************************************************************************/ + public void setContents(String contents) + { + this.contents = contents; + } + + + + /******************************************************************************* + ** Fluent setter for contents + *******************************************************************************/ + public ScriptRevisionFile withContents(String contents) + { + this.contents = contents; + return (this); + } + +} \ No newline at end of file diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptType.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptType.java index e2fe2354..e26908ef 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptType.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptType.java @@ -52,6 +52,9 @@ public class ScriptType extends QRecordEntity @QField() private String sampleCode; + @QField(possibleValueSourceName = ScriptTypeFileMode.NAME) + private Integer fileMode; + /******************************************************************************* @@ -256,4 +259,35 @@ public class ScriptType extends QRecordEntity return (this); } + + + /******************************************************************************* + ** Getter for fileMode + *******************************************************************************/ + public Integer getFileMode() + { + return (this.fileMode); + } + + + + /******************************************************************************* + ** Setter for fileMode + *******************************************************************************/ + public void setFileMode(Integer fileMode) + { + this.fileMode = fileMode; + } + + + + /******************************************************************************* + ** Fluent setter for fileMode + *******************************************************************************/ + public ScriptType withFileMode(Integer fileMode) + { + this.fileMode = fileMode; + return (this); + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptTypeFileMode.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptTypeFileMode.java new file mode 100644 index 00000000..560d48ce --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptTypeFileMode.java @@ -0,0 +1,96 @@ +/* + * 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 . + */ + +package com.kingsrook.qqq.backend.core.model.scripts; + + +import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PossibleValueEnum; + + +/******************************************************************************* + ** ScriptTypeFileMode - possible value enum + *******************************************************************************/ +public enum ScriptTypeFileMode implements PossibleValueEnum +{ + SINGLE(1, "Single File"), + MULTI_PRE_DEFINED(2, "Multi File (Pre-defined)"), + MULTI_AD_HOC(3, "Multi File (ad hoc)"); + + private final Integer id; + private final String label; + + public static final String NAME = "scriptTypeFileMode"; + + + + /******************************************************************************* + ** + *******************************************************************************/ + ScriptTypeFileMode(Integer id, String label) + { + this.id = id; + this.label = label; + } + + + + /******************************************************************************* + ** Getter for id + ** + *******************************************************************************/ + public Integer getId() + { + return id; + } + + + + /******************************************************************************* + ** Getter for label + ** + *******************************************************************************/ + public String getLabel() + { + return label; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public Integer getPossibleValueId() + { + return (getId()); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public String getPossibleValueLabel() + { + return (getLabel()); + } +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptTypeFileSchema.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptTypeFileSchema.java new file mode 100644 index 00000000..2b0279b2 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptTypeFileSchema.java @@ -0,0 +1,265 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. 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 . + */ + +package com.kingsrook.qqq.backend.core.model.scripts; + + +import java.time.Instant; +import com.kingsrook.qqq.backend.core.exceptions.QException; +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; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class ScriptTypeFileSchema extends QRecordEntity +{ + public static final String TABLE_NAME = "scriptTypeFileSchema"; + + @QField(isEditable = false) + private Integer id; + + @QField(isEditable = false) + private Instant createDate; + + @QField(isEditable = false) + private Instant modifyDate; + + @QField(possibleValueSourceName = "scriptType") + private Integer scriptTypeId; + + @QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.ERROR) + private String name; + + @QField(maxLength = 50, valueTooLongBehavior = ValueTooLongBehavior.ERROR) + private String fileType; + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public ScriptTypeFileSchema() + { + } + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public ScriptTypeFileSchema(QRecord qRecord) throws QException + { + populateFromQRecord(qRecord); + } + + + + /******************************************************************************* + ** 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 ScriptTypeFileSchema 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 ScriptTypeFileSchema 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 ScriptTypeFileSchema withModifyDate(Instant modifyDate) + { + this.modifyDate = modifyDate; + return (this); + } + + + + /******************************************************************************* + ** Getter for scriptTypeId + *******************************************************************************/ + public Integer getScriptTypeId() + { + return (this.scriptTypeId); + } + + + + /******************************************************************************* + ** Setter for scriptTypeId + *******************************************************************************/ + public void setScriptTypeId(Integer scriptTypeId) + { + this.scriptTypeId = scriptTypeId; + } + + + + /******************************************************************************* + ** Fluent setter for scriptTypeId + *******************************************************************************/ + public ScriptTypeFileSchema withScriptTypeId(Integer scriptTypeId) + { + this.scriptTypeId = scriptTypeId; + return (this); + } + + + + /******************************************************************************* + ** Getter for name + *******************************************************************************/ + public String getName() + { + return (this.name); + } + + + + /******************************************************************************* + ** Setter for name + *******************************************************************************/ + public void setName(String name) + { + this.name = name; + } + + + + /******************************************************************************* + ** Fluent setter for name + *******************************************************************************/ + public ScriptTypeFileSchema withName(String name) + { + this.name = name; + return (this); + } + + + + /******************************************************************************* + ** Getter for fileType + *******************************************************************************/ + public String getFileType() + { + return (this.fileType); + } + + + + /******************************************************************************* + ** Setter for fileType + *******************************************************************************/ + public void setFileType(String fileType) + { + this.fileType = fileType; + } + + + + /******************************************************************************* + ** Fluent setter for fileType + *******************************************************************************/ + public ScriptTypeFileSchema withFileType(String fileType) + { + this.fileType = fileType; + return (this); + } + +} \ No newline at end of file diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptsMetaDataProvider.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptsMetaDataProvider.java index fd5ed077..bd0c6f12 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptsMetaDataProvider.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/scripts/ScriptsMetaDataProvider.java @@ -46,7 +46,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn; import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType; import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon; +import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PVSValueFormatAndFields; import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource; +import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType; import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType; import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData; @@ -76,6 +78,8 @@ public class ScriptsMetaDataProvider public static final String SCRIPT_TYPE_NAME_RECORD = "Record Script"; + public static final String CURRENT_SCRIPT_REVISION_JOIN_NAME = "currentScriptRevision"; + /******************************************************************************* @@ -173,6 +177,14 @@ public class ScriptsMetaDataProvider .withLabel("Recent Logs") .getWidgetMetaData()); + instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(QJoinMetaData.makeInferredJoinName(ScriptType.TABLE_NAME, ScriptTypeFileSchema.TABLE_NAME))) + .withLabel("File Schema") + .getWidgetMetaData()); + + instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(QJoinMetaData.makeInferredJoinName(ScriptRevision.TABLE_NAME, ScriptRevisionFile.TABLE_NAME))).withMaxRows(50) + .withLabel("Files") + .getWidgetMetaData()); + instance.addWidget(new QWidgetMetaData() .withName("scriptViewer") .withLabel("Contents") @@ -209,7 +221,7 @@ public class ScriptsMetaDataProvider .withLeftTable(Script.TABLE_NAME) .withRightTable(ScriptRevision.TABLE_NAME) .withJoinOn(new JoinOn("currentScriptRevisionId", "id")) - .withName("currentScriptRevision")); + .withName(CURRENT_SCRIPT_REVISION_JOIN_NAME)); instance.addJoin(new QJoinMetaData() .withType(JoinType.ONE_TO_MANY) @@ -227,6 +239,22 @@ public class ScriptsMetaDataProvider .withOrderBy(new QFilterOrderBy("id")) .withInferredName()); + instance.addJoin(new QJoinMetaData() + .withType(JoinType.ONE_TO_MANY) + .withLeftTable(ScriptType.TABLE_NAME) + .withRightTable(ScriptTypeFileSchema.TABLE_NAME) + .withJoinOn(new JoinOn("id", "scriptTypeId")) + .withOrderBy(new QFilterOrderBy("id")) + .withInferredName()); + + instance.addJoin(new QJoinMetaData() + .withType(JoinType.ONE_TO_MANY) + .withLeftTable(ScriptRevision.TABLE_NAME) + .withRightTable(ScriptRevisionFile.TABLE_NAME) + .withJoinOn(new JoinOn("id", "scriptRevisionId")) + .withOrderBy(new QFilterOrderBy("id")) + .withInferredName()); + } @@ -251,23 +279,25 @@ public class ScriptsMetaDataProvider { instance.addPossibleValueSource(new QPossibleValueSource() .withName(Script.TABLE_NAME) - .withTableName(Script.TABLE_NAME) - ); + .withTableName(Script.TABLE_NAME)); instance.addPossibleValueSource(new QPossibleValueSource() .withName(ScriptRevision.TABLE_NAME) - .withTableName(ScriptRevision.TABLE_NAME) - ); + .withTableName(ScriptRevision.TABLE_NAME)); instance.addPossibleValueSource(new QPossibleValueSource() .withName(ScriptType.TABLE_NAME) - .withTableName(ScriptType.TABLE_NAME) - ); + .withTableName(ScriptType.TABLE_NAME)); instance.addPossibleValueSource(new QPossibleValueSource() .withName(ScriptLog.TABLE_NAME) - .withTableName(ScriptLog.TABLE_NAME) - ); + .withTableName(ScriptLog.TABLE_NAME)); + + instance.addPossibleValueSource(new QPossibleValueSource() + .withName(ScriptTypeFileMode.NAME) + .withType(QPossibleValueSourceType.ENUM) + .withValuesFromEnum(ScriptTypeFileMode.values()) + .withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY)); } @@ -279,8 +309,10 @@ public class ScriptsMetaDataProvider { List rs = new ArrayList<>(); rs.add(enrich(backendDetailEnricher, defineScriptTypeTable(backendName))); + rs.add(enrich(backendDetailEnricher, defineScriptTypeFileSchemaTable(backendName))); rs.add(enrich(backendDetailEnricher, defineScriptTable(backendName))); rs.add(enrich(backendDetailEnricher, defineScriptRevisionTable(backendName))); + rs.add(enrich(backendDetailEnricher, defineScriptRevisionFileTable(backendName))); rs.add(enrich(backendDetailEnricher, defineScriptLogTable(backendName))); rs.add(enrich(backendDetailEnricher, defineScriptLogLineTable(backendName))); rs.add(enrich(backendDetailEnricher, defineTableTriggerTable(backendName))); @@ -372,7 +404,8 @@ public class ScriptsMetaDataProvider { QTableMetaData tableMetaData = defineStandardTable(backendName, ScriptType.TABLE_NAME, ScriptType.class) .withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "name"))) - .withSection(new QFieldSection("details", new QIcon().withName("dataset"), Tier.T2, List.of("helpText", "sampleCode"))) + .withSection(new QFieldSection("details", new QIcon().withName("dataset"), Tier.T2, List.of("helpText", "sampleCode", "fileMode"))) + .withSection(new QFieldSection("files", new QIcon().withName("description"), Tier.T2).withWidgetName(QJoinMetaData.makeInferredJoinName(ScriptType.TABLE_NAME, ScriptTypeFileSchema.TABLE_NAME))) .withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate"))); tableMetaData.getField("sampleCode").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("javascript"))); tableMetaData.getField("helpText").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("text"))); @@ -381,6 +414,22 @@ public class ScriptsMetaDataProvider + /******************************************************************************* + ** + *******************************************************************************/ + private QTableMetaData defineScriptTypeFileSchemaTable(String backendName) throws QException + { + QTableMetaData tableMetaData = defineStandardTable(backendName, ScriptTypeFileSchema.TABLE_NAME, ScriptTypeFileSchema.class) + .withRecordLabelFormat("%s - %s") + .withRecordLabelFields(List.of("scriptTypeId", "name")) + .withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "name", "scriptTypeId"))) + .withSection(new QFieldSection("details", new QIcon().withName("dataset"), Tier.T2, List.of("fileType"))) + .withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate"))); + return (tableMetaData); + } + + + /******************************************************************************* ** *******************************************************************************/ @@ -392,6 +441,7 @@ public class ScriptsMetaDataProvider .withRecordLabelFields(List.of("scriptId", "sequenceNo")) .withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "scriptId", "sequenceNo"))) .withSection(new QFieldSection("code", new QIcon().withName("data_object"), Tier.T2, List.of("contents"))) + .withSection(new QFieldSection("files", new QIcon().withName("description"), Tier.T2).withWidgetName(QJoinMetaData.makeInferredJoinName(ScriptRevision.TABLE_NAME, ScriptRevisionFile.TABLE_NAME))) .withSection(new QFieldSection("changeManagement", new QIcon().withName("history"), Tier.T2, List.of("commitMessage", "author"))) .withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate"))); @@ -420,6 +470,27 @@ public class ScriptsMetaDataProvider + /******************************************************************************* + ** + *******************************************************************************/ + private QTableMetaData defineScriptRevisionFileTable(String backendName) throws QException + { + QTableMetaData tableMetaData = defineStandardTable(backendName, ScriptRevisionFile.TABLE_NAME, ScriptRevisionFile.class) + .withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE) + .withRecordLabelFormat("%s - %s") + .withRecordLabelFields(List.of("scriptRevisionId", "fileName")) + .withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "scriptRevisionId", "fileName"))) + .withSection(new QFieldSection("code", new QIcon().withName("data_object"), Tier.T2, List.of("contents"))) + .withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate"))); + + tableMetaData.getField("contents").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("velocity"))); // todo - dynamic?! + tableMetaData.getField("scriptRevisionId").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment()); + + return (tableMetaData); + } + + + /******************************************************************************* ** *******************************************************************************/ diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/StoreScriptRevisionProcessStep.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/StoreScriptRevisionProcessStep.java index 2efb278e..7e14b2e2 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/StoreScriptRevisionProcessStep.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/StoreScriptRevisionProcessStep.java @@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.processes.implementations.scripts; import java.util.List; import com.kingsrook.qqq.backend.core.actions.ActionHelper; +import com.kingsrook.qqq.backend.core.actions.QBackendTransaction; import com.kingsrook.qqq.backend.core.actions.processes.BackendStep; import com.kingsrook.qqq.backend.core.actions.tables.GetAction; import com.kingsrook.qqq.backend.core.actions.tables.InsertAction; @@ -44,6 +45,8 @@ 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.actions.tables.update.UpdateInput; import com.kingsrook.qqq.backend.core.model.data.QRecord; +import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevisionFile; +import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.StringUtils; @@ -63,87 +66,132 @@ public class StoreScriptRevisionProcessStep implements BackendStep @Override public void run(RunBackendStepInput input, RunBackendStepOutput output) throws QException { - ActionHelper.validateSession(input); - - ////////////////////////////////////////////////////////////////// - // check if there's currently a script referenced by the record // - ////////////////////////////////////////////////////////////////// - Integer scriptId = input.getValueInteger("scriptId"); - Integer nextSequenceNo = 1; - - //////////////////////////////////////// - // get the existing script, to update // - //////////////////////////////////////// - GetInput getInput = new GetInput(); - getInput.setTableName("script"); - getInput.setPrimaryKey(scriptId); - GetOutput getOutput = new GetAction().execute(getInput); - QRecord script = getOutput.getRecord(); - - QueryInput queryInput = new QueryInput(); - queryInput.setTableName("scriptRevision"); - queryInput.setFilter(new QQueryFilter() - .withCriteria(new QFilterCriteria("scriptId", QCriteriaOperator.EQUALS, List.of(script.getValue("id")))) - .withOrderBy(new QFilterOrderBy("sequenceNo", false)) - .withLimit(1)); - QueryOutput queryOutput = new QueryAction().execute(queryInput); - if(!queryOutput.getRecords().isEmpty()) - { - nextSequenceNo = queryOutput.getRecords().get(0).getValueInteger("sequenceNo") + 1; - } - - ////////////////////////////////// - // insert a new script revision // - ////////////////////////////////// - String commitMessage = input.getValueString("commitMessage"); - if(!StringUtils.hasContent(commitMessage)) - { - if(nextSequenceNo == 1) - { - commitMessage = "Initial version"; - } - else - { - commitMessage = "No commit message given"; - } - } - - QRecord scriptRevision = new QRecord() - .withValue("scriptId", script.getValue("id")) - .withValue("contents", input.getValueString("contents")) - .withValue("apiName", input.getValueString("apiName")) - .withValue("apiVersion", input.getValueString("apiVersion")) - .withValue("commitMessage", commitMessage) - .withValue("sequenceNo", nextSequenceNo); + InsertAction insertAction = new InsertAction(); + InsertInput insertInput = new InsertInput(); + insertInput.setTableName("scriptRevision"); + QBackendTransaction transaction = insertAction.openTransaction(insertInput); + insertInput.setTransaction(transaction); try { - scriptRevision.setValue("author", input.getSession().getUser().getFullName()); + ActionHelper.validateSession(input); + + ////////////////////////////////////////////////////////////////// + // check if there's currently a script referenced by the record // + ////////////////////////////////////////////////////////////////// + Integer scriptId = input.getValueInteger("scriptId"); + Integer nextSequenceNo = 1; + + //////////////////////////////////////// + // get the existing script, to update // + //////////////////////////////////////// + GetInput getInput = new GetInput(); + getInput.setTableName("script"); + getInput.setPrimaryKey(scriptId); + getInput.setTransaction(transaction); + GetOutput getOutput = new GetAction().execute(getInput); + QRecord script = getOutput.getRecord(); + + QueryInput queryInput = new QueryInput(); + queryInput.setTableName("scriptRevision"); + queryInput.setFilter(new QQueryFilter() + .withCriteria(new QFilterCriteria("scriptId", QCriteriaOperator.EQUALS, List.of(script.getValue("id")))) + .withOrderBy(new QFilterOrderBy("sequenceNo", false)) + .withLimit(1)); + queryInput.setTransaction(transaction); + QueryOutput queryOutput = new QueryAction().execute(queryInput); + if(!queryOutput.getRecords().isEmpty()) + { + nextSequenceNo = queryOutput.getRecords().get(0).getValueInteger("sequenceNo") + 1; + } + + ////////////////////////////////// + // insert a new script revision // + ////////////////////////////////// + String commitMessage = input.getValueString("commitMessage"); + if(!StringUtils.hasContent(commitMessage)) + { + if(nextSequenceNo == 1) + { + commitMessage = "Initial version"; + } + else + { + commitMessage = "No commit message given"; + } + } + + QRecord scriptRevision = new QRecord() + .withValue("scriptId", script.getValue("id")) + .withValue("apiName", input.getValueString("apiName")) + .withValue("apiVersion", input.getValueString("apiVersion")) + .withValue("commitMessage", commitMessage) + .withValue("sequenceNo", nextSequenceNo); + + if(input.getValue("contents") != null) + { + scriptRevision.withValue("contents", input.getValueString("contents")); + } + + try + { + scriptRevision.setValue("author", input.getSession().getUser().getFullName()); + } + catch(Exception e) + { + scriptRevision.setValue("author", "Unknown"); + } + + insertInput.setRecords(List.of(scriptRevision)); + InsertOutput insertOutput = insertAction.execute(insertInput); + scriptRevision = insertOutput.getRecords().get(0); + Integer scriptRevisionId = scriptRevision.getValueInteger("id"); + + ////////////////////////////////////////////////////////////////////////////////////////// + // if there's a list of file contents (instead of just a single string), store them all // + ////////////////////////////////////////////////////////////////////////////////////////// + @SuppressWarnings("unchecked") + List fileContents = (List) input.getValue("fileContents"); + if(CollectionUtils.nullSafeHasContents(fileContents)) + { + List scriptRevisionRecords = fileContents.stream().map(r -> new ScriptRevisionFile() + .withScriptRevisionId(scriptRevisionId) + .withFileName(r.getValueString("fileName")) + .withContents(r.getValueString("contents")) + .toQRecord()).toList(); + + InsertInput scriptRevisionFileInsertInput = new InsertInput(); + scriptRevisionFileInsertInput.setTableName(ScriptRevisionFile.TABLE_NAME); + scriptRevisionFileInsertInput.setRecords(scriptRevisionRecords); + scriptRevisionFileInsertInput.setTransaction(transaction); + new InsertAction().execute(scriptRevisionFileInsertInput); + } + + //////////////////////////////////////////////////// + // update the script to point at the new revision // + //////////////////////////////////////////////////// + script.setValue("currentScriptRevisionId", scriptRevision.getValue("id")); + UpdateInput updateInput = new UpdateInput(); + updateInput.setTableName("script"); + updateInput.setRecords(List.of(script)); + updateInput.setTransaction(transaction); + new UpdateAction().execute(updateInput); + + transaction.commit(); + + output.addValue("scriptId", script.getValueInteger("id")); + output.addValue("scriptName", script.getValueString("name")); + output.addValue("scriptRevisionId", scriptRevisionId); + output.addValue("scriptRevisionSequenceNo", scriptRevision.getValueInteger("sequenceNo")); } catch(Exception e) { - scriptRevision.setValue("author", "Unknown"); + transaction.rollback(); + } + finally + { + transaction.close(); } - - InsertInput insertInput = new InsertInput(); - insertInput.setTableName("scriptRevision"); - insertInput.setRecords(List.of(scriptRevision)); - InsertOutput insertOutput = new InsertAction().execute(insertInput); - scriptRevision = insertOutput.getRecords().get(0); - - //////////////////////////////////////////////////// - // update the script to point at the new revision // - //////////////////////////////////////////////////// - script.setValue("currentScriptRevisionId", scriptRevision.getValue("id")); - UpdateInput updateInput = new UpdateInput(); - updateInput.setTableName("script"); - updateInput.setRecords(List.of(script)); - new UpdateAction().execute(updateInput); - - output.addValue("scriptId", script.getValueInteger("id")); - output.addValue("scriptName", script.getValueString("name")); - output.addValue("scriptRevisionId", scriptRevision.getValueInteger("id")); - output.addValue("scriptRevisionSequenceNo", scriptRevision.getValueInteger("sequenceNo")); } } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/CollectionUtils.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/CollectionUtils.java index 08526658..80b2e3f8 100755 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/CollectionUtils.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/CollectionUtils.java @@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.utils; import java.io.Serializable; +import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -34,6 +35,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.google.gson.reflect.TypeToken; +import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException; import com.kingsrook.qqq.backend.core.model.data.QRecord; @@ -550,4 +553,68 @@ public class CollectionUtils return (rs); } + + + /******************************************************************************* + ** For cases where you have a Collection (of an unknown type), and you know you + ** want/need it in a specific concrete type (say, ArrayList), but you don't want + ** to just blindly copy it (e.g., as that may be expensive), call this method. + ** + ** ArrayList[String] myStrings = CollectionUtils.useOrWrap(yourStrings, new TypeToken<>() {}); + ** CollectionUtils.useOrWrap(yourStrings, TypeToken.get(ArrayList.class)); + ** + ** Note that you may always just pass `new TypeToken() {}` as the 2nd arg - then + ** the compiler will infer the type (T) based on the variable you're assigning into. + *******************************************************************************/ + public static > T useOrWrap(Collection collection, TypeToken typeToken) + { + try + { + Class targetClass = (Class) typeToken.getRawType(); + if(targetClass.isInstance(collection)) + { + return (targetClass.cast(collection)); + } + + Constructor constructor = targetClass.getConstructor(Collection.class); + return (constructor.newInstance(collection)); + } + catch(Exception e) + { + throw (new QRuntimeException("Error wrapping collection", e)); + } + } + + + + /******************************************************************************* + ** For cases where you have a Collection (of an unknown type), and you know you + ** want/need it in a specific concrete type (say, ArrayList), but you don't want + ** to just blindly copy it (e.g., as that may be expensive), call this method. + ** + ** HashMap[String,Integer] myMap = CollectionUtils.useOrWrap(yourMap, new TypeToken<>() {}); + ** CollectionUtils.useOrWrap(yourMap, TypeToken.get(HashMap.class)); + ** + ** Note that you may always just pass `new TypeToken() {}` as the 2nd arg - then + ** the compiler will infer the type (T) based on the variable you're assigning into. + *******************************************************************************/ + public static > T useOrWrap(Map collection, TypeToken typeToken) + { + try + { + Class targetClass = (Class) typeToken.getRawType(); + if(targetClass.isInstance(collection)) + { + return (targetClass.cast(collection)); + } + + Constructor constructor = targetClass.getConstructor(Map.class); + return (constructor.newInstance(collection)); + } + catch(Exception e) + { + throw (new QRuntimeException("Error wrapping collection", e)); + } + } + } diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/templates/ConvertHtmlToPdfActionTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/templates/ConvertHtmlToPdfActionTest.java index 62393cf8..83dd98ef 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/templates/ConvertHtmlToPdfActionTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/templates/ConvertHtmlToPdfActionTest.java @@ -30,10 +30,10 @@ import java.util.Map; import com.kingsrook.qqq.backend.core.BaseTest; import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.actions.templates.ConvertHtmlToPdfInput; +import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateInput; +import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateOutput; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; -import com.kingsrook.qqq.backend.core.model.templates.ConvertHtmlToPdfInput; -import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateInput; -import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateOutput; import com.kingsrook.qqq.backend.core.model.templates.TemplateType; import org.junit.jupiter.api.Test; diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/templates/RenderTemplateActionTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/templates/RenderTemplateActionTest.java index 926b80f5..1f448b69 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/templates/RenderTemplateActionTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/templates/RenderTemplateActionTest.java @@ -25,8 +25,8 @@ package com.kingsrook.qqq.backend.core.actions.templates; import java.util.Map; import com.kingsrook.qqq.backend.core.BaseTest; import com.kingsrook.qqq.backend.core.exceptions.QException; -import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateInput; -import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateOutput; +import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateInput; +import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateOutput; import com.kingsrook.qqq.backend.core.model.templates.TemplateType; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/data/QRecordEntityTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/data/QRecordEntityTest.java index add452ab..5d736e55 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/data/QRecordEntityTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/data/QRecordEntityTest.java @@ -119,6 +119,29 @@ class QRecordEntityTest extends BaseTest + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testQRecordFromJoinToItem() throws QException + { + QRecord qRecord = new QRecord() + .withValue("item.sku", "WXYZ-9876") + .withValue("item.description", "Items are cool") + .withValue("item.quantity", 42) + .withValue("item.price", new BigDecimal("3.50")) + .withValue("item.featured", false); + + Item item = QRecordEntity.fromQRecord(Item.class, qRecord, "item."); + assertEquals("WXYZ-9876", item.getSku()); + assertEquals("Items are cool", item.getDescription()); + assertEquals(42, item.getQuantity()); + assertEquals(new BigDecimal("3.50"), item.getPrice()); + assertFalse(item.getFeatured()); + } + + + /******************************************************************************* ** *******************************************************************************/ diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/StoreScriptRevisionProcessStepTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/StoreScriptRevisionProcessStepTest.java index aee18116..718ff0f2 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/StoreScriptRevisionProcessStepTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/StoreScriptRevisionProcessStepTest.java @@ -22,6 +22,7 @@ package com.kingsrook.qqq.backend.core.processes.implementations.scripts; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import com.kingsrook.qqq.backend.core.BaseTest; @@ -33,10 +34,12 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.scripts.Script; import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevision; +import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevisionFile; import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider; import com.kingsrook.qqq.backend.core.utils.TestUtils; import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -51,7 +54,7 @@ class StoreScriptRevisionProcessStepTest extends BaseTest ** *******************************************************************************/ @Test - void test() throws QException + void testSingleFileScriptType() throws QException { QInstance qInstance = QContext.getQInstance(); new ScriptsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null); @@ -59,7 +62,7 @@ class StoreScriptRevisionProcessStepTest extends BaseTest Integer scriptId = 1701; String scriptContents = "logger.log('Hi');"; - TestUtils.insertRecords(qInstance, qInstance.getTable(Script.TABLE_NAME), List.of(new QRecord().withValue("id", 1701))); + TestUtils.insertRecords(qInstance, qInstance.getTable(Script.TABLE_NAME), List.of(new QRecord().withValue("id", scriptId))); List scripts = TestUtils.queryTable(Script.TABLE_NAME); assertNull(scripts.get(0).getValueInteger("currentScriptRevisionId")); @@ -73,7 +76,7 @@ class StoreScriptRevisionProcessStepTest extends BaseTest List scriptRevisions = TestUtils.queryTable(ScriptRevision.TABLE_NAME); QRecord scriptRevision = scriptRevisions.get(0); - assertEquals(1701, scriptRevision.getValueInteger("scriptId")); + assertEquals(scriptId, scriptRevision.getValueInteger("scriptId")); assertEquals(1, scriptRevision.getValueInteger("sequenceNo")); assertEquals("Initial version", scriptRevision.getValueString("commitMessage")); assertEquals(scriptContents, scriptRevision.getValueString("contents")); @@ -88,10 +91,94 @@ class StoreScriptRevisionProcessStepTest extends BaseTest scriptRevisions = TestUtils.queryTable(ScriptRevision.TABLE_NAME).stream().filter(r -> r.getValueInteger("id").equals(2)).collect(Collectors.toList()); scriptRevision = scriptRevisions.get(0); - assertEquals(1701, scriptRevision.getValueInteger("scriptId")); + assertEquals(scriptId, scriptRevision.getValueInteger("scriptId")); assertEquals(2, scriptRevision.getValueInteger("sequenceNo")); assertEquals("No commit message given", scriptRevision.getValueString("commitMessage")); assertEquals(scriptContents, scriptRevision.getValueString("contents")); } + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testMultiFileScriptType() throws QException + { + QInstance qInstance = QContext.getQInstance(); + new ScriptsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null); + + Integer scriptId = 1701; + String scriptContents = "logger.log('Hi');"; + String templateContents = "

Hey

"; + + TestUtils.insertRecords(qInstance, qInstance.getTable(Script.TABLE_NAME), List.of(new QRecord().withValue("id", scriptId))); + List scripts = TestUtils.queryTable(Script.TABLE_NAME); + assertNull(scripts.get(0).getValueInteger("currentScriptRevisionId")); + + ArrayList fileContents = new ArrayList<>(); + fileContents.add(new QRecord().withValue("fileName", "script").withValue("contents", scriptContents)); + fileContents.add(new QRecord().withValue("fileName", "template").withValue("contents", templateContents)); + + RunBackendStepInput runBackendStepInput = new RunBackendStepInput(); + runBackendStepInput.addValue("scriptId", scriptId); + runBackendStepInput.addValue("fileContents", fileContents); + new StoreScriptRevisionProcessStep().run(runBackendStepInput, new RunBackendStepOutput()); + + scripts = TestUtils.queryTable(Script.TABLE_NAME); + assertEquals(1, scripts.get(0).getValueInteger("currentScriptRevisionId")); + + List scriptRevisions = TestUtils.queryTable(ScriptRevision.TABLE_NAME); + QRecord scriptRevision = scriptRevisions.get(0); + assertEquals(scriptId, scriptRevision.getValueInteger("scriptId")); + assertEquals(1, scriptRevision.getValueInteger("sequenceNo")); + assertEquals("Initial version", scriptRevision.getValueString("commitMessage")); + assertNull(scriptRevision.getValueString("contents")); + + List scriptRevisionFiles = TestUtils.queryTable(ScriptRevisionFile.TABLE_NAME); + assertThat(scriptRevisionFiles.stream().filter(srf -> srf.getValueString("fileName").equals("script")).findFirst()) + .isPresent().get() + .matches(r -> r.getValueString("contents").equals(scriptContents)); + + assertThat(scriptRevisionFiles.stream().filter(srf -> srf.getValueString("fileName").equals("template")).findFirst()) + .isPresent().get() + .matches(r -> r.getValueString("contents").equals(templateContents)); + + //////////////////////////// + // now add a new revision // + //////////////////////////// + String updatedScriptContents = "logger.log('Really, Hi');"; + String updatedTemplateContents = "

Hey, what's up

"; + + fileContents = new ArrayList<>(); + fileContents.add(new QRecord().withValue("fileName", "script").withValue("contents", updatedScriptContents)); + fileContents.add(new QRecord().withValue("fileName", "template").withValue("contents", updatedTemplateContents)); + + runBackendStepInput = new RunBackendStepInput(); + runBackendStepInput.addValue("scriptId", scriptId); + runBackendStepInput.addValue("fileContents", fileContents); + runBackendStepInput.addValue("commitMessage", "Updated files"); + new StoreScriptRevisionProcessStep().run(runBackendStepInput, new RunBackendStepOutput()); + + scripts = TestUtils.queryTable(Script.TABLE_NAME); + assertEquals(2, scripts.get(0).getValueInteger("currentScriptRevisionId")); + + scriptRevisions = TestUtils.queryTable(ScriptRevision.TABLE_NAME).stream().filter(r -> r.getValueInteger("id").equals(2)).collect(Collectors.toList()); + scriptRevision = scriptRevisions.get(0); + assertEquals(scriptId, scriptRevision.getValueInteger("scriptId")); + assertEquals(2, scriptRevision.getValueInteger("id")); + assertEquals(2, scriptRevision.getValueInteger("sequenceNo")); + assertEquals("Updated files", scriptRevision.getValueString("commitMessage")); + assertNull(scriptRevision.getValueString("contents")); + + scriptRevisionFiles = TestUtils.queryTable(ScriptRevisionFile.TABLE_NAME); + assertThat(scriptRevisionFiles.stream().filter(srf -> srf.getValueString("fileName").equals("script") && srf.getValueInteger("scriptRevisionId").equals(2)).findFirst()) + .isPresent().get() + .matches(r -> r.getValueString("contents").equals(updatedScriptContents)); + + assertThat(scriptRevisionFiles.stream().filter(srf -> srf.getValueString("fileName").equals("template") && srf.getValueInteger("scriptRevisionId").equals(2)).findFirst()) + .isPresent().get() + .matches(r -> r.getValueString("contents").equals(updatedTemplateContents)); + } + } \ No newline at end of file diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/CollectionUtilsTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/CollectionUtilsTest.java index 860a4ba6..0f4a5dfa 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/CollectionUtilsTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/CollectionUtilsTest.java @@ -25,15 +25,21 @@ package com.kingsrook.qqq.backend.core.utils; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.Hashtable; +import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.TreeMap; import java.util.function.Function; +import com.google.gson.reflect.TypeToken; import com.kingsrook.qqq.backend.core.BaseTest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -544,4 +550,44 @@ class CollectionUtilsTest extends BaseTest assertEquals(List.of(1, 2, 3), CollectionUtils.mergeLists(null, List.of(1, 2, 3), null)); } + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testUseOrWrap() + { + { + List originalList = new ArrayList<>(List.of("A", "B", "C")); + ArrayList reallyArrayList = CollectionUtils.useOrWrap(originalList, new TypeToken<>() {}); + assertSame(originalList, reallyArrayList); + } + + { + List originalList = new LinkedList<>(List.of("A", "B", "C")); + ArrayList reallyArrayList = CollectionUtils.useOrWrap(originalList, new TypeToken<>() {}); + assertNotSame(originalList, reallyArrayList); + assertEquals(ArrayList.class, reallyArrayList.getClass()); + } + + assertEquals(ArrayList.class, CollectionUtils.useOrWrap(new LinkedList<>(), TypeToken.get(ArrayList.class)).getClass()); + + { + Map originalMap = new HashMap<>(Map.of("A", 1, "B", 2)); + HashMap reallyHashMap = CollectionUtils.useOrWrap(originalMap, new TypeToken<>() {}); + assertSame(originalMap, reallyHashMap); + } + + { + Map originalMap = new TreeMap<>(Map.of("A", 1, "B", 2)); + HashMap reallyHashMap = CollectionUtils.useOrWrap(originalMap, new TypeToken<>() {}); + assertNotSame(originalMap, reallyHashMap); + assertEquals(HashMap.class, reallyHashMap.getClass()); + } + + assertEquals(TreeMap.class, CollectionUtils.useOrWrap(new Hashtable<>(), TypeToken.get(TreeMap.class)).getClass()); + + } + }