mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
More BLOB; FILE_DOWNLOAD adornment; javalin field download endpoint
This commit is contained in:
@ -22,6 +22,7 @@
|
|||||||
package com.kingsrook.qqq.backend.core.instances;
|
package com.kingsrook.qqq.backend.core.instances;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -52,6 +53,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.QMiddlewareInstanceMetaData
|
|||||||
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.QCodeType;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||||
@ -82,6 +85,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheOf;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheUseCase;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheUseCase;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -418,7 +422,7 @@ public class QInstanceValidator
|
|||||||
{
|
{
|
||||||
table.getFields().forEach((fieldName, field) ->
|
table.getFields().forEach((fieldName, field) ->
|
||||||
{
|
{
|
||||||
validateTableField(qInstance, tableName, fieldName, field);
|
validateTableField(qInstance, tableName, fieldName, table, field);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -659,7 +663,7 @@ public class QInstanceValidator
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void validateTableField(QInstance qInstance, String tableName, String fieldName, QFieldMetaData field)
|
private void validateTableField(QInstance qInstance, String tableName, String fieldName, QTableMetaData table, QFieldMetaData field)
|
||||||
{
|
{
|
||||||
assertCondition(Objects.equals(fieldName, field.getName()),
|
assertCondition(Objects.equals(fieldName, field.getName()),
|
||||||
"Inconsistent naming in table " + tableName + " for field " + fieldName + "/" + field.getName() + ".");
|
"Inconsistent naming in table " + tableName + " for field " + fieldName + "/" + field.getName() + ".");
|
||||||
@ -701,6 +705,55 @@ public class QInstanceValidator
|
|||||||
assertCondition(fieldSecurityLock.getDefaultBehavior() != null, prefix + "has a fieldSecurityLock that is missing a defaultBehavior");
|
assertCondition(fieldSecurityLock.getDefaultBehavior() != null, prefix + "has a fieldSecurityLock that is missing a defaultBehavior");
|
||||||
assertCondition(CollectionUtils.nullSafeHasContents(fieldSecurityLock.getOverrideValues()), prefix + "has a fieldSecurityLock that is missing overrideValues");
|
assertCondition(CollectionUtils.nullSafeHasContents(fieldSecurityLock.getOverrideValues()), prefix + "has a fieldSecurityLock that is missing overrideValues");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(FieldAdornment adornment : CollectionUtils.nonNullList(field.getAdornments()))
|
||||||
|
{
|
||||||
|
Map<String, Serializable> adornmentValues = CollectionUtils.nonNullMap(adornment.getValues());
|
||||||
|
if(assertCondition(adornment.getType() != null, prefix + "has an adornment that is missing a type"))
|
||||||
|
{
|
||||||
|
String adornmentPrefix = prefix.trim() + ", " + adornment.getType() + " adornment ";
|
||||||
|
switch(adornment.getType())
|
||||||
|
{
|
||||||
|
case SIZE ->
|
||||||
|
{
|
||||||
|
String width = ValueUtils.getValueAsString(adornmentValues.get("width"));
|
||||||
|
if(assertCondition(StringUtils.hasContent(width), adornmentPrefix + "is missing a width value"))
|
||||||
|
{
|
||||||
|
assertNoException(() -> AdornmentType.Size.valueOf(width.toUpperCase()), adornmentPrefix + "has an unrecognized width value [" + width + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case FILE_DOWNLOAD ->
|
||||||
|
{
|
||||||
|
String fileNameField = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FIELD));
|
||||||
|
if(StringUtils.hasContent(fileNameField)) // file name isn't required - but if given, must be a field on the table.
|
||||||
|
{
|
||||||
|
assertNoException(() -> table.getField(fileNameField), adornmentPrefix + "specifies an unrecognized fileNameField [" + fileNameField + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(adornmentValues.containsKey(AdornmentType.FileDownloadValues.FILE_NAME_FORMAT_FIELDS))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<String> formatFieldNames = (List<String>) adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FORMAT_FIELDS);
|
||||||
|
for(String formatFieldName : CollectionUtils.nonNullList(formatFieldNames))
|
||||||
|
{
|
||||||
|
assertNoException(() -> table.getField(formatFieldName), adornmentPrefix + "specifies an unrecognized field name in fileNameFormatFields [" + formatFieldName + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
errors.add(adornmentPrefix + "fileNameFormatFields could not be accessed (is it a List<String>?)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default ->
|
||||||
|
{
|
||||||
|
// no validations by default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ public enum AdornmentType
|
|||||||
CODE_EDITOR,
|
CODE_EDITOR,
|
||||||
RENDER_HTML,
|
RENDER_HTML,
|
||||||
REVEAL,
|
REVEAL,
|
||||||
|
FILE_DOWNLOAD,
|
||||||
ERROR;
|
ERROR;
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// keep these values in sync with AdornmentType.ts in qqq-frontend-core //
|
// keep these values in sync with AdornmentType.ts in qqq-frontend-core //
|
||||||
@ -58,6 +59,26 @@ public enum AdornmentType
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public interface FileDownloadValues
|
||||||
|
{
|
||||||
|
String FILE_NAME_FIELD = "fileNameField";
|
||||||
|
String DEFAULT_EXTENSION = "defaultExtension";
|
||||||
|
String DEFAULT_MIME_TYPE = "defaultMimeType";
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
// use these two together, as in: //
|
||||||
|
// FILE_NAME_FORMAT = "Order %s Packing Slip.pdf" //
|
||||||
|
// FILE_NAME_FORMAT_FIELDS = "orderId" //
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
String FILE_NAME_FORMAT = "fileNameFormat";
|
||||||
|
String FILE_NAME_FORMAT_FIELDS = "fileNameFormatFields";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -112,6 +133,7 @@ public enum AdornmentType
|
|||||||
XSMALL,
|
XSMALL,
|
||||||
SMALL,
|
SMALL,
|
||||||
MEDIUM,
|
MEDIUM,
|
||||||
|
MEDLARGE,
|
||||||
LARGE,
|
LARGE,
|
||||||
XLARGE;
|
XLARGE;
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ public class QFrontendFieldMetaData
|
|||||||
private QFieldType type;
|
private QFieldType type;
|
||||||
private boolean isRequired;
|
private boolean isRequired;
|
||||||
private boolean isEditable;
|
private boolean isEditable;
|
||||||
|
private boolean isHeavy;
|
||||||
private String possibleValueSourceName;
|
private String possibleValueSourceName;
|
||||||
private String displayFormat;
|
private String displayFormat;
|
||||||
|
|
||||||
@ -64,6 +65,7 @@ public class QFrontendFieldMetaData
|
|||||||
this.type = fieldMetaData.getType();
|
this.type = fieldMetaData.getType();
|
||||||
this.isRequired = fieldMetaData.getIsRequired();
|
this.isRequired = fieldMetaData.getIsRequired();
|
||||||
this.isEditable = fieldMetaData.getIsEditable();
|
this.isEditable = fieldMetaData.getIsEditable();
|
||||||
|
this.isHeavy = fieldMetaData.getIsHeavy();
|
||||||
this.possibleValueSourceName = fieldMetaData.getPossibleValueSourceName();
|
this.possibleValueSourceName = fieldMetaData.getPossibleValueSourceName();
|
||||||
this.displayFormat = fieldMetaData.getDisplayFormat();
|
this.displayFormat = fieldMetaData.getDisplayFormat();
|
||||||
this.adornments = fieldMetaData.getAdornments();
|
this.adornments = fieldMetaData.getAdornments();
|
||||||
@ -126,6 +128,17 @@ public class QFrontendFieldMetaData
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for isHeavy
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public boolean getIsHeavy()
|
||||||
|
{
|
||||||
|
return isHeavy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for displayFormat
|
** Getter for displayFormat
|
||||||
**
|
**
|
||||||
|
@ -47,6 +47,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||||
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.code.QCodeUsage;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
||||||
@ -1732,6 +1734,35 @@ class QInstanceValidatorTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testFieldAdornments()
|
||||||
|
{
|
||||||
|
Function<QInstance, QFieldMetaData> fieldExtractor = qInstance -> qInstance.getTable(TestUtils.TABLE_NAME_PERSON).getField("firstName");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance -> fieldExtractor.apply(qInstance).withFieldAdornment(new FieldAdornment())), "adornment that is missing a type");
|
||||||
|
assertValidationSuccess((qInstance -> fieldExtractor.apply(qInstance).withFieldAdornment(new FieldAdornment().withType(AdornmentType.REVEAL))));
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// type-specific value checks //
|
||||||
|
////////////////////////////////
|
||||||
|
assertValidationFailureReasons((qInstance -> fieldExtractor.apply(qInstance).withFieldAdornment(new FieldAdornment(AdornmentType.SIZE))), "missing a width value");
|
||||||
|
assertValidationFailureReasons((qInstance -> fieldExtractor.apply(qInstance).withFieldAdornment(new FieldAdornment(AdornmentType.SIZE).withValue("width", "foo"))), "unrecognized width value");
|
||||||
|
assertValidationSuccess((qInstance -> fieldExtractor.apply(qInstance).withFieldAdornment(new FieldAdornment(AdornmentType.SIZE).withValue("width", AdornmentType.Size.MEDIUM))));
|
||||||
|
assertValidationSuccess((qInstance -> fieldExtractor.apply(qInstance).withFieldAdornment(AdornmentType.Size.SMALL.toAdornment())));
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance -> fieldExtractor.apply(qInstance).withFieldAdornment(new FieldAdornment(AdornmentType.FILE_DOWNLOAD).withValue("fileNameField", "foo"))), "unrecognized fileNameField [foo]");
|
||||||
|
assertValidationSuccess((qInstance -> fieldExtractor.apply(qInstance).withFieldAdornment(new FieldAdornment(AdornmentType.FILE_DOWNLOAD).withValue("fileNameField", "lastName"))));
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance -> fieldExtractor.apply(qInstance).withFieldAdornment(new FieldAdornment(AdornmentType.FILE_DOWNLOAD).withValue("fileNameFormatFields", "foo"))), "fileNameFormatFields could not be accessed");
|
||||||
|
assertValidationFailureReasons((qInstance -> fieldExtractor.apply(qInstance).withFieldAdornment(new FieldAdornment(AdornmentType.FILE_DOWNLOAD).withValue("fileNameFormatFields", new ArrayList<>(List.of("foo"))))), "unrecognized field name in fileNameFormatFields [foo]");
|
||||||
|
assertValidationSuccess((qInstance -> fieldExtractor.apply(qInstance).withFieldAdornment(new FieldAdornment(AdornmentType.FILE_DOWNLOAD).withValue("fileNameFormatFields", new ArrayList<>(List.of("lastName"))))));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -872,6 +872,10 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
|||||||
{
|
{
|
||||||
return (QueryManager.getBoolean(resultSet, i));
|
return (QueryManager.getBoolean(resultSet, i));
|
||||||
}
|
}
|
||||||
|
case BLOB:
|
||||||
|
{
|
||||||
|
return (QueryManager.getByteArray(resultSet, i));
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
throw new IllegalStateException("Unexpected field type: " + type);
|
throw new IllegalStateException("Unexpected field type: " + type);
|
||||||
|
@ -38,6 +38,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
|||||||
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.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
|
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
|
||||||
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -58,7 +59,7 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
|
|||||||
|
|
||||||
if(CollectionUtils.nullSafeIsEmpty(insertInput.getRecords()))
|
if(CollectionUtils.nullSafeIsEmpty(insertInput.getRecords()))
|
||||||
{
|
{
|
||||||
LOG.debug("Insert request called with 0 records. Returning with no-op");
|
LOG.debug("Insert request called with 0 records. Returning with no-op", logPair("tableName", insertInput.getTableName()));
|
||||||
rs.setRecords(new ArrayList<>());
|
rs.setRecords(new ArrayList<>());
|
||||||
return (rs);
|
return (rs);
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import java.time.Duration;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -53,6 +54,7 @@ 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;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.SearchPossibleValueSourceAction;
|
import com.kingsrook.qqq.backend.core.actions.values.SearchPossibleValueSourceAction;
|
||||||
import com.kingsrook.qqq.backend.core.adapters.QInstanceAdapter;
|
import com.kingsrook.qqq.backend.core.adapters.QInstanceAdapter;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
@ -95,7 +97,10 @@ import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
|
||||||
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.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
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.session.QSession;
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
@ -354,6 +359,9 @@ public class QJavalinImplementation
|
|||||||
put("", QJavalinImplementation::dataUpdate); // todo - want different semantics??
|
put("", QJavalinImplementation::dataUpdate); // todo - want different semantics??
|
||||||
delete("", QJavalinImplementation::dataDelete);
|
delete("", QJavalinImplementation::dataDelete);
|
||||||
|
|
||||||
|
get("/{fieldName}/{filename}", QJavalinImplementation::dataDownloadRecordField);
|
||||||
|
post("/{fieldName}/{filename}", QJavalinImplementation::dataDownloadRecordField);
|
||||||
|
|
||||||
QJavalinScriptsHandler.defineRecordRoutes();
|
QJavalinScriptsHandler.defineRecordRoutes();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -733,6 +741,7 @@ public class QJavalinImplementation
|
|||||||
getInput.setTableName(tableName);
|
getInput.setTableName(tableName);
|
||||||
getInput.setShouldGenerateDisplayValues(true);
|
getInput.setShouldGenerateDisplayValues(true);
|
||||||
getInput.setShouldTranslatePossibleValues(true);
|
getInput.setShouldTranslatePossibleValues(true);
|
||||||
|
getInput.setShouldFetchHeavyFields(true);
|
||||||
|
|
||||||
PermissionsHelper.checkTablePermissionThrowing(getInput, TablePermissionSubType.READ);
|
PermissionsHelper.checkTablePermissionThrowing(getInput, TablePermissionSubType.READ);
|
||||||
|
|
||||||
@ -744,6 +753,149 @@ public class QJavalinImplementation
|
|||||||
GetAction getAction = new GetAction();
|
GetAction getAction = new GetAction();
|
||||||
GetOutput getOutput = getAction.execute(getInput);
|
GetOutput getOutput = getAction.execute(getInput);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
// throw a not found error if the record isn't found //
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
QRecord record = getOutput.getRecord();
|
||||||
|
if(record == null)
|
||||||
|
{
|
||||||
|
throw (new QNotFoundException("Could not find " + table.getLabel() + " with "
|
||||||
|
+ table.getFields().get(table.getPrimaryKeyField()).getLabel() + " of " + primaryKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
setBlobValuesToDownloadUrls(table, List.of(record));
|
||||||
|
|
||||||
|
QJavalinAccessLogger.logEndSuccess();
|
||||||
|
context.result(JsonUtils.toJson(record));
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
QJavalinAccessLogger.logEndFail(e);
|
||||||
|
handleException(context, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** For any BLOB type fields in the list of records, change their value to
|
||||||
|
** the URL where they can be downloaded, and set their display value to a file name.
|
||||||
|
*******************************************************************************/
|
||||||
|
private static void setBlobValuesToDownloadUrls(QTableMetaData table, List<QRecord> records)
|
||||||
|
{
|
||||||
|
for(QFieldMetaData field : table.getFields().values())
|
||||||
|
{
|
||||||
|
if(field.getType().equals(QFieldType.BLOB))
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// file name comes from: //
|
||||||
|
// if there's a FILE_DOWNLOAD adornment, with a FILE_NAME_FIELD value, then the full filename comes from that field //
|
||||||
|
// - unless it was empty - then we do the "default thing": //
|
||||||
|
// else - the "default thing" is: //
|
||||||
|
// - tableLabel primaryKey fieldLabel //
|
||||||
|
// - and - if the FILE_DOWNLOAD adornment had a DEFAULT_EXTENSION, then it gets added (preceded by a dot) //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Optional<FieldAdornment> fileDownloadAdornment = field.getAdornments().stream().filter(a -> a.getType().equals(AdornmentType.FILE_DOWNLOAD)).findFirst();
|
||||||
|
Map<String, Serializable> adornmentValues = Collections.emptyMap();
|
||||||
|
|
||||||
|
if(fileDownloadAdornment.isPresent())
|
||||||
|
{
|
||||||
|
adornmentValues = fileDownloadAdornment.get().getValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileNameField = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FIELD));
|
||||||
|
String fileNameFormat = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FORMAT));
|
||||||
|
String defaultExtension = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.DEFAULT_EXTENSION));
|
||||||
|
|
||||||
|
for(QRecord record : records)
|
||||||
|
{
|
||||||
|
Serializable primaryKey = record.getValue(table.getPrimaryKeyField());
|
||||||
|
String fileName = null;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// try to make file name from the fileNameField //
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
if(StringUtils.hasContent(fileNameField))
|
||||||
|
{
|
||||||
|
fileName = record.getValueString(fileNameField);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!StringUtils.hasContent(fileName))
|
||||||
|
{
|
||||||
|
if(StringUtils.hasContent(fileNameFormat))
|
||||||
|
{
|
||||||
|
@SuppressWarnings("unchecked") // instance validation should make this safe!
|
||||||
|
List<String> fileNameFormatFields = (List<String>) adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FORMAT_FIELDS);
|
||||||
|
fileName = QValueFormatter.formatStringWithValues(fileNameFormat, fileNameFormatFields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!StringUtils.hasContent(fileName))
|
||||||
|
{
|
||||||
|
//////////////////////////////////
|
||||||
|
// make default name if missing //
|
||||||
|
//////////////////////////////////
|
||||||
|
fileName = table.getLabel() + " " + primaryKey + " " + field.getLabel();
|
||||||
|
|
||||||
|
if(StringUtils.hasContent(defaultExtension))
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// add default extension if we have one //
|
||||||
|
//////////////////////////////////////////
|
||||||
|
fileName += "." + defaultExtension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record.setValue(field.getName(), "/data/" + table.getName() + "/" + primaryKey + "/" + field.getName() + "/" + fileName);
|
||||||
|
record.setDisplayValue(field.getName(), fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static void dataDownloadRecordField(Context context)
|
||||||
|
{
|
||||||
|
String tableName = context.pathParam("table");
|
||||||
|
String primaryKey = context.pathParam("primaryKey");
|
||||||
|
String fieldName = context.pathParam("fieldName");
|
||||||
|
String filename = context.pathParam("filename");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
QTableMetaData table = qInstance.getTable(tableName);
|
||||||
|
GetInput getInput = new GetInput();
|
||||||
|
|
||||||
|
setupSession(context, getInput);
|
||||||
|
QJavalinAccessLogger.logStart("downloadRecordField", logPair("table", tableName), logPair("primaryKey", primaryKey), logPair("fieldName", fieldName));
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// validate field name - 404 if not found //
|
||||||
|
////////////////////////////////////////////
|
||||||
|
QFieldMetaData fieldMetaData;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fieldMetaData = table.getField(fieldName);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
throw (new QNotFoundException("Could not find field named " + fieldName + " on table " + tableName));
|
||||||
|
}
|
||||||
|
|
||||||
|
getInput.setTableName(tableName);
|
||||||
|
getInput.setShouldFetchHeavyFields(true);
|
||||||
|
|
||||||
|
PermissionsHelper.checkTablePermissionThrowing(getInput, TablePermissionSubType.READ);
|
||||||
|
|
||||||
|
getInput.setPrimaryKey(primaryKey);
|
||||||
|
|
||||||
|
GetAction getAction = new GetAction();
|
||||||
|
GetOutput getOutput = getAction.execute(getInput);
|
||||||
|
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
// throw a not found error if the record isn't found //
|
// throw a not found error if the record isn't found //
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
@ -753,8 +905,27 @@ public class QJavalinImplementation
|
|||||||
+ table.getFields().get(table.getPrimaryKeyField()).getLabel() + " of " + primaryKey));
|
+ table.getFields().get(table.getPrimaryKeyField()).getLabel() + " of " + primaryKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String mimeType = null;
|
||||||
|
Optional<FieldAdornment> fileDownloadAdornment = fieldMetaData.getAdornments().stream().filter(a -> a.getType().equals(AdornmentType.FILE_DOWNLOAD)).findFirst();
|
||||||
|
if(fileDownloadAdornment.isPresent())
|
||||||
|
{
|
||||||
|
Map<String, Serializable> values = fileDownloadAdornment.get().getValues();
|
||||||
|
mimeType = ValueUtils.getValueAsString(values.get(AdornmentType.FileDownloadValues.DEFAULT_MIME_TYPE));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mimeType != null)
|
||||||
|
{
|
||||||
|
context.contentType(mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(context.queryParamMap().containsKey("download") || context.formParamMap().containsKey("download"))
|
||||||
|
{
|
||||||
|
context.header("Content-Disposition", "attachment; filename=" + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.result(getOutput.getRecord().getValueByteArray(fieldName));
|
||||||
|
|
||||||
QJavalinAccessLogger.logEndSuccess();
|
QJavalinAccessLogger.logEndSuccess();
|
||||||
context.result(JsonUtils.toJson(getOutput.getRecord()));
|
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
@ -888,6 +1059,8 @@ public class QJavalinImplementation
|
|||||||
QueryAction queryAction = new QueryAction();
|
QueryAction queryAction = new QueryAction();
|
||||||
QueryOutput queryOutput = queryAction.execute(queryInput);
|
QueryOutput queryOutput = queryAction.execute(queryInput);
|
||||||
|
|
||||||
|
setBlobValuesToDownloadUrls(QContext.getQInstance().getTable(table), queryOutput.getRecords());
|
||||||
|
|
||||||
QJavalinAccessLogger.logEndSuccess(logPair("recordCount", queryOutput.getRecords().size()), logPairIfSlow("filter", filter, SLOW_LOG_THRESHOLD_MS), logPairIfSlow("joins", queryJoins, SLOW_LOG_THRESHOLD_MS));
|
QJavalinAccessLogger.logEndSuccess(logPair("recordCount", queryOutput.getRecords().size()), logPairIfSlow("filter", filter, SLOW_LOG_THRESHOLD_MS), logPairIfSlow("joins", queryJoins, SLOW_LOG_THRESHOLD_MS));
|
||||||
context.result(JsonUtils.toJson(queryOutput));
|
context.result(JsonUtils.toJson(queryOutput));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user