mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
Add security, required fields, record-exists validation to UpdateAction. refactor InsertAction to help there
This commit is contained in:
@ -41,6 +41,7 @@ import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPostInsertCust
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.helpers.UniqueKeyHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.helpers.ValidateRecordSecurityLockHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
@ -48,27 +49,16 @@ import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.audits.DMLAuditInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
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.joins.JoinOn;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
@ -100,7 +90,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
||||
ValueBehaviorApplier.applyFieldBehaviors(insertInput.getInstance(), table, insertInput.getRecords());
|
||||
setErrorsIfUniqueKeyErrors(insertInput, table);
|
||||
validateRequiredFields(insertInput);
|
||||
validateSecurityFields(insertInput);
|
||||
ValidateRecordSecurityLockHelper.validateSecurityFields(insertInput.getTable(), insertInput.getRecords(), ValidateRecordSecurityLockHelper.Action.INSERT);
|
||||
|
||||
InsertOutput insertOutput = qModule.getInsertInterface().execute(insertInput);
|
||||
List<String> errors = insertOutput.getRecords().stream().flatMap(r -> r.getErrors().stream()).toList();
|
||||
@ -153,231 +143,6 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void validateSecurityFields(InsertInput insertInput) throws QException
|
||||
{
|
||||
QTableMetaData table = insertInput.getTable();
|
||||
List<RecordSecurityLock> recordSecurityLocks = table.getRecordSecurityLocks();
|
||||
List<RecordSecurityLock> locksToCheck = new ArrayList<>();
|
||||
|
||||
////////////////////////////////////////
|
||||
// if there are no locks, just return //
|
||||
////////////////////////////////////////
|
||||
if(CollectionUtils.nullSafeIsEmpty(recordSecurityLocks))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// decide if any locks need checked - where one may not need checked if it has an all-access key, and the user has all-access //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
for(RecordSecurityLock recordSecurityLock : recordSecurityLocks)
|
||||
{
|
||||
QSecurityKeyType securityKeyType = QContext.getQInstance().getSecurityKeyType(recordSecurityLock.getSecurityKeyType());
|
||||
if(StringUtils.hasContent(securityKeyType.getAllAccessKeyName()) && QContext.getQSession().hasSecurityKeyValue(securityKeyType.getAllAccessKeyName(), true, QFieldType.BOOLEAN))
|
||||
{
|
||||
LOG.debug("Session has " + securityKeyType.getAllAccessKeyName() + " - not checking this lock.");
|
||||
}
|
||||
else
|
||||
{
|
||||
locksToCheck.add(recordSecurityLock);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// if there are no locks to check, just return //
|
||||
/////////////////////////////////////////////////
|
||||
if(locksToCheck.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// actually check lock values //
|
||||
////////////////////////////////
|
||||
for(RecordSecurityLock recordSecurityLock : locksToCheck)
|
||||
{
|
||||
if(CollectionUtils.nullSafeIsEmpty(recordSecurityLock.getJoinNameChain()))
|
||||
{
|
||||
for(QRecord record : insertInput.getRecords())
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// handle the value being in the table we're inserting (e.g., no join) //
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
QFieldMetaData field = table.getField(recordSecurityLock.getFieldName());
|
||||
Serializable recordSecurityValue = record.getValue(recordSecurityLock.getFieldName());
|
||||
validateRecordSecurityValue(table, record, recordSecurityLock, recordSecurityValue, field.getType());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// else look for the joined record - if it isn't found, assume a fail - else validate security value if found //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QJoinMetaData leftMostJoin = QContext.getQInstance().getJoin(recordSecurityLock.getJoinNameChain().get(0));
|
||||
QJoinMetaData rightMostJoin = QContext.getQInstance().getJoin(recordSecurityLock.getJoinNameChain().get(recordSecurityLock.getJoinNameChain().size() - 1));
|
||||
QTableMetaData leftMostJoinTable = QContext.getQInstance().getTable(leftMostJoin.getLeftTable());
|
||||
|
||||
for(List<QRecord> inputRecordPage : CollectionUtils.getPages(insertInput.getRecords(), 500))
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// set up a query for joined records //
|
||||
// query will be like (fkey1=? and fkey2=?) OR (fkey1=? and fkey2=?) OR (fkey1=? and fkey2=?) //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(leftMostJoin.getLeftTable());
|
||||
QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.OR);
|
||||
queryInput.setFilter(filter);
|
||||
|
||||
for(String joinName : recordSecurityLock.getJoinNameChain())
|
||||
{
|
||||
///////////////////////////////////////
|
||||
// we don't need the right-most join //
|
||||
///////////////////////////////////////
|
||||
if(!joinName.equals(rightMostJoin.getName()))
|
||||
{
|
||||
queryInput.withQueryJoin(new QueryJoin().withJoinMetaData(QContext.getQInstance().getJoin(joinName)).withSelect(true));
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// foreach input record (in this page), put it in a listing hash, with key = list of join-values //
|
||||
// e.g., (17,47)=(QRecord1), (18,48)=(QRecord2,QRecord3) //
|
||||
// also build up the query's sub-filters here (only adding them if they're unique). //
|
||||
// e.g., 2 order-lines referencing the same orderId don't need to be added to the query twice //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
ListingHash<List<Serializable>, QRecord> inputRecordMapByJoinFields = new ListingHash<>();
|
||||
for(QRecord inputRecord : inputRecordPage)
|
||||
{
|
||||
List<Serializable> inputRecordJoinValues = new ArrayList<>();
|
||||
QQueryFilter subFilter = new QQueryFilter();
|
||||
|
||||
for(JoinOn joinOn : rightMostJoin.getJoinOns())
|
||||
{
|
||||
Serializable inputRecordValue = inputRecord.getValue(joinOn.getRightField());
|
||||
inputRecordJoinValues.add(inputRecordValue);
|
||||
|
||||
subFilter.addCriteria(inputRecordValue == null
|
||||
? new QFilterCriteria(rightMostJoin.getLeftTable() + "." + joinOn.getLeftField(), QCriteriaOperator.IS_BLANK)
|
||||
: new QFilterCriteria(rightMostJoin.getLeftTable() + "." + joinOn.getLeftField(), QCriteriaOperator.EQUALS, inputRecordValue));
|
||||
}
|
||||
|
||||
if(!inputRecordMapByJoinFields.containsKey(inputRecordJoinValues))
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// only add this sub-filter if it's for a list of keys we haven't seen before //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
filter.addSubFilter(subFilter);
|
||||
}
|
||||
|
||||
inputRecordMapByJoinFields.add(inputRecordJoinValues, inputRecord);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// execute the query for joined records - then put them in a map with keys corresponding to the join values //
|
||||
// e.g., (17,47)=(JoinRecord), (18,48)=(JoinRecord) //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
Map<List<Serializable>, QRecord> joinRecordMapByJoinFields = new HashMap<>();
|
||||
for(QRecord joinRecord : queryOutput.getRecords())
|
||||
{
|
||||
List<Serializable> joinRecordValues = new ArrayList<>();
|
||||
for(JoinOn joinOn : rightMostJoin.getJoinOns())
|
||||
{
|
||||
Serializable joinValue = joinRecord.getValue(rightMostJoin.getLeftTable() + "." + joinOn.getLeftField());
|
||||
if(joinValue == null && joinRecord.getValues().keySet().stream().anyMatch(n -> !n.contains(".")))
|
||||
{
|
||||
joinValue = joinRecord.getValue(joinOn.getLeftField());
|
||||
}
|
||||
joinRecordValues.add(joinValue);
|
||||
}
|
||||
|
||||
joinRecordMapByJoinFields.put(joinRecordValues, joinRecord);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// now for each input record, look for its joinRecord - if it isn't found, then this insert //
|
||||
// isn't allowed. if it is found, then validate its value matches this session's security keys //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
for(Map.Entry<List<Serializable>, List<QRecord>> entry : inputRecordMapByJoinFields.entrySet())
|
||||
{
|
||||
List<Serializable> inputRecordJoinValues = entry.getKey();
|
||||
List<QRecord> inputRecords = entry.getValue();
|
||||
if(joinRecordMapByJoinFields.containsKey(inputRecordJoinValues))
|
||||
{
|
||||
QRecord joinRecord = joinRecordMapByJoinFields.get(inputRecordJoinValues);
|
||||
|
||||
String fieldName = recordSecurityLock.getFieldName().replaceFirst(".*\\.", "");
|
||||
QFieldMetaData field = leftMostJoinTable.getField(fieldName);
|
||||
Serializable recordSecurityValue = joinRecord.getValue(fieldName);
|
||||
if(recordSecurityValue == null && joinRecord.getValues().keySet().stream().anyMatch(n -> n.contains(".")))
|
||||
{
|
||||
recordSecurityValue = joinRecord.getValue(recordSecurityLock.getFieldName());
|
||||
}
|
||||
|
||||
for(QRecord inputRecord : inputRecords)
|
||||
{
|
||||
validateRecordSecurityValue(table, inputRecord, recordSecurityLock, recordSecurityValue, field.getType());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(QRecord inputRecord : inputRecords)
|
||||
{
|
||||
if(RecordSecurityLock.NullValueBehavior.DENY.equals(recordSecurityLock.getNullValueBehavior()))
|
||||
{
|
||||
inputRecord.addError("You do not have permission to insert this record - the referenced " + leftMostJoinTable.getLabel() + " was not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void validateRecordSecurityValue(QTableMetaData table, QRecord record, RecordSecurityLock recordSecurityLock, Serializable recordSecurityValue, QFieldType fieldType)
|
||||
{
|
||||
if(recordSecurityValue == null)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// handle null values - error if the NullValueBehavior is DENY //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
if(RecordSecurityLock.NullValueBehavior.DENY.equals(recordSecurityLock.getNullValueBehavior()))
|
||||
{
|
||||
String lockLabel = CollectionUtils.nullSafeHasContents(recordSecurityLock.getJoinNameChain()) ? recordSecurityLock.getSecurityKeyType() : table.getField(recordSecurityLock.getFieldName()).getLabel();
|
||||
record.addError("You do not have permission to insert a record without a value in the field: " + lockLabel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!QContext.getQSession().hasSecurityKeyValue(recordSecurityLock.getSecurityKeyType(), recordSecurityValue, fieldType))
|
||||
{
|
||||
if(CollectionUtils.nullSafeHasContents(recordSecurityLock.getJoinNameChain()))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// avoid telling the user a value from a foreign record that they didn't pass in themselves. //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
record.addError("You do not have permission to insert this record.");
|
||||
}
|
||||
else
|
||||
{
|
||||
QFieldMetaData field = table.getField(recordSecurityLock.getFieldName());
|
||||
record.addError("You do not have permission to insert a record with a value of " + recordSecurityValue + " in the field: " + field.getLabel());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -24,13 +24,16 @@ package com.kingsrook.qqq.backend.core.actions.tables;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.audits.DMLAuditAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationStatusUpdater;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.helpers.ValidateRecordSecurityLockHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
@ -57,6 +60,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
@ -86,7 +90,10 @@ public class UpdateAction
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(updateInput.getBackend());
|
||||
|
||||
validatePrimaryKeysAreGiven(updateInput);
|
||||
validateRecordsExistAndCanBeAccessed(updateInput, oldRecordList);
|
||||
validateRequiredFields(updateInput);
|
||||
ValidateRecordSecurityLockHelper.validateSecurityFields(updateInput.getTable(), updateInput.getRecords(), ValidateRecordSecurityLockHelper.Action.UPDATE);
|
||||
|
||||
// todo pre-customization - just get to modify the request?
|
||||
UpdateOutput updateOutput = qModule.getUpdateInterface().execute(updateInput);
|
||||
@ -114,6 +121,86 @@ public class UpdateAction
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void validatePrimaryKeysAreGiven(UpdateInput updateInput)
|
||||
{
|
||||
QTableMetaData table = updateInput.getTable();
|
||||
for(QRecord record : CollectionUtils.nonNullList(updateInput.getRecords()))
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// to update a record, we must have its primary key value - so - check - if it's missing, mark it as an error //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(record.getValue(table.getPrimaryKeyField()) == null)
|
||||
{
|
||||
record.addError("Missing value in primary key field");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Note - the "can be accessed" part of this method name - it implies that
|
||||
** records that you can't see because of security - that they won't be found
|
||||
** by the query here, so it's the same to you as if they don't exist at all!
|
||||
*******************************************************************************/
|
||||
private void validateRecordsExistAndCanBeAccessed(UpdateInput updateInput, List<QRecord> oldRecordList) throws QException
|
||||
{
|
||||
QTableMetaData table = updateInput.getTable();
|
||||
QFieldMetaData primaryKeyField = table.getField(table.getPrimaryKeyField());
|
||||
|
||||
for(List<QRecord> page : CollectionUtils.getPages(updateInput.getRecords(), 1000))
|
||||
{
|
||||
List<Serializable> primaryKeysToLookup = new ArrayList<>();
|
||||
for(QRecord record : page)
|
||||
{
|
||||
Serializable primaryKeyValue = record.getValue(table.getPrimaryKeyField());
|
||||
if(primaryKeyValue != null)
|
||||
{
|
||||
primaryKeysToLookup.add(primaryKeyValue);
|
||||
}
|
||||
}
|
||||
|
||||
Map<Serializable, QRecord> lookedUpRecords = new HashMap<>();
|
||||
if(CollectionUtils.nullSafeHasContents(oldRecordList))
|
||||
{
|
||||
for(QRecord record : oldRecordList)
|
||||
{
|
||||
lookedUpRecords.put(record.getValue(table.getPrimaryKeyField()), record);
|
||||
}
|
||||
}
|
||||
else if(!primaryKeysToLookup.isEmpty())
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(table.getName());
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, primaryKeysToLookup)));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
for(QRecord record : queryOutput.getRecords())
|
||||
{
|
||||
lookedUpRecords.put(record.getValue(table.getPrimaryKeyField()), record);
|
||||
}
|
||||
}
|
||||
|
||||
for(QRecord record : page)
|
||||
{
|
||||
Serializable value = ValueUtils.getValueAsFieldType(primaryKeyField.getType(), record.getValue(table.getPrimaryKeyField()));
|
||||
if(value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!lookedUpRecords.containsKey(value))
|
||||
{
|
||||
record.addError("No record was found to update for " + primaryKeyField.getLabel() + " = " + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -126,7 +213,7 @@ public class UpdateAction
|
||||
|
||||
if(!requiredFields.isEmpty())
|
||||
{
|
||||
for(QRecord record : updateInput.getRecords())
|
||||
for(QRecord record : CollectionUtils.nonNullList(updateInput.getRecords()))
|
||||
{
|
||||
for(QFieldMetaData requiredField : requiredFields)
|
||||
{
|
||||
@ -279,7 +366,7 @@ public class UpdateAction
|
||||
if(AuditLevel.FIELD.equals(auditLevel))
|
||||
{
|
||||
String primaryKeyField = updateInput.getTable().getPrimaryKeyField();
|
||||
List<Serializable> pkeysBeingUpdated = updateInput.getRecords().stream().map(r -> r.getValue(primaryKeyField)).toList();
|
||||
List<Serializable> pkeysBeingUpdated = CollectionUtils.nonNullList(updateInput.getRecords()).stream().map(r -> r.getValue(primaryKeyField)).toList();
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(updateInput.getTableName());
|
||||
|
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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.actions.tables.helpers;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
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.joins.JoinOn;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
|
||||
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;
|
||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ValidateRecordSecurityLockHelper
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(ValidateRecordSecurityLockHelper.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum Action
|
||||
{
|
||||
INSERT,
|
||||
UPDATE,
|
||||
SELECT
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void validateSecurityFields(QTableMetaData table, List<QRecord> records, Action action) throws QException
|
||||
{
|
||||
List<RecordSecurityLock> locksToCheck = getRecordSecurityLocks(table);
|
||||
if(CollectionUtils.nullSafeIsEmpty(locksToCheck))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// actually check lock values //
|
||||
////////////////////////////////
|
||||
for(RecordSecurityLock recordSecurityLock : locksToCheck)
|
||||
{
|
||||
if(CollectionUtils.nullSafeIsEmpty(recordSecurityLock.getJoinNameChain()))
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// handle the value being in the table we're inserting/updating (e.g., no join) //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
QFieldMetaData field = table.getField(recordSecurityLock.getFieldName());
|
||||
|
||||
for(QRecord record : records)
|
||||
{
|
||||
if(action.equals(Action.UPDATE) && !record.getValues().containsKey(field.getName()))
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// if not updating the security field, then no error can come from it! //
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
continue;
|
||||
}
|
||||
|
||||
Serializable recordSecurityValue = record.getValue(field.getName());
|
||||
validateRecordSecurityValue(table, record, recordSecurityLock, recordSecurityValue, field.getType(), action);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// else look for the joined record - if it isn't found, assume a fail - else validate security value if found //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QJoinMetaData leftMostJoin = QContext.getQInstance().getJoin(recordSecurityLock.getJoinNameChain().get(0));
|
||||
QJoinMetaData rightMostJoin = QContext.getQInstance().getJoin(recordSecurityLock.getJoinNameChain().get(recordSecurityLock.getJoinNameChain().size() - 1));
|
||||
QTableMetaData leftMostJoinTable = QContext.getQInstance().getTable(leftMostJoin.getLeftTable());
|
||||
|
||||
for(List<QRecord> inputRecordPage : CollectionUtils.getPages(records, 500))
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// set up a query for joined records //
|
||||
// query will be like (fkey1=? and fkey2=?) OR (fkey1=? and fkey2=?) OR (fkey1=? and fkey2=?) //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(leftMostJoin.getLeftTable());
|
||||
QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.OR);
|
||||
queryInput.setFilter(filter);
|
||||
|
||||
for(String joinName : recordSecurityLock.getJoinNameChain())
|
||||
{
|
||||
///////////////////////////////////////
|
||||
// we don't need the right-most join //
|
||||
///////////////////////////////////////
|
||||
if(!joinName.equals(rightMostJoin.getName()))
|
||||
{
|
||||
queryInput.withQueryJoin(new QueryJoin().withJoinMetaData(QContext.getQInstance().getJoin(joinName)).withSelect(true));
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// foreach input record (in this page), put it in a listing hash, with key = list of join-values //
|
||||
// e.g., (17,47)=(QRecord1), (18,48)=(QRecord2,QRecord3) //
|
||||
// also build up the query's sub-filters here (only adding them if they're unique). //
|
||||
// e.g., 2 order-lines referencing the same orderId don't need to be added to the query twice //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
ListingHash<List<Serializable>, QRecord> inputRecordMapByJoinFields = new ListingHash<>();
|
||||
for(QRecord inputRecord : inputRecordPage)
|
||||
{
|
||||
List<Serializable> inputRecordJoinValues = new ArrayList<>();
|
||||
QQueryFilter subFilter = new QQueryFilter();
|
||||
|
||||
for(JoinOn joinOn : rightMostJoin.getJoinOns())
|
||||
{
|
||||
Serializable inputRecordValue = inputRecord.getValue(joinOn.getRightField());
|
||||
inputRecordJoinValues.add(inputRecordValue);
|
||||
|
||||
subFilter.addCriteria(inputRecordValue == null
|
||||
? new QFilterCriteria(rightMostJoin.getLeftTable() + "." + joinOn.getLeftField(), QCriteriaOperator.IS_BLANK)
|
||||
: new QFilterCriteria(rightMostJoin.getLeftTable() + "." + joinOn.getLeftField(), QCriteriaOperator.EQUALS, inputRecordValue));
|
||||
}
|
||||
|
||||
if(!inputRecordMapByJoinFields.containsKey(inputRecordJoinValues))
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// only add this sub-filter if it's for a list of keys we haven't seen before //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
filter.addSubFilter(subFilter);
|
||||
}
|
||||
|
||||
inputRecordMapByJoinFields.add(inputRecordJoinValues, inputRecord);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// execute the query for joined records - then put them in a map with keys corresponding to the join values //
|
||||
// e.g., (17,47)=(JoinRecord), (18,48)=(JoinRecord) //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
Map<List<Serializable>, QRecord> joinRecordMapByJoinFields = new HashMap<>();
|
||||
for(QRecord joinRecord : queryOutput.getRecords())
|
||||
{
|
||||
List<Serializable> joinRecordValues = new ArrayList<>();
|
||||
for(JoinOn joinOn : rightMostJoin.getJoinOns())
|
||||
{
|
||||
Serializable joinValue = joinRecord.getValue(rightMostJoin.getLeftTable() + "." + joinOn.getLeftField());
|
||||
if(joinValue == null && joinRecord.getValues().keySet().stream().anyMatch(n -> !n.contains(".")))
|
||||
{
|
||||
joinValue = joinRecord.getValue(joinOn.getLeftField());
|
||||
}
|
||||
joinRecordValues.add(joinValue);
|
||||
}
|
||||
|
||||
joinRecordMapByJoinFields.put(joinRecordValues, joinRecord);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// now for each input record, look for its joinRecord - if it isn't found, then this insert //
|
||||
// isn't allowed. if it is found, then validate its value matches this session's security keys //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
for(Map.Entry<List<Serializable>, List<QRecord>> entry : inputRecordMapByJoinFields.entrySet())
|
||||
{
|
||||
List<Serializable> inputRecordJoinValues = entry.getKey();
|
||||
List<QRecord> inputRecords = entry.getValue();
|
||||
if(joinRecordMapByJoinFields.containsKey(inputRecordJoinValues))
|
||||
{
|
||||
QRecord joinRecord = joinRecordMapByJoinFields.get(inputRecordJoinValues);
|
||||
|
||||
String fieldName = recordSecurityLock.getFieldName().replaceFirst(".*\\.", "");
|
||||
QFieldMetaData field = leftMostJoinTable.getField(fieldName);
|
||||
Serializable recordSecurityValue = joinRecord.getValue(fieldName);
|
||||
if(recordSecurityValue == null && joinRecord.getValues().keySet().stream().anyMatch(n -> n.contains(".")))
|
||||
{
|
||||
recordSecurityValue = joinRecord.getValue(recordSecurityLock.getFieldName());
|
||||
}
|
||||
|
||||
for(QRecord inputRecord : inputRecords)
|
||||
{
|
||||
validateRecordSecurityValue(table, inputRecord, recordSecurityLock, recordSecurityValue, field.getType(), action);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(QRecord inputRecord : inputRecords)
|
||||
{
|
||||
if(RecordSecurityLock.NullValueBehavior.DENY.equals(recordSecurityLock.getNullValueBehavior()))
|
||||
{
|
||||
inputRecord.addError("You do not have permission to " + action.name().toLowerCase() + " this record - the referenced " + leftMostJoinTable.getLabel() + " was not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static List<RecordSecurityLock> getRecordSecurityLocks(QTableMetaData table)
|
||||
{
|
||||
List<RecordSecurityLock> recordSecurityLocks = table.getRecordSecurityLocks();
|
||||
List<RecordSecurityLock> locksToCheck = new ArrayList<>();
|
||||
|
||||
////////////////////////////////////////
|
||||
// if there are no locks, just return //
|
||||
////////////////////////////////////////
|
||||
if(CollectionUtils.nullSafeIsEmpty(recordSecurityLocks))
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// decide if any locks need checked - where one may not need checked if it has an all-access key, and the user has all-access //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
for(RecordSecurityLock recordSecurityLock : recordSecurityLocks)
|
||||
{
|
||||
QSecurityKeyType securityKeyType = QContext.getQInstance().getSecurityKeyType(recordSecurityLock.getSecurityKeyType());
|
||||
if(StringUtils.hasContent(securityKeyType.getAllAccessKeyName()) && QContext.getQSession().hasSecurityKeyValue(securityKeyType.getAllAccessKeyName(), true, QFieldType.BOOLEAN))
|
||||
{
|
||||
LOG.debug("Session has " + securityKeyType.getAllAccessKeyName() + " - not checking this lock.");
|
||||
}
|
||||
else
|
||||
{
|
||||
locksToCheck.add(recordSecurityLock);
|
||||
}
|
||||
}
|
||||
|
||||
return (locksToCheck);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
static void validateRecordSecurityValue(QTableMetaData table, QRecord record, RecordSecurityLock recordSecurityLock, Serializable recordSecurityValue, QFieldType fieldType, Action action)
|
||||
{
|
||||
if(recordSecurityValue == null)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// handle null values - error if the NullValueBehavior is DENY //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
if(RecordSecurityLock.NullValueBehavior.DENY.equals(recordSecurityLock.getNullValueBehavior()))
|
||||
{
|
||||
String lockLabel = CollectionUtils.nullSafeHasContents(recordSecurityLock.getJoinNameChain()) ? recordSecurityLock.getSecurityKeyType() : table.getField(recordSecurityLock.getFieldName()).getLabel();
|
||||
record.addError("You do not have permission to " + action.name().toLowerCase() + " a record without a value in the field: " + lockLabel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!QContext.getQSession().hasSecurityKeyValue(recordSecurityLock.getSecurityKeyType(), recordSecurityValue, fieldType))
|
||||
{
|
||||
if(CollectionUtils.nullSafeHasContents(recordSecurityLock.getJoinNameChain()))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// avoid telling the user a value from a foreign record that they didn't pass in themselves. //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
record.addError("You do not have permission to " + action.name().toLowerCase() + " this record.");
|
||||
}
|
||||
else
|
||||
{
|
||||
QFieldMetaData field = table.getField(recordSecurityLock.getFieldName());
|
||||
record.addError("You do not have permission to " + action.name().toLowerCase() + " a record with a value of " + recordSecurityValue + " in the field: " + field.getLabel());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -30,7 +30,10 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.helpers.ValidateRecordSecurityLockHelper;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||
@ -54,6 +57,8 @@ import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
*******************************************************************************/
|
||||
public class MemoryRecordStore
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(MemoryRecordStore.class);
|
||||
|
||||
private static MemoryRecordStore instance;
|
||||
|
||||
private Map<String, Map<Serializable, QRecord>> data;
|
||||
@ -135,7 +140,7 @@ public class MemoryRecordStore
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<QRecord> query(QueryInput input)
|
||||
public List<QRecord> query(QueryInput input) throws QException
|
||||
{
|
||||
incrementStatistic(input);
|
||||
|
||||
@ -153,7 +158,20 @@ public class MemoryRecordStore
|
||||
|
||||
if(recordMatches)
|
||||
{
|
||||
records.add(qRecord);
|
||||
qRecord.setErrors(new ArrayList<>());
|
||||
ValidateRecordSecurityLockHelper.validateSecurityFields(input.getTable(), List.of(qRecord), ValidateRecordSecurityLockHelper.Action.SELECT);
|
||||
if(CollectionUtils.nullSafeHasContents(qRecord.getErrors()))
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// security error! no record for you. but remove the error, so future generations won't see it... //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
qRecord.setErrors(new ArrayList<>());
|
||||
LOG.trace("Error selecting record (presumably security?): " + qRecord.getErrors());
|
||||
}
|
||||
else
|
||||
{
|
||||
records.add(qRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,7 +193,7 @@ public class MemoryRecordStore
|
||||
for(QRecord record : getTableData(leftTable).values())
|
||||
{
|
||||
QRecord productRecord = new QRecord();
|
||||
addRecordToProduct(productRecord, record, leftTable.getName());
|
||||
addRecordToProduct(productRecord, record, null);
|
||||
crossProduct.add(productRecord);
|
||||
}
|
||||
|
||||
@ -220,7 +238,9 @@ public class MemoryRecordStore
|
||||
{
|
||||
for(JoinOn joinOn : queryJoin.getJoinMetaData().getJoinOns())
|
||||
{
|
||||
Serializable leftValue = productRecord.getValue(queryJoin.getBaseTableOrAlias() + "." + joinOn.getLeftField());
|
||||
Serializable leftValue = productRecord.getValues().containsKey(queryJoin.getBaseTableOrAlias() + "." + joinOn.getLeftField())
|
||||
? productRecord.getValue(queryJoin.getBaseTableOrAlias() + "." + joinOn.getLeftField())
|
||||
: productRecord.getValue(joinOn.getLeftField());
|
||||
Serializable rightValue = nextTableRecord.getValue(joinOn.getRightField());
|
||||
if(!Objects.equals(leftValue, rightValue))
|
||||
{
|
||||
@ -240,7 +260,7 @@ public class MemoryRecordStore
|
||||
{
|
||||
for(Map.Entry<String, Serializable> entry : record.getValues().entrySet())
|
||||
{
|
||||
productRecord.withValue(tableNameOrAlias + "." + entry.getKey(), entry.getValue());
|
||||
productRecord.withValue(tableNameOrAlias == null ? entry.getKey() : tableNameOrAlias + "." + entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,7 +269,7 @@ public class MemoryRecordStore
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer count(CountInput input)
|
||||
public Integer count(CountInput input) throws QException
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(input.getTableName());
|
||||
@ -349,9 +369,8 @@ public class MemoryRecordStore
|
||||
{
|
||||
Serializable primaryKeyValue = ValueUtils.getValueAsFieldType(primaryKeyField.getType(), record.getValue(primaryKeyField.getName()));
|
||||
|
||||
if(primaryKeyValue == null)
|
||||
if(CollectionUtils.nullSafeHasContents(record.getErrors()))
|
||||
{
|
||||
record.addError("Missing value in primary key field");
|
||||
outputRecords.add(record);
|
||||
continue;
|
||||
}
|
||||
|
@ -341,7 +341,7 @@ class InsertActionTest extends BaseTest
|
||||
insertLineItemExtrinsicInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
insertLineItemExtrinsicInput.setRecords(List.of(new QRecord().withValue("lineItemId", 4200).withValue("key", "kidsCanCallYou").withValue("value", "HoJu")));
|
||||
InsertOutput insertLineItemExtrinsicOutput = new InsertAction().execute(insertLineItemExtrinsicInput);
|
||||
assertEquals("You do not have permission to insert this record.", insertLineItemExtrinsicOutput.getRecords().get(0).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertLineItemExtrinsicOutput.getRecords().get(0).getErrors().get(0));
|
||||
}
|
||||
|
||||
{
|
||||
@ -373,7 +373,7 @@ class InsertActionTest extends BaseTest
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record.", insertOutput.getRecords().get(2).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(2).getErrors().get(0));
|
||||
assertEquals(0, insertOutput.getRecords().get(3).getErrors().size());
|
||||
assertNotNull(insertOutput.getRecords().get(3).getValueInteger("id"));
|
||||
}
|
||||
@ -397,12 +397,12 @@ class InsertActionTest extends BaseTest
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record.", insertOutput.getRecords().get(2).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(2).getErrors().get(0));
|
||||
assertEquals(0, insertOutput.getRecords().get(3).getErrors().size());
|
||||
assertNotNull(insertOutput.getRecords().get(3).getValueInteger("id"));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(4).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(5).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record.", insertOutput.getRecords().get(6).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(6).getErrors().get(0));
|
||||
assertEquals(0, insertOutput.getRecords().get(7).getErrors().size());
|
||||
assertNotNull(insertOutput.getRecords().get(7).getValueInteger("id"));
|
||||
}
|
||||
@ -457,7 +457,7 @@ class InsertActionTest extends BaseTest
|
||||
insertLineItemInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
insertLineItemInput.setRecords(List.of(new QRecord().withValue("orderId", 42).withValue("sku", "BASIC1").withValue("quantity", 1)));
|
||||
InsertOutput insertLineItemOutput = new InsertAction().execute(insertLineItemInput);
|
||||
assertEquals("You do not have permission to insert this record.", insertLineItemOutput.getRecords().get(0).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertLineItemOutput.getRecords().get(0).getErrors().get(0));
|
||||
}
|
||||
|
||||
{
|
||||
@ -482,7 +482,7 @@ class InsertActionTest extends BaseTest
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record.", insertOutput.getRecords().get(2).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(2).getErrors().get(0));
|
||||
assertEquals(0, insertOutput.getRecords().get(3).getErrors().size());
|
||||
assertNotNull(insertOutput.getRecords().get(3).getValueInteger("id"));
|
||||
}
|
||||
@ -506,12 +506,12 @@ class InsertActionTest extends BaseTest
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record.", insertOutput.getRecords().get(2).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(2).getErrors().get(0));
|
||||
assertEquals(0, insertOutput.getRecords().get(3).getErrors().size());
|
||||
assertNotNull(insertOutput.getRecords().get(3).getValueInteger("id"));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(4).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(5).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record.", insertOutput.getRecords().get(6).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(6).getErrors().get(0));
|
||||
assertEquals(0, insertOutput.getRecords().get(7).getErrors().size());
|
||||
assertNotNull(insertOutput.getRecords().get(7).getValueInteger("id"));
|
||||
}
|
||||
|
@ -29,13 +29,15 @@ import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
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.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@ -76,7 +78,6 @@ class UpdateActionTest extends BaseTest
|
||||
@Test
|
||||
void testUpdateAssociationsUpdateOneChild() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true);
|
||||
|
||||
insert2OrdersWith3Lines3LineExtrinsicsAnd4OrderExtrinsicAssociations();
|
||||
@ -93,18 +94,18 @@ class UpdateActionTest extends BaseTest
|
||||
));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
List<QRecord> orders = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER);
|
||||
List<QRecord> orders = TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER);
|
||||
assertEquals(2, orders.size());
|
||||
assertEquals("ORD123-b", orders.get(0).getValueString("orderNo"));
|
||||
|
||||
List<QRecord> orderLines = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
List<QRecord> orderLines = TestUtils.queryTable(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
assertEquals(3, orderLines.size());
|
||||
assertEquals(17, orderLines.get(0).getValueInteger("quantity"));
|
||||
|
||||
List<QRecord> lineItemExtrinsics = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
List<QRecord> lineItemExtrinsics = TestUtils.queryTable(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
assertEquals(3, lineItemExtrinsics.size());
|
||||
|
||||
List<QRecord> orderExtrinsics = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
|
||||
List<QRecord> orderExtrinsics = TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
|
||||
assertEquals(4, orderExtrinsics.size());
|
||||
}
|
||||
|
||||
@ -116,7 +117,6 @@ class UpdateActionTest extends BaseTest
|
||||
@Test
|
||||
void testUpdateAssociationsUpdateOneGrandChild() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true);
|
||||
|
||||
insert2OrdersWith3Lines3LineExtrinsicsAnd4OrderExtrinsicAssociations();
|
||||
@ -134,18 +134,18 @@ class UpdateActionTest extends BaseTest
|
||||
));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
List<QRecord> orders = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER);
|
||||
List<QRecord> orders = TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER);
|
||||
assertEquals(2, orders.size());
|
||||
assertEquals("ORD123-b", orders.get(0).getValueString("orderNo"));
|
||||
|
||||
List<QRecord> orderLines = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
List<QRecord> orderLines = TestUtils.queryTable(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
assertEquals(3, orderLines.size());
|
||||
|
||||
List<QRecord> lineItemExtrinsics = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
List<QRecord> lineItemExtrinsics = TestUtils.queryTable(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
assertEquals(3, lineItemExtrinsics.size());
|
||||
assertEquals("LINE-VAL-1-updated", lineItemExtrinsics.get(0).getValueString("value"));
|
||||
|
||||
List<QRecord> orderExtrinsics = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
|
||||
List<QRecord> orderExtrinsics = TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
|
||||
assertEquals(4, orderExtrinsics.size());
|
||||
}
|
||||
|
||||
@ -157,7 +157,6 @@ class UpdateActionTest extends BaseTest
|
||||
@Test
|
||||
void testUpdateAssociationsDeleteOneChild() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true);
|
||||
|
||||
insert2OrdersWith3Lines3LineExtrinsicsAnd4OrderExtrinsicAssociations();
|
||||
@ -173,18 +172,18 @@ class UpdateActionTest extends BaseTest
|
||||
));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
List<QRecord> orders = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER);
|
||||
List<QRecord> orders = TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER);
|
||||
assertEquals(2, orders.size());
|
||||
assertEquals("ORD123-b", orders.get(0).getValueString("orderNo"));
|
||||
|
||||
List<QRecord> orderLines = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
List<QRecord> orderLines = TestUtils.queryTable(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
assertEquals(2, orderLines.size());
|
||||
assertTrue(orderLines.stream().noneMatch(r -> r.getValueInteger("id").equals(1))); // id=1 should be deleted
|
||||
|
||||
List<QRecord> lineItemExtrinsics = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
List<QRecord> lineItemExtrinsics = TestUtils.queryTable(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
assertEquals(2, lineItemExtrinsics.size()); // one was deleted (when its parent was deleted)
|
||||
|
||||
List<QRecord> orderExtrinsics = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
|
||||
List<QRecord> orderExtrinsics = TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
|
||||
assertEquals(4, orderExtrinsics.size());
|
||||
}
|
||||
|
||||
@ -196,7 +195,6 @@ class UpdateActionTest extends BaseTest
|
||||
@Test
|
||||
void testUpdateAssociationsDeleteGrandchildren() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true);
|
||||
|
||||
insert2OrdersWith3Lines3LineExtrinsicsAnd4OrderExtrinsicAssociations();
|
||||
@ -213,17 +211,17 @@ class UpdateActionTest extends BaseTest
|
||||
));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
List<QRecord> orders = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER);
|
||||
List<QRecord> orders = TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER);
|
||||
assertEquals(2, orders.size());
|
||||
assertEquals("ORD123-b", orders.get(0).getValueString("orderNo"));
|
||||
|
||||
List<QRecord> orderLines = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
List<QRecord> orderLines = TestUtils.queryTable(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
assertEquals(3, orderLines.size());
|
||||
|
||||
List<QRecord> lineItemExtrinsics = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
List<QRecord> lineItemExtrinsics = TestUtils.queryTable(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
assertEquals(1, lineItemExtrinsics.size()); // deleted the two beneath line item id=2
|
||||
|
||||
List<QRecord> orderExtrinsics = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
|
||||
List<QRecord> orderExtrinsics = TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
|
||||
assertEquals(4, orderExtrinsics.size());
|
||||
}
|
||||
|
||||
@ -235,7 +233,6 @@ class UpdateActionTest extends BaseTest
|
||||
@Test
|
||||
void testUpdateAssociationsInsertOneChild() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true);
|
||||
|
||||
insert2OrdersWith3Lines3LineExtrinsicsAnd4OrderExtrinsicAssociations();
|
||||
@ -253,19 +250,19 @@ class UpdateActionTest extends BaseTest
|
||||
));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
List<QRecord> orders = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER);
|
||||
List<QRecord> orders = TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER);
|
||||
assertEquals(2, orders.size());
|
||||
assertEquals("ORD123-b", orders.get(0).getValueString("orderNo"));
|
||||
|
||||
List<QRecord> orderLines = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
List<QRecord> orderLines = TestUtils.queryTable(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
assertEquals(4, orderLines.size());
|
||||
assertEquals("BASIC4", orderLines.get(3).getValueString("sku"));
|
||||
assertEquals(47, orderLines.get(3).getValueInteger("quantity"));
|
||||
|
||||
List<QRecord> lineItemExtrinsics = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
List<QRecord> lineItemExtrinsics = TestUtils.queryTable(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
assertEquals(3, lineItemExtrinsics.size());
|
||||
|
||||
List<QRecord> orderExtrinsics = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
|
||||
List<QRecord> orderExtrinsics = TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
|
||||
assertEquals(4, orderExtrinsics.size());
|
||||
}
|
||||
|
||||
@ -277,7 +274,6 @@ class UpdateActionTest extends BaseTest
|
||||
@Test
|
||||
void testUpdateAssociationsDeleteAllChildren() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true);
|
||||
|
||||
insert2OrdersWith3Lines3LineExtrinsicsAnd4OrderExtrinsicAssociations();
|
||||
@ -293,16 +289,16 @@ class UpdateActionTest extends BaseTest
|
||||
));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
List<QRecord> orders = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER);
|
||||
List<QRecord> orders = TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER);
|
||||
assertEquals(2, orders.size());
|
||||
|
||||
List<QRecord> lineItemExtrinsics = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
List<QRecord> lineItemExtrinsics = TestUtils.queryTable(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
assertEquals(0, lineItemExtrinsics.size());
|
||||
|
||||
List<QRecord> orderLines = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
List<QRecord> orderLines = TestUtils.queryTable(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
assertEquals(0, orderLines.size()); // all of these got deleted too.
|
||||
|
||||
List<QRecord> orderExtrinsics = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
|
||||
List<QRecord> orderExtrinsics = TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
|
||||
assertEquals(4, orderExtrinsics.size());
|
||||
}
|
||||
|
||||
@ -359,7 +355,7 @@ class UpdateActionTest extends BaseTest
|
||||
new QRecord().withValue("id", 3).withValue("storeId", 999).withValue("orderNo", "ORD3"),
|
||||
new QRecord().withValue("id", 4).withValue("storeId", 999).withValue("orderNo", "ORD4")
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
new InsertAction().execute(insertInput);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// do our update that we'll test the results of //
|
||||
@ -395,6 +391,361 @@ class UpdateActionTest extends BaseTest
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
assertEquals(1, updateOutput.getRecords().get(3).getErrors().size());
|
||||
assertEquals("Missing value in required field: Order No", updateOutput.getRecords().get(3).getErrors().get(0));
|
||||
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
/*
|
||||
@Test
|
||||
void testInsertMultiLevelSecurityJoins() throws QException
|
||||
{
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE, 1);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// null value in the foreign key to the join-table that provides the security value //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
insertInput.setRecords(List.of(new QRecord().withValue("lineItemId", null).withValue("key", "kidsCanCallYou").withValue("value", "HoJu")));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// value in the foreign key to the join-table that provides the security value, but the referenced record isn't found //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
insertInput.setRecords(List.of(new QRecord().withValue("lineItemId", 1701).withValue("key", "kidsCanCallYou").withValue("value", "HoJu")));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
|
||||
}
|
||||
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// insert an order and lineItem with storeId=2 - then, reset our session to only have storeId=1 in it - and try to insert an order-line referencing that order. //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QContext.getQSession().withSecurityKeyValues(new HashMap<>());
|
||||
QContext.getQSession().withSecurityKeyValues(TestUtils.SECURITY_KEY_TYPE_STORE, List.of(2));
|
||||
InsertInput insertOrderInput = new InsertInput();
|
||||
insertOrderInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
insertOrderInput.setRecords(List.of(new QRecord().withValue("id", 42).withValue("storeId", 2)));
|
||||
InsertOutput insertOrderOutput = new InsertAction().execute(insertOrderInput);
|
||||
assertEquals(42, insertOrderOutput.getRecords().get(0).getValueInteger("id"));
|
||||
|
||||
InsertInput insertLineItemInput = new InsertInput();
|
||||
insertLineItemInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
insertLineItemInput.setRecords(List.of(new QRecord().withValue("id", 4200).withValue("orderId", 42).withValue("sku", "BASIC1").withValue("quantity", 24)));
|
||||
InsertOutput insertLineItemOutput = new InsertAction().execute(insertLineItemInput);
|
||||
assertEquals(4200, insertLineItemOutput.getRecords().get(0).getValueInteger("id"));
|
||||
|
||||
QContext.getQSession().withSecurityKeyValues(new HashMap<>());
|
||||
QContext.getQSession().withSecurityKeyValues(TestUtils.SECURITY_KEY_TYPE_STORE, List.of(1));
|
||||
InsertInput insertLineItemExtrinsicInput = new InsertInput();
|
||||
insertLineItemExtrinsicInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
insertLineItemExtrinsicInput.setRecords(List.of(new QRecord().withValue("lineItemId", 4200).withValue("key", "kidsCanCallYou").withValue("value", "HoJu")));
|
||||
InsertOutput insertLineItemExtrinsicOutput = new InsertAction().execute(insertLineItemExtrinsicInput);
|
||||
assertEquals("You do not have permission to insert this record.", insertLineItemExtrinsicOutput.getRecords().get(0).getErrors().get(0));
|
||||
}
|
||||
|
||||
{
|
||||
QContext.getQSession().withSecurityKeyValues(new HashMap<>());
|
||||
QContext.getQSession().withSecurityKeyValues(TestUtils.SECURITY_KEY_TYPE_STORE, List.of(1));
|
||||
InsertInput insertOrderInput = new InsertInput();
|
||||
insertOrderInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
insertOrderInput.setRecords(List.of(new QRecord().withValue("id", 47).withValue("storeId", 1)));
|
||||
InsertOutput insertOrderOutput = new InsertAction().execute(insertOrderInput);
|
||||
assertEquals(47, insertOrderOutput.getRecords().get(0).getValueInteger("id"));
|
||||
|
||||
InsertInput insertLineItemInput = new InsertInput();
|
||||
insertLineItemInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
insertLineItemInput.setRecords(List.of(new QRecord().withValue("id", 4700).withValue("orderId", 47).withValue("sku", "BASIC1").withValue("quantity", 74)));
|
||||
InsertOutput insertLineItemOutput = new InsertAction().execute(insertLineItemInput);
|
||||
assertEquals(4700, insertLineItemOutput.getRecords().get(0).getValueInteger("id"));
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// combine all the above, plus one record that works //
|
||||
///////////////////////////////////////////////////////
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("lineItemId", null).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu"),
|
||||
new QRecord().withValue("lineItemId", 1701).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu"),
|
||||
new QRecord().withValue("lineItemId", 4200).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu"),
|
||||
new QRecord().withValue("lineItemId", 4700).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu")
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record.", insertOutput.getRecords().get(2).getErrors().get(0));
|
||||
assertEquals(0, insertOutput.getRecords().get(3).getErrors().size());
|
||||
assertNotNull(insertOutput.getRecords().get(3).getValueInteger("id"));
|
||||
}
|
||||
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// one more time, but with multiple input records referencing each foreign key //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("lineItemId", null).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu"),
|
||||
new QRecord().withValue("lineItemId", 1701).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu"),
|
||||
new QRecord().withValue("lineItemId", 4200).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu"),
|
||||
new QRecord().withValue("lineItemId", 4700).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu"),
|
||||
new QRecord().withValue("lineItemId", null).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu"),
|
||||
new QRecord().withValue("lineItemId", 1701).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu"),
|
||||
new QRecord().withValue("lineItemId", 4200).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu"),
|
||||
new QRecord().withValue("lineItemId", 4700).withValue("key", "theKidsCanCallYou").withValue("value", "HoJu")
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record.", insertOutput.getRecords().get(2).getErrors().get(0));
|
||||
assertEquals(0, insertOutput.getRecords().get(3).getErrors().size());
|
||||
assertNotNull(insertOutput.getRecords().get(3).getValueInteger("id"));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(4).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(5).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record.", insertOutput.getRecords().get(6).getErrors().get(0));
|
||||
assertEquals(0, insertOutput.getRecords().get(7).getErrors().size());
|
||||
assertNotNull(insertOutput.getRecords().get(7).getValueInteger("id"));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
/*
|
||||
@Test
|
||||
void testInsertSingleLevelSecurityJoins() throws QException
|
||||
{
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE, 1);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// null value in the foreign key to the join-table that provides the security value //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
insertInput.setRecords(List.of(new QRecord().withValue("orderId", null).withValue("sku", "BASIC1").withValue("quantity", 1)));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// value in the foreign key to the join-table that provides the security value, but the referenced record isn't found //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
insertInput.setRecords(List.of(new QRecord().withValue("orderId", 1701).withValue("sku", "BASIC1").withValue("quantity", 1)));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
|
||||
}
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// insert an order with storeId=2 - then, reset our session to only have storeId=1 in it - and try to insert an order-line referencing that order. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QContext.getQSession().withSecurityKeyValues(new HashMap<>());
|
||||
QContext.getQSession().withSecurityKeyValues(TestUtils.SECURITY_KEY_TYPE_STORE, List.of(2));
|
||||
InsertInput insertOrderInput = new InsertInput();
|
||||
insertOrderInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
insertOrderInput.setRecords(List.of(new QRecord().withValue("id", 42).withValue("storeId", 2)));
|
||||
InsertOutput insertOrderOutput = new InsertAction().execute(insertOrderInput);
|
||||
assertEquals(42, insertOrderOutput.getRecords().get(0).getValueInteger("id"));
|
||||
|
||||
QContext.getQSession().withSecurityKeyValues(new HashMap<>());
|
||||
QContext.getQSession().withSecurityKeyValues(TestUtils.SECURITY_KEY_TYPE_STORE, List.of(1));
|
||||
InsertInput insertLineItemInput = new InsertInput();
|
||||
insertLineItemInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
insertLineItemInput.setRecords(List.of(new QRecord().withValue("orderId", 42).withValue("sku", "BASIC1").withValue("quantity", 1)));
|
||||
InsertOutput insertLineItemOutput = new InsertAction().execute(insertLineItemInput);
|
||||
assertEquals("You do not have permission to insert this record.", insertLineItemOutput.getRecords().get(0).getErrors().get(0));
|
||||
}
|
||||
|
||||
{
|
||||
QContext.getQSession().withSecurityKeyValues(new HashMap<>());
|
||||
QContext.getQSession().withSecurityKeyValues(TestUtils.SECURITY_KEY_TYPE_STORE, List.of(1));
|
||||
InsertInput insertOrderInput = new InsertInput();
|
||||
insertOrderInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
insertOrderInput.setRecords(List.of(new QRecord().withValue("id", 47).withValue("storeId", 1)));
|
||||
new InsertAction().execute(insertOrderInput);
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// combine all the above, plus one record that works //
|
||||
///////////////////////////////////////////////////////
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("orderId", null).withValue("sku", "BASIC1").withValue("quantity", 1),
|
||||
new QRecord().withValue("orderId", 1701).withValue("sku", "BASIC1").withValue("quantity", 1),
|
||||
new QRecord().withValue("orderId", 42).withValue("sku", "BASIC1").withValue("quantity", 1),
|
||||
new QRecord().withValue("orderId", 47).withValue("sku", "BASIC1").withValue("quantity", 1)
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record.", insertOutput.getRecords().get(2).getErrors().get(0));
|
||||
assertEquals(0, insertOutput.getRecords().get(3).getErrors().size());
|
||||
assertNotNull(insertOutput.getRecords().get(3).getValueInteger("id"));
|
||||
}
|
||||
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// one more time, but with multiple input records referencing each foreign key //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("orderId", null).withValue("sku", "BASIC1").withValue("quantity", 1),
|
||||
new QRecord().withValue("orderId", 1701).withValue("sku", "BASIC1").withValue("quantity", 1),
|
||||
new QRecord().withValue("orderId", 42).withValue("sku", "BASIC1").withValue("quantity", 1),
|
||||
new QRecord().withValue("orderId", 47).withValue("sku", "BASIC1").withValue("quantity", 1),
|
||||
new QRecord().withValue("orderId", null).withValue("sku", "BASIC1").withValue("quantity", 1),
|
||||
new QRecord().withValue("orderId", 1701).withValue("sku", "BASIC1").withValue("quantity", 1),
|
||||
new QRecord().withValue("orderId", 42).withValue("sku", "BASIC1").withValue("quantity", 1),
|
||||
new QRecord().withValue("orderId", 47).withValue("sku", "BASIC1").withValue("quantity", 1)
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(0).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(1).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record.", insertOutput.getRecords().get(2).getErrors().get(0));
|
||||
assertEquals(0, insertOutput.getRecords().get(3).getErrors().size());
|
||||
assertNotNull(insertOutput.getRecords().get(3).getValueInteger("id"));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(4).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record - the referenced Order was not found.", insertOutput.getRecords().get(5).getErrors().get(0));
|
||||
assertEquals("You do not have permission to insert this record.", insertOutput.getRecords().get(6).getErrors().get(0));
|
||||
assertEquals(0, insertOutput.getRecords().get(7).getErrors().size());
|
||||
assertNotNull(insertOutput.getRecords().get(7).getValueInteger("id"));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUpdateNotFoundFails() throws QException
|
||||
{
|
||||
QContext.getQSession().withSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, ListBuilder.of(true)));
|
||||
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 999).withValue("orderNo", "updated")));
|
||||
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
|
||||
assertEquals("No record was found to update for Id = 999", updateOutput.getRecords().get(0).getErrors().get(0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testSecurityKeyValueDenied() throws QException
|
||||
{
|
||||
////////////////////////////////
|
||||
// insert an order in store 1 //
|
||||
////////////////////////////////
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE, 1);
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
insertInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("orderNo", "original").withValue("storeId", 1)));
|
||||
new InsertAction().execute(insertInput);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// now, as a session with store 2, try to update that store 1 order //
|
||||
// it should error as "not found" //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
QContext.getQSession().withSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE, ListBuilder.of(2)));
|
||||
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("orderNo", "updated")));
|
||||
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
|
||||
assertEquals("No record was found to update for Id = 1", updateOutput.getRecords().get(0).getErrors().get(0));
|
||||
|
||||
QContext.getQSession().withSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, ListBuilder.of(true)));
|
||||
assertThat(TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER)).noneMatch(r -> r.getValueString("orderNo").equals("updated"));
|
||||
assertThat(TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER)).anyMatch(r -> r.getValueString("orderNo").equals("original"));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// now, go back to store 1 in session, and try to change the order to store 2. //
|
||||
// that should fail, as you don't have permission to write to store 2. //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
QContext.getQSession().withSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE, ListBuilder.of(1)));
|
||||
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("orderNo", "updated").withValue("storeId", 2)));
|
||||
updateOutput = new UpdateAction().execute(updateInput);
|
||||
assertEquals("You do not have permission to update a record with a value of 2 in the field: Store Id", updateOutput.getRecords().get(0).getErrors().get(0));
|
||||
|
||||
QContext.getQSession().withSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, ListBuilder.of(true)));
|
||||
assertThat(TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER)).noneMatch(r -> r.getValueString("orderNo").equals("updated"));
|
||||
assertThat(TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER)).anyMatch(r -> r.getValueString("orderNo").equals("original"));
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
/*
|
||||
@Test
|
||||
void testSecurityKeyNullDenied() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE, 1);
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
insertInput.setRecords(List.of(new QRecord()));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals("You do not have permission to insert a record without a value in the field: Store Id", insertOutput.getRecords().get(0).getErrors().get(0));
|
||||
assertEquals(0, TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER).size());
|
||||
}
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
/*
|
||||
@Test
|
||||
void testSecurityKeyNullAllowed() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_ORDER).getRecordSecurityLocks().get(0).setNullValueBehavior(RecordSecurityLock.NullValueBehavior.ALLOW);
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE, 1);
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
insertInput.setRecords(List.of(new QRecord()));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals(0, insertOutput.getRecords().get(0).getErrors().size());
|
||||
assertEquals(1, TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER).size());
|
||||
}
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
/*
|
||||
@Test
|
||||
void testSecurityKeyAllAccess() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_ORDER).getRecordSecurityLocks().get(0).setNullValueBehavior(RecordSecurityLock.NullValueBehavior.ALLOW);
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true);
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("storeId", 999),
|
||||
new QRecord().withValue("storeId", null)
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals(2, TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER).size());
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
@ -325,7 +325,7 @@ public class TableBasedAuthenticationModuleTest extends BaseTest
|
||||
MemoryRecordStore.setCollectStatistics(true);
|
||||
assertTrue(new TableBasedAuthenticationModule().isSessionValid(qInstance, session));
|
||||
Map<String, Integer> statistics = MemoryRecordStore.getStatistics();
|
||||
assertEquals(3, statistics.get(MemoryRecordStore.STAT_QUERIES_RAN));
|
||||
assertEquals(4, statistics.get(MemoryRecordStore.STAT_QUERIES_RAN));
|
||||
}
|
||||
|
||||
|
||||
|
@ -892,6 +892,16 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Make it easy (e.g., for tests) to turn on logging of SQL
|
||||
*******************************************************************************/
|
||||
public static void setLogSQL(boolean on)
|
||||
{
|
||||
System.setProperty("qqq.rdbms.logSQL", String.valueOf(on));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -104,14 +104,6 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte
|
||||
.toList();
|
||||
recordsByFieldBeingUpdated.add(updatableFields, record);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// to update a record, we must have its primary key value - so - check - if it's missing, mark it as an error //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(record.getValue(table.getPrimaryKeyField()) == null)
|
||||
{
|
||||
record.addError("Missing value in primary key field");
|
||||
}
|
||||
|
||||
if(CollectionUtils.nullSafeIsEmpty(record.getErrors()))
|
||||
{
|
||||
haveAnyWithoutErorrs = true;
|
||||
|
@ -27,6 +27,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
@ -74,7 +75,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
{
|
||||
UpdateInput updateInput = initUpdateRequest();
|
||||
updateInput.setRecords(null);
|
||||
UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput);
|
||||
UpdateOutput updateResult = new UpdateAction().execute(updateInput);
|
||||
assertEquals(0, updateResult.getRecords().size());
|
||||
}
|
||||
|
||||
@ -88,8 +89,8 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
{
|
||||
UpdateInput updateInput = initUpdateRequest();
|
||||
updateInput.setRecords(Collections.emptyList());
|
||||
new RDBMSUpdateAction().execute(updateInput);
|
||||
UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput);
|
||||
new UpdateAction().execute(updateInput);
|
||||
UpdateOutput updateResult = new UpdateAction().execute(updateInput);
|
||||
assertEquals(0, updateResult.getRecords().size());
|
||||
}
|
||||
|
||||
@ -110,9 +111,9 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
.withValue("birthDate", "2210-05-20");
|
||||
updateInput.setRecords(List.of(record));
|
||||
|
||||
UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput);
|
||||
UpdateOutput updateResult = new UpdateAction().execute(updateInput);
|
||||
Map<String, Integer> statistics = QueryManager.getStatistics();
|
||||
assertEquals(1, statistics.get(QueryManager.STAT_QUERIES_RAN));
|
||||
assertEquals(2, statistics.get(QueryManager.STAT_QUERIES_RAN));
|
||||
|
||||
assertEquals(1, updateResult.getRecords().size(), "Should return 1 row");
|
||||
assertEquals(2, updateResult.getRecords().get(0).getValue("id"), "Should have id=2 in the row");
|
||||
@ -163,12 +164,12 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
|
||||
updateInput.setRecords(List.of(record1, record2, record3));
|
||||
|
||||
UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput);
|
||||
UpdateOutput updateResult = new UpdateAction().execute(updateInput);
|
||||
|
||||
// this test runs one batch and one regular query
|
||||
Map<String, Integer> statistics = QueryManager.getStatistics();
|
||||
assertEquals(1, statistics.get(QueryManager.STAT_BATCHES_RAN));
|
||||
assertEquals(1, statistics.get(QueryManager.STAT_QUERIES_RAN));
|
||||
assertEquals(2, statistics.get(QueryManager.STAT_QUERIES_RAN));
|
||||
|
||||
assertEquals(3, updateResult.getRecords().size(), "Should return 3 rows");
|
||||
assertEquals(1, updateResult.getRecords().get(0).getValue("id"), "Should have expected ids in the row");
|
||||
@ -234,7 +235,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
|
||||
updateInput.setRecords(List.of(record1, record2));
|
||||
|
||||
UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput);
|
||||
UpdateOutput updateResult = new UpdateAction().execute(updateInput);
|
||||
Map<String, Integer> statistics = QueryManager.getStatistics();
|
||||
assertEquals(1, statistics.get(QueryManager.STAT_BATCHES_RAN));
|
||||
|
||||
@ -287,9 +288,9 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
|
||||
updateInput.setRecords(records);
|
||||
|
||||
UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput);
|
||||
UpdateOutput updateResult = new UpdateAction().execute(updateInput);
|
||||
Map<String, Integer> statistics = QueryManager.getStatistics();
|
||||
assertEquals(1, statistics.get(QueryManager.STAT_QUERIES_RAN));
|
||||
assertEquals(2, statistics.get(QueryManager.STAT_QUERIES_RAN));
|
||||
|
||||
assertEquals(5, updateResult.getRecords().size(), "Should return 5 rows");
|
||||
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
@ -320,7 +321,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
.withValue("id", 1)
|
||||
.withValue("firstName", "Johnny Updated"));
|
||||
updateInput.setRecords(records);
|
||||
new RDBMSUpdateAction().execute(updateInput);
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
String updatedModifyDate = selectModifyDate(1);
|
||||
|
||||
@ -345,7 +346,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
.withValue("createDate", "2022-10-03T10:29:35Z")
|
||||
.withValue("firstName", "Johnny Updated"));
|
||||
updateInput.setRecords(records);
|
||||
new RDBMSUpdateAction().execute(updateInput);
|
||||
new UpdateAction().execute(updateInput);
|
||||
}
|
||||
|
||||
|
||||
@ -362,7 +363,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
records.add(new QRecord()
|
||||
.withValue("firstName", "Johnny Updated"));
|
||||
updateInput.setRecords(records);
|
||||
UpdateOutput updateOutput = new RDBMSUpdateAction().execute(updateInput);
|
||||
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
|
||||
assertFalse(updateOutput.getRecords().get(0).getErrors().isEmpty());
|
||||
assertEquals("Missing value in primary key field", updateOutput.getRecords().get(0).getErrors().get(0));
|
||||
}
|
||||
@ -374,7 +375,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
.withValue("id", null)
|
||||
.withValue("firstName", "Johnny Updated"));
|
||||
updateInput.setRecords(records);
|
||||
UpdateOutput updateOutput = new RDBMSUpdateAction().execute(updateInput);
|
||||
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
|
||||
assertFalse(updateOutput.getRecords().get(0).getErrors().isEmpty());
|
||||
assertEquals("Missing value in primary key field", updateOutput.getRecords().get(0).getErrors().get(0));
|
||||
}
|
||||
@ -389,7 +390,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
.withValue("id", 2)
|
||||
.withValue("firstName", "Johnny Updated"));
|
||||
updateInput.setRecords(records);
|
||||
UpdateOutput updateOutput = new RDBMSUpdateAction().execute(updateInput);
|
||||
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
|
||||
|
||||
assertFalse(updateOutput.getRecords().get(0).getErrors().isEmpty());
|
||||
assertEquals("Missing value in primary key field", updateOutput.getRecords().get(0).getErrors().get(0));
|
||||
|
@ -76,9 +76,6 @@ class QJavalinApiHandlerTest extends BaseTest
|
||||
|
||||
protected static QJavalinImplementation qJavalinImplementation;
|
||||
|
||||
private static final String OAUTH_CLIENT_ID = "test-oauth-client-id";
|
||||
private static final String OAUTH_CLIENT_SECRET = "test-oauth-client-secret";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
Reference in New Issue
Block a user