mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
api association fixes; mostly about propagating ids/fkeys, and having fields (in maps) as expected field types
This commit is contained in:
@ -53,6 +53,7 @@ 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.insert.InsertOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
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.joins.QJoinMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
||||||
@ -63,6 +64,7 @@ import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
|
|||||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
||||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
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;
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
|
|
||||||
|
|
||||||
@ -237,7 +239,8 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
|||||||
{
|
{
|
||||||
for(JoinOn joinOn : join.getJoinOns())
|
for(JoinOn joinOn : join.getJoinOns())
|
||||||
{
|
{
|
||||||
associatedRecord.setValue(joinOn.getRightField(), record.getValue(joinOn.getLeftField()));
|
QFieldType type = table.getField(joinOn.getLeftField()).getType();
|
||||||
|
associatedRecord.setValue(joinOn.getRightField(), ValueUtils.getValueAsFieldType(type, record.getValue(joinOn.getLeftField())));
|
||||||
}
|
}
|
||||||
nextLevelInserts.add(associatedRecord);
|
nextLevelInserts.add(associatedRecord);
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@ import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
|||||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -123,7 +124,6 @@ public class QueryAction
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void manageAssociations(QueryInput queryInput, List<QRecord> queryOutputRecords) throws QException
|
private void manageAssociations(QueryInput queryInput, List<QRecord> queryOutputRecords) throws QException
|
||||||
{
|
{
|
||||||
LOG.info("In manageAssociations for " + queryInput.getTableName() + " with " + queryOutputRecords.size() + " records");
|
|
||||||
QTableMetaData table = queryInput.getTable();
|
QTableMetaData table = queryInput.getTable();
|
||||||
for(Association association : CollectionUtils.nonNullList(table.getAssociations()))
|
for(Association association : CollectionUtils.nonNullList(table.getAssociations()))
|
||||||
{
|
{
|
||||||
@ -149,9 +149,10 @@ public class QueryAction
|
|||||||
Set<Serializable> values = new HashSet<>();
|
Set<Serializable> values = new HashSet<>();
|
||||||
for(QRecord record : queryOutputRecords)
|
for(QRecord record : queryOutputRecords)
|
||||||
{
|
{
|
||||||
Serializable value = record.getValue(joinOn.getLeftField());
|
Serializable value = record.getValue(joinOn.getLeftField());
|
||||||
values.add(value);
|
Serializable valueAsType = ValueUtils.getValueAsFieldType(table.getField(joinOn.getLeftField()).getType(), value);
|
||||||
outerResultMap.add(List.of(value), record);
|
values.add(valueAsType);
|
||||||
|
outerResultMap.add(List.of(valueAsType), record);
|
||||||
}
|
}
|
||||||
filter.addCriteria(new QFilterCriteria(joinOn.getRightField(), QCriteriaOperator.IN, new ArrayList<>(values)));
|
filter.addCriteria(new QFilterCriteria(joinOn.getRightField(), QCriteriaOperator.IN, new ArrayList<>(values)));
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,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.actions.tables.update.UpdateOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
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.joins.QJoinMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
||||||
@ -400,7 +401,8 @@ public class UpdateAction
|
|||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
for(JoinOn joinOn : join.getJoinOns())
|
for(JoinOn joinOn : join.getJoinOns())
|
||||||
{
|
{
|
||||||
associatedRecord.setValue(joinOn.getRightField(), record.getValue(joinOn.getLeftField()));
|
QFieldType type = table.getField(joinOn.getLeftField()).getType();
|
||||||
|
associatedRecord.setValue(joinOn.getRightField(), ValueUtils.getValueAsFieldType(type, record.getValue(joinOn.getLeftField())));
|
||||||
}
|
}
|
||||||
nextLevelInserts.add(associatedRecord);
|
nextLevelInserts.add(associatedRecord);
|
||||||
}
|
}
|
||||||
@ -411,6 +413,16 @@ public class UpdateAction
|
|||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
idsBeingUpdated.add(associatedId);
|
idsBeingUpdated.add(associatedId);
|
||||||
nextLevelUpdates.add(associatedRecord);
|
nextLevelUpdates.add(associatedRecord);
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// make sure the child record being updated has its join fields populated (same as an insert). //
|
||||||
|
// this will make the next update action much happier //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
for(JoinOn joinOn : join.getJoinOns())
|
||||||
|
{
|
||||||
|
QFieldType type = table.getField(joinOn.getLeftField()).getType();
|
||||||
|
associatedRecord.setValue(joinOn.getRightField(), ValueUtils.getValueAsFieldType(type, record.getValue(joinOn.getLeftField())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ import com.kingsrook.qqq.backend.core.model.statusmessages.PermissionDeniedMessa
|
|||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -114,9 +115,10 @@ public class ValidateRecordSecurityLockHelper
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// else look for the joined record - if it isn't found, assume a fail - else validate security value if found //
|
// 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 leftMostJoin = QContext.getQInstance().getJoin(recordSecurityLock.getJoinNameChain().get(0));
|
||||||
QJoinMetaData rightMostJoin = QContext.getQInstance().getJoin(recordSecurityLock.getJoinNameChain().get(recordSecurityLock.getJoinNameChain().size() - 1));
|
QJoinMetaData rightMostJoin = QContext.getQInstance().getJoin(recordSecurityLock.getJoinNameChain().get(recordSecurityLock.getJoinNameChain().size() - 1));
|
||||||
QTableMetaData leftMostJoinTable = QContext.getQInstance().getTable(leftMostJoin.getLeftTable());
|
QTableMetaData rightMostJoinTable = QContext.getQInstance().getTable(rightMostJoin.getRightTable());
|
||||||
|
QTableMetaData leftMostJoinTable = QContext.getQInstance().getTable(leftMostJoin.getLeftTable());
|
||||||
|
|
||||||
for(List<QRecord> inputRecordPage : CollectionUtils.getPages(records, 500))
|
for(List<QRecord> inputRecordPage : CollectionUtils.getPages(records, 500))
|
||||||
{
|
{
|
||||||
@ -154,7 +156,8 @@ public class ValidateRecordSecurityLockHelper
|
|||||||
|
|
||||||
for(JoinOn joinOn : rightMostJoin.getJoinOns())
|
for(JoinOn joinOn : rightMostJoin.getJoinOns())
|
||||||
{
|
{
|
||||||
Serializable inputRecordValue = inputRecord.getValue(joinOn.getRightField());
|
QFieldType type = rightMostJoinTable.getField(joinOn.getRightField()).getType();
|
||||||
|
Serializable inputRecordValue = ValueUtils.getValueAsFieldType(type, inputRecord.getValue(joinOn.getRightField()));
|
||||||
inputRecordJoinValues.add(inputRecordValue);
|
inputRecordJoinValues.add(inputRecordValue);
|
||||||
|
|
||||||
subFilter.addCriteria(inputRecordValue == null
|
subFilter.addCriteria(inputRecordValue == null
|
||||||
|
@ -183,7 +183,10 @@ public class QRecordApiAdapter
|
|||||||
{
|
{
|
||||||
if(subObject instanceof JSONObject subJsonObject)
|
if(subObject instanceof JSONObject subJsonObject)
|
||||||
{
|
{
|
||||||
QRecord subRecord = apiJsonObjectToQRecord(subJsonObject, association.getAssociatedTableName(), apiName, apiVersion, includePrimaryKey);
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// make sure to always include primary keys (boolean param) on calls for children - to help determine insert/update cases //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
QRecord subRecord = apiJsonObjectToQRecord(subJsonObject, association.getAssociatedTableName(), apiName, apiVersion, true);
|
||||||
qRecord.withAssociatedRecord(association.getName(), subRecord);
|
qRecord.withAssociatedRecord(association.getName(), subRecord);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -938,6 +938,42 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testUpdateAssociations() throws QException
|
||||||
|
{
|
||||||
|
insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic();
|
||||||
|
|
||||||
|
HttpResponse<String> response = Unirest.patch(BASE_URL + "/api/" + VERSION + "/order/1")
|
||||||
|
.body("""
|
||||||
|
{"orderLines":
|
||||||
|
[
|
||||||
|
{"id": 1, "lineNumber": 1, "sku": "BASIC1", "quantity": 47},
|
||||||
|
{"id": 2},
|
||||||
|
{"id": 3}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
.asString();
|
||||||
|
assertErrorResponse(HttpStatus.NO_CONTENT_204, null, response);
|
||||||
|
|
||||||
|
QRecord orderRecord = getRecord(TestUtils.TABLE_NAME_ORDER, 1);
|
||||||
|
List<QRecord> orderLines = orderRecord.getAssociatedRecords().get("orderLines");
|
||||||
|
assertThat(orderLines)
|
||||||
|
.withFailMessage("order lines should be found on order").isNotNull().isNotEmpty()
|
||||||
|
.withFailMessage("Should have a line with id 1").filteredOn(r -> r.getValueInteger("id").equals(1)).hasSize(1)
|
||||||
|
.withFailMessage("line with id 1 should have quantity updated to 47").first().matches(r -> r.getValue("quantity").equals(47));
|
||||||
|
|
||||||
|
assertThat(orderLines)
|
||||||
|
.withFailMessage("order lines should be found on order").isNotNull().isNotEmpty()
|
||||||
|
.withFailMessage("Should have a line with id 2").filteredOn(r -> r.getValueInteger("id").equals(2)).hasSize(1)
|
||||||
|
.withFailMessage("line with id 2 should have original quantity (42)").first().matches(r -> r.getValue("quantity").equals(42));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -1400,6 +1436,7 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
GetInput getInput = new GetInput();
|
GetInput getInput = new GetInput();
|
||||||
getInput.setTableName(tableName);
|
getInput.setTableName(tableName);
|
||||||
getInput.setPrimaryKey(id);
|
getInput.setPrimaryKey(id);
|
||||||
|
getInput.setIncludeAssociations(true);
|
||||||
GetOutput getOutput = new GetAction().execute(getInput);
|
GetOutput getOutput = new GetAction().execute(getInput);
|
||||||
QRecord record = getOutput.getRecord();
|
QRecord record = getOutput.getRecord();
|
||||||
return record;
|
return record;
|
||||||
|
Reference in New Issue
Block a user