mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-22 15:08:45 +00:00
Compare commits
22 Commits
snapshot-f
...
snapshot-f
Author | SHA1 | Date | |
---|---|---|---|
abc6331131 | |||
e84fe7eb18 | |||
63a48eeafa | |||
5434721c8e | |||
f3546da8cc | |||
cfd3100535 | |||
0dbac39ef5 | |||
00b4708d80 | |||
b5959b4b89 | |||
243ffe81a5 | |||
76118bfca1 | |||
6e91149b0a | |||
cfeb71aa2f | |||
edaabc3523 | |||
e53e00c520 | |||
e970d613a7 | |||
f5c1573102 | |||
2103d578b3 | |||
daad8a720a | |||
0ef01efcaa | |||
6ef0a89533 | |||
ce50120234 |
@ -1,23 +1,51 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
if [ -z "$CIRCLE_BRANCH" ] && [ -z "$CIRCLE_TAG" ]; then
|
############################################################################
|
||||||
echo "Error: env vars CIRCLE_BRANCH and CIRCLE_TAG were not set."
|
## adjust-pom.version.sh
|
||||||
exit 1;
|
## During CircleCI builds - edit the qqq parent pom.xml, to set the
|
||||||
fi
|
## <revision> value such that:
|
||||||
|
## - feature-branch builds, tagged as snapshot-*, deploy with a version
|
||||||
|
## number that includes that tag's name (minus the snapshot- part)
|
||||||
|
## - integration-branch builds deploy with a version number that includes
|
||||||
|
## the branch name slugified
|
||||||
|
## - we never deploy -SNAPSHOT versions any more - because we don't believe
|
||||||
|
## it is ever valid to not know exactly what versions you are getting
|
||||||
|
## (perhaps because we are too loose with our versioning?)
|
||||||
|
############################################################################
|
||||||
|
|
||||||
if [ "$CIRCLE_BRANCH" == "dev" ] || [ "$CIRCLE_BRANCH" == "staging" ] || [ "$CIRCLE_BRANCH" == "main" ] || [ \! -z $(echo "$CIRCLE_TAG" | grep "^version-") ]; then
|
POM=$(dirname $0)/../pom.xml
|
||||||
echo "On a primary branch or tag [${CIRCLE_BRANCH}${CIRCLE_TAG}] - will not edit the pom version.";
|
echo "On branch: $CIRCLE_BRANCH, tag: $CIRCLE_TAG..."
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
## ## only do anything if the committed pom has a -SNAPSHOT version ##
|
||||||
|
######################################################################
|
||||||
|
REVISION=$(grep '<revision>' $POM | sed 's/.*<revision>//;s/<.*//');
|
||||||
|
echo "<revision> in pom.xml is: $REVISION"
|
||||||
|
if [ \! $(echo "$REVISION" | grep SNAPSHOT) ]; then
|
||||||
|
echo "Not on a SNAPSHOT revision, so nothing to do here."
|
||||||
exit 0;
|
exit 0;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$CIRCLE_BRANCH" ]; then
|
##################################################################################
|
||||||
SLUG=$(echo $CIRCLE_BRANCH | sed 's/[^a-zA-Z0-9]/-/g')
|
## ## figure out if we need a SLUG: a snapshot- tag, or an integration/ branch ##
|
||||||
else
|
##################################################################################
|
||||||
SLUG=$(echo $CIRCLE_TAG | sed 's/^snapshot-//g')
|
SLUG=""
|
||||||
|
if [ $(echo "$CIRCLE_TAG" | grep ^snapshot-) ]; then
|
||||||
|
SLUG=$(echo "$CIRCLE_TAG" | sed "s/^snapshot-//")-
|
||||||
|
echo "Using slug [$SLUG] from tag [$CIRCLE_TAG]"
|
||||||
|
|
||||||
|
elif [ $(echo "$CIRCLE_BRANCH" | grep ^integration/) ]; then
|
||||||
|
SLUG=$(echo "$CIRCLE_BRANCH" | sed "s,/,-,g")-
|
||||||
|
echo "Using slug [$SLUG] from branch [$CIRCLE_BRANCH]"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
POM=$(dirname $0)/../pom.xml
|
################################################################
|
||||||
|
## ## build the replcaement for -SNAPSHOT, and update the pom ##
|
||||||
|
################################################################
|
||||||
|
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||||||
|
REPLACEMENT=${SLUG}${TIMESTAMP}
|
||||||
|
|
||||||
echo "Updating $POM <revision> to: $SLUG-SNAPSHOT"
|
echo "Updating $POM -SNAPSHOT to: -$REPLACEMENT"
|
||||||
sed -i "s/<revision>.*/<revision>$SLUG-SNAPSHOT<\/revision>/" $POM
|
sed -i "s/-SNAPSHOT<\/revision>/-$REPLACEMENT<\/revision>/" $POM
|
||||||
git diff $POM
|
git diff $POM
|
||||||
|
|
||||||
|
2
pom.xml
2
pom.xml
@ -47,7 +47,7 @@
|
|||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>0.23.0-SNAPSHOT</revision>
|
<revision>0.24.0-SNAPSHOT</revision>
|
||||||
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
@ -40,9 +40,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData;
|
|||||||
** - alertType - name of entry in AlertType enum (ERROR, WARNING, SUCCESS)
|
** - alertType - name of entry in AlertType enum (ERROR, WARNING, SUCCESS)
|
||||||
** - alertHtml - html to display inside the alert (other than its icon)
|
** - alertHtml - html to display inside the alert (other than its icon)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class ProcessAlertWidget extends AbstractWidgetRenderer implements MetaDataProducerInterface<QWidgetMetaData>
|
public class AlertWidgetRenderer extends AbstractWidgetRenderer implements MetaDataProducerInterface<QWidgetMetaData>
|
||||||
{
|
{
|
||||||
public static final String NAME = "ProcessAlertWidget";
|
public static final String NAME = "AlertWidgetRenderer";
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -301,6 +301,9 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
widgetData.setAllowRecordEdit(BooleanUtils.isTrue(ValueUtils.getValueAsBoolean(input.getQueryParams().get("allowRecordEdit"))));
|
||||||
|
widgetData.setAllowRecordDelete(BooleanUtils.isTrue(ValueUtils.getValueAsBoolean(input.getQueryParams().get("allowRecordDelete"))));
|
||||||
|
|
||||||
return (new RenderWidgetOutput(widgetData));
|
return (new RenderWidgetOutput(widgetData));
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
|
@ -28,7 +28,6 @@ import java.time.LocalDateTime;
|
|||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -468,7 +467,8 @@ public class QValueFormatter
|
|||||||
{
|
{
|
||||||
for(QFieldMetaData field : table.getFields().values())
|
for(QFieldMetaData field : table.getFields().values())
|
||||||
{
|
{
|
||||||
if(field.getType().equals(QFieldType.BLOB))
|
Optional<FieldAdornment> fileDownloadAdornment = field.getAdornment(AdornmentType.FILE_DOWNLOAD);
|
||||||
|
if(fileDownloadAdornment.isPresent())
|
||||||
{
|
{
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// file name comes from: //
|
// file name comes from: //
|
||||||
@ -478,20 +478,7 @@ public class QValueFormatter
|
|||||||
// - tableLabel primaryKey fieldLabel //
|
// - tableLabel primaryKey fieldLabel //
|
||||||
// - and - if the FILE_DOWNLOAD adornment had a DEFAULT_EXTENSION, then it gets added (preceded by a dot) //
|
// - and - if the FILE_DOWNLOAD adornment had a DEFAULT_EXTENSION, then it gets added (preceded by a dot) //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
Optional<FieldAdornment> fileDownloadAdornment = field.getAdornment(AdornmentType.FILE_DOWNLOAD);
|
Map<String, Serializable> adornmentValues = fileDownloadAdornment.get().getValues();
|
||||||
Map<String, Serializable> adornmentValues = Collections.emptyMap();
|
|
||||||
|
|
||||||
if(fileDownloadAdornment.isPresent())
|
|
||||||
{
|
|
||||||
adornmentValues = fileDownloadAdornment.get().getValues();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
///////////////////////////////////////////////////////
|
|
||||||
// don't change blobs unless they are file-downloads //
|
|
||||||
///////////////////////////////////////////////////////
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String fileNameField = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FIELD));
|
String fileNameField = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FIELD));
|
||||||
String fileNameFormat = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FORMAT));
|
String fileNameFormat = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FORMAT));
|
||||||
@ -542,7 +529,13 @@ public class QValueFormatter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
record.setValue(field.getName(), "/data/" + table.getName() + "/" + primaryKey + "/" + field.getName() + "/" + fileName);
|
/////////////////////////////////////////////
|
||||||
|
// if field type is blob, update its value //
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
if(QFieldType.BLOB.equals(field.getType()))
|
||||||
|
{
|
||||||
|
record.setValue(field.getName(), "/data/" + table.getName() + "/" + primaryKey + "/" + field.getName() + "/" + fileName);
|
||||||
|
}
|
||||||
record.setDisplayValue(field.getName(), fileName);
|
record.setDisplayValue(field.getName(), fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
|||||||
public class StorageInput extends AbstractTableActionInput
|
public class StorageInput extends AbstractTableActionInput
|
||||||
{
|
{
|
||||||
private String reference;
|
private String reference;
|
||||||
|
private String contentType;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -74,4 +75,35 @@ public class StorageInput extends AbstractTableActionInput
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for contentType
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getContentType()
|
||||||
|
{
|
||||||
|
return (this.contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for contentType
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setContentType(String contentType)
|
||||||
|
{
|
||||||
|
this.contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for contentType
|
||||||
|
*******************************************************************************/
|
||||||
|
public StorageInput withContentType(String contentType)
|
||||||
|
{
|
||||||
|
this.contentType = contentType;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
package com.kingsrook.qqq.backend.core.model.dashboard.widgets;
|
package com.kingsrook.qqq.backend.core.model.dashboard.widgets;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Model containing datastructure expected by frontend alert widget
|
** Model containing datastructure expected by frontend alert widget
|
||||||
**
|
**
|
||||||
@ -40,8 +43,10 @@ public class AlertData extends QWidgetData
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
private String html;
|
private String html;
|
||||||
private AlertType alertType;
|
private AlertType alertType;
|
||||||
|
private Boolean hideWidget = false;
|
||||||
|
private List<String> bulletList;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -139,4 +144,66 @@ public class AlertData extends QWidgetData
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for hideWidget
|
||||||
|
*******************************************************************************/
|
||||||
|
public boolean getHideWidget()
|
||||||
|
{
|
||||||
|
return (this.hideWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for hideWidget
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setHideWidget(boolean hideWidget)
|
||||||
|
{
|
||||||
|
this.hideWidget = hideWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for hideWidget
|
||||||
|
*******************************************************************************/
|
||||||
|
public AlertData withHideWidget(boolean hideWidget)
|
||||||
|
{
|
||||||
|
this.hideWidget = hideWidget;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for bulletList
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<String> getBulletList()
|
||||||
|
{
|
||||||
|
return (this.bulletList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for bulletList
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setBulletList(List<String> bulletList)
|
||||||
|
{
|
||||||
|
this.bulletList = bulletList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for bulletList
|
||||||
|
*******************************************************************************/
|
||||||
|
public AlertData withBulletList(List<String> bulletList)
|
||||||
|
{
|
||||||
|
this.bulletList = bulletList;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,14 @@ public class ChildRecordListData extends QWidgetData
|
|||||||
private QueryOutput queryOutput;
|
private QueryOutput queryOutput;
|
||||||
private QTableMetaData childTableMetaData;
|
private QTableMetaData childTableMetaData;
|
||||||
|
|
||||||
|
private String tableName;
|
||||||
private String tablePath;
|
private String tablePath;
|
||||||
private String viewAllLink;
|
private String viewAllLink;
|
||||||
private Integer totalRows;
|
private Integer totalRows;
|
||||||
|
private Boolean disableRowClick = false;
|
||||||
|
private Boolean allowRecordEdit = false;
|
||||||
|
private Boolean allowRecordDelete = false;
|
||||||
|
private Boolean isInProcess = false;
|
||||||
|
|
||||||
private boolean canAddChildRecord = false;
|
private boolean canAddChildRecord = false;
|
||||||
private Map<String, Serializable> defaultValuesForNewChildRecords;
|
private Map<String, Serializable> defaultValuesForNewChildRecords;
|
||||||
@ -352,4 +357,173 @@ public class ChildRecordListData extends QWidgetData
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for tableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getTableName()
|
||||||
|
{
|
||||||
|
return (this.tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for tableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setTableName(String tableName)
|
||||||
|
{
|
||||||
|
this.tableName = tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for tableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public ChildRecordListData withTableName(String tableName)
|
||||||
|
{
|
||||||
|
this.tableName = tableName;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for tablePath
|
||||||
|
*******************************************************************************/
|
||||||
|
public ChildRecordListData withTablePath(String tablePath)
|
||||||
|
{
|
||||||
|
this.tablePath = tablePath;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for disableRowClick
|
||||||
|
*******************************************************************************/
|
||||||
|
public Boolean getDisableRowClick()
|
||||||
|
{
|
||||||
|
return (this.disableRowClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for disableRowClick
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setDisableRowClick(Boolean disableRowClick)
|
||||||
|
{
|
||||||
|
this.disableRowClick = disableRowClick;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for disableRowClick
|
||||||
|
*******************************************************************************/
|
||||||
|
public ChildRecordListData withDisableRowClick(Boolean disableRowClick)
|
||||||
|
{
|
||||||
|
this.disableRowClick = disableRowClick;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for allowRecordEdit
|
||||||
|
*******************************************************************************/
|
||||||
|
public Boolean getAllowRecordEdit()
|
||||||
|
{
|
||||||
|
return (this.allowRecordEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for allowRecordEdit
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setAllowRecordEdit(Boolean allowRecordEdit)
|
||||||
|
{
|
||||||
|
this.allowRecordEdit = allowRecordEdit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for allowRecordEdit
|
||||||
|
*******************************************************************************/
|
||||||
|
public ChildRecordListData withAllowRecordEdit(Boolean allowRecordEdit)
|
||||||
|
{
|
||||||
|
this.allowRecordEdit = allowRecordEdit;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for allowRecordDelete
|
||||||
|
*******************************************************************************/
|
||||||
|
public Boolean getAllowRecordDelete()
|
||||||
|
{
|
||||||
|
return (this.allowRecordDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for allowRecordDelete
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setAllowRecordDelete(Boolean allowRecordDelete)
|
||||||
|
{
|
||||||
|
this.allowRecordDelete = allowRecordDelete;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for allowRecordDelete
|
||||||
|
*******************************************************************************/
|
||||||
|
public ChildRecordListData withAllowRecordDelete(Boolean allowRecordDelete)
|
||||||
|
{
|
||||||
|
this.allowRecordDelete = allowRecordDelete;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for isInProcess
|
||||||
|
*******************************************************************************/
|
||||||
|
public Boolean getIsInProcess()
|
||||||
|
{
|
||||||
|
return (this.isInProcess);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for isInProcess
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setIsInProcess(Boolean isInProcess)
|
||||||
|
{
|
||||||
|
this.isInProcess = isInProcess;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for isInProcess
|
||||||
|
*******************************************************************************/
|
||||||
|
public ChildRecordListData withIsInProcess(Boolean isInProcess)
|
||||||
|
{
|
||||||
|
this.isInProcess = isInProcess;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,6 +94,29 @@ public class CountingHash<K extends Serializable> extends AbstractMap<K, Integer
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** increment the value for the specified key
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer put(K key)
|
||||||
|
{
|
||||||
|
return (add(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Set the value for the specified key by the supplied value
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer put(K key, Integer value)
|
||||||
|
{
|
||||||
|
this.map.put(key, value);
|
||||||
|
return (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -30,11 +30,14 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
@ -54,6 +57,11 @@ public class JsonUtils
|
|||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(JsonUtils.class);
|
private static final QLogger LOG = QLogger.getLogger(JsonUtils.class);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// see https://www.baeldung.com/jackson-map-null-values-or-null-key //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
public static NullKeyToEmptyStringSerializer nullKeyToEmptyStringSerializer = new NullKeyToEmptyStringSerializer();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -396,4 +404,41 @@ public class JsonUtils
|
|||||||
return (record);
|
return (record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public static class NullKeyToEmptyStringSerializer extends StdSerializer<Object>
|
||||||
|
{
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public NullKeyToEmptyStringSerializer()
|
||||||
|
{
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public NullKeyToEmptyStringSerializer(Class<Object> t)
|
||||||
|
{
|
||||||
|
super(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void serialize(Object nullKey, JsonGenerator jsonGenerator, SerializerProvider unused) throws IOException
|
||||||
|
{
|
||||||
|
jsonGenerator.writeFieldName("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Unit test for ProcessAlertWidget
|
** Unit test for AlertWidgetRenderer
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
class ProcessAlertWidgetTest extends BaseTest
|
class AlertWidgetRendererTest extends BaseTest
|
||||||
{
|
{
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -46,10 +46,10 @@ class ProcessAlertWidgetTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void test() throws QException
|
void test() throws QException
|
||||||
{
|
{
|
||||||
MetaDataProducerHelper.processAllMetaDataProducersInPackage(QContext.getQInstance(), ProcessAlertWidget.class.getPackageName());
|
MetaDataProducerHelper.processAllMetaDataProducersInPackage(QContext.getQInstance(), AlertWidgetRenderer.class.getPackageName());
|
||||||
|
|
||||||
RenderWidgetInput input = new RenderWidgetInput();
|
RenderWidgetInput input = new RenderWidgetInput();
|
||||||
input.setWidgetMetaData(QContext.getQInstance().getWidget(ProcessAlertWidget.NAME));
|
input.setWidgetMetaData(QContext.getQInstance().getWidget(AlertWidgetRenderer.NAME));
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// make sure we run w/o exceptions (and w/ default outputs) if there are no query params //
|
// make sure we run w/o exceptions (and w/ default outputs) if there are no query params //
|
||||||
@ -69,4 +69,4 @@ class ProcessAlertWidgetTest extends BaseTest
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -30,7 +30,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Unit test for CountingHash
|
** Unit test for CountingHash
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
class CountingHashTest extends BaseTest
|
class CountingHashTest extends BaseTest
|
||||||
{
|
{
|
||||||
@ -73,4 +73,19 @@ class CountingHashTest extends BaseTest
|
|||||||
assertEquals(1, alwaysMutable.get("B"));
|
assertEquals(1, alwaysMutable.get("B"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testPut()
|
||||||
|
{
|
||||||
|
CountingHash<String> alwaysMutable = new CountingHash<>(Map.of("A", 5));
|
||||||
|
alwaysMutable.put("A", 25);
|
||||||
|
assertEquals(25, alwaysMutable.get("A"));
|
||||||
|
alwaysMutable.put("A");
|
||||||
|
assertEquals(26, alwaysMutable.get("A"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -35,11 +35,13 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
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;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
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.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
@ -81,7 +83,7 @@ class JsonUtilsTest extends BaseTest
|
|||||||
{
|
{
|
||||||
objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
||||||
});
|
});
|
||||||
|
|
||||||
assertThat(json).contains("""
|
assertThat(json).contains("""
|
||||||
"values":{"foo":"Foo","bar":3.14159,"baz":null}""");
|
"values":{"foo":"Foo","bar":3.14159,"baz":null}""");
|
||||||
}
|
}
|
||||||
@ -318,4 +320,27 @@ class JsonUtilsTest extends BaseTest
|
|||||||
assertEquals("age", qQueryFilter.getOrderBys().get(0).getFieldName());
|
assertEquals("age", qQueryFilter.getOrderBys().get(0).getFieldName());
|
||||||
assertTrue(qQueryFilter.getOrderBys().get(0).getIsAscending());
|
assertTrue(qQueryFilter.getOrderBys().get(0).getIsAscending());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testNullKeyInMap()
|
||||||
|
{
|
||||||
|
Map<Object, String> mapWithNullKey = MapBuilder.of(null, "foo");
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// assert default behavior throws with null map key //
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
assertThatThrownBy(() -> JsonUtils.toJson(mapWithNullKey)).rootCause().hasMessageContaining("Null key for a Map not allowed in JSON");
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// assert that the nullKeyToEmptyStringSerializer does what we expect //
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
assertEquals("""
|
||||||
|
{"":"foo"}""", JsonUtils.toJson(mapWithNullKey, mapper -> mapper.getSerializerProvider().setNullKeySerializer(JsonUtils.nullKeyToEmptyStringSerializer)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ public class S3StorageAction extends AbstractS3Action implements QStorageInterfa
|
|||||||
|
|
||||||
AmazonS3 amazonS3 = getS3Utils().getAmazonS3();
|
AmazonS3 amazonS3 = getS3Utils().getAmazonS3();
|
||||||
String fullPath = getFullPath(storageInput);
|
String fullPath = getFullPath(storageInput);
|
||||||
S3UploadOutputStream s3UploadOutputStream = new S3UploadOutputStream(amazonS3, backend.getBucketName(), fullPath);
|
S3UploadOutputStream s3UploadOutputStream = new S3UploadOutputStream(amazonS3, backend.getBucketName(), fullPath, storageInput.getContentType());
|
||||||
return (s3UploadOutputStream);
|
return (s3UploadOutputStream);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
|
@ -53,6 +53,7 @@ public class S3UploadOutputStream extends OutputStream
|
|||||||
private final AmazonS3 amazonS3;
|
private final AmazonS3 amazonS3;
|
||||||
private final String bucketName;
|
private final String bucketName;
|
||||||
private final String key;
|
private final String key;
|
||||||
|
private final String contentType;
|
||||||
|
|
||||||
private byte[] buffer = new byte[5 * 1024 * 1024];
|
private byte[] buffer = new byte[5 * 1024 * 1024];
|
||||||
private int offset = 0;
|
private int offset = 0;
|
||||||
@ -68,11 +69,12 @@ public class S3UploadOutputStream extends OutputStream
|
|||||||
** Constructor
|
** Constructor
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public S3UploadOutputStream(AmazonS3 amazonS3, String bucketName, String key)
|
public S3UploadOutputStream(AmazonS3 amazonS3, String bucketName, String key, String contentType)
|
||||||
{
|
{
|
||||||
this.amazonS3 = amazonS3;
|
this.amazonS3 = amazonS3;
|
||||||
this.bucketName = bucketName;
|
this.bucketName = bucketName;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
|
this.contentType = contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -96,6 +98,13 @@ public class S3UploadOutputStream extends OutputStream
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void uploadIfNeeded()
|
private void uploadIfNeeded()
|
||||||
{
|
{
|
||||||
|
ObjectMetadata objectMetadata = null;
|
||||||
|
if(this.contentType != null)
|
||||||
|
{
|
||||||
|
objectMetadata = new ObjectMetadata();
|
||||||
|
objectMetadata.setContentType(this.contentType);
|
||||||
|
}
|
||||||
|
|
||||||
if(offset == buffer.length)
|
if(offset == buffer.length)
|
||||||
{
|
{
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
@ -104,7 +113,8 @@ public class S3UploadOutputStream extends OutputStream
|
|||||||
if(initiateMultipartUploadResult == null)
|
if(initiateMultipartUploadResult == null)
|
||||||
{
|
{
|
||||||
LOG.info("Initiating a multipart upload", logPair("key", key));
|
LOG.info("Initiating a multipart upload", logPair("key", key));
|
||||||
initiateMultipartUploadResult = amazonS3.initiateMultipartUpload(new InitiateMultipartUploadRequest(bucketName, key));
|
initiateMultipartUploadResult = amazonS3.initiateMultipartUpload(new InitiateMultipartUploadRequest(bucketName, key, objectMetadata));
|
||||||
|
|
||||||
uploadPartResultList = new ArrayList<>();
|
uploadPartResultList = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +125,8 @@ public class S3UploadOutputStream extends OutputStream
|
|||||||
.withInputStream(new ByteArrayInputStream(buffer))
|
.withInputStream(new ByteArrayInputStream(buffer))
|
||||||
.withBucketName(bucketName)
|
.withBucketName(bucketName)
|
||||||
.withKey(key)
|
.withKey(key)
|
||||||
.withPartSize(buffer.length);
|
.withPartSize(buffer.length)
|
||||||
|
.withObjectMetadata(objectMetadata);
|
||||||
|
|
||||||
uploadPartResultList.add(amazonS3.uploadPart(uploadPartRequest));
|
uploadPartResultList.add(amazonS3.uploadPart(uploadPartRequest));
|
||||||
|
|
||||||
@ -166,6 +177,13 @@ public class S3UploadOutputStream extends OutputStream
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ObjectMetadata objectMetadata = null;
|
||||||
|
if(this.contentType != null)
|
||||||
|
{
|
||||||
|
objectMetadata = new ObjectMetadata();
|
||||||
|
objectMetadata.setContentType(this.contentType);
|
||||||
|
}
|
||||||
|
|
||||||
if(initiateMultipartUploadResult != null)
|
if(initiateMultipartUploadResult != null)
|
||||||
{
|
{
|
||||||
if(offset > 0)
|
if(offset > 0)
|
||||||
@ -180,7 +198,8 @@ public class S3UploadOutputStream extends OutputStream
|
|||||||
.withInputStream(new ByteArrayInputStream(buffer, 0, offset))
|
.withInputStream(new ByteArrayInputStream(buffer, 0, offset))
|
||||||
.withBucketName(bucketName)
|
.withBucketName(bucketName)
|
||||||
.withKey(key)
|
.withKey(key)
|
||||||
.withPartSize(offset);
|
.withPartSize(offset)
|
||||||
|
.withObjectMetadata(objectMetadata);
|
||||||
uploadPartResultList.add(amazonS3.uploadPart(uploadPartRequest));
|
uploadPartResultList.add(amazonS3.uploadPart(uploadPartRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,8 +212,12 @@ public class S3UploadOutputStream extends OutputStream
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if(objectMetadata == null)
|
||||||
|
{
|
||||||
|
objectMetadata = new ObjectMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
LOG.info("Putting object (non-multipart)", logPair("key", key), logPair("length", offset));
|
LOG.info("Putting object (non-multipart)", logPair("key", key), logPair("length", offset));
|
||||||
ObjectMetadata objectMetadata = new ObjectMetadata();
|
|
||||||
objectMetadata.setContentLength(offset);
|
objectMetadata.setContentLength(offset);
|
||||||
PutObjectResult putObjectResult = amazonS3.putObject(bucketName, key, new ByteArrayInputStream(buffer, 0, offset), objectMetadata);
|
PutObjectResult putObjectResult = amazonS3.putObject(bucketName, key, new ByteArrayInputStream(buffer, 0, offset), objectMetadata);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Unit test for S3UploadOutputStream
|
** Unit test for S3UploadOutputStream
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
class S3UploadOutputStreamTest extends BaseS3Test
|
class S3UploadOutputStreamTest extends BaseS3Test
|
||||||
{
|
{
|
||||||
@ -57,11 +57,11 @@ class S3UploadOutputStreamTest extends BaseS3Test
|
|||||||
outputStream.write("\n]\n".getBytes(StandardCharsets.UTF_8));
|
outputStream.write("\n]\n".getBytes(StandardCharsets.UTF_8));
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
|
|
||||||
S3UploadOutputStream s3UploadOutputStream = new S3UploadOutputStream(getS3Utils().getAmazonS3(), bucketName, key);
|
S3UploadOutputStream s3UploadOutputStream = new S3UploadOutputStream(getS3Utils().getAmazonS3(), bucketName, key, null);
|
||||||
s3UploadOutputStream.write(outputStream.toByteArray(), 0, 5 * 1024 * 1024);
|
s3UploadOutputStream.write(outputStream.toByteArray(), 0, 5 * 1024 * 1024);
|
||||||
s3UploadOutputStream.write(outputStream.toByteArray(), 0, 3 * 1024 * 1024);
|
s3UploadOutputStream.write(outputStream.toByteArray(), 0, 3 * 1024 * 1024);
|
||||||
s3UploadOutputStream.write(outputStream.toByteArray(), 0, 3 * 1024 * 1024);
|
s3UploadOutputStream.write(outputStream.toByteArray(), 0, 3 * 1024 * 1024);
|
||||||
s3UploadOutputStream.close();
|
s3UploadOutputStream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
0.23.0
|
0.24.0
|
||||||
|
@ -352,6 +352,8 @@ public class QJavalinProcessHandler
|
|||||||
Map<String, Object> resultForCaller = new HashMap<>();
|
Map<String, Object> resultForCaller = new HashMap<>();
|
||||||
Exception returningException = null;
|
Exception returningException = null;
|
||||||
|
|
||||||
|
String processName = context.pathParam("processName");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(processUUID == null)
|
if(processUUID == null)
|
||||||
@ -360,7 +362,6 @@ public class QJavalinProcessHandler
|
|||||||
}
|
}
|
||||||
resultForCaller.put("processUUID", processUUID);
|
resultForCaller.put("processUUID", processUUID);
|
||||||
|
|
||||||
String processName = context.pathParam("processName");
|
|
||||||
LOG.info(startAfterStep == null ? "Initiating process [" + processName + "] [" + processUUID + "]"
|
LOG.info(startAfterStep == null ? "Initiating process [" + processName + "] [" + processUUID + "]"
|
||||||
: "Resuming process [" + processName + "] [" + processUUID + "] after step [" + startAfterStep + "]");
|
: "Resuming process [" + processName + "] [" + processUUID + "] after step [" + startAfterStep + "]");
|
||||||
|
|
||||||
@ -441,10 +442,30 @@ public class QJavalinProcessHandler
|
|||||||
// negative side-effects - but be aware. //
|
// negative side-effects - but be aware. //
|
||||||
// One could imagine that we'd need this to be configurable in the future? //
|
// One could imagine that we'd need this to be configurable in the future? //
|
||||||
///////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
context.result(JsonUtils.toJson(resultForCaller, mapper ->
|
try
|
||||||
{
|
{
|
||||||
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
String json = JsonUtils.toJson(resultForCaller, mapper ->
|
||||||
}));
|
{
|
||||||
|
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// use this custom serializer to convert null map-keys to empty-strings (rather than having an exception!) //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
mapper.getSerializerProvider().setNullKeySerializer(JsonUtils.nullKeyToEmptyStringSerializer);
|
||||||
|
});
|
||||||
|
context.result(json);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// related to the change above - we've seen at least one new error that can come from the //
|
||||||
|
// Include.ALWAYS change (a null key in a map -> jackson exception). So, try-catch around //
|
||||||
|
// the above serialization, and if it does throw, log, but continue trying a default serialization //
|
||||||
|
// as that is probably preferable to an exception for the caller... //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
LOG.warn("Error deserializing process results with serializationInclusion:ALWAYS - will retry with default settings", e, logPair("processName", processName), logPair("processUUID", processUUID));
|
||||||
|
context.result(JsonUtils.toJson(resultForCaller));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import java.io.Serializable;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessInitOrStepOrStatusOutputInterface;
|
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessInitOrStepOrStatusOutputInterface;
|
||||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.SchemaBuilder;
|
import com.kingsrook.qqq.middleware.javalin.schemabuilder.SchemaBuilder;
|
||||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||||
@ -117,6 +118,7 @@ public class ProcessInitOrStepOrStatusResponseV1 implements ProcessInitOrStepOrS
|
|||||||
** Getter for values
|
** Getter for values
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@JsonIgnore // we are doing custom serialization of the values map, so mark as ignore.
|
||||||
public Map<String, Serializable> getValues()
|
public Map<String, Serializable> getValues()
|
||||||
{
|
{
|
||||||
return values;
|
return values;
|
||||||
|
@ -28,6 +28,7 @@ import java.time.LocalDate;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||||
@ -141,12 +142,14 @@ public class ProcessSpecUtilsV1
|
|||||||
errorResponse.setError("Illegal Argument Exception: NaN");
|
errorResponse.setError("Illegal Argument Exception: NaN");
|
||||||
errorResponse.setUserFacingError("The process could not be completed due to invalid input.");
|
errorResponse.setUserFacingError("The process could not be completed due to invalid input.");
|
||||||
|
|
||||||
|
Function<ProcessInitOrStepOrStatusResponseV1, Example> responseToExample = response -> new Example().withValue(convertResponseToJSONObject(response).toMap());
|
||||||
|
|
||||||
return MapBuilder.of(() -> new LinkedHashMap<String, Example>())
|
return MapBuilder.of(() -> new LinkedHashMap<String, Example>())
|
||||||
.with("COMPLETE", new Example().withValue(completeResponse))
|
.with("COMPLETE", responseToExample.apply(completeResponse))
|
||||||
.with("COMPLETE with metaDataAdjustment", new Example().withValue(completeResponseWithMetaDataAdjustment))
|
.with("COMPLETE with metaDataAdjustment", responseToExample.apply(completeResponseWithMetaDataAdjustment))
|
||||||
.with("JOB_STARTED", new Example().withValue(jobStartedResponse))
|
.with("JOB_STARTED", responseToExample.apply(jobStartedResponse))
|
||||||
.with("RUNNING", new Example().withValue(runningResponse))
|
.with("RUNNING", responseToExample.apply(runningResponse))
|
||||||
.with("ERROR", new Example().withValue(errorResponse))
|
.with("ERROR", responseToExample.apply(errorResponse))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +158,21 @@ public class ProcessSpecUtilsV1
|
|||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
**
|
**
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
public static void handleOutput(Context context, ProcessInitOrStepOrStatusResponseV1 output)
|
public static void handleOutput(Context context, ProcessInitOrStepOrStatusResponseV1 response)
|
||||||
|
{
|
||||||
|
JSONObject outputJsonObject = convertResponseToJSONObject(response);
|
||||||
|
|
||||||
|
String json = outputJsonObject.toString(3);
|
||||||
|
System.out.println(json);
|
||||||
|
context.result(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
private static JSONObject convertResponseToJSONObject(ProcessInitOrStepOrStatusResponseV1 response)
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// normally, we like the JsonUtils behavior of excluding null/empty elements. //
|
// normally, we like the JsonUtils behavior of excluding null/empty elements. //
|
||||||
@ -163,7 +180,7 @@ public class ProcessSpecUtilsV1
|
|||||||
// so, go through a loop of object → JSON String → JSONObject → String... //
|
// so, go through a loop of object → JSON String → JSONObject → String... //
|
||||||
// also - work with the TypedResponse sub-object within this response class //
|
// also - work with the TypedResponse sub-object within this response class //
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ProcessInitOrStepOrStatusResponseV1.TypedResponse typedOutput = output.getTypedResponse();
|
ProcessInitOrStepOrStatusResponseV1.TypedResponse typedOutput = response.getTypedResponse();
|
||||||
|
|
||||||
String outputJson = JsonUtils.toJson(typedOutput);
|
String outputJson = JsonUtils.toJson(typedOutput);
|
||||||
JSONObject outputJsonObject = new JSONObject(outputJson);
|
JSONObject outputJsonObject = new JSONObject(outputJson);
|
||||||
@ -183,6 +200,14 @@ public class ProcessSpecUtilsV1
|
|||||||
String name = valueEntry.getKey();
|
String name = valueEntry.getKey();
|
||||||
Serializable value = valueEntry.getValue();
|
Serializable value = valueEntry.getValue();
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// follow the strategy that we use for JsonUtils.nullKeyToEmptyStringSerializer in this rare case... //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(name == null)
|
||||||
|
{
|
||||||
|
name = "";
|
||||||
|
}
|
||||||
|
|
||||||
Serializable valueToMakeIntoJson = value;
|
Serializable valueToMakeIntoJson = value;
|
||||||
if(value instanceof String s)
|
if(value instanceof String s)
|
||||||
{
|
{
|
||||||
@ -213,11 +238,31 @@ public class ProcessSpecUtilsV1
|
|||||||
valueToMakeIntoJson = new WidgetBlock(abstractBlockWidgetData);
|
valueToMakeIntoJson = new WidgetBlock(abstractBlockWidgetData);
|
||||||
}
|
}
|
||||||
|
|
||||||
String valueAsJsonString = JsonUtils.toJson(valueToMakeIntoJson, mapper ->
|
///////////////////////////////////////////////
|
||||||
|
// ok now, make the value into a JSON string //
|
||||||
|
///////////////////////////////////////////////
|
||||||
|
String valueAsJsonString;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
valueAsJsonString = JsonUtils.toJson(valueToMakeIntoJson, mapper ->
|
||||||
});
|
{
|
||||||
|
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// use this custom serializer to convert null map-keys to empty-strings (rather than having an exception!) //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
mapper.getSerializerProvider().setNullKeySerializer(JsonUtils.nullKeyToEmptyStringSerializer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.warn("Error deserializing process results with serializationInclusion:ALWAYS - will retry with default settings", e);
|
||||||
|
valueAsJsonString = JsonUtils.toJson(valueToMakeIntoJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// THEN - make it back into a JSONObject or JSONArray, and add it to the valuesAsJsonObject JSONObject //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
if(valueAsJsonString.startsWith("["))
|
if(valueAsJsonString.startsWith("["))
|
||||||
{
|
{
|
||||||
valuesAsJsonObject.put(name, new JSONArray(valueAsJsonString));
|
valuesAsJsonObject.put(name, new JSONArray(valueAsJsonString));
|
||||||
@ -252,10 +297,7 @@ public class ProcessSpecUtilsV1
|
|||||||
outputJsonObject.put("values", valuesAsJsonObject);
|
outputJsonObject.put("values", valuesAsJsonObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return outputJsonObject;
|
||||||
String json = outputJsonObject.toString(3);
|
|
||||||
System.out.println(json);
|
|
||||||
context.result(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1652,66 +1652,61 @@ paths:
|
|||||||
examples:
|
examples:
|
||||||
COMPLETE:
|
COMPLETE:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
values:
|
||||||
nextStep: "reviewScreen"
|
firstLastName: "Aabramson"
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
totalAge: 32768
|
||||||
type: "COMPLETE"
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
values:
|
nextStep: "reviewScreen"
|
||||||
totalAge: 32768
|
type: "COMPLETE"
|
||||||
firstLastName: "Aabramson"
|
|
||||||
COMPLETE with metaDataAdjustment:
|
COMPLETE with metaDataAdjustment:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
values:
|
||||||
nextStep: "inputScreen"
|
firstLastName: "Aabramson"
|
||||||
processMetaDataAdjustment:
|
totalAge: 32768
|
||||||
updatedFields:
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
someField:
|
nextStep: "inputScreen"
|
||||||
displayFormat: "%s"
|
processMetaDataAdjustment:
|
||||||
isEditable: true
|
updatedFields:
|
||||||
isHeavy: false
|
someField:
|
||||||
isHidden: false
|
isRequired: true
|
||||||
isRequired: true
|
isEditable: true
|
||||||
name: "someField"
|
name: "someField"
|
||||||
type: "STRING"
|
displayFormat: "%s"
|
||||||
updatedFrontendStepList:
|
type: "STRING"
|
||||||
- components:
|
isHeavy: false
|
||||||
- type: "EDIT_FORM"
|
isHidden: false
|
||||||
formFields:
|
updatedFrontendStepList:
|
||||||
- displayFormat: "%s"
|
- components:
|
||||||
isEditable: true
|
- type: "EDIT_FORM"
|
||||||
isHeavy: false
|
name: "inputScreen"
|
||||||
isHidden: false
|
formFields:
|
||||||
isRequired: false
|
- isRequired: false
|
||||||
name: "someField"
|
isEditable: true
|
||||||
type: "STRING"
|
name: "someField"
|
||||||
name: "inputScreen"
|
displayFormat: "%s"
|
||||||
- components:
|
type: "STRING"
|
||||||
- type: "PROCESS_SUMMARY_RESULTS"
|
isHeavy: false
|
||||||
name: "resultScreen"
|
isHidden: false
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
- components:
|
||||||
type: "COMPLETE"
|
- type: "PROCESS_SUMMARY_RESULTS"
|
||||||
values:
|
name: "resultScreen"
|
||||||
totalAge: 32768
|
type: "COMPLETE"
|
||||||
firstLastName: "Aabramson"
|
|
||||||
JOB_STARTED:
|
JOB_STARTED:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
jobUUID: "98765432-10FE-DCBA-9876-543210FEDCBA"
|
||||||
jobUUID: "98765432-10FE-DCBA-9876-543210FEDCBA"
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
type: "JOB_STARTED"
|
||||||
type: "JOB_STARTED"
|
|
||||||
RUNNING:
|
RUNNING:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
current: 47
|
||||||
current: 47
|
total: 1701
|
||||||
message: "Processing person records"
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
type: "RUNNING"
|
||||||
total: 1701
|
message: "Processing person records"
|
||||||
type: "RUNNING"
|
|
||||||
ERROR:
|
ERROR:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
type: "RUNNING"
|
||||||
type: "RUNNING"
|
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/ProcessStepResponseV1"
|
$ref: "#/components/schemas/ProcessStepResponseV1"
|
||||||
description: "State of the initialization of the job, with different fields\
|
description: "State of the initialization of the job, with different fields\
|
||||||
@ -1788,66 +1783,61 @@ paths:
|
|||||||
examples:
|
examples:
|
||||||
COMPLETE:
|
COMPLETE:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
values:
|
||||||
nextStep: "reviewScreen"
|
firstLastName: "Aabramson"
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
totalAge: 32768
|
||||||
type: "COMPLETE"
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
values:
|
nextStep: "reviewScreen"
|
||||||
totalAge: 32768
|
type: "COMPLETE"
|
||||||
firstLastName: "Aabramson"
|
|
||||||
COMPLETE with metaDataAdjustment:
|
COMPLETE with metaDataAdjustment:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
values:
|
||||||
nextStep: "inputScreen"
|
firstLastName: "Aabramson"
|
||||||
processMetaDataAdjustment:
|
totalAge: 32768
|
||||||
updatedFields:
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
someField:
|
nextStep: "inputScreen"
|
||||||
displayFormat: "%s"
|
processMetaDataAdjustment:
|
||||||
isEditable: true
|
updatedFields:
|
||||||
isHeavy: false
|
someField:
|
||||||
isHidden: false
|
isRequired: true
|
||||||
isRequired: true
|
isEditable: true
|
||||||
name: "someField"
|
name: "someField"
|
||||||
type: "STRING"
|
displayFormat: "%s"
|
||||||
updatedFrontendStepList:
|
type: "STRING"
|
||||||
- components:
|
isHeavy: false
|
||||||
- type: "EDIT_FORM"
|
isHidden: false
|
||||||
formFields:
|
updatedFrontendStepList:
|
||||||
- displayFormat: "%s"
|
- components:
|
||||||
isEditable: true
|
- type: "EDIT_FORM"
|
||||||
isHeavy: false
|
name: "inputScreen"
|
||||||
isHidden: false
|
formFields:
|
||||||
isRequired: false
|
- isRequired: false
|
||||||
name: "someField"
|
isEditable: true
|
||||||
type: "STRING"
|
name: "someField"
|
||||||
name: "inputScreen"
|
displayFormat: "%s"
|
||||||
- components:
|
type: "STRING"
|
||||||
- type: "PROCESS_SUMMARY_RESULTS"
|
isHeavy: false
|
||||||
name: "resultScreen"
|
isHidden: false
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
- components:
|
||||||
type: "COMPLETE"
|
- type: "PROCESS_SUMMARY_RESULTS"
|
||||||
values:
|
name: "resultScreen"
|
||||||
totalAge: 32768
|
type: "COMPLETE"
|
||||||
firstLastName: "Aabramson"
|
|
||||||
JOB_STARTED:
|
JOB_STARTED:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
jobUUID: "98765432-10FE-DCBA-9876-543210FEDCBA"
|
||||||
jobUUID: "98765432-10FE-DCBA-9876-543210FEDCBA"
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
type: "JOB_STARTED"
|
||||||
type: "JOB_STARTED"
|
|
||||||
RUNNING:
|
RUNNING:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
current: 47
|
||||||
current: 47
|
total: 1701
|
||||||
message: "Processing person records"
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
type: "RUNNING"
|
||||||
total: 1701
|
message: "Processing person records"
|
||||||
type: "RUNNING"
|
|
||||||
ERROR:
|
ERROR:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
type: "RUNNING"
|
||||||
type: "RUNNING"
|
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/ProcessStepResponseV1"
|
$ref: "#/components/schemas/ProcessStepResponseV1"
|
||||||
description: "State of the backend's running of the next step(s) of the\
|
description: "State of the backend's running of the next step(s) of the\
|
||||||
@ -1895,66 +1885,61 @@ paths:
|
|||||||
examples:
|
examples:
|
||||||
COMPLETE:
|
COMPLETE:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
values:
|
||||||
nextStep: "reviewScreen"
|
firstLastName: "Aabramson"
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
totalAge: 32768
|
||||||
type: "COMPLETE"
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
values:
|
nextStep: "reviewScreen"
|
||||||
totalAge: 32768
|
type: "COMPLETE"
|
||||||
firstLastName: "Aabramson"
|
|
||||||
COMPLETE with metaDataAdjustment:
|
COMPLETE with metaDataAdjustment:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
values:
|
||||||
nextStep: "inputScreen"
|
firstLastName: "Aabramson"
|
||||||
processMetaDataAdjustment:
|
totalAge: 32768
|
||||||
updatedFields:
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
someField:
|
nextStep: "inputScreen"
|
||||||
displayFormat: "%s"
|
processMetaDataAdjustment:
|
||||||
isEditable: true
|
updatedFields:
|
||||||
isHeavy: false
|
someField:
|
||||||
isHidden: false
|
isRequired: true
|
||||||
isRequired: true
|
isEditable: true
|
||||||
name: "someField"
|
name: "someField"
|
||||||
type: "STRING"
|
displayFormat: "%s"
|
||||||
updatedFrontendStepList:
|
type: "STRING"
|
||||||
- components:
|
isHeavy: false
|
||||||
- type: "EDIT_FORM"
|
isHidden: false
|
||||||
formFields:
|
updatedFrontendStepList:
|
||||||
- displayFormat: "%s"
|
- components:
|
||||||
isEditable: true
|
- type: "EDIT_FORM"
|
||||||
isHeavy: false
|
name: "inputScreen"
|
||||||
isHidden: false
|
formFields:
|
||||||
isRequired: false
|
- isRequired: false
|
||||||
name: "someField"
|
isEditable: true
|
||||||
type: "STRING"
|
name: "someField"
|
||||||
name: "inputScreen"
|
displayFormat: "%s"
|
||||||
- components:
|
type: "STRING"
|
||||||
- type: "PROCESS_SUMMARY_RESULTS"
|
isHeavy: false
|
||||||
name: "resultScreen"
|
isHidden: false
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
- components:
|
||||||
type: "COMPLETE"
|
- type: "PROCESS_SUMMARY_RESULTS"
|
||||||
values:
|
name: "resultScreen"
|
||||||
totalAge: 32768
|
type: "COMPLETE"
|
||||||
firstLastName: "Aabramson"
|
|
||||||
JOB_STARTED:
|
JOB_STARTED:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
jobUUID: "98765432-10FE-DCBA-9876-543210FEDCBA"
|
||||||
jobUUID: "98765432-10FE-DCBA-9876-543210FEDCBA"
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
type: "JOB_STARTED"
|
||||||
type: "JOB_STARTED"
|
|
||||||
RUNNING:
|
RUNNING:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
current: 47
|
||||||
current: 47
|
total: 1701
|
||||||
message: "Processing person records"
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
type: "RUNNING"
|
||||||
total: 1701
|
message: "Processing person records"
|
||||||
type: "RUNNING"
|
|
||||||
ERROR:
|
ERROR:
|
||||||
value:
|
value:
|
||||||
typedResponse:
|
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||||
processUUID: "01234567-89AB-CDEF-0123-456789ABCDEF"
|
type: "RUNNING"
|
||||||
type: "RUNNING"
|
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/ProcessStepResponseV1"
|
$ref: "#/components/schemas/ProcessStepResponseV1"
|
||||||
description: "State of the backend's running of the specified job, with\
|
description: "State of the backend's running of the specified job, with\
|
||||||
|
@ -655,4 +655,28 @@ class QJavalinProcessHandlerTest extends QJavalinTestBase
|
|||||||
assertEquals(200, response.getStatus());
|
assertEquals(200, response.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** test running a process who has a value with a null key.
|
||||||
|
*
|
||||||
|
** This was a regression - that threw an exception from jackson at one point in time.
|
||||||
|
**
|
||||||
|
** Note: ported to v1
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
public void test_processPutsNullKeyInMap()
|
||||||
|
{
|
||||||
|
HttpResponse<String> response = Unirest.get(BASE_URL + "/processes/" + TestUtils.PROCESS_NAME_PUTS_NULL_KEY_IN_MAP + "/init").asString();
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
|
||||||
|
assertNotNull(jsonObject);
|
||||||
|
JSONObject values = jsonObject.getJSONObject("values");
|
||||||
|
JSONObject mapWithNullKey = values.getJSONObject("mapWithNullKey");
|
||||||
|
assertTrue(mapWithNullKey.has("")); // null key currently set to become empty-string key...
|
||||||
|
assertEquals("hadNullKey", mapWithNullKey.getString(""));
|
||||||
|
assertTrue(mapWithNullKey.has("one"));
|
||||||
|
assertEquals("1", mapWithNullKey.getString("one"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.javalin;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -53,6 +54,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReferenceLambda;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||||
@ -112,6 +114,8 @@ public class TestUtils
|
|||||||
public static final String PROCESS_NAME_SIMPLE_THROW = "simpleThrow";
|
public static final String PROCESS_NAME_SIMPLE_THROW = "simpleThrow";
|
||||||
public static final String PROCESS_NAME_SLEEP_INTERACTIVE = "sleepInteractive";
|
public static final String PROCESS_NAME_SLEEP_INTERACTIVE = "sleepInteractive";
|
||||||
|
|
||||||
|
public static final String PROCESS_NAME_PUTS_NULL_KEY_IN_MAP = "putsNullKeyInMap";
|
||||||
|
|
||||||
public static final String STEP_NAME_SLEEPER = "sleeper";
|
public static final String STEP_NAME_SLEEPER = "sleeper";
|
||||||
public static final String STEP_NAME_THROWER = "thrower";
|
public static final String STEP_NAME_THROWER = "thrower";
|
||||||
|
|
||||||
@ -177,6 +181,7 @@ public class TestUtils
|
|||||||
qInstance.addProcess(defineProcessGreetPeopleInteractive());
|
qInstance.addProcess(defineProcessGreetPeopleInteractive());
|
||||||
qInstance.addProcess(defineProcessSimpleSleep());
|
qInstance.addProcess(defineProcessSimpleSleep());
|
||||||
qInstance.addProcess(defineProcessScreenThenSleep());
|
qInstance.addProcess(defineProcessScreenThenSleep());
|
||||||
|
qInstance.addProcess(defineProcessPutsNullKeyInMap());
|
||||||
qInstance.addProcess(defineProcessSimpleThrow());
|
qInstance.addProcess(defineProcessSimpleThrow());
|
||||||
qInstance.addReport(definePersonsReport());
|
qInstance.addReport(definePersonsReport());
|
||||||
qInstance.addPossibleValueSource(definePossibleValueSourcePerson());
|
qInstance.addPossibleValueSource(definePossibleValueSourcePerson());
|
||||||
@ -554,6 +559,26 @@ public class TestUtils
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Define an interactive version of the 'greet people' process
|
||||||
|
*******************************************************************************/
|
||||||
|
private static QProcessMetaData defineProcessPutsNullKeyInMap()
|
||||||
|
{
|
||||||
|
return new QProcessMetaData()
|
||||||
|
.withName(PROCESS_NAME_PUTS_NULL_KEY_IN_MAP)
|
||||||
|
.withTableName(TABLE_NAME_PERSON)
|
||||||
|
.withStep(new QBackendStepMetaData().withName("step")
|
||||||
|
.withCode(new QCodeReferenceLambda<BackendStep>((runBackendStepInput, runBackendStepOutput) ->
|
||||||
|
{
|
||||||
|
HashMap<String, String> mapWithNullKey = new HashMap<>();
|
||||||
|
mapWithNullKey.put(null, "hadNullKey");
|
||||||
|
mapWithNullKey.put("one", "1");
|
||||||
|
runBackendStepOutput.addValue("mapWithNullKey", mapWithNullKey);
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Define a process with just one step that sleeps and then throws
|
** Define a process with just one step that sleeps and then throws
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -38,7 +38,7 @@ import org.junit.jupiter.api.BeforeEach;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public abstract class SpecTestBase
|
public abstract class SpecTestBase
|
||||||
{
|
{
|
||||||
private static int PORT = 6263;
|
private static int PORT = 6273;
|
||||||
|
|
||||||
protected static Javalin service;
|
protected static Javalin service;
|
||||||
|
|
||||||
|
@ -216,4 +216,26 @@ class ProcessInitSpecV1Test extends SpecTestBase
|
|||||||
// todo - in a higher-level test, resume test_processInitGoingAsync at the // request job status before sleep is done // line
|
// todo - in a higher-level test, resume test_processInitGoingAsync at the // request job status before sleep is done // line
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testProcessPutsNullKeyInMap()
|
||||||
|
{
|
||||||
|
HttpResponse<String> response = Unirest.post(getBaseUrlAndPath() + "/processes/" + TestUtils.PROCESS_NAME_PUTS_NULL_KEY_IN_MAP + "/init")
|
||||||
|
.multiPartContent()
|
||||||
|
.asString();
|
||||||
|
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
|
||||||
|
|
||||||
|
JSONObject values = jsonObject.getJSONObject("values");
|
||||||
|
JSONObject mapWithNullKey = values.getJSONObject("mapWithNullKey");
|
||||||
|
assertTrue(mapWithNullKey.has("")); // null key currently set to become empty-string key...
|
||||||
|
assertEquals("hadNullKey", mapWithNullKey.getString(""));
|
||||||
|
assertTrue(mapWithNullKey.has("one"));
|
||||||
|
assertEquals("1", mapWithNullKey.getString("one"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user