mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Add association api-meta data (so they can be versioned or excluded); add api field custom value mapper
This commit is contained in:
@ -281,6 +281,10 @@ public class ApiImplementation
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// todo - deal with removed fields; fields w/ custom value mappers (need new method(s) there) //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
QFieldMetaData field = table.getField(name);
|
QFieldMetaData field = table.getField(name);
|
||||||
for(String value : values)
|
for(String value : values)
|
||||||
{
|
{
|
||||||
|
@ -48,6 +48,7 @@ import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessMetaData;
|
|||||||
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessMetaDataContainer;
|
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessMetaDataContainer;
|
||||||
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessOutputInterface;
|
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessOutputInterface;
|
||||||
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessUtils;
|
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessUtils;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiAssociationMetaData;
|
||||||
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaData;
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaData;
|
||||||
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaDataContainer;
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaDataContainer;
|
||||||
import com.kingsrook.qqq.api.model.openapi.Components;
|
import com.kingsrook.qqq.api.model.openapi.Components;
|
||||||
@ -428,7 +429,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
// build the schemas for this table //
|
// build the schemas for this table //
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
Schema tableSchema = buildTableSchema(apiInstanceMetaData, table, tableApiFields);
|
Schema tableSchema = buildTableSchema(apiInstanceMetaData, version, table, tableApiFields);
|
||||||
componentSchemas.put(tableApiName, tableSchema);
|
componentSchemas.put(tableApiName, tableSchema);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
@ -1188,7 +1189,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
.withVersion(version)
|
.withVersion(version)
|
||||||
.withApiName(apiName)).getFields();
|
.withApiName(apiName)).getFields();
|
||||||
|
|
||||||
componentSchemas.put(tableApiName, buildTableSchema(apiInstanceMetaData, table, tableApiFields));
|
componentSchemas.put(tableApiName, buildTableSchema(apiInstanceMetaData, version, table, tableApiFields));
|
||||||
addedAny = true;
|
addedAny = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1202,7 +1203,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private Schema buildTableSchema(ApiInstanceMetaData apiInstanceMetaData, QTableMetaData table, List<QFieldMetaData> tableApiFields)
|
private Schema buildTableSchema(ApiInstanceMetaData apiInstanceMetaData, String version, QTableMetaData table, List<QFieldMetaData> tableApiFields)
|
||||||
{
|
{
|
||||||
LinkedHashMap<String, Schema> tableFields = new LinkedHashMap<>();
|
LinkedHashMap<String, Schema> tableFields = new LinkedHashMap<>();
|
||||||
Schema tableSchema = new Schema()
|
Schema tableSchema = new Schema()
|
||||||
@ -1225,7 +1226,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
// recursively add associations //
|
// recursively add associations //
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
addAssociations(apiInstanceMetaData.getName(), table, tableSchema);
|
addAssociations(apiInstanceMetaData.getName(), version, table, tableSchema);
|
||||||
|
|
||||||
return (tableSchema);
|
return (tableSchema);
|
||||||
}
|
}
|
||||||
@ -1367,8 +1368,10 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void addAssociations(String apiName, QTableMetaData table, Schema tableSchema)
|
private void addAssociations(String apiName, String version, QTableMetaData table, Schema tableSchema)
|
||||||
{
|
{
|
||||||
|
ApiTableMetaData thisApiTableMetaData = ObjectUtils.tryElse(() -> ApiTableMetaDataContainer.of(table).getApiTableMetaData(apiName), new ApiTableMetaData());
|
||||||
|
|
||||||
for(Association association : CollectionUtils.nonNullList(table.getAssociations()))
|
for(Association association : CollectionUtils.nonNullList(table.getAssociations()))
|
||||||
{
|
{
|
||||||
String associatedTableName = association.getAssociatedTableName();
|
String associatedTableName = association.getAssociatedTableName();
|
||||||
@ -1376,6 +1379,23 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
ApiTableMetaData associatedApiTableMetaData = ObjectUtils.tryElse(() -> ApiTableMetaDataContainer.of(associatedTable).getApiTableMetaData(apiName), new ApiTableMetaData());
|
ApiTableMetaData associatedApiTableMetaData = ObjectUtils.tryElse(() -> ApiTableMetaDataContainer.of(associatedTable).getApiTableMetaData(apiName), new ApiTableMetaData());
|
||||||
String associatedTableApiName = StringUtils.hasContent(associatedApiTableMetaData.getApiTableName()) ? associatedApiTableMetaData.getApiTableName() : associatedTableName;
|
String associatedTableApiName = StringUtils.hasContent(associatedApiTableMetaData.getApiTableName()) ? associatedApiTableMetaData.getApiTableName() : associatedTableName;
|
||||||
|
|
||||||
|
ApiAssociationMetaData apiAssociationMetaData = thisApiTableMetaData.getApiAssociationMetaData().get(association.getName());
|
||||||
|
if(apiAssociationMetaData != null)
|
||||||
|
{
|
||||||
|
if(BooleanUtils.isTrue(apiAssociationMetaData.getIsExcluded()))
|
||||||
|
{
|
||||||
|
LOG.debug("Omitting table [" + table.getName() + "] association [" + association.getName() + "] because it is marked as excluded.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
APIVersionRange apiVersionRange = apiAssociationMetaData.getApiVersionRange();
|
||||||
|
if(!apiVersionRange.includes(new APIVersion(version)))
|
||||||
|
{
|
||||||
|
LOG.debug("Omitting table [" + table.getName() + "] association [" + association.getName() + "] because its api version range [" + apiVersionRange + "] does not include this version [" + version + "]");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
neededTableSchemas.add(associatedTable.getName());
|
neededTableSchemas.add(associatedTable.getName());
|
||||||
|
|
||||||
tableSchema.getProperties().put(association.getName(), new Schema()
|
tableSchema.getProperties().put(association.getName(), new Schema()
|
||||||
|
@ -31,9 +31,16 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import com.kingsrook.qqq.api.javalin.QBadRequestException;
|
import com.kingsrook.qqq.api.javalin.QBadRequestException;
|
||||||
|
import com.kingsrook.qqq.api.model.APIVersion;
|
||||||
|
import com.kingsrook.qqq.api.model.APIVersionRange;
|
||||||
|
import com.kingsrook.qqq.api.model.actions.ApiFieldCustomValueMapper;
|
||||||
import com.kingsrook.qqq.api.model.actions.GetTableApiFieldsInput;
|
import com.kingsrook.qqq.api.model.actions.GetTableApiFieldsInput;
|
||||||
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
|
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
|
||||||
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaDataContainer;
|
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaDataContainer;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiAssociationMetaData;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaData;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaDataContainer;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
@ -45,6 +52,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.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
|
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
@ -87,6 +95,11 @@ public class QRecordApiAdapter
|
|||||||
{
|
{
|
||||||
value = record.getValue(apiFieldMetaData.getReplacedByFieldName());
|
value = record.getValue(apiFieldMetaData.getReplacedByFieldName());
|
||||||
}
|
}
|
||||||
|
else if(apiFieldMetaData.getCustomValueMapper() != null)
|
||||||
|
{
|
||||||
|
ApiFieldCustomValueMapper customValueMapper = QCodeLoader.getAdHoc(ApiFieldCustomValueMapper.class, apiFieldMetaData.getCustomValueMapper());
|
||||||
|
value = customValueMapper.produceApiValue(record);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
value = record.getValue(field.getName());
|
value = record.getValue(field.getName());
|
||||||
@ -107,6 +120,11 @@ public class QRecordApiAdapter
|
|||||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
||||||
for(Association association : CollectionUtils.nonNullList(table.getAssociations()))
|
for(Association association : CollectionUtils.nonNullList(table.getAssociations()))
|
||||||
{
|
{
|
||||||
|
if(isAssociationOmitted(apiName, apiVersion, table, association))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ArrayList<Map<String, Serializable>> associationList = new ArrayList<>();
|
ArrayList<Map<String, Serializable>> associationList = new ArrayList<>();
|
||||||
outputRecord.put(association.getName(), associationList);
|
outputRecord.put(association.getName(), associationList);
|
||||||
|
|
||||||
@ -121,6 +139,31 @@ public class QRecordApiAdapter
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static boolean isAssociationOmitted(String apiName, String apiVersion, QTableMetaData table, Association association)
|
||||||
|
{
|
||||||
|
ApiTableMetaData thisApiTableMetaData = ObjectUtils.tryElse(() -> ApiTableMetaDataContainer.of(table).getApiTableMetaData(apiName), new ApiTableMetaData());
|
||||||
|
ApiAssociationMetaData apiAssociationMetaData = thisApiTableMetaData.getApiAssociationMetaData().get(association.getName());
|
||||||
|
if(apiAssociationMetaData != null)
|
||||||
|
{
|
||||||
|
if(BooleanUtils.isTrue(apiAssociationMetaData.getIsExcluded()))
|
||||||
|
{
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
APIVersionRange apiVersionRange = apiAssociationMetaData.getApiVersionRange();
|
||||||
|
if(!apiVersionRange.includes(new APIVersion(apiVersion)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -136,9 +179,12 @@ public class QRecordApiAdapter
|
|||||||
Map<String, Association> associationMap = new HashMap<>();
|
Map<String, Association> associationMap = new HashMap<>();
|
||||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
||||||
for(Association association : CollectionUtils.nonNullList(table.getAssociations()))
|
for(Association association : CollectionUtils.nonNullList(table.getAssociations()))
|
||||||
|
{
|
||||||
|
if(!isAssociationOmitted(apiName, apiVersion, table, association))
|
||||||
{
|
{
|
||||||
associationMap.put(association.getName(), association);
|
associationMap.put(association.getName(), association);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
// iterate over keys in the json object //
|
// iterate over keys in the json object //
|
||||||
@ -179,6 +225,11 @@ public class QRecordApiAdapter
|
|||||||
{
|
{
|
||||||
qRecord.setValue(apiFieldMetaData.getReplacedByFieldName(), value);
|
qRecord.setValue(apiFieldMetaData.getReplacedByFieldName(), value);
|
||||||
}
|
}
|
||||||
|
else if(apiFieldMetaData.getCustomValueMapper() != null)
|
||||||
|
{
|
||||||
|
ApiFieldCustomValueMapper customValueMapper = QCodeLoader.getAdHoc(ApiFieldCustomValueMapper.class, apiFieldMetaData.getCustomValueMapper());
|
||||||
|
customValueMapper.consumeApiValue(qRecord, value, jsonObject);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
qRecord.setValue(field.getName(), value);
|
qRecord.setValue(field.getName(), value);
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.model.actions;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public abstract class ApiFieldCustomValueMapper
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Serializable produceApiValue(QRecord record)
|
||||||
|
{
|
||||||
|
/////////////////////
|
||||||
|
// null by default //
|
||||||
|
/////////////////////
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void consumeApiValue(QRecord record, Object value, JSONObject fullApiJsonObject)
|
||||||
|
{
|
||||||
|
/////////////////////
|
||||||
|
// noop by default //
|
||||||
|
/////////////////////
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -123,6 +123,11 @@ public class ApiInstanceMetaDataProvider
|
|||||||
ApiInstanceMetaData apiInstanceMetaData = entry.getValue();
|
ApiInstanceMetaData apiInstanceMetaData = entry.getValue();
|
||||||
allVersions.addAll(apiInstanceMetaData.getPastVersions());
|
allVersions.addAll(apiInstanceMetaData.getPastVersions());
|
||||||
allVersions.addAll(apiInstanceMetaData.getSupportedVersions());
|
allVersions.addAll(apiInstanceMetaData.getSupportedVersions());
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// I think we don't want future-versions in this dropdown, I think... //
|
||||||
|
// grr, actually todo maybe we want this to be a table-backed enum, with past/present/future columns //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
allVersions.addAll(apiInstanceMetaData.getFutureVersions());
|
allVersions.addAll(apiInstanceMetaData.getFutureVersions());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ package com.kingsrook.qqq.api.model.metadata.fields;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.kingsrook.qqq.api.model.openapi.Example;
|
import com.kingsrook.qqq.api.model.openapi.Example;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||||
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.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ public class ApiFieldMetaData
|
|||||||
|
|
||||||
private Boolean isExcluded;
|
private Boolean isExcluded;
|
||||||
private String replacedByFieldName;
|
private String replacedByFieldName;
|
||||||
|
private QCodeReference customValueMapper;
|
||||||
|
|
||||||
private Example example;
|
private Example example;
|
||||||
private Map<String, Example> examples;
|
private Map<String, Example> examples;
|
||||||
@ -313,4 +315,35 @@ public class ApiFieldMetaData
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for customValueMapper
|
||||||
|
*******************************************************************************/
|
||||||
|
public QCodeReference getCustomValueMapper()
|
||||||
|
{
|
||||||
|
return (this.customValueMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for customValueMapper
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setCustomValueMapper(QCodeReference customValueMapper)
|
||||||
|
{
|
||||||
|
this.customValueMapper = customValueMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for customValueMapper
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiFieldMetaData withCustomValueMapper(QCodeReference customValueMapper)
|
||||||
|
{
|
||||||
|
this.customValueMapper = customValueMapper;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ public class ApiFieldMetaDataContainer extends QSupplementalFieldMetaData
|
|||||||
private ApiFieldMetaData defaultApiFieldMetaData;
|
private ApiFieldMetaData defaultApiFieldMetaData;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Constructor
|
** Constructor
|
||||||
**
|
**
|
||||||
@ -61,6 +62,23 @@ public class ApiFieldMetaDataContainer extends QSupplementalFieldMetaData
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** either get the container attached to a field - or create a new one and attach
|
||||||
|
** it to the field, and return that.
|
||||||
|
*******************************************************************************/
|
||||||
|
public static ApiFieldMetaDataContainer ofOrWithNew(QFieldMetaData field)
|
||||||
|
{
|
||||||
|
ApiFieldMetaDataContainer apiFieldMetaDataContainer = (ApiFieldMetaDataContainer) field.getSupplementalMetaData(ApiSupplementType.NAME);
|
||||||
|
if(apiFieldMetaDataContainer == null)
|
||||||
|
{
|
||||||
|
apiFieldMetaDataContainer = new ApiFieldMetaDataContainer();
|
||||||
|
field.withSupplementalMetaData(apiFieldMetaDataContainer);
|
||||||
|
}
|
||||||
|
return (apiFieldMetaDataContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** either get the container attached to a field - or a new one - note - the new
|
** either get the container attached to a field - or a new one - note - the new
|
||||||
** one will NOT be attached to the field!!
|
** one will NOT be attached to the field!!
|
||||||
|
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.model.metadata.tables;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.api.model.APIVersionRange;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class ApiAssociationMetaData
|
||||||
|
{
|
||||||
|
private String initialVersion;
|
||||||
|
private String finalVersion;
|
||||||
|
private Boolean isExcluded;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public APIVersionRange getApiVersionRange()
|
||||||
|
{
|
||||||
|
if(getInitialVersion() == null)
|
||||||
|
{
|
||||||
|
return APIVersionRange.none();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (getFinalVersion() != null
|
||||||
|
? APIVersionRange.betweenAndIncluding(getInitialVersion(), getFinalVersion())
|
||||||
|
: APIVersionRange.afterAndIncluding(getInitialVersion()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for initialVersion
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getInitialVersion()
|
||||||
|
{
|
||||||
|
return (this.initialVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for initialVersion
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setInitialVersion(String initialVersion)
|
||||||
|
{
|
||||||
|
this.initialVersion = initialVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for initialVersion
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiAssociationMetaData withInitialVersion(String initialVersion)
|
||||||
|
{
|
||||||
|
this.initialVersion = initialVersion;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for finalVersion
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getFinalVersion()
|
||||||
|
{
|
||||||
|
return (this.finalVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for finalVersion
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setFinalVersion(String finalVersion)
|
||||||
|
{
|
||||||
|
this.finalVersion = finalVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for finalVersion
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiAssociationMetaData withFinalVersion(String finalVersion)
|
||||||
|
{
|
||||||
|
this.finalVersion = finalVersion;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for isExcluded
|
||||||
|
*******************************************************************************/
|
||||||
|
public Boolean getIsExcluded()
|
||||||
|
{
|
||||||
|
return (this.isExcluded);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for isExcluded
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setIsExcluded(Boolean isExcluded)
|
||||||
|
{
|
||||||
|
this.isExcluded = isExcluded;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for isExcluded
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiAssociationMetaData withIsExcluded(Boolean isExcluded)
|
||||||
|
{
|
||||||
|
this.isExcluded = isExcluded;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -24,8 +24,10 @@ package com.kingsrook.qqq.api.model.metadata.tables;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import com.kingsrook.qqq.api.ApiSupplementType;
|
import com.kingsrook.qqq.api.ApiSupplementType;
|
||||||
import com.kingsrook.qqq.api.model.APIVersionRange;
|
import com.kingsrook.qqq.api.model.APIVersionRange;
|
||||||
@ -53,6 +55,8 @@ public class ApiTableMetaData implements ApiOperation.EnabledOperationsProvider
|
|||||||
private Set<ApiOperation> enabledOperations = new HashSet<>();
|
private Set<ApiOperation> enabledOperations = new HashSet<>();
|
||||||
private Set<ApiOperation> disabledOperations = new HashSet<>();
|
private Set<ApiOperation> disabledOperations = new HashSet<>();
|
||||||
|
|
||||||
|
private Map<String, ApiAssociationMetaData> apiAssociationMetaData = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -410,4 +414,50 @@ public class ApiTableMetaData implements ApiOperation.EnabledOperationsProvider
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for apiAssociationMetaData
|
||||||
|
*******************************************************************************/
|
||||||
|
public Map<String, ApiAssociationMetaData> getApiAssociationMetaData()
|
||||||
|
{
|
||||||
|
return (this.apiAssociationMetaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for apiAssociationMetaData
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setApiAssociationMetaData(Map<String, ApiAssociationMetaData> apiAssociationMetaData)
|
||||||
|
{
|
||||||
|
this.apiAssociationMetaData = apiAssociationMetaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for apiAssociationMetaData
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiTableMetaData withApiAssociationMetaData(Map<String, ApiAssociationMetaData> apiAssociationMetaData)
|
||||||
|
{
|
||||||
|
this.apiAssociationMetaData = apiAssociationMetaData;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for apiAssociationMetaData
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiTableMetaData withApiAssociationMetaData(String associationName, ApiAssociationMetaData apiAssociationMetaData)
|
||||||
|
{
|
||||||
|
if(this.apiAssociationMetaData == null)
|
||||||
|
{
|
||||||
|
this.apiAssociationMetaData = new HashMap<>();
|
||||||
|
}
|
||||||
|
this.apiAssociationMetaData.put(associationName, apiAssociationMetaData);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,23 @@ public class ApiTableMetaDataContainer extends QSupplementalTableMetaData
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** either get the container attached to a table - or create a new one and attach
|
||||||
|
** it to the table, and return that.
|
||||||
|
*******************************************************************************/
|
||||||
|
public static ApiTableMetaDataContainer ofOrWithNew(QTableMetaData table)
|
||||||
|
{
|
||||||
|
ApiTableMetaDataContainer apiTableMetaDataContainer = (ApiTableMetaDataContainer) table.getSupplementalMetaData(ApiSupplementType.NAME);
|
||||||
|
if(apiTableMetaDataContainer == null)
|
||||||
|
{
|
||||||
|
apiTableMetaDataContainer = new ApiTableMetaDataContainer();
|
||||||
|
table.withSupplementalMetaData(apiTableMetaDataContainer);
|
||||||
|
}
|
||||||
|
return (apiTableMetaDataContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -101,6 +118,26 @@ public class ApiTableMetaDataContainer extends QSupplementalTableMetaData
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for api
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiTableMetaData getOrWithNewApiTableMetaData(String apiName)
|
||||||
|
{
|
||||||
|
if(this.apis == null)
|
||||||
|
{
|
||||||
|
this.apis = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this.apis.containsKey(apiName))
|
||||||
|
{
|
||||||
|
this.apis.put(apiName, new ApiTableMetaData());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this.apis.get(apiName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Setter for apis
|
** Setter for apis
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -546,6 +546,28 @@ public class TestUtils
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static void insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic() throws QException
|
||||||
|
{
|
||||||
|
InsertInput insertInput = new InsertInput();
|
||||||
|
insertInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||||
|
insertInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("orderNo", "ORD123").withValue("storeId", 47)
|
||||||
|
.withAssociatedRecord("orderLines", new QRecord().withValue("lineNumber", 1).withValue("sku", "BASIC1").withValue("quantity", 42)
|
||||||
|
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "Size").withValue("value", "Medium"))
|
||||||
|
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "Discount").withValue("value", "3.50"))
|
||||||
|
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "Color").withValue("value", "Red")))
|
||||||
|
.withAssociatedRecord("orderLines", new QRecord().withValue("lineNumber", 2).withValue("sku", "BASIC2").withValue("quantity", 42)
|
||||||
|
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "Size").withValue("value", "Medium")))
|
||||||
|
.withAssociatedRecord("orderLines", new QRecord().withValue("lineNumber", 3).withValue("sku", "BASIC3").withValue("quantity", 42))
|
||||||
|
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "shopifyOrderNo").withValue("value", "#1032"))
|
||||||
|
));
|
||||||
|
new InsertAction().execute(insertInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.actions;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Map;
|
||||||
|
import com.kingsrook.qqq.api.BaseTest;
|
||||||
|
import com.kingsrook.qqq.api.TestUtils;
|
||||||
|
import com.kingsrook.qqq.api.model.actions.ApiFieldCustomValueMapper;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaDataContainer;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiAssociationMetaData;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaData;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaDataContainer;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||||
|
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.get.GetInput;
|
||||||
|
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.code.QCodeReference;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for ApiImplementation
|
||||||
|
*******************************************************************************/
|
||||||
|
class ApiImplementationTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testExcludedAssociation() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
ApiInstanceMetaData apiInstanceMetaData = ApiInstanceMetaDataContainer.of(qInstance).getApiInstanceMetaData(TestUtils.API_NAME);
|
||||||
|
|
||||||
|
TestUtils.insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic();
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
// get the order - make sure it has extrinsics //
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
Map<String, Serializable> order = ApiImplementation.get(apiInstanceMetaData, TestUtils.CURRENT_API_VERSION, "order", "1");
|
||||||
|
assertTrue(order.containsKey("extrinsics"));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
// turn off the extrinsics association for the api //
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_ORDER);
|
||||||
|
ApiTableMetaDataContainer apiTableMetaDataContainer = ApiTableMetaDataContainer.of(table);
|
||||||
|
ApiTableMetaData apiTableMetaData = apiTableMetaDataContainer.getApiTableMetaData(TestUtils.API_NAME);
|
||||||
|
apiTableMetaData.withApiAssociationMetaData("extrinsics", new ApiAssociationMetaData().withIsExcluded(true));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
// re-fetch - should no longer have extrinsics //
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
order = ApiImplementation.get(apiInstanceMetaData, TestUtils.CURRENT_API_VERSION, "order", "1");
|
||||||
|
assertFalse(order.containsKey("extrinsics"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testAssociationVersions() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
ApiInstanceMetaData apiInstanceMetaData = ApiInstanceMetaDataContainer.of(qInstance).getApiInstanceMetaData(TestUtils.API_NAME);
|
||||||
|
|
||||||
|
TestUtils.insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic();
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
// get the order - make sure it has extrinsics //
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
Map<String, Serializable> order = ApiImplementation.get(apiInstanceMetaData, TestUtils.CURRENT_API_VERSION, "order", "1");
|
||||||
|
assertTrue(order.containsKey("extrinsics"));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
// set the initial version for the association //
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_ORDER);
|
||||||
|
ApiTableMetaDataContainer apiTableMetaDataContainer = ApiTableMetaDataContainer.of(table);
|
||||||
|
ApiTableMetaData apiTableMetaData = apiTableMetaDataContainer.getApiTableMetaData(TestUtils.API_NAME);
|
||||||
|
apiTableMetaData.withApiAssociationMetaData("extrinsics", new ApiAssociationMetaData().withInitialVersion(TestUtils.V2023_Q1));
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
// re-fetch - should have or not based on version //
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
assertFalse(ApiImplementation.get(apiInstanceMetaData, TestUtils.V2022_Q4, "order", "1").containsKey("extrinsics"));
|
||||||
|
assertTrue(ApiImplementation.get(apiInstanceMetaData, TestUtils.V2023_Q1, "order", "1").containsKey("extrinsics"));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
// set the final version for the association //
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
apiTableMetaData.withApiAssociationMetaData("extrinsics", new ApiAssociationMetaData().withInitialVersion(TestUtils.V2022_Q4).withFinalVersion(TestUtils.V2022_Q4));
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
// re-fetch - should have or not based on version //
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
assertTrue(ApiImplementation.get(apiInstanceMetaData, TestUtils.V2022_Q4, "order", "1").containsKey("extrinsics"));
|
||||||
|
assertFalse(ApiImplementation.get(apiInstanceMetaData, TestUtils.V2023_Q1, "order", "1").containsKey("extrinsics"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testValueCustomizer() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
ApiInstanceMetaData apiInstanceMetaData = ApiInstanceMetaDataContainer.of(qInstance).getApiInstanceMetaData(TestUtils.API_NAME);
|
||||||
|
TestUtils.insertSimpsons();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// set up a custom value mapper on lastName field of person table //
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_PERSON);
|
||||||
|
QFieldMetaData field = table.getField("lastName");
|
||||||
|
field.withSupplementalMetaData(new ApiFieldMetaDataContainer()
|
||||||
|
.withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData()
|
||||||
|
.withInitialVersion(TestUtils.V2022_Q4)
|
||||||
|
.withCustomValueMapper(new QCodeReference(PersonLastNameApiValueCustomizer.class))));
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// get a person - make sure custom method ran //
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
Map<String, Serializable> person = ApiImplementation.get(apiInstanceMetaData, TestUtils.CURRENT_API_VERSION, "person", "1");
|
||||||
|
assertEquals("customValue-Simpson", person.get("lastName"));
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
// insert a person - make sure custom method runs //
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
ApiImplementation.insert(apiInstanceMetaData, TestUtils.CURRENT_API_VERSION, "person", """
|
||||||
|
{"firstName": "Ned", "lastName": "stripThisAway-Flanders"}
|
||||||
|
""");
|
||||||
|
QRecord insertedPerson = new GetAction().executeForRecord(new GetInput(TestUtils.TABLE_NAME_PERSON).withPrimaryKey(6));
|
||||||
|
assertEquals("Flanders", insertedPerson.getValueString("lastName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static class PersonLastNameApiValueCustomizer extends ApiFieldCustomValueMapper
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public Serializable produceApiValue(QRecord record)
|
||||||
|
{
|
||||||
|
return ("customValue-" + record.getValueString("lastName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void consumeApiValue(QRecord record, Object value, JSONObject fullApiJsonObject)
|
||||||
|
{
|
||||||
|
String valueString = ValueUtils.getValueAsString(value);
|
||||||
|
valueString = valueString.replaceFirst("^stripThisAway-", "");
|
||||||
|
record.setValue("lastName", valueString);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -219,7 +219,7 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testGetAssociations() throws QException
|
void testGetAssociations() throws QException
|
||||||
{
|
{
|
||||||
insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic();
|
TestUtils.insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic();
|
||||||
|
|
||||||
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/order/1").asString();
|
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/order/1").asString();
|
||||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||||
@ -529,7 +529,7 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testQueryAssociations() throws QException
|
void testQueryAssociations() throws QException
|
||||||
{
|
{
|
||||||
insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic();
|
TestUtils.insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic();
|
||||||
|
|
||||||
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/order/query?id=1").asString();
|
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/order/query?id=1").asString();
|
||||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||||
@ -959,7 +959,7 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testUpdateErrorsFromCustomizer() throws QException
|
void testUpdateErrorsFromCustomizer() throws QException
|
||||||
{
|
{
|
||||||
insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic();
|
TestUtils.insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic();
|
||||||
|
|
||||||
HttpResponse<String> response = Unirest.patch(BASE_URL + "/api/" + VERSION + "/order/1")
|
HttpResponse<String> response = Unirest.patch(BASE_URL + "/api/" + VERSION + "/order/1")
|
||||||
.body("""
|
.body("""
|
||||||
@ -1010,7 +1010,7 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testUpdateAssociations() throws QException
|
void testUpdateAssociations() throws QException
|
||||||
{
|
{
|
||||||
insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic();
|
TestUtils.insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic();
|
||||||
|
|
||||||
HttpResponse<String> response = Unirest.patch(BASE_URL + "/api/" + VERSION + "/order/1")
|
HttpResponse<String> response = Unirest.patch(BASE_URL + "/api/" + VERSION + "/order/1")
|
||||||
.body("""
|
.body("""
|
||||||
@ -1331,7 +1331,7 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testDeleteAssociations() throws QException
|
void testDeleteAssociations() throws QException
|
||||||
{
|
{
|
||||||
insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic();
|
TestUtils.insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic();
|
||||||
|
|
||||||
assertEquals(1, queryTable(TestUtils.TABLE_NAME_ORDER).size());
|
assertEquals(1, queryTable(TestUtils.TABLE_NAME_ORDER).size());
|
||||||
assertEquals(4, queryTable(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC).size());
|
assertEquals(4, queryTable(TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC).size());
|
||||||
@ -1350,28 +1350,6 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private static void insert1Order3Lines4LineExtrinsicsAnd1OrderExtrinsic() throws QException
|
|
||||||
{
|
|
||||||
InsertInput insertInput = new InsertInput();
|
|
||||||
insertInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
|
||||||
insertInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("orderNo", "ORD123").withValue("storeId", 47)
|
|
||||||
.withAssociatedRecord("orderLines", new QRecord().withValue("lineNumber", 1).withValue("sku", "BASIC1").withValue("quantity", 42)
|
|
||||||
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "Size").withValue("value", "Medium"))
|
|
||||||
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "Discount").withValue("value", "3.50"))
|
|
||||||
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "Color").withValue("value", "Red")))
|
|
||||||
.withAssociatedRecord("orderLines", new QRecord().withValue("lineNumber", 2).withValue("sku", "BASIC2").withValue("quantity", 42)
|
|
||||||
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "Size").withValue("value", "Medium")))
|
|
||||||
.withAssociatedRecord("orderLines", new QRecord().withValue("lineNumber", 3).withValue("sku", "BASIC3").withValue("quantity", 42))
|
|
||||||
.withAssociatedRecord("extrinsics", new QRecord().withValue("key", "shopifyOrderNo").withValue("value", "#1032"))
|
|
||||||
));
|
|
||||||
new InsertAction().execute(insertInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
Reference in New Issue
Block a user