Add check for records pre-delete action (for security and better errors); 404s and ids in 207s for bulk update & delete; ignore non-editable fields;

This commit is contained in:
2023-03-31 12:11:12 -05:00
parent 21e3cdd0a5
commit 084630918f
15 changed files with 690 additions and 388 deletions

View File

@ -24,7 +24,11 @@ package com.kingsrook.qqq.backend.core.actions.tables;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
import com.kingsrook.qqq.backend.core.actions.audits.DMLAuditAction;
import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface;
@ -41,12 +45,14 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
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.audits.AuditLevel;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
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.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;
/*******************************************************************************
@ -57,6 +63,8 @@ public class DeleteAction
{
private static final QLogger LOG = QLogger.getLogger(DeleteAction.class);
public static final String NOT_FOUND_ERROR_PREFIX = "No record was found to delete";
/*******************************************************************************
@ -68,7 +76,6 @@ public class DeleteAction
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(deleteInput.getBackend());
// todo pre-customization - just get to modify the request?
if(CollectionUtils.nullSafeHasContents(deleteInput.getPrimaryKeys()) && deleteInput.getQueryFilter() != null)
{
@ -92,13 +99,24 @@ public class DeleteAction
}
}
List<QRecord> recordListForAudit = getRecordListForAuditIfNeeded(deleteInput);
List<QRecord> recordListForAudit = getRecordListForAuditIfNeeded(deleteInput);
List<QRecord> recordsWithValidationErrors = validateRecordsExistAndCanBeAccessed(deleteInput, recordListForAudit);
DeleteOutput deleteOutput = deleteInterface.execute(deleteInput);
manageAssociations(deleteInput);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// merge the backend's output with any validation errors we found (whose ids wouldn't have gotten into the backend delete) //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
List<QRecord> outputRecordsWithErrors = deleteOutput.getRecordsWithErrors();
if(outputRecordsWithErrors == null)
{
deleteOutput.setRecordsWithErrors(new ArrayList<>());
outputRecordsWithErrors = deleteOutput.getRecordsWithErrors();
}
// todo post-customization - can do whatever w/ the result if you want
outputRecordsWithErrors.addAll(recordsWithValidationErrors);
manageAssociations(deleteInput);
new DMLAuditAction().execute(new DMLAuditInput().withTableActionInput(deleteInput).withRecordList(recordListForAudit));
@ -188,6 +206,84 @@ public class DeleteAction
/*******************************************************************************
** 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!
**
** This method, if it finds any missing records, will:
** - remove those ids from the deleteInput
** - create a QRecord with that id and a not-found error message.
*******************************************************************************/
private List<QRecord> validateRecordsExistAndCanBeAccessed(DeleteInput deleteInput, List<QRecord> oldRecordList) throws QException
{
List<QRecord> recordsWithErrors = new ArrayList<>();
QTableMetaData table = deleteInput.getTable();
QFieldMetaData primaryKeyField = table.getField(table.getPrimaryKeyField());
Set<Serializable> primaryKeysToRemoveFromInput = new HashSet<>();
List<List<Serializable>> pages = CollectionUtils.getPages(deleteInput.getPrimaryKeys(), 1000);
for(List<Serializable> page : pages)
{
List<Serializable> primaryKeysToLookup = new ArrayList<>();
for(Serializable primaryKeyValue : page)
{
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(Serializable primaryKeyValue : page)
{
primaryKeyValue = ValueUtils.getValueAsFieldType(primaryKeyField.getType(), primaryKeyValue);
if(!lookedUpRecords.containsKey(primaryKeyValue))
{
QRecord recordWithError = new QRecord();
recordsWithErrors.add(recordWithError);
recordWithError.setValue(primaryKeyField.getName(), primaryKeyValue);
recordWithError.addError(NOT_FOUND_ERROR_PREFIX + " for " + primaryKeyField.getLabel() + " = " + primaryKeyValue);
primaryKeysToRemoveFromInput.add(primaryKeyValue);
}
}
/////////////////////////////////////////////////////////////////
// do one mass removal of any bad keys from the input key list //
/////////////////////////////////////////////////////////////////
if(!primaryKeysToRemoveFromInput.isEmpty())
{
deleteInput.getPrimaryKeys().removeAll(primaryKeysToRemoveFromInput);
primaryKeysToRemoveFromInput.clear();
}
}
return (recordsWithErrors);
}
/*******************************************************************************
** For an implementation that doesn't support a queryFilter as its input,
** but a scenario where a query filter was passed in - run the query, to

View File

@ -72,6 +72,8 @@ public class UpdateAction
{
private static final QLogger LOG = QLogger.getLogger(UpdateAction.class);
public static final String NOT_FOUND_ERROR_PREFIX = "No record was found to update";
/*******************************************************************************
@ -193,7 +195,7 @@ public class UpdateAction
if(!lookedUpRecords.containsKey(value))
{
record.addError("No record was found to update for " + primaryKeyField.getLabel() + " = " + value);
record.addError(NOT_FOUND_ERROR_PREFIX + " for " + primaryKeyField.getLabel() + " = " + value);
}
}
}

View File

@ -27,6 +27,7 @@ import java.util.List;
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.utils.collections.MutableList;
/*******************************************************************************
@ -101,7 +102,10 @@ public class DeleteInput extends AbstractTableActionInput
*******************************************************************************/
public void setPrimaryKeys(List<Serializable> primaryKeys)
{
this.primaryKeys = primaryKeys;
///////////////////////////////////////////////////////////////////////////////////////////////
// the action may edit this list (e.g., to remove keys w/ errors), so wrap it in MutableList //
///////////////////////////////////////////////////////////////////////////////////////////////
this.primaryKeys = new MutableList<>(primaryKeys);
}
@ -112,7 +116,7 @@ public class DeleteInput extends AbstractTableActionInput
*******************************************************************************/
public DeleteInput withPrimaryKeys(List<Serializable> primaryKeys)
{
this.primaryKeys = primaryKeys;
setPrimaryKeys(primaryKeys);
return (this);
}

View File

@ -348,7 +348,7 @@ public class BackendQueryFilterUtils
}
}
if(!criterion.getValues().contains(value))
if(value == null || !criterion.getValues().contains(value))
{
return (false);
}

View File

@ -27,10 +27,15 @@ import java.util.Map;
import java.util.function.Supplier;
@SuppressWarnings({ "checkstyle:javadoc", "DanglingJavadoc" })
/*******************************************************************************
** Map.of is "great", but annoying because it makes unmodifiable maps, and it
** NPE's on nulls... So, replace it with this, which returns HashMaps, which
** "don't suck"
** NPE's on nulls... So, replace it with this, which returns HashMaps (or maps
** of the type you choose).
**
** Can use it 2 ways:
** MapBuilder.of(key, value, key2, value2, ...) => Map (a HashMap)
** MapBuilder.<KeyType ValueType>of(SomeMap::new).with(key, value).with(key2, value2)...build() => SomeMap (the type you specify)
*******************************************************************************/
public class MapBuilder<K, V>
{

View File

@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.actions.tables;
import java.util.List;
import java.util.Objects;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
@ -40,8 +41,9 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.audits.AuditLevel;
import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
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.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@ -57,8 +59,6 @@ class DeleteActionTest extends BaseTest
{
/*******************************************************************************
** At the core level, there isn't much that can be asserted, as it uses the
** mock implementation - just confirming that all of the "wiring" works.
**
*******************************************************************************/
@Test
@ -66,11 +66,17 @@ class DeleteActionTest extends BaseTest
{
DeleteInput request = new DeleteInput();
request.setTableName("person");
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// the mock backend - it'll find a record for id=1, but not for id=2 - so we can test both a found & deleted, and a not-found here //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
request.setPrimaryKeys(List.of(1, 2));
DeleteOutput result = new DeleteAction().execute(request);
assertNotNull(result);
assertEquals(2, result.getDeletedRecordCount());
assertTrue(CollectionUtils.nullSafeIsEmpty(result.getRecordsWithErrors()));
assertEquals(1, result.getDeletedRecordCount());
assertEquals(1, result.getRecordsWithErrors().size());
assertEquals(2, result.getRecordsWithErrors().get(0).getValueInteger("id"));
assertEquals("No record was found to delete for Id = 2", result.getRecordsWithErrors().get(0).getErrors().get(0));
}
@ -295,4 +301,102 @@ class DeleteActionTest extends BaseTest
return (queryOutput.getRecords());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testSecurityKeys() throws QException
{
QContext.getQSession().setSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE, ListBuilder.of(1)));
insert2OrdersWith3Lines3LineExtrinsicsAnd4OrderExtrinsicAssociations();
///////////////////////////////////////////////////////
// make sure we inserted the records we think we did //
///////////////////////////////////////////////////////
assertIdsExist(TestUtils.TABLE_NAME_ORDER, List.of(1, 2));
assertIdsExist(TestUtils.TABLE_NAME_LINE_ITEM, List.of(1, 2, 3));
assertIdsExist(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC, List.of(1, 2, 3));
assertIdsExist(TestUtils.TABLE_NAME_ORDER_EXTRINSIC, List.of(1, 2, 3, 4));
QContext.getQSession().setSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE, ListBuilder.of(2)));
//////////////////////////////////////////////////
// assert can't delete the records at any level //
//////////////////////////////////////////////////
DeleteInput deleteInput = new DeleteInput();
deleteInput.setTableName(TestUtils.TABLE_NAME_ORDER);
deleteInput.setPrimaryKeys(List.of(1));
DeleteOutput deleteOutput = new DeleteAction().execute(deleteInput);
assertEquals(0, deleteOutput.getDeletedRecordCount());
assertEquals(1, deleteOutput.getRecordsWithErrors().size());
assertEquals("No record was found to delete for Id = 1", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0));
deleteInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
deleteInput.setPrimaryKeys(List.of(1));
deleteOutput = new DeleteAction().execute(deleteInput);
assertEquals(0, deleteOutput.getDeletedRecordCount());
assertEquals(1, deleteOutput.getRecordsWithErrors().size());
assertEquals("No record was found to delete for Id = 1", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0));
deleteInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
deleteInput.setPrimaryKeys(List.of(1));
deleteOutput = new DeleteAction().execute(deleteInput);
assertEquals(0, deleteOutput.getDeletedRecordCount());
assertEquals(1, deleteOutput.getRecordsWithErrors().size());
assertEquals("No record was found to delete for Id = 1", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0));
deleteInput.setTableName(TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
deleteInput.setPrimaryKeys(List.of(1));
deleteOutput = new DeleteAction().execute(deleteInput);
assertEquals(0, deleteOutput.getDeletedRecordCount());
assertEquals(1, deleteOutput.getRecordsWithErrors().size());
assertEquals("No record was found to delete for Id = 1", deleteOutput.getRecordsWithErrors().get(0).getErrors().get(0));
}
/*******************************************************************************
**
*******************************************************************************/
private void assertIdsExist(String tableName, List<Integer> ids) throws QException
{
List<QRecord> records = TestUtils.queryTable(tableName);
for(Integer id : ids)
{
assertTrue(records.stream().anyMatch(r -> Objects.equals(id, r.getValueInteger("id"))));
}
}
/*******************************************************************************
**
*******************************************************************************/
private static void insert2OrdersWith3Lines3LineExtrinsicsAnd4OrderExtrinsicAssociations() throws QException
{
InsertInput insertInput = new InsertInput();
insertInput.setTableName(TestUtils.TABLE_NAME_ORDER);
insertInput.setRecords(List.of(
new QRecord().withValue("storeId", 1).withValue("orderNo", "ORD123")
.withAssociatedRecord("orderLine", new QRecord().withValue("sku", "BASIC1").withValue("quantity", 1)
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "LINE-EXT-1.1").withValue("value", "LINE-VAL-1")))
.withAssociatedRecord("orderLine", new QRecord().withValue("sku", "BASIC2").withValue("quantity", 2)
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "LINE-EXT-2.1").withValue("value", "LINE-VAL-2"))
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "LINE-EXT-2.2").withValue("value", "LINE-VAL-3")))
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "MY-FIELD-1").withValue("value", "MY-VALUE-1"))
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "MY-FIELD-2").withValue("value", "MY-VALUE-2"))
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "MY-FIELD-3").withValue("value", "MY-VALUE-3")),
new QRecord().withValue("storeId", 1).withValue("orderNo", "ORD124")
.withAssociatedRecord("orderLine", new QRecord().withValue("sku", "BASIC3").withValue("quantity", 3))
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "YOUR-FIELD-1").withValue("value", "YOUR-VALUE-1"))
));
new InsertAction().execute(insertInput);
}
}

View File

@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.actions.tables;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
@ -33,6 +34,7 @@ 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.model.metadata.security.RecordSecurityLock;
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;
@ -393,237 +395,114 @@ class UpdateActionTest extends BaseTest
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
void testUpdateSecurityJoins() throws QException
{
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE, 1);
QContext.getQSession().setSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE, ListBuilder.of(1, 2)));
//////////////////////////////////////////////////////////////////////////////////////
// null value in the foreign key to the join-table that provides the security value //
//////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////
// insert an order in each of store 1 and store 2 //
// with some lines and line-extrinsics //
////////////////////////////////////////////////////
InsertInput insertInput = new InsertInput();
insertInput.setTableName(TestUtils.TABLE_NAME_ORDER);
insertInput.setRecords(List.of(
new QRecord().withValue("id", 1).withValue("orderNo", "O1").withValue("storeId", 1),
new QRecord().withValue("id", 2).withValue("orderNo", "O2").withValue("storeId", 2)
));
new InsertAction().execute(insertInput);
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
insertInput.setRecords(List.of(
new QRecord().withValue("id", 10).withValue("orderId", 1).withValue("sku", "BASIC1"),
new QRecord().withValue("id", 20).withValue("orderId", 2).withValue("sku", "BASIC2")
));
new InsertAction().execute(insertInput);
insertInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
insertInput.setRecords(List.of(
new QRecord().withValue("id", 100).withValue("lineItemId", 10).withValue("key", "Key1").withValue("value", "Value1"),
new QRecord().withValue("id", 200).withValue("lineItemId", 20).withValue("key", "Key2").withValue("value", "Value2")
));
new InsertAction().execute(insertInput);
///////////////////////////////////////////////////////////
// try to remove the value that provides the 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)));
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));
UpdateInput updateInput = new UpdateInput();
updateInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
updateInput.setRecords(List.of(new QRecord().withValue("id", 10).withValue("orderId", null).withValue("sku", "BASIC2")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("You do not have permission to update this record - the referenced Order was not found.", updateOutput.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 //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// with a session that can only access store 1, try to update the line in store 2 //
// should fail as a not-found - you can't see that record. //
////////////////////////////////////////////////////////////////////////////////////
{
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().setSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE, ListBuilder.of(1)));
UpdateInput updateInput = new UpdateInput();
updateInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
updateInput.setRecords(List.of(new QRecord().withValue("id", 20).withValue("sku", "BASIC3")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("No record was found to update for Id = 20", updateOutput.getRecords().get(0).getErrors().get(0));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// with a session that can only access store 1, try to update the line from the order in store 1 to be in store 2 //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{
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"));
QContext.getQSession().setSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE, ListBuilder.of(1)));
UpdateInput updateInput = new UpdateInput();
updateInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
updateInput.setRecords(List.of(new QRecord().withValue("id", 10).withValue("orderId", 2).withValue("sku", "BASIC3")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("You do not have permission to update this record - the referenced Order was not found.", updateOutput.getRecords().get(0).getErrors().get(0));
}
///////////////////////////////////////////////////////////
// try to remove the value that provides the foreign key //
///////////////////////////////////////////////////////////
{
/////////////////////////////////////////////////////////////////////////////////
// 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"));
UpdateInput updateInput = new UpdateInput();
updateInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
updateInput.setRecords(List.of(new QRecord().withValue("id", 100).withValue("lineItemId", null).withValue("key", "updatedKey")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("You do not have permission to update this record - the referenced Order was not found.", updateOutput.getRecords().get(0).getErrors().get(0));
}
//////////////////////////////////////////////////////////////////////////////////////////////
// with a session that can only access store 1, try to update the line-extrinsic in store 2 //
// should fail as a not-found - you can't see that record. //
//////////////////////////////////////////////////////////////////////////////////////////////
{
QContext.getQSession().setSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE, ListBuilder.of(1)));
UpdateInput updateInput = new UpdateInput();
updateInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
updateInput.setRecords(List.of(new QRecord().withValue("id", 200).withValue("key", "updatedKey")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("No record was found to update for Id = 200", updateOutput.getRecords().get(0).getErrors().get(0));
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// with a session that can only access store 1, try to update the line-extrinsic from the order in store 1 to be in store 2 //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{
QContext.getQSession().setSecurityKeyValues(MapBuilder.of(TestUtils.SECURITY_KEY_TYPE_STORE, ListBuilder.of(1)));
UpdateInput updateInput = new UpdateInput();
updateInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC);
updateInput.setRecords(List.of(new QRecord().withValue("id", 100).withValue("lineItemId", 20).withValue("key", "updatedKey")));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("You do not have permission to update this record - the referenced Order was not found.", updateOutput.getRecords().get(0).getErrors().get(0));
}
}
*/
@ -690,62 +569,101 @@ class UpdateActionTest extends BaseTest
assertThat(TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER)).anyMatch(r -> r.getValueString("orderNo").equals("original"));
}
/*******************************************************************************
**
*******************************************************************************/
/*
@Test
void testSecurityKeyNullDenied() throws QException
{
QInstance qInstance = QContext.getQInstance();
////////////////////////////////
// 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()));
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());
insertInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("orderNo", "original").withValue("storeId", 1)));
new InsertAction().execute(insertInput);
///////////////////////////////////////////
// try to update its storeId to null now //
///////////////////////////////////////////
UpdateInput updateInput = new UpdateInput();
updateInput.setTableName(TestUtils.TABLE_NAME_ORDER);
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("storeId", null)));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals("You do not have permission to update a record without a value in the field: Store Id", updateOutput.getRecords().get(0).getErrors().get(0));
assertEquals(0, TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER).stream().filter(r -> r.getValue("storeId") == null).count());
}
*/
/*******************************************************************************
**
*******************************************************************************/
/*
@Test
void testSecurityKeyNullAllowed() throws QException
{
/////////////////////////////////////
// change storeId to be allow-null //
/////////////////////////////////////
QInstance qInstance = QContext.getQInstance();
qInstance.getTable(TestUtils.TABLE_NAME_ORDER).getRecordSecurityLocks().get(0).setNullValueBehavior(RecordSecurityLock.NullValueBehavior.ALLOW);
////////////////////////////////
// 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()));
InsertOutput insertOutput = new InsertAction().execute(insertInput);
assertEquals(0, insertOutput.getRecords().get(0).getErrors().size());
assertEquals(1, TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER).size());
insertInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("orderNo", "original").withValue("storeId", 1)));
new InsertAction().execute(insertInput);
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE, 1);
UpdateInput updateInput = new UpdateInput();
updateInput.setTableName(TestUtils.TABLE_NAME_ORDER);
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("storeId", null)));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals(0, updateOutput.getRecords().get(0).getErrors().size());
assertEquals(1, TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER).stream().filter(r -> r.getValue("storeId") == null).count());
}
*/
/*******************************************************************************
**
*******************************************************************************/
/*
@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);
////////////////////////////////
// insert 2 orders 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("storeId", 999),
new QRecord().withValue("storeId", null)
new QRecord().withValue("id", 1).withValue("orderNo", "O1").withValue("storeId", 1),
new QRecord().withValue("id", 2).withValue("orderNo", "O2").withValue("storeId", 1)
));
InsertOutput insertOutput = new InsertAction().execute(insertInput);
assertEquals(2, TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER).size());
new InsertAction().execute(insertInput);
/////////////////////////////////////////////////////////
// make sure with all-access key we can update however //
/////////////////////////////////////////////////////////
QContext.getQSession().setSecurityKeyValues(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", 1).withValue("storeId", 999),
new QRecord().withValue("id", 2).withValue("storeId", null)
));
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
assertEquals(0, updateOutput.getRecords().get(0).getErrors().size());
assertEquals(0, updateOutput.getRecords().get(1).getErrors().size());
assertEquals(1, TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER).stream().filter(r -> Objects.equals(r.getValue("storeId"), 999)).count());
assertEquals(1, TestUtils.queryTable(TestUtils.TABLE_NAME_ORDER).stream().filter(r -> r.getValue("storeId") == null).count());
}
*/
}

View File

@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.modules.backend.implementations.utils;
import java.util.List;
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.utils.collections.ListBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -97,6 +98,21 @@ class BackendQueryFilterUtilsTest
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.NOT_LIKE, "Test"), "f", "Tst"));
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.NOT_LIKE, "T%"), "f", "Rest"));
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.NOT_LIKE, "T_st"), "f", "Toast"));
//////////////
// IN & NOT //
//////////////
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.IN, "A"), "f", "A"));
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.IN, "A", "B"), "f", "A"));
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.IN, "A", "B"), "f", "B"));
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.IN, List.of()), "f", "A"));
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.IN, ListBuilder.of(null)), "f", "A"));
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.NOT_IN, "A"), "f", "A"));
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.NOT_IN, "A", "B"), "f", "A"));
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.NOT_IN, "A", "B"), "f", "B"));
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.NOT_IN, List.of()), "f", "A"));
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.NOT_IN, ListBuilder.of(null)), "f", "A"));
}

View File

@ -619,6 +619,10 @@ public class TestUtils
.withName(TABLE_NAME_ORDER_EXTRINSIC)
.withBackendName(MEMORY_BACKEND_NAME)
.withPrimaryKeyField("id")
.withRecordSecurityLock(new RecordSecurityLock()
.withSecurityKeyType(SECURITY_KEY_TYPE_STORE)
.withFieldName("order.storeId")
.withJoinNameChain(List.of("orderOrderExtrinsic")))
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withIsEditable(false))
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withIsEditable(false))

View File

@ -23,8 +23,10 @@ package com.kingsrook.qqq.backend.core.utils.collections;
import java.util.Map;
import java.util.TreeMap;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
@ -50,6 +52,7 @@ class MapBuilderTest
}
/*******************************************************************************
**
*******************************************************************************/
@ -61,9 +64,22 @@ class MapBuilderTest
///////////////////////////////
Map<String, Object> map = MapBuilder.of("1", null);
///////////////////////////////////////
// this too, doesn't freaking throw. //
///////////////////////////////////////
map.put("2", null);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testTypeYouRequest()
{
Map<String, Integer> myTreeMap = MapBuilder.<String, Integer>of(TreeMap::new).with("1", 1).with("2", 2).build();
assertTrue(myTreeMap instanceof TreeMap);
}
}