CE-1772 - update fileDownload adornment type to be able to specify a process name or custom code-ref, to run along with downloading a field's file.

This commit is contained in:
2024-12-17 11:40:11 -06:00
parent 96761b7162
commit c5f41a8042
7 changed files with 371 additions and 11 deletions

View File

@ -44,6 +44,7 @@ import java.util.Optional;
import java.util.function.Supplier;
import com.fasterxml.jackson.core.type.TypeReference;
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobManager;
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
import com.kingsrook.qqq.backend.core.actions.dashboard.RenderWidgetAction;
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataAction;
import com.kingsrook.qqq.backend.core.actions.metadata.ProcessMetaDataAction;
@ -51,6 +52,8 @@ import com.kingsrook.qqq.backend.core.actions.metadata.TableMetaDataAction;
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionCheckResult;
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
import com.kingsrook.qqq.backend.core.actions.permissions.TablePermissionSubType;
import com.kingsrook.qqq.backend.core.actions.processes.QProcessCallbackFactory;
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
import com.kingsrook.qqq.backend.core.actions.reporting.ExportAction;
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
@ -77,6 +80,7 @@ import com.kingsrook.qqq.backend.core.model.actions.metadata.ProcessMetaDataInpu
import com.kingsrook.qqq.backend.core.model.actions.metadata.ProcessMetaDataOutput;
import com.kingsrook.qqq.backend.core.model.actions.metadata.TableMetaDataInput;
import com.kingsrook.qqq.backend.core.model.actions.metadata.TableMetaDataOutput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ExportInput;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ExportOutput;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportDestination;
@ -106,6 +110,7 @@ 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.metadata.QBackendMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
@ -130,6 +135,7 @@ import com.kingsrook.qqq.backend.core.utils.ValueUtils;
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeConsumer;
import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeFunction;
import com.kingsrook.qqq.middleware.javalin.misc.DownloadFileSupplementalAction;
import io.javalin.Javalin;
import io.javalin.apibuilder.EndpointGroup;
import io.javalin.http.Context;
@ -1089,12 +1095,13 @@ public class QJavalinImplementation
throw (new QNotFoundException("Could not find " + table.getLabel() + " with " + 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();
String mimeType = null;
Optional<FieldAdornment> fileDownloadAdornment = fieldMetaData.getAdornments().stream().filter(a -> a.getType().equals(AdornmentType.FILE_DOWNLOAD)).findFirst();
Map<String, Serializable> adornmentValues = null;
if(fileDownloadAdornment.isPresent())
{
Map<String, Serializable> values = fileDownloadAdornment.get().getValues();
mimeType = ValueUtils.getValueAsString(values.get(AdornmentType.FileDownloadValues.DEFAULT_MIME_TYPE));
adornmentValues = fileDownloadAdornment.get().getValues();
mimeType = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.DEFAULT_MIME_TYPE));
}
if(mimeType != null)
@ -1107,7 +1114,56 @@ public class QJavalinImplementation
context.header("Content-Disposition", "attachment; filename=" + filename);
}
context.result(getOutput.getRecord().getValueByteArray(fieldName));
//////////////////////////////////////////////////////////////////////////////////////////////
// if the adornment has a supplemental process name in it, or a supplemental code reference //
// then execute that custom code - e.g., to log that the file was downloaded. //
//////////////////////////////////////////////////////////////////////////////////////////////
if(fileDownloadAdornment.isPresent())
{
String processName = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.SUPPLEMENTAL_PROCESS_NAME));
if(StringUtils.hasContent(processName))
{
RunProcessInput input = new RunProcessInput();
input.setProcessName(processName);
input.setCallback(QProcessCallbackFactory.forRecord(getOutput.getRecord()));
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.SKIP);
input.addValue("tableName", tableName);
input.addValue("primaryKey", primaryKey);
input.addValue("fieldName", fieldName);
input.addValue("filename", filename);
new RunProcessAction().execute(input);
}
else if(adornmentValues.containsKey(AdornmentType.FileDownloadValues.SUPPLEMENTAL_CODE_REFERENCE))
{
QCodeReference codeReference = (QCodeReference) adornmentValues.get(AdornmentType.FileDownloadValues.SUPPLEMENTAL_CODE_REFERENCE);
DownloadFileSupplementalAction action = QCodeLoader.getAdHoc(DownloadFileSupplementalAction.class, codeReference);
DownloadFileSupplementalAction.DownloadFileSupplementalActionInput input = new DownloadFileSupplementalAction.DownloadFileSupplementalActionInput()
.withTableName(tableName)
.withFieldName(fieldName)
.withPrimaryKey(primaryKey)
.withFileName(filename);
DownloadFileSupplementalAction.DownloadFileSupplementalActionOutput output = new DownloadFileSupplementalAction.DownloadFileSupplementalActionOutput();
action.run(input, output);
}
}
/////////////////////////////////////////////////////////
// if the field is a BLOB - send the bytes to the user //
/////////////////////////////////////////////////////////
if(QFieldType.BLOB.equals(fieldMetaData.getType()))
{
context.result(getOutput.getRecord().getValueByteArray(fieldName));
}
else
{
//////////////////////////////////////////////////////////////////
// else - assume a string is a URL - and issue a redirect to it //
//////////////////////////////////////////////////////////////////
context.redirect(getOutput.getRecord().getValueString(fieldName));
}
QJavalinAccessLogger.logEndSuccess();
}

View File

@ -0,0 +1,198 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.middleware.javalin.misc;
import com.kingsrook.qqq.backend.core.exceptions.QException;
/*******************************************************************************
** custom code that can run when user downloads a file. Set as a code-reference
** on a field adornment.
*******************************************************************************/
public interface DownloadFileSupplementalAction
{
/***************************************************************************
**
***************************************************************************/
void run(DownloadFileSupplementalActionInput input, DownloadFileSupplementalActionOutput output) throws QException;
/***************************************************************************
**
***************************************************************************/
class DownloadFileSupplementalActionInput
{
private String tableName;
private String primaryKey;
private String fieldName;
private String fileName;
/*******************************************************************************
** Getter for tableName
**
*******************************************************************************/
public String getTableName()
{
return tableName;
}
/*******************************************************************************
** Setter for tableName
**
*******************************************************************************/
public void setTableName(String tableName)
{
this.tableName = tableName;
}
/*******************************************************************************
** Fluent setter for tableName
**
*******************************************************************************/
public DownloadFileSupplementalActionInput withTableName(String tableName)
{
this.tableName = tableName;
return (this);
}
/*******************************************************************************
** Getter for primaryKey
**
*******************************************************************************/
public String getPrimaryKey()
{
return primaryKey;
}
/*******************************************************************************
** Setter for primaryKey
**
*******************************************************************************/
public void setPrimaryKey(String primaryKey)
{
this.primaryKey = primaryKey;
}
/*******************************************************************************
** Fluent setter for primaryKey
**
*******************************************************************************/
public DownloadFileSupplementalActionInput withPrimaryKey(String primaryKey)
{
this.primaryKey = primaryKey;
return (this);
}
/*******************************************************************************
** Getter for fieldName
**
*******************************************************************************/
public String getFieldName()
{
return fieldName;
}
/*******************************************************************************
** Setter for fieldName
**
*******************************************************************************/
public void setFieldName(String fieldName)
{
this.fieldName = fieldName;
}
/*******************************************************************************
** Fluent setter for fieldName
**
*******************************************************************************/
public DownloadFileSupplementalActionInput withFieldName(String fieldName)
{
this.fieldName = fieldName;
return (this);
}
/*******************************************************************************
** Getter for fileName
**
*******************************************************************************/
public String getFileName()
{
return fileName;
}
/*******************************************************************************
** Setter for fileName
**
*******************************************************************************/
public void setFileName(String fileName)
{
this.fileName = fileName;
}
/*******************************************************************************
** Fluent setter for fileName
**
*******************************************************************************/
public DownloadFileSupplementalActionInput withFileName(String fileName)
{
this.fileName = fileName;
return (this);
}
}
/***************************************************************************
**
***************************************************************************/
class DownloadFileSupplementalActionOutput
{
}
}