api association fixes; mostly about propagating ids/fkeys, and having fields (in maps) as expected field types (cherry pick 74d003ed)

This commit is contained in:
2023-05-22 16:05:34 -05:00
parent 9dc6f4ccf8
commit 7491e5f819
6 changed files with 70 additions and 11 deletions

View File

@ -52,6 +52,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.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.tables.Association;
@ -60,6 +61,7 @@ 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.ValueUtils;
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
@ -183,7 +185,8 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
{
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);
}

View File

@ -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.utils.CollectionUtils;
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
{
LOG.info("In manageAssociations for " + queryInput.getTableName() + " with " + queryOutputRecords.size() + " records");
QTableMetaData table = queryInput.getTable();
for(Association association : CollectionUtils.nonNullList(table.getAssociations()))
{
@ -149,9 +149,10 @@ public class QueryAction
Set<Serializable> values = new HashSet<>();
for(QRecord record : queryOutputRecords)
{
Serializable value = record.getValue(joinOn.getLeftField());
values.add(value);
outerResultMap.add(List.of(value), record);
Serializable value = record.getValue(joinOn.getLeftField());
Serializable valueAsType = ValueUtils.getValueAsFieldType(table.getField(joinOn.getLeftField()).getType(), value);
values.add(valueAsType);
outerResultMap.add(List.of(valueAsType), record);
}
filter.addCriteria(new QFilterCriteria(joinOn.getRightField(), QCriteriaOperator.IN, new ArrayList<>(values)));
}

View File

@ -54,6 +54,7 @@ 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.audits.AuditLevel;
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.tables.Association;
@ -296,7 +297,8 @@ public class UpdateAction
//////////////////////////////////////////////////////////////////////////////////////////////////////////
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);
}
@ -307,6 +309,16 @@ public class UpdateAction
///////////////////////////////////////////////////////////////////////////////
idsBeingUpdated.add(associatedId);
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())));
}
}
}

View File

@ -48,6 +48,7 @@ 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;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
/*******************************************************************************
@ -113,9 +114,10 @@ public class ValidateRecordSecurityLockHelper
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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());
QJoinMetaData leftMostJoin = QContext.getQInstance().getJoin(recordSecurityLock.getJoinNameChain().get(0));
QJoinMetaData rightMostJoin = QContext.getQInstance().getJoin(recordSecurityLock.getJoinNameChain().get(recordSecurityLock.getJoinNameChain().size() - 1));
QTableMetaData rightMostJoinTable = QContext.getQInstance().getTable(rightMostJoin.getRightTable());
QTableMetaData leftMostJoinTable = QContext.getQInstance().getTable(leftMostJoin.getLeftTable());
for(List<QRecord> inputRecordPage : CollectionUtils.getPages(records, 500))
{
@ -153,7 +155,8 @@ public class ValidateRecordSecurityLockHelper
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);
subFilter.addCriteria(inputRecordValue == null

View File

@ -183,7 +183,10 @@ public class QRecordApiAdapter
{
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);
}
else

View File

@ -892,6 +892,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));
}
/*******************************************************************************
**
*******************************************************************************/
@ -1252,6 +1288,7 @@ class QJavalinApiHandlerTest extends BaseTest
GetInput getInput = new GetInput();
getInput.setTableName(tableName);
getInput.setPrimaryKey(id);
getInput.setIncludeAssociations(true);
GetOutput getOutput = new GetAction().execute(getInput);
QRecord record = getOutput.getRecord();
return record;