mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
updated to do bulk audits better, along with audit details
This commit is contained in:
@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.actions.audits;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -36,6 +37,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.audits.AuditInput;
|
import com.kingsrook.qqq.backend.core.model.actions.audits.AuditInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.audits.AuditOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.audits.AuditOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.audits.AuditSingleInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
@ -45,31 +47,78 @@ import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock
|
|||||||
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.QUser;
|
import com.kingsrook.qqq.backend.core.model.session.QUser;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.Pair;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Insert an audit (e.g., the same one) against 1 or more records.
|
** Insert 1 or more audits (and optionally their children, auditDetails)
|
||||||
**
|
**
|
||||||
** Takes care of managing the foreign key tables.
|
** Takes care of managing the foreign key tables (auditTable, auditUser).
|
||||||
**
|
**
|
||||||
** Enforces that security key values are provided, if the table has any.
|
** Enforces that security key values are provided, if the table has any. Note that
|
||||||
|
** might mean a null is given for a particular key, but at least the key must be present.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput>
|
public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput>
|
||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(AuditAction.class);
|
private static final QLogger LOG = QLogger.getLogger(AuditAction.class);
|
||||||
|
|
||||||
|
private Map<Pair<String, String>, Integer> cachedFetches = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
** Execute to insert 1 audit, with no details (child records)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static void execute(String tableName, Integer recordId, Map<String, Serializable> securityKeyValues, String message)
|
public static void execute(String tableName, Integer recordId, Map<String, Serializable> securityKeyValues, String message)
|
||||||
{
|
{
|
||||||
new AuditAction().execute(new AuditInput()
|
execute(tableName, recordId, securityKeyValues, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Execute to insert 1 audit, with a list of details (child records)
|
||||||
|
*******************************************************************************/
|
||||||
|
public static void execute(String tableName, Integer recordId, Map<String, Serializable> securityKeyValues, String message, List<String> details)
|
||||||
|
{
|
||||||
|
new AuditAction().execute(new AuditInput().withAuditSingleInput(new AuditSingleInput()
|
||||||
.withAuditTableName(tableName)
|
.withAuditTableName(tableName)
|
||||||
.withRecordIdList(List.of(recordId))
|
.withRecordId(recordId)
|
||||||
.withSecurityKeyValues(securityKeyValues)
|
.withSecurityKeyValues(securityKeyValues)
|
||||||
.withMessage(message));
|
.withMessage(message)
|
||||||
|
.withDetails(details)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Add 1 auditSingleInput to an AuditInput object - with no details (child records).
|
||||||
|
*******************************************************************************/
|
||||||
|
public static AuditInput appendToInput(AuditInput auditInput, String tableName, Integer recordId, Map<String, Serializable> securityKeyValues, String message)
|
||||||
|
{
|
||||||
|
return (appendToInput(auditInput, tableName, recordId, securityKeyValues, message, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Add 1 auditSingleInput to an AuditInput object - with a list of details (child records).
|
||||||
|
*******************************************************************************/
|
||||||
|
public static AuditInput appendToInput(AuditInput auditInput, String tableName, Integer recordId, Map<String, Serializable> securityKeyValues, String message, List<String> details)
|
||||||
|
{
|
||||||
|
if(auditInput == null)
|
||||||
|
{
|
||||||
|
auditInput = new AuditInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
return auditInput.withAuditSingleInput(new AuditSingleInput()
|
||||||
|
.withAuditTableName(tableName)
|
||||||
|
.withRecordId(recordId)
|
||||||
|
.withSecurityKeyValues(securityKeyValues)
|
||||||
|
.withMessage(message)
|
||||||
|
.withDetails(details)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -81,39 +130,55 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
|
|||||||
public AuditOutput execute(AuditInput input)
|
public AuditOutput execute(AuditInput input)
|
||||||
{
|
{
|
||||||
AuditOutput auditOutput = new AuditOutput();
|
AuditOutput auditOutput = new AuditOutput();
|
||||||
|
|
||||||
|
if(CollectionUtils.nullSafeHasContents(input.getAuditSingleInputList()))
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(input.getAuditTableName());
|
List<QRecord> auditRecords = new ArrayList<>();
|
||||||
|
|
||||||
|
for(AuditSingleInput auditSingleInput : CollectionUtils.nonNullList(input.getAuditSingleInputList()))
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////
|
||||||
|
// validate table is known in instance //
|
||||||
|
/////////////////////////////////////////
|
||||||
|
QTableMetaData table = QContext.getQInstance().getTable(auditSingleInput.getAuditTableName());
|
||||||
if(table == null)
|
if(table == null)
|
||||||
{
|
{
|
||||||
throw (new QException("Requested audit for an unrecognized table name: " + input.getAuditTableName()));
|
throw (new QException("Requested audit for an unrecognized table name: " + auditSingleInput.getAuditTableName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
// validate security keys on the table are given //
|
||||||
|
///////////////////////////////////////////////////
|
||||||
for(RecordSecurityLock recordSecurityLock : CollectionUtils.nonNullList(table.getRecordSecurityLocks()))
|
for(RecordSecurityLock recordSecurityLock : CollectionUtils.nonNullList(table.getRecordSecurityLocks()))
|
||||||
{
|
{
|
||||||
if(input.getSecurityKeyValues() == null || !input.getSecurityKeyValues().containsKey(recordSecurityLock.getSecurityKeyType()))
|
if(auditSingleInput.getSecurityKeyValues() == null || !auditSingleInput.getSecurityKeyValues().containsKey(recordSecurityLock.getSecurityKeyType()))
|
||||||
{
|
{
|
||||||
throw (new QException("Missing securityKeyValue [" + recordSecurityLock.getSecurityKeyType() + "] in audit request for table " + input.getAuditTableName()));
|
throw (new QException("Missing securityKeyValue [" + recordSecurityLock.getSecurityKeyType() + "] in audit request for table " + auditSingleInput.getAuditTableName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer auditTableId = getIdForName("auditTable", input.getAuditTableName());
|
////////////////////////////////////////////////
|
||||||
Integer auditUserId = getIdForName("auditUser", Objects.requireNonNullElse(input.getAuditUserName(), getSessionUserName()));
|
// map names to ids and handle default values //
|
||||||
Instant timestamp = Objects.requireNonNullElse(input.getTimestamp(), Instant.now());
|
////////////////////////////////////////////////
|
||||||
|
Integer auditTableId = getIdForName("auditTable", auditSingleInput.getAuditTableName());
|
||||||
|
Integer auditUserId = getIdForName("auditUser", Objects.requireNonNullElse(auditSingleInput.getAuditUserName(), getSessionUserName()));
|
||||||
|
Instant timestamp = Objects.requireNonNullElse(auditSingleInput.getTimestamp(), Instant.now());
|
||||||
|
|
||||||
List<QRecord> auditRecords = new ArrayList<>();
|
//////////////////
|
||||||
for(Integer recordId : input.getRecordIdList())
|
// build record //
|
||||||
{
|
//////////////////
|
||||||
QRecord record = new QRecord()
|
QRecord record = new QRecord()
|
||||||
.withValue("auditTableId", auditTableId)
|
.withValue("auditTableId", auditTableId)
|
||||||
.withValue("auditUserId", auditUserId)
|
.withValue("auditUserId", auditUserId)
|
||||||
.withValue("timestamp", timestamp)
|
.withValue("timestamp", timestamp)
|
||||||
.withValue("message", input.getMessage())
|
.withValue("message", auditSingleInput.getMessage())
|
||||||
.withValue("recordId", recordId);
|
.withValue("recordId", auditSingleInput.getRecordId());
|
||||||
|
|
||||||
if(input.getSecurityKeyValues() != null)
|
if(auditSingleInput.getSecurityKeyValues() != null)
|
||||||
{
|
{
|
||||||
for(Map.Entry<String, Serializable> entry : input.getSecurityKeyValues().entrySet())
|
for(Map.Entry<String, Serializable> entry : auditSingleInput.getSecurityKeyValues().entrySet())
|
||||||
{
|
{
|
||||||
record.setValue(entry.getKey(), entry.getValue());
|
record.setValue(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
@ -122,15 +187,48 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
|
|||||||
auditRecords.add(record);
|
auditRecords.add(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// do a single bulk insert //
|
||||||
|
/////////////////////////////
|
||||||
InsertInput insertInput = new InsertInput();
|
InsertInput insertInput = new InsertInput();
|
||||||
insertInput.setTableName("audit");
|
insertInput.setTableName("audit");
|
||||||
insertInput.setRecords(auditRecords);
|
insertInput.setRecords(auditRecords);
|
||||||
|
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// now look for children (auditDetails) //
|
||||||
|
//////////////////////////////////////////
|
||||||
|
int i = 0;
|
||||||
|
List<QRecord> auditDetailRecords = new ArrayList<>();
|
||||||
|
for(AuditSingleInput auditSingleInput : CollectionUtils.nonNullList(input.getAuditSingleInputList()))
|
||||||
|
{
|
||||||
|
Integer auditId = insertOutput.getRecords().get(i++).getValueInteger("id");
|
||||||
|
if(auditId == null)
|
||||||
|
{
|
||||||
|
LOG.warn("Missing an id for inserted audit - so won't be able to store its child details...");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(String detail : CollectionUtils.nonNullList(auditSingleInput.getDetails()))
|
||||||
|
{
|
||||||
|
auditDetailRecords.add(new QRecord()
|
||||||
|
.withValue("auditId", auditId)
|
||||||
|
.withValue("message", detail)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insertInput = new InsertInput();
|
||||||
|
insertInput.setTableName("auditDetail");
|
||||||
|
insertInput.setRecords(auditDetailRecords);
|
||||||
new InsertAction().execute(insertInput);
|
new InsertAction().execute(insertInput);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
LOG.error("Error performing an audit", e);
|
LOG.error("Error performing an audit", e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (auditOutput);
|
return (auditOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,9 +254,14 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private Integer getIdForName(String tableName, String nameValue) throws QException
|
private Integer getIdForName(String tableName, String nameValue) throws QException
|
||||||
{
|
{
|
||||||
|
Pair<String, String> key = new Pair<>(tableName, nameValue);
|
||||||
|
if(!cachedFetches.containsKey(key))
|
||||||
|
{
|
||||||
|
|
||||||
Integer id = fetchIdFromName(tableName, nameValue);
|
Integer id = fetchIdFromName(tableName, nameValue);
|
||||||
if(id != null)
|
if(id != null)
|
||||||
{
|
{
|
||||||
|
cachedFetches.put(key, id);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,6 +286,7 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
|
|||||||
id = insertOutput.getRecords().get(0).getValueInteger("id");
|
id = insertOutput.getRecords().get(0).getValueInteger("id");
|
||||||
if(id != null)
|
if(id != null)
|
||||||
{
|
{
|
||||||
|
cachedFetches.put(key, id);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -197,6 +301,7 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
|
|||||||
id = fetchIdFromName(tableName, nameValue);
|
id = fetchIdFromName(tableName, nameValue);
|
||||||
if(id != null)
|
if(id != null)
|
||||||
{
|
{
|
||||||
|
cachedFetches.put(key, id);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,12 +311,15 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
|
|||||||
throw (new QException("Unable to get id for " + tableName + " named " + nameValue));
|
throw (new QException("Unable to get id for " + tableName + " named " + nameValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (cachedFetches.get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private static Integer fetchIdFromName(String tableName, String nameValue) throws QException
|
private Integer fetchIdFromName(String tableName, String nameValue) throws QException
|
||||||
{
|
{
|
||||||
GetInput getInput = new GetInput();
|
GetInput getInput = new GetInput();
|
||||||
getInput.setTableName(tableName);
|
getInput.setTableName(tableName);
|
||||||
@ -221,7 +329,8 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
|
|||||||
{
|
{
|
||||||
return (getOutput.getRecord().getValueInteger("id"));
|
return (getOutput.getRecord().getValueInteger("id"));
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
return (null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,208 +23,72 @@ package com.kingsrook.qqq.backend.core.model.actions.audits;
|
|||||||
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.time.Instant;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
** Input object for the audit action - an object which contains a list of "single"
|
||||||
|
** audit inputs - e.g., the data needed to insert 1 audit.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class AuditInput extends AbstractActionInput
|
public class AuditInput extends AbstractActionInput implements Serializable
|
||||||
{
|
{
|
||||||
private String auditTableName;
|
private List<AuditSingleInput> auditSingleInputList = new ArrayList<>();
|
||||||
private String auditUserName;
|
|
||||||
private Instant timestamp;
|
|
||||||
private String message;
|
|
||||||
private List<Integer> recordIdList;
|
|
||||||
|
|
||||||
private Map<String, Serializable> securityKeyValues;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for auditTableName
|
** Getter for auditSingleInputList
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public String getAuditTableName()
|
public List<AuditSingleInput> getAuditSingleInputList()
|
||||||
{
|
{
|
||||||
return (this.auditTableName);
|
return (this.auditSingleInputList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Setter for auditTableName
|
** Setter for auditSingleInputList
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void setAuditTableName(String auditTableName)
|
public void setAuditSingleInputList(List<AuditSingleInput> auditSingleInputList)
|
||||||
{
|
{
|
||||||
this.auditTableName = auditTableName;
|
this.auditSingleInputList = auditSingleInputList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for auditTableName
|
** Fluent setter for auditSingleInputList
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public AuditInput withAuditTableName(String auditTableName)
|
public AuditInput withAuditSingleInputList(List<AuditSingleInput> auditSingleInputList)
|
||||||
{
|
{
|
||||||
this.auditTableName = auditTableName;
|
this.auditSingleInputList = auditSingleInputList;
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for auditUserName
|
** Add a single auditSingleInput
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public String getAuditUserName()
|
public void addAuditSingleInput(AuditSingleInput auditSingleInput)
|
||||||
{
|
{
|
||||||
return (this.auditUserName);
|
if(this.auditSingleInputList == null)
|
||||||
|
{
|
||||||
|
this.auditSingleInputList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.auditSingleInputList.add(auditSingleInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Setter for auditUserName
|
** Fluent setter to add a single auditSingleInput
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void setAuditUserName(String auditUserName)
|
public AuditInput withAuditSingleInput(AuditSingleInput auditSingleInput)
|
||||||
{
|
{
|
||||||
this.auditUserName = auditUserName;
|
addAuditSingleInput(auditSingleInput);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for auditUserName
|
|
||||||
*******************************************************************************/
|
|
||||||
public AuditInput withAuditUserName(String auditUserName)
|
|
||||||
{
|
|
||||||
this.auditUserName = auditUserName;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for timestamp
|
|
||||||
*******************************************************************************/
|
|
||||||
public Instant getTimestamp()
|
|
||||||
{
|
|
||||||
return (this.timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for timestamp
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setTimestamp(Instant timestamp)
|
|
||||||
{
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for timestamp
|
|
||||||
*******************************************************************************/
|
|
||||||
public AuditInput withTimestamp(Instant timestamp)
|
|
||||||
{
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for message
|
|
||||||
*******************************************************************************/
|
|
||||||
public String getMessage()
|
|
||||||
{
|
|
||||||
return (this.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for message
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setMessage(String message)
|
|
||||||
{
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for message
|
|
||||||
*******************************************************************************/
|
|
||||||
public AuditInput withMessage(String message)
|
|
||||||
{
|
|
||||||
this.message = message;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for recordIdList
|
|
||||||
*******************************************************************************/
|
|
||||||
public List<Integer> getRecordIdList()
|
|
||||||
{
|
|
||||||
return (this.recordIdList);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for recordIdList
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setRecordIdList(List<Integer> recordIdList)
|
|
||||||
{
|
|
||||||
this.recordIdList = recordIdList;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for recordIdList
|
|
||||||
*******************************************************************************/
|
|
||||||
public AuditInput withRecordIdList(List<Integer> recordIdList)
|
|
||||||
{
|
|
||||||
this.recordIdList = recordIdList;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for securityKeyValues
|
|
||||||
*******************************************************************************/
|
|
||||||
public Map<String, Serializable> getSecurityKeyValues()
|
|
||||||
{
|
|
||||||
return (this.securityKeyValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for securityKeyValues
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setSecurityKeyValues(Map<String, Serializable> securityKeyValues)
|
|
||||||
{
|
|
||||||
this.securityKeyValues = securityKeyValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for securityKeyValues
|
|
||||||
*******************************************************************************/
|
|
||||||
public AuditInput withSecurityKeyValues(Map<String, Serializable> securityKeyValues)
|
|
||||||
{
|
|
||||||
this.securityKeyValues = securityKeyValues;
|
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,298 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.model.actions.audits;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Input data to insert a single audit record (with optional child record)..
|
||||||
|
*******************************************************************************/
|
||||||
|
public class AuditSingleInput
|
||||||
|
{
|
||||||
|
private String auditTableName;
|
||||||
|
private String auditUserName;
|
||||||
|
private Instant timestamp;
|
||||||
|
private String message;
|
||||||
|
private Integer recordId;
|
||||||
|
|
||||||
|
private Map<String, Serializable> securityKeyValues;
|
||||||
|
|
||||||
|
private List<String> details;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for auditTableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getAuditTableName()
|
||||||
|
{
|
||||||
|
return (this.auditTableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for auditTableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setAuditTableName(String auditTableName)
|
||||||
|
{
|
||||||
|
this.auditTableName = auditTableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for auditTableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public AuditSingleInput withAuditTableName(String auditTableName)
|
||||||
|
{
|
||||||
|
this.auditTableName = auditTableName;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for auditUserName
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getAuditUserName()
|
||||||
|
{
|
||||||
|
return (this.auditUserName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for auditUserName
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setAuditUserName(String auditUserName)
|
||||||
|
{
|
||||||
|
this.auditUserName = auditUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for auditUserName
|
||||||
|
*******************************************************************************/
|
||||||
|
public AuditSingleInput withAuditUserName(String auditUserName)
|
||||||
|
{
|
||||||
|
this.auditUserName = auditUserName;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for timestamp
|
||||||
|
*******************************************************************************/
|
||||||
|
public Instant getTimestamp()
|
||||||
|
{
|
||||||
|
return (this.timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for timestamp
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setTimestamp(Instant timestamp)
|
||||||
|
{
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for timestamp
|
||||||
|
*******************************************************************************/
|
||||||
|
public AuditSingleInput withTimestamp(Instant timestamp)
|
||||||
|
{
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for message
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getMessage()
|
||||||
|
{
|
||||||
|
return (this.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for message
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setMessage(String message)
|
||||||
|
{
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for message
|
||||||
|
*******************************************************************************/
|
||||||
|
public AuditSingleInput withMessage(String message)
|
||||||
|
{
|
||||||
|
this.message = message;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for securityKeyValues
|
||||||
|
*******************************************************************************/
|
||||||
|
public Map<String, Serializable> getSecurityKeyValues()
|
||||||
|
{
|
||||||
|
return (this.securityKeyValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for securityKeyValues
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setSecurityKeyValues(Map<String, Serializable> securityKeyValues)
|
||||||
|
{
|
||||||
|
this.securityKeyValues = securityKeyValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for securityKeyValues
|
||||||
|
*******************************************************************************/
|
||||||
|
public AuditSingleInput withSecurityKeyValues(Map<String, Serializable> securityKeyValues)
|
||||||
|
{
|
||||||
|
this.securityKeyValues = securityKeyValues;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for recordId
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getRecordId()
|
||||||
|
{
|
||||||
|
return (this.recordId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for recordId
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setRecordId(Integer recordId)
|
||||||
|
{
|
||||||
|
this.recordId = recordId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for recordId
|
||||||
|
*******************************************************************************/
|
||||||
|
public AuditSingleInput withRecordId(Integer recordId)
|
||||||
|
{
|
||||||
|
this.recordId = recordId;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public AuditSingleInput forRecord(QTableMetaData table, QRecord record)
|
||||||
|
{
|
||||||
|
setRecordId(record.getValueInteger(table.getPrimaryKeyField())); // todo support non-integer
|
||||||
|
setAuditTableName(table.getName());
|
||||||
|
|
||||||
|
this.securityKeyValues = new HashMap<>();
|
||||||
|
for(RecordSecurityLock recordSecurityLock : CollectionUtils.nonNullList(table.getRecordSecurityLocks()))
|
||||||
|
{
|
||||||
|
this.securityKeyValues.put(recordSecurityLock.getFieldName(), record.getValueInteger(recordSecurityLock.getFieldName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for details
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<String> getDetails()
|
||||||
|
{
|
||||||
|
return (this.details);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for details
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setDetails(List<String> details)
|
||||||
|
{
|
||||||
|
this.details = details;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for details
|
||||||
|
*******************************************************************************/
|
||||||
|
public AuditSingleInput withDetails(List<String> details)
|
||||||
|
{
|
||||||
|
this.details = details;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void addDetail(String detail)
|
||||||
|
{
|
||||||
|
if(this.details == null)
|
||||||
|
{
|
||||||
|
this.details = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.details.add(detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -46,6 +46,7 @@ public class AuditsMetaDataProvider
|
|||||||
public static final String TABLE_NAME_AUDIT_TABLE = "auditTable";
|
public static final String TABLE_NAME_AUDIT_TABLE = "auditTable";
|
||||||
public static final String TABLE_NAME_AUDIT_USER = "auditUser";
|
public static final String TABLE_NAME_AUDIT_USER = "auditUser";
|
||||||
public static final String TABLE_NAME_AUDIT = "audit";
|
public static final String TABLE_NAME_AUDIT = "audit";
|
||||||
|
public static final String TABLE_NAME_AUDIT_DETAIL = "auditDetail";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -80,6 +81,13 @@ public class AuditsMetaDataProvider
|
|||||||
.withType(JoinType.MANY_TO_ONE)
|
.withType(JoinType.MANY_TO_ONE)
|
||||||
.withJoinOn(new JoinOn("auditUserId", "id")));
|
.withJoinOn(new JoinOn("auditUserId", "id")));
|
||||||
|
|
||||||
|
instance.addJoin(new QJoinMetaData()
|
||||||
|
.withLeftTable(TABLE_NAME_AUDIT)
|
||||||
|
.withRightTable(TABLE_NAME_AUDIT_DETAIL)
|
||||||
|
.withInferredName()
|
||||||
|
.withType(JoinType.ONE_TO_MANY)
|
||||||
|
.withJoinOn(new JoinOn("id", "auditId")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -111,6 +119,11 @@ public class AuditsMetaDataProvider
|
|||||||
.withName(TABLE_NAME_AUDIT_USER)
|
.withName(TABLE_NAME_AUDIT_USER)
|
||||||
.withTableName(TABLE_NAME_AUDIT_USER)
|
.withTableName(TABLE_NAME_AUDIT_USER)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||||
|
.withName(TABLE_NAME_AUDIT)
|
||||||
|
.withTableName(TABLE_NAME_AUDIT)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -124,6 +137,7 @@ public class AuditsMetaDataProvider
|
|||||||
rs.add(enrich(backendDetailEnricher, defineAuditUserTable(backendName)));
|
rs.add(enrich(backendDetailEnricher, defineAuditUserTable(backendName)));
|
||||||
rs.add(enrich(backendDetailEnricher, defineAuditTableTable(backendName)));
|
rs.add(enrich(backendDetailEnricher, defineAuditTableTable(backendName)));
|
||||||
rs.add(enrich(backendDetailEnricher, defineAuditTable(backendName)));
|
rs.add(enrich(backendDetailEnricher, defineAuditTable(backendName)));
|
||||||
|
rs.add(enrich(backendDetailEnricher, defineAuditDetailTable(backendName)));
|
||||||
return (rs);
|
return (rs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,4 +217,22 @@ public class AuditsMetaDataProvider
|
|||||||
.withField(new QFieldMetaData("timestamp", QFieldType.DATE_TIME));
|
.withField(new QFieldMetaData("timestamp", QFieldType.DATE_TIME));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private QTableMetaData defineAuditDetailTable(String backendName)
|
||||||
|
{
|
||||||
|
return new QTableMetaData()
|
||||||
|
.withName(TABLE_NAME_AUDIT_DETAIL)
|
||||||
|
.withBackendName(backendName)
|
||||||
|
.withRecordLabelFormat("%s")
|
||||||
|
.withRecordLabelFields("id")
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||||
|
.withField(new QFieldMetaData("auditId", QFieldType.INTEGER).withPossibleValueSourceName(TABLE_NAME_AUDIT))
|
||||||
|
.withField(new QFieldMetaData("message", QFieldType.STRING).withMaxLength(250).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,15 @@
|
|||||||
package com.kingsrook.qqq.backend.core.actions.audits;
|
package com.kingsrook.qqq.backend.core.actions.audits;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.audits.AuditInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.audits.AuditsMetaDataProvider;
|
import com.kingsrook.qqq.backend.core.model.audits.AuditsMetaDataProvider;
|
||||||
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;
|
||||||
@ -34,7 +39,10 @@ import com.kingsrook.qqq.backend.core.model.session.QUser;
|
|||||||
import com.kingsrook.qqq.backend.core.processes.utils.GeneralProcessUtils;
|
import com.kingsrook.qqq.backend.core.processes.utils.GeneralProcessUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -47,7 +55,7 @@ class AuditActionTest extends BaseTest
|
|||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
void test() throws QException
|
void testSingle() throws QException
|
||||||
{
|
{
|
||||||
QInstance qInstance = TestUtils.defineInstance();
|
QInstance qInstance = TestUtils.defineInstance();
|
||||||
new AuditsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
new AuditsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||||
@ -67,4 +75,126 @@ class AuditActionTest extends BaseTest
|
|||||||
assertEquals("Test Audit", auditRecord.getValueString("message"));
|
assertEquals("Test Audit", auditRecord.getValueString("message"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testFailWithoutSecurityKey() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = TestUtils.defineInstance();
|
||||||
|
new AuditsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||||
|
|
||||||
|
String userName = "John Doe";
|
||||||
|
QContext.init(qInstance, new QSession().withUser(new QUser().withFullName(userName)));
|
||||||
|
|
||||||
|
int recordId = 1701;
|
||||||
|
AuditAction.execute(TestUtils.TABLE_NAME_ORDER, recordId, Map.of(), "Test Audit");
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////
|
||||||
|
// it should not throw, but it should also not insert the audit. //
|
||||||
|
///////////////////////////////////////////////////////////////////
|
||||||
|
Optional<QRecord> auditRecord = GeneralProcessUtils.getRecordByField(null, "audit", "recordId", recordId);
|
||||||
|
assertFalse(auditRecord.isPresent());
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// try again with a null value in the key - that should be ok - as at least you were thinking //
|
||||||
|
// about the key and put in SOME value (null has its own semantics in security keys) //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Map<String, Serializable> securityKeys = new HashMap<>();
|
||||||
|
securityKeys.put(TestUtils.SECURITY_KEY_TYPE_STORE, null);
|
||||||
|
AuditAction.execute(TestUtils.TABLE_NAME_ORDER, recordId, securityKeys, "Test Audit");
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
// now the audit should be stored. //
|
||||||
|
/////////////////////////////////////
|
||||||
|
auditRecord = GeneralProcessUtils.getRecordByField(null, "audit", "recordId", recordId);
|
||||||
|
assertTrue(auditRecord.isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testMulti() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = TestUtils.defineInstance();
|
||||||
|
new AuditsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||||
|
|
||||||
|
String userName = "John Doe";
|
||||||
|
QContext.init(qInstance, new QSession().withUser(new QUser().withFullName(userName)));
|
||||||
|
|
||||||
|
Integer recordId1 = 1701;
|
||||||
|
Integer recordId2 = 1702;
|
||||||
|
AuditInput auditInput = new AuditInput();
|
||||||
|
AuditAction.appendToInput(auditInput, TestUtils.TABLE_NAME_PERSON_MEMORY, recordId1, Map.of(), "Test Audit");
|
||||||
|
AuditAction.appendToInput(auditInput, TestUtils.TABLE_NAME_ORDER, recordId2, Map.of(TestUtils.SECURITY_KEY_TYPE_STORE, 47), "Test Another Audit");
|
||||||
|
new AuditAction().execute(auditInput);
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
// make sure things can be fetched //
|
||||||
|
/////////////////////////////////////
|
||||||
|
GeneralProcessUtils.getRecordByFieldOrElseThrow(null, "auditTable", "name", TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||||
|
GeneralProcessUtils.getRecordByFieldOrElseThrow(null, "auditUser", "name", userName);
|
||||||
|
QRecord auditRecord = GeneralProcessUtils.getRecordByFieldOrElseThrow(null, "audit", "recordId", recordId1);
|
||||||
|
assertEquals("Test Audit", auditRecord.getValueString("message"));
|
||||||
|
|
||||||
|
auditRecord = GeneralProcessUtils.getRecordByFieldOrElseThrow(null, "audit", "recordId", recordId2);
|
||||||
|
assertEquals("Test Another Audit", auditRecord.getValueString("message"));
|
||||||
|
assertEquals(47, auditRecord.getValueInteger(TestUtils.SECURITY_KEY_TYPE_STORE));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testMultiWithDetails() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = TestUtils.defineInstance();
|
||||||
|
new AuditsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||||
|
|
||||||
|
String userName = "John Doe";
|
||||||
|
QContext.init(qInstance, new QSession().withUser(new QUser().withFullName(userName)));
|
||||||
|
|
||||||
|
Integer recordId1 = 1701;
|
||||||
|
Integer recordId2 = 1702;
|
||||||
|
Integer recordId3 = 1703;
|
||||||
|
AuditInput auditInput = new AuditInput();
|
||||||
|
AuditAction.appendToInput(auditInput, TestUtils.TABLE_NAME_PERSON_MEMORY, recordId1, Map.of(), "Test Audit", List.of("Detail1", "Detail2"));
|
||||||
|
AuditAction.appendToInput(auditInput, TestUtils.TABLE_NAME_ORDER, recordId2, Map.of(TestUtils.SECURITY_KEY_TYPE_STORE, 47), "Test Another Audit", null);
|
||||||
|
AuditAction.appendToInput(auditInput, TestUtils.TABLE_NAME_PERSON_MEMORY, recordId3, Map.of(TestUtils.SECURITY_KEY_TYPE_STORE, 42), "Audit 3", List.of("Detail3"));
|
||||||
|
new AuditAction().execute(auditInput);
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
// make sure things can be fetched //
|
||||||
|
/////////////////////////////////////
|
||||||
|
GeneralProcessUtils.getRecordByFieldOrElseThrow(null, "auditTable", "name", TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||||
|
GeneralProcessUtils.getRecordByFieldOrElseThrow(null, "auditUser", "name", userName);
|
||||||
|
QRecord auditRecord = GeneralProcessUtils.getRecordByFieldOrElseThrow(null, "audit", "recordId", recordId1);
|
||||||
|
assertEquals("Test Audit", auditRecord.getValueString("message"));
|
||||||
|
|
||||||
|
List<QRecord> auditDetails = GeneralProcessUtils.getRecordListByField(null, "auditDetail", "auditId", auditRecord.getValue("id"));
|
||||||
|
assertEquals(2, auditDetails.size());
|
||||||
|
assertThat(auditDetails).anyMatch(r -> r.getValueString("message").equals("Detail1"));
|
||||||
|
assertThat(auditDetails).anyMatch(r -> r.getValueString("message").equals("Detail2"));
|
||||||
|
|
||||||
|
auditRecord = GeneralProcessUtils.getRecordByFieldOrElseThrow(null, "audit", "recordId", recordId2);
|
||||||
|
assertEquals("Test Another Audit", auditRecord.getValueString("message"));
|
||||||
|
assertEquals(47, auditRecord.getValueInteger(TestUtils.SECURITY_KEY_TYPE_STORE));
|
||||||
|
auditDetails = GeneralProcessUtils.getRecordListByField(null, "auditDetail", "auditId", auditRecord.getValue("id"));
|
||||||
|
assertEquals(0, auditDetails.size());
|
||||||
|
|
||||||
|
auditRecord = GeneralProcessUtils.getRecordByFieldOrElseThrow(null, "audit", "recordId", recordId3);
|
||||||
|
assertEquals("Audit 3", auditRecord.getValueString("message"));
|
||||||
|
assertEquals(42, auditRecord.getValueInteger(TestUtils.SECURITY_KEY_TYPE_STORE));
|
||||||
|
auditDetails = GeneralProcessUtils.getRecordListByField(null, "auditDetail", "auditId", auditRecord.getValue("id"));
|
||||||
|
assertEquals(1, auditDetails.size());
|
||||||
|
assertThat(auditDetails).anyMatch(r -> r.getValueString("message").equals("Detail3"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user