Adding scriptType fileModes, with multi-files under a script Revision.

This commit is contained in:
2023-06-23 16:38:00 -05:00
parent b75fd29a57
commit 9d1266036c
22 changed files with 1155 additions and 115 deletions

View File

@ -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.metadata.code.AdHocScriptCodeReference;
import com.kingsrook.qqq.backend.core.model.scripts.Script; 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.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.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.ValueUtils; import com.kingsrook.qqq.backend.core.utils.ValueUtils;
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
@ -74,7 +75,7 @@ public class RunRecordScriptAutomationHandler extends RecordAutomationHandler
QueryInput queryInput = new QueryInput(); QueryInput queryInput = new QueryInput();
queryInput.setTableName(ScriptRevision.TABLE_NAME); queryInput.setTableName(ScriptRevision.TABLE_NAME);
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("scriptId", QCriteriaOperator.EQUALS, scriptId))); 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); QueryOutput queryOutput = new QueryAction().execute(queryInput);
if(CollectionUtils.nullSafeIsEmpty(queryOutput.getRecords())) if(CollectionUtils.nullSafeIsEmpty(queryOutput.getRecords()))
{ {

View File

@ -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.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.scripts.Script; 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.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.CollectionUtils;
@ -197,7 +198,7 @@ public class RunAdHocRecordScriptAction
QueryInput queryInput = new QueryInput(); QueryInput queryInput = new QueryInput();
queryInput.setTableName(ScriptRevision.TABLE_NAME); queryInput.setTableName(ScriptRevision.TABLE_NAME);
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("script.id", QCriteriaOperator.EQUALS, codeReference.getScriptId()))); 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); QueryOutput queryOutput = new QueryAction().execute(queryInput);
if(CollectionUtils.nullSafeHasContents(queryOutput.getRecords())) if(CollectionUtils.nullSafeHasContents(queryOutput.getRecords()))

View File

@ -26,8 +26,8 @@ import java.nio.file.Path;
import java.util.Map; import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.AbstractQActionFunction; import com.kingsrook.qqq.backend.core.actions.AbstractQActionFunction;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.templates.ConvertHtmlToPdfInput; import com.kingsrook.qqq.backend.core.model.actions.templates.ConvertHtmlToPdfInput;
import com.kingsrook.qqq.backend.core.model.templates.ConvertHtmlToPdfOutput; import com.kingsrook.qqq.backend.core.model.actions.templates.ConvertHtmlToPdfOutput;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;

View File

@ -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.exceptions.QException;
import com.kingsrook.qqq.backend.core.logging.QLogger; import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateInput; import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateInput;
import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateOutput; 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.model.templates.TemplateType;
import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.core.utils.StringUtils;
import org.apache.velocity.VelocityContext; import org.apache.velocity.VelocityContext;
@ -62,7 +62,12 @@ public class RenderTemplateAction extends AbstractQActionFunction<RenderTemplate
if(TemplateType.VELOCITY.equals(input.getTemplateType())) if(TemplateType.VELOCITY.equals(input.getTemplateType()))
{ {
Velocity.init(); Velocity.init();
Context context = new VelocityContext(input.getContext()); Context context = new VelocityContext();
for(Map.Entry<String, ?> entry : input.getContext().entrySet())
{
context.put(entry.getKey(), entry.getValue());
}
setupEventHandlers(context); setupEventHandlers(context);

View File

@ -1,6 +1,6 @@
/* /*
* QQQ - Low-code Application Framework for Engineers. * 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 * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com * contact@kingsrook.com
* https://github.com/Kingsrook/ * https://github.com/Kingsrook/
@ -19,7 +19,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.kingsrook.qqq.backend.core.model.templates; package com.kingsrook.qqq.backend.core.model.actions.templates;
import java.io.OutputStream; import java.io.OutputStream;

View File

@ -1,6 +1,6 @@
/* /*
* QQQ - Low-code Application Framework for Engineers. * 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 * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com * contact@kingsrook.com
* https://github.com/Kingsrook/ * https://github.com/Kingsrook/
@ -19,7 +19,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
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; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;

View File

@ -1,6 +1,6 @@
/* /*
* QQQ - Low-code Application Framework for Engineers. * 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 * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com * contact@kingsrook.com
* https://github.com/Kingsrook/ * https://github.com/Kingsrook/
@ -19,11 +19,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.kingsrook.qqq.backend.core.model.templates; package com.kingsrook.qqq.backend.core.model.actions.templates;
import java.util.Map; import java.util.Map;
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
import com.kingsrook.qqq.backend.core.model.templates.TemplateType;
/******************************************************************************* /*******************************************************************************
@ -35,7 +36,7 @@ public class RenderTemplateInput extends AbstractActionInput
private String code; // todo - TemplateReference, like CodeReference?? private String code; // todo - TemplateReference, like CodeReference??
private TemplateType templateType; private TemplateType templateType;
private Map<String, Object> context; private Map<String, ? extends Object> context;
@ -120,7 +121,7 @@ public class RenderTemplateInput extends AbstractActionInput
** Getter for context ** Getter for context
** **
*******************************************************************************/ *******************************************************************************/
public Map<String, Object> getContext() public Map<String, ? extends Object> getContext()
{ {
return context; return context;
} }
@ -131,7 +132,7 @@ public class RenderTemplateInput extends AbstractActionInput
** Setter for context ** Setter for context
** **
*******************************************************************************/ *******************************************************************************/
public void setContext(Map<String, Object> context) public void setContext(Map<String, ? extends Object> context)
{ {
this.context = context; this.context = context;
} }

View File

@ -1,6 +1,6 @@
/* /*
* QQQ - Low-code Application Framework for Engineers. * 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 * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com * contact@kingsrook.com
* https://github.com/Kingsrook/ * https://github.com/Kingsrook/
@ -19,7 +19,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
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; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;

View File

@ -61,11 +61,23 @@ public abstract class QRecordEntity
** **
*******************************************************************************/ *******************************************************************************/
public static <T extends QRecordEntity> T fromQRecord(Class<T> c, QRecord qRecord) throws QException public static <T extends QRecordEntity> T fromQRecord(Class<T> 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 extends QRecordEntity> T fromQRecord(Class<T> c, QRecord qRecord, String fieldNamePrefix) throws QException
{ {
try try
{ {
T entity = c.getConstructor().newInstance(); T entity = c.getConstructor().newInstance();
entity.populateFromQRecord(qRecord); entity.populateFromQRecord(qRecord, fieldNamePrefix);
return (entity); return (entity);
} }
catch(Exception e) catch(Exception e)
@ -81,14 +93,32 @@ public abstract class QRecordEntity
** **
*******************************************************************************/ *******************************************************************************/
protected <T extends QRecordEntity> void populateFromQRecord(QRecord qRecord) throws QRuntimeException protected <T extends QRecordEntity> 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 <T extends QRecordEntity> void populateFromQRecord(QRecord qRecord, String fieldNamePrefix) throws QRuntimeException
{ {
try try
{ {
List<QRecordEntityField> fieldList = getFieldList(this.getClass()); List<QRecordEntityField> fieldList = getFieldList(this.getClass());
originalRecordValues = new HashMap<>(); originalRecordValues = new HashMap<>();
if(fieldNamePrefix == null)
{
fieldNamePrefix = "";
}
for(QRecordEntityField qRecordEntityField : fieldList) for(QRecordEntityField qRecordEntityField : fieldList)
{ {
Serializable value = qRecord.getValue(qRecordEntityField.getFieldName()); Serializable value = qRecord.getValue(fieldNamePrefix + qRecordEntityField.getFieldName());
Object typedValue = qRecordEntityField.convertValueType(value); Object typedValue = qRecordEntityField.convertValueType(value);
qRecordEntityField.getSetter().invoke(this, typedValue); qRecordEntityField.getSetter().invoke(this, typedValue);
originalRecordValues.put(qRecordEntityField.getFieldName(), value); originalRecordValues.put(qRecordEntityField.getFieldName(), value);

View File

@ -28,8 +28,8 @@ import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.templates.RenderTemplateAction; import com.kingsrook.qqq.backend.core.actions.templates.RenderTemplateAction;
import com.kingsrook.qqq.backend.core.exceptions.QException; 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.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateInput; import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateInput;
import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateOutput; 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.model.templates.TemplateType;

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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);
}
}

View File

@ -52,6 +52,9 @@ public class ScriptType extends QRecordEntity
@QField() @QField()
private String sampleCode; private String sampleCode;
@QField(possibleValueSourceName = ScriptTypeFileMode.NAME)
private Integer fileMode;
/******************************************************************************* /*******************************************************************************
@ -256,4 +259,35 @@ public class ScriptType extends QRecordEntity
return (this); 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);
}
} }

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<Integer>
{
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());
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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);
}
}

View File

@ -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.JoinType;
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData; import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon; 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.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.QBackendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType; import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData; 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 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") .withLabel("Recent Logs")
.getWidgetMetaData()); .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() instance.addWidget(new QWidgetMetaData()
.withName("scriptViewer") .withName("scriptViewer")
.withLabel("Contents") .withLabel("Contents")
@ -209,7 +221,7 @@ public class ScriptsMetaDataProvider
.withLeftTable(Script.TABLE_NAME) .withLeftTable(Script.TABLE_NAME)
.withRightTable(ScriptRevision.TABLE_NAME) .withRightTable(ScriptRevision.TABLE_NAME)
.withJoinOn(new JoinOn("currentScriptRevisionId", "id")) .withJoinOn(new JoinOn("currentScriptRevisionId", "id"))
.withName("currentScriptRevision")); .withName(CURRENT_SCRIPT_REVISION_JOIN_NAME));
instance.addJoin(new QJoinMetaData() instance.addJoin(new QJoinMetaData()
.withType(JoinType.ONE_TO_MANY) .withType(JoinType.ONE_TO_MANY)
@ -227,6 +239,22 @@ public class ScriptsMetaDataProvider
.withOrderBy(new QFilterOrderBy("id")) .withOrderBy(new QFilterOrderBy("id"))
.withInferredName()); .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() instance.addPossibleValueSource(new QPossibleValueSource()
.withName(Script.TABLE_NAME) .withName(Script.TABLE_NAME)
.withTableName(Script.TABLE_NAME) .withTableName(Script.TABLE_NAME));
);
instance.addPossibleValueSource(new QPossibleValueSource() instance.addPossibleValueSource(new QPossibleValueSource()
.withName(ScriptRevision.TABLE_NAME) .withName(ScriptRevision.TABLE_NAME)
.withTableName(ScriptRevision.TABLE_NAME) .withTableName(ScriptRevision.TABLE_NAME));
);
instance.addPossibleValueSource(new QPossibleValueSource() instance.addPossibleValueSource(new QPossibleValueSource()
.withName(ScriptType.TABLE_NAME) .withName(ScriptType.TABLE_NAME)
.withTableName(ScriptType.TABLE_NAME) .withTableName(ScriptType.TABLE_NAME));
);
instance.addPossibleValueSource(new QPossibleValueSource() instance.addPossibleValueSource(new QPossibleValueSource()
.withName(ScriptLog.TABLE_NAME) .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<QTableMetaData> rs = new ArrayList<>(); List<QTableMetaData> rs = new ArrayList<>();
rs.add(enrich(backendDetailEnricher, defineScriptTypeTable(backendName))); rs.add(enrich(backendDetailEnricher, defineScriptTypeTable(backendName)));
rs.add(enrich(backendDetailEnricher, defineScriptTypeFileSchemaTable(backendName)));
rs.add(enrich(backendDetailEnricher, defineScriptTable(backendName))); rs.add(enrich(backendDetailEnricher, defineScriptTable(backendName)));
rs.add(enrich(backendDetailEnricher, defineScriptRevisionTable(backendName))); rs.add(enrich(backendDetailEnricher, defineScriptRevisionTable(backendName)));
rs.add(enrich(backendDetailEnricher, defineScriptRevisionFileTable(backendName)));
rs.add(enrich(backendDetailEnricher, defineScriptLogTable(backendName))); rs.add(enrich(backendDetailEnricher, defineScriptLogTable(backendName)));
rs.add(enrich(backendDetailEnricher, defineScriptLogLineTable(backendName))); rs.add(enrich(backendDetailEnricher, defineScriptLogLineTable(backendName)));
rs.add(enrich(backendDetailEnricher, defineTableTriggerTable(backendName))); rs.add(enrich(backendDetailEnricher, defineTableTriggerTable(backendName)));
@ -372,7 +404,8 @@ public class ScriptsMetaDataProvider
{ {
QTableMetaData tableMetaData = defineStandardTable(backendName, ScriptType.TABLE_NAME, ScriptType.class) 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("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"))); .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("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"))); 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")) .withRecordLabelFields(List.of("scriptId", "sequenceNo"))
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "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("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("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"))); .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);
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/

View File

@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.processes.implementations.scripts;
import java.util.List; import java.util.List;
import com.kingsrook.qqq.backend.core.actions.ActionHelper; 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.processes.BackendStep;
import com.kingsrook.qqq.backend.core.actions.tables.GetAction; import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction; 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.query.QueryOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput; import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.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; import com.kingsrook.qqq.backend.core.utils.StringUtils;
@ -63,87 +66,132 @@ public class StoreScriptRevisionProcessStep implements BackendStep
@Override @Override
public void run(RunBackendStepInput input, RunBackendStepOutput output) throws QException public void run(RunBackendStepInput input, RunBackendStepOutput output) throws QException
{ {
ActionHelper.validateSession(input); InsertAction insertAction = new InsertAction();
InsertInput insertInput = new InsertInput();
////////////////////////////////////////////////////////////////// insertInput.setTableName("scriptRevision");
// check if there's currently a script referenced by the record // QBackendTransaction transaction = insertAction.openTransaction(insertInput);
////////////////////////////////////////////////////////////////// insertInput.setTransaction(transaction);
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);
try 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<QRecord> fileContents = (List<QRecord>) input.getValue("fileContents");
if(CollectionUtils.nullSafeHasContents(fileContents))
{
List<QRecord> 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) 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"));
} }
} }

View File

@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.utils;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; 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.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 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; import com.kingsrook.qqq.backend.core.model.data.QRecord;
@ -550,4 +553,68 @@ public class CollectionUtils
return (rs); 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 <E, T extends Collection<E>> T useOrWrap(Collection<E> collection, TypeToken<T> typeToken)
{
try
{
Class<T> targetClass = (Class<T>) typeToken.getRawType();
if(targetClass.isInstance(collection))
{
return (targetClass.cast(collection));
}
Constructor<T> 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 <K, V, T extends Map<K, V>> T useOrWrap(Map<K, V> collection, TypeToken<T> typeToken)
{
try
{
Class<T> targetClass = (Class<T>) typeToken.getRawType();
if(targetClass.isInstance(collection))
{
return (targetClass.cast(collection));
}
Constructor<T> constructor = targetClass.getConstructor(Map.class);
return (constructor.newInstance(collection));
}
catch(Exception e)
{
throw (new QRuntimeException("Error wrapping collection", e));
}
}
} }

View File

@ -30,10 +30,10 @@ import java.util.Map;
import com.kingsrook.qqq.backend.core.BaseTest; import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.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.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 com.kingsrook.qqq.backend.core.model.templates.TemplateType;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;

View File

@ -25,8 +25,8 @@ package com.kingsrook.qqq.backend.core.actions.templates;
import java.util.Map; import java.util.Map;
import com.kingsrook.qqq.backend.core.BaseTest; import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateInput; import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateInput;
import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateOutput; 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.model.templates.TemplateType;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;

View File

@ -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());
}
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/

View File

@ -22,6 +22,7 @@
package com.kingsrook.qqq.backend.core.processes.implementations.scripts; package com.kingsrook.qqq.backend.core.processes.implementations.scripts;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.BaseTest; 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.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.scripts.Script; 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.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.model.scripts.ScriptsMetaDataProvider;
import com.kingsrook.qqq.backend.core.utils.TestUtils; import com.kingsrook.qqq.backend.core.utils.TestUtils;
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder; import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
import org.junit.jupiter.api.Test; 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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
@ -51,7 +54,7 @@ class StoreScriptRevisionProcessStepTest extends BaseTest
** **
*******************************************************************************/ *******************************************************************************/
@Test @Test
void test() throws QException void testSingleFileScriptType() throws QException
{ {
QInstance qInstance = QContext.getQInstance(); QInstance qInstance = QContext.getQInstance();
new ScriptsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null); new ScriptsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
@ -59,7 +62,7 @@ class StoreScriptRevisionProcessStepTest extends BaseTest
Integer scriptId = 1701; Integer scriptId = 1701;
String scriptContents = "logger.log('Hi');"; 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<QRecord> scripts = TestUtils.queryTable(Script.TABLE_NAME); List<QRecord> scripts = TestUtils.queryTable(Script.TABLE_NAME);
assertNull(scripts.get(0).getValueInteger("currentScriptRevisionId")); assertNull(scripts.get(0).getValueInteger("currentScriptRevisionId"));
@ -73,7 +76,7 @@ class StoreScriptRevisionProcessStepTest extends BaseTest
List<QRecord> scriptRevisions = TestUtils.queryTable(ScriptRevision.TABLE_NAME); List<QRecord> scriptRevisions = TestUtils.queryTable(ScriptRevision.TABLE_NAME);
QRecord scriptRevision = scriptRevisions.get(0); QRecord scriptRevision = scriptRevisions.get(0);
assertEquals(1701, scriptRevision.getValueInteger("scriptId")); assertEquals(scriptId, scriptRevision.getValueInteger("scriptId"));
assertEquals(1, scriptRevision.getValueInteger("sequenceNo")); assertEquals(1, scriptRevision.getValueInteger("sequenceNo"));
assertEquals("Initial version", scriptRevision.getValueString("commitMessage")); assertEquals("Initial version", scriptRevision.getValueString("commitMessage"));
assertEquals(scriptContents, scriptRevision.getValueString("contents")); 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()); scriptRevisions = TestUtils.queryTable(ScriptRevision.TABLE_NAME).stream().filter(r -> r.getValueInteger("id").equals(2)).collect(Collectors.toList());
scriptRevision = scriptRevisions.get(0); scriptRevision = scriptRevisions.get(0);
assertEquals(1701, scriptRevision.getValueInteger("scriptId")); assertEquals(scriptId, scriptRevision.getValueInteger("scriptId"));
assertEquals(2, scriptRevision.getValueInteger("sequenceNo")); assertEquals(2, scriptRevision.getValueInteger("sequenceNo"));
assertEquals("No commit message given", scriptRevision.getValueString("commitMessage")); assertEquals("No commit message given", scriptRevision.getValueString("commitMessage"));
assertEquals(scriptContents, scriptRevision.getValueString("contents")); 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 = "<h1>Hey</h1>";
TestUtils.insertRecords(qInstance, qInstance.getTable(Script.TABLE_NAME), List.of(new QRecord().withValue("id", scriptId)));
List<QRecord> scripts = TestUtils.queryTable(Script.TABLE_NAME);
assertNull(scripts.get(0).getValueInteger("currentScriptRevisionId"));
ArrayList<QRecord> 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<QRecord> 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<QRecord> 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 = "<h1>Hey, what's up</h1>";
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));
}
} }

View File

@ -25,15 +25,21 @@ package com.kingsrook.qqq.backend.core.utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap;
import java.util.function.Function; import java.util.function.Function;
import com.google.gson.reflect.TypeToken;
import com.kingsrook.qqq.backend.core.BaseTest; import com.kingsrook.qqq.backend.core.BaseTest;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; 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.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue; 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)); assertEquals(List.of(1, 2, 3), CollectionUtils.mergeLists(null, List.of(1, 2, 3), null));
} }
/*******************************************************************************
**
*******************************************************************************/
@Test
void testUseOrWrap()
{
{
List<String> originalList = new ArrayList<>(List.of("A", "B", "C"));
ArrayList<String> reallyArrayList = CollectionUtils.useOrWrap(originalList, new TypeToken<>() {});
assertSame(originalList, reallyArrayList);
}
{
List<String> originalList = new LinkedList<>(List.of("A", "B", "C"));
ArrayList<String> 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<String, Integer> originalMap = new HashMap<>(Map.of("A", 1, "B", 2));
HashMap<String, Integer> reallyHashMap = CollectionUtils.useOrWrap(originalMap, new TypeToken<>() {});
assertSame(originalMap, reallyHashMap);
}
{
Map<String, Integer> originalMap = new TreeMap<>(Map.of("A", 1, "B", 2));
HashMap<String, Integer> 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());
}
} }