mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Checkpoint, WIP on processes in api
This commit is contained in:
@ -370,7 +370,7 @@ public class QInstanceEnricher
|
|||||||
|
|
||||||
for(QSupplementalProcessMetaData supplementalProcessMetaData : CollectionUtils.nonNullMap(process.getSupplementalMetaData()).values())
|
for(QSupplementalProcessMetaData supplementalProcessMetaData : CollectionUtils.nonNullMap(process.getSupplementalMetaData()).values())
|
||||||
{
|
{
|
||||||
supplementalProcessMetaData.enrich(process);
|
supplementalProcessMetaData.enrich(this, process);
|
||||||
}
|
}
|
||||||
|
|
||||||
enrichPermissionRules(process);
|
enrichPermissionRules(process);
|
||||||
|
@ -54,6 +54,9 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
|||||||
private BasepullConfiguration basepullConfiguration;
|
private BasepullConfiguration basepullConfiguration;
|
||||||
private QPermissionRules permissionRules;
|
private QPermissionRules permissionRules;
|
||||||
|
|
||||||
|
private Integer minInputRecords = null;
|
||||||
|
private Integer maxInputRecords = null;
|
||||||
|
|
||||||
private List<QStepMetaData> stepList; // these are the steps that are ran, by-default, in the order they are ran in
|
private List<QStepMetaData> stepList; // these are the steps that are ran, by-default, in the order they are ran in
|
||||||
private Map<String, QStepMetaData> steps; // this is the full map of possible steps
|
private Map<String, QStepMetaData> steps; // this is the full map of possible steps
|
||||||
|
|
||||||
@ -64,6 +67,7 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
|||||||
private Map<String, QSupplementalProcessMetaData> supplementalMetaData;
|
private Map<String, QSupplementalProcessMetaData> supplementalMetaData;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -605,4 +609,66 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for minInputRecords
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getMinInputRecords()
|
||||||
|
{
|
||||||
|
return (this.minInputRecords);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for minInputRecords
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setMinInputRecords(Integer minInputRecords)
|
||||||
|
{
|
||||||
|
this.minInputRecords = minInputRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for minInputRecords
|
||||||
|
*******************************************************************************/
|
||||||
|
public QProcessMetaData withMinInputRecords(Integer minInputRecords)
|
||||||
|
{
|
||||||
|
this.minInputRecords = minInputRecords;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for maxInputRecords
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getMaxInputRecords()
|
||||||
|
{
|
||||||
|
return (this.maxInputRecords);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for maxInputRecords
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setMaxInputRecords(Integer maxInputRecords)
|
||||||
|
{
|
||||||
|
this.maxInputRecords = maxInputRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for maxInputRecords
|
||||||
|
*******************************************************************************/
|
||||||
|
public QProcessMetaData withMaxInputRecords(Integer maxInputRecords)
|
||||||
|
{
|
||||||
|
this.maxInputRecords = maxInputRecords;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
package com.kingsrook.qqq.backend.core.model.metadata.processes;
|
package com.kingsrook.qqq.backend.core.model.metadata.processes;
|
||||||
|
|
||||||
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -69,7 +69,7 @@ public abstract class QSupplementalProcessMetaData
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void enrich(QProcessMetaData process)
|
public void enrich(QInstanceEnricher qInstanceEnricher, QProcessMetaData process)
|
||||||
{
|
{
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// noop in base class //
|
// noop in base class //
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class CouldNotFindQueryFilterForExtractStepException extends QException
|
||||||
|
{
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public CouldNotFindQueryFilterForExtractStepException(String message)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -223,7 +223,7 @@ public class ExtractViaQueryStep extends AbstractExtractStep
|
|||||||
return (new QQueryFilter().withCriteria(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, idStrings)));
|
return (new QQueryFilter().withCriteria(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, idStrings)));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw (new QException("Could not find query filter for Extract step."));
|
throw (new CouldNotFindQueryFilterForExtractStepException("Could not find query filter for Extract step."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -407,6 +407,30 @@ public class StreamedETLWithFrontendProcess
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for minInputRecords
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withMinInputRecords(Integer minInputRecords)
|
||||||
|
{
|
||||||
|
processMetaData.setMinInputRecords(minInputRecords);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for maxInputRecords
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withMaxInputRecords(Integer maxInputRecords)
|
||||||
|
{
|
||||||
|
processMetaData.setMaxInputRecords(maxInputRecords);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
package com.kingsrook.qqq.backend.core.utils;
|
package com.kingsrook.qqq.backend.core.utils;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeConsumer;
|
||||||
import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeSupplier;
|
import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeSupplier;
|
||||||
|
|
||||||
|
|
||||||
@ -96,4 +99,44 @@ public class ObjectUtils
|
|||||||
return (defaultIfThrew);
|
return (defaultIfThrew);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static <T> void ifNotNull(T object, Consumer<T> consumer)
|
||||||
|
{
|
||||||
|
if(object != null)
|
||||||
|
{
|
||||||
|
consumer.accept(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static <T, E extends Exception> void ifNotNullUnsafe(T object, UnsafeConsumer<T, E> consumer) throws E
|
||||||
|
{
|
||||||
|
if(object != null)
|
||||||
|
{
|
||||||
|
consumer.run(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static <T> T requireConditionElse(T a, Predicate<T> condition, T b)
|
||||||
|
{
|
||||||
|
if(condition.test(a))
|
||||||
|
{
|
||||||
|
return (a);
|
||||||
|
}
|
||||||
|
return (b);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,18 +35,18 @@ import java.util.function.Supplier;
|
|||||||
**
|
**
|
||||||
** Can use it 2 ways:
|
** Can use it 2 ways:
|
||||||
** MapBuilder.of(key, value, key2, value2, ...) => Map (a HashMap)
|
** 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)
|
** MapBuilder.of(() -> new SomeMap<SomeKeyType, SomeValueType>()).with(key, value).with(key2, value2)...build() => SomeMap (the type you specify)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class MapBuilder<K, V>
|
public class MapBuilder<K, V, M extends Map<K, V>>
|
||||||
{
|
{
|
||||||
private Map<K, V> map;
|
private M map;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private MapBuilder(Map<K, V> map)
|
private MapBuilder(M map)
|
||||||
{
|
{
|
||||||
this.map = map;
|
this.map = map;
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ public class MapBuilder<K, V>
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static <K, V> MapBuilder<K, V> of(Supplier<Map<K, V>> mapSupplier)
|
public static <K, V, M extends Map<K, V>> MapBuilder<K, V, M> of(Supplier<M> mapSupplier)
|
||||||
{
|
{
|
||||||
return (new MapBuilder<>(mapSupplier.get()));
|
return (new MapBuilder<>(mapSupplier.get()));
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ public class MapBuilder<K, V>
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public MapBuilder<K, V> with(K key, V value)
|
public MapBuilder<K, V, M> with(K key, V value)
|
||||||
{
|
{
|
||||||
map.put(key, value);
|
map.put(key, value);
|
||||||
return (this);
|
return (this);
|
||||||
@ -77,7 +77,7 @@ public class MapBuilder<K, V>
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public Map<K, V> build()
|
public M build()
|
||||||
{
|
{
|
||||||
return (this.map);
|
return (this.map);
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ class MapBuilderTest
|
|||||||
@Test
|
@Test
|
||||||
void testTypeYouRequest()
|
void testTypeYouRequest()
|
||||||
{
|
{
|
||||||
Map<String, Integer> myTreeMap = MapBuilder.<String, Integer>of(TreeMap::new).with("1", 1).with("2", 2).build();
|
TreeMap<String, Integer> myTreeMap = MapBuilder.of(() -> new TreeMap<String, Integer>()).with("1", 1).with("2", 2).build();
|
||||||
assertTrue(myTreeMap instanceof TreeMap);
|
assertTrue(myTreeMap instanceof TreeMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.api.actions;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -33,12 +34,15 @@ import java.util.Set;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
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.APIVersion;
|
||||||
import com.kingsrook.qqq.api.model.APIVersionRange;
|
import com.kingsrook.qqq.api.model.actions.HttpApiResponse;
|
||||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
||||||
import com.kingsrook.qqq.api.model.metadata.ApiOperation;
|
import com.kingsrook.qqq.api.model.metadata.ApiOperation;
|
||||||
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessCustomizers;
|
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessCustomizers;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessInput;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessInputFieldsContainer;
|
||||||
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessMetaData;
|
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.ApiProcessOutputInterface;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessUtils;
|
||||||
import com.kingsrook.qqq.api.model.metadata.processes.PostRunApiProcessCustomizer;
|
import com.kingsrook.qqq.api.model.metadata.processes.PostRunApiProcessCustomizer;
|
||||||
import com.kingsrook.qqq.api.model.metadata.processes.PreRunApiProcessCustomizer;
|
import com.kingsrook.qqq.api.model.metadata.processes.PreRunApiProcessCustomizer;
|
||||||
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaData;
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaData;
|
||||||
@ -46,6 +50,7 @@ import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaDataContainer;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||||
import com.kingsrook.qqq.backend.core.actions.permissions.TablePermissionSubType;
|
import com.kingsrook.qqq.backend.core.actions.permissions.TablePermissionSubType;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.processes.QProcessCallback;
|
||||||
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
||||||
@ -89,6 +94,7 @@ import com.kingsrook.qqq.backend.core.model.statusmessages.PermissionDeniedMessa
|
|||||||
import com.kingsrook.qqq.backend.core.model.statusmessages.QErrorMessage;
|
import com.kingsrook.qqq.backend.core.model.statusmessages.QErrorMessage;
|
||||||
import com.kingsrook.qqq.backend.core.model.statusmessages.QStatusMessage;
|
import com.kingsrook.qqq.backend.core.model.statusmessages.QStatusMessage;
|
||||||
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
|
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.CouldNotFindQueryFilterForExtractStepException;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.Pair;
|
import com.kingsrook.qqq.backend.core.utils.Pair;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
@ -100,6 +106,7 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.json.JSONTokener;
|
import org.json.JSONTokener;
|
||||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
|
import static com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator.IN;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -113,7 +120,6 @@ public class ApiImplementation
|
|||||||
// key: Pair<apiName, apiVersion>, value: Map<name => metaData> //
|
// key: Pair<apiName, apiVersion>, value: Map<name => metaData> //
|
||||||
///////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////
|
||||||
private static Map<Pair<String, String>, Map<String, QTableMetaData>> tableApiNameMap = new HashMap<>();
|
private static Map<Pair<String, String>, Map<String, QTableMetaData>> tableApiNameMap = new HashMap<>();
|
||||||
private static Map<Pair<String, String>, Map<String, QProcessMetaData>> processApiNameMap = new HashMap<>();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -913,14 +919,15 @@ public class ApiImplementation
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static Map<String, Serializable> runProcess(ApiInstanceMetaData apiInstanceMetaData, String version, String processApiName, Map<String, String> paramMap) throws QException
|
public static HttpApiResponse runProcess(ApiInstanceMetaData apiInstanceMetaData, String version, String processApiName, Map<String, String> paramMap) throws QException
|
||||||
{
|
{
|
||||||
QProcessMetaData process = validateProcessAndVersion(apiInstanceMetaData, version, processApiName);
|
Pair<ApiProcessMetaData, QProcessMetaData> pair = ApiProcessUtils.getProcessMetaDataPair(apiInstanceMetaData, version, processApiName);
|
||||||
|
|
||||||
|
ApiProcessMetaData apiProcessMetaData = pair.getA();
|
||||||
|
QProcessMetaData process = pair.getB();
|
||||||
String processName = process.getName();
|
String processName = process.getName();
|
||||||
ApiProcessMetaData apiProcessMetaData = getApiProcessMetaDataIfProcessIsInApi(apiInstanceMetaData, process);
|
|
||||||
|
|
||||||
List<String> badRequestMessages = new ArrayList<>();
|
List<String> badRequestMessages = new ArrayList<>();
|
||||||
Map<String, Serializable> output = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
String processUUID = UUID.randomUUID().toString();
|
String processUUID = UUID.randomUUID().toString();
|
||||||
|
|
||||||
@ -928,27 +935,37 @@ public class ApiImplementation
|
|||||||
runProcessInput.setProcessName(processName);
|
runProcessInput.setProcessName(processName);
|
||||||
runProcessInput.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.SKIP);
|
runProcessInput.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.SKIP);
|
||||||
runProcessInput.setProcessUUID(processUUID);
|
runProcessInput.setProcessUUID(processUUID);
|
||||||
// todo i don't think runProcessInput.setCallback();
|
|
||||||
// todo i don't think runProcessInput.setAsyncJobCallback();
|
// todo i don't think runProcessInput.setAsyncJobCallback();
|
||||||
|
|
||||||
//////////////////////
|
//////////////////////
|
||||||
// map input values //
|
// map input values //
|
||||||
//////////////////////
|
//////////////////////
|
||||||
for(QFieldMetaData inputField : CollectionUtils.nonNullList(apiProcessMetaData.getInputFields()))
|
ApiProcessInput apiProcessInput = apiProcessMetaData.getInput();
|
||||||
|
if(apiProcessInput != null)
|
||||||
{
|
{
|
||||||
String value = paramMap.get(inputField.getName());
|
processProcessInputFields(paramMap, badRequestMessages, runProcessInput, apiProcessInput.getQueryStringParams());
|
||||||
if(!StringUtils.hasContent(value) && inputField.getIsRequired())
|
processProcessInputFields(paramMap, badRequestMessages, runProcessInput, apiProcessInput.getFormParams());
|
||||||
{
|
processProcessInputFields(paramMap, badRequestMessages, runProcessInput, apiProcessInput.getObjectBodyParams());
|
||||||
badRequestMessages.add("Missing value for required input field " + inputField.getName());
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo - types?
|
////////////////////////////////////////
|
||||||
|
// get records for process, if needed //
|
||||||
|
////////////////////////////////////////
|
||||||
|
if(process.getMinInputRecords() != null && process.getMinInputRecords() > 0)
|
||||||
|
{
|
||||||
|
if(apiProcessInput != null && apiProcessInput.getRecordIdsParamName() != null)
|
||||||
|
{
|
||||||
|
String idParam = apiProcessInput.getRecordIdsParamName();
|
||||||
|
if(StringUtils.hasContent(idParam) && StringUtils.hasContent(paramMap.get(idParam)))
|
||||||
|
{
|
||||||
|
String[] ids = paramMap.get(idParam).split(",");
|
||||||
|
|
||||||
runProcessInput.addValue(inputField.getName(), value);
|
QTableMetaData table = QContext.getQInstance().getTable(process.getTableName());
|
||||||
|
QQueryFilter filter = new QQueryFilter(new QFilterCriteria(table.getPrimaryKeyField(), IN, Arrays.asList(ids)));
|
||||||
|
runProcessInput.setCallback(getCallback(filter));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo! runProcessInput.setRecords(records);
|
|
||||||
|
|
||||||
/////////////////////////////////////////
|
/////////////////////////////////////////
|
||||||
// throw if bad inputs have been noted //
|
// throw if bad inputs have been noted //
|
||||||
@ -978,8 +995,17 @@ public class ApiImplementation
|
|||||||
/////////////////////
|
/////////////////////
|
||||||
// run the process //
|
// run the process //
|
||||||
/////////////////////
|
/////////////////////
|
||||||
|
RunProcessOutput runProcessOutput;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
RunProcessAction runProcessAction = new RunProcessAction();
|
RunProcessAction runProcessAction = new RunProcessAction();
|
||||||
RunProcessOutput runProcessOutput = runProcessAction.execute(runProcessInput);
|
runProcessOutput = runProcessAction.execute(runProcessInput);
|
||||||
|
}
|
||||||
|
catch(CouldNotFindQueryFilterForExtractStepException e)
|
||||||
|
{
|
||||||
|
throw (new QBadRequestException("Records to run through this process were not specified."));
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////
|
/////////////////////////////////////////
|
||||||
// run post-customizer, if there is one //
|
// run post-customizer, if there is one //
|
||||||
@ -993,12 +1019,42 @@ public class ApiImplementation
|
|||||||
///////////////////////
|
///////////////////////
|
||||||
// map output values //
|
// map output values //
|
||||||
///////////////////////
|
///////////////////////
|
||||||
for(QFieldMetaData outputField : apiProcessMetaData.getOutputFields())
|
ApiProcessOutputInterface output = apiProcessMetaData.getOutput();
|
||||||
|
if(output != null)
|
||||||
{
|
{
|
||||||
output.put(outputField.getName(), runProcessOutput.getValues().get(outputField.getName()));
|
return (new HttpApiResponse(output.getSuccessStatusCode(runProcessInput, runProcessOutput), output.getOutputForProcess(runProcessInput, runProcessOutput)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (new HttpApiResponse(HttpStatus.Code.NO_CONTENT, ""));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (output);
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static void processProcessInputFields(Map<String, String> paramMap, List<String> badRequestMessages, RunProcessInput runProcessInput, ApiProcessInputFieldsContainer fieldsContainer)
|
||||||
|
{
|
||||||
|
if(fieldsContainer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(QFieldMetaData inputField : CollectionUtils.nonNullList(fieldsContainer.getFields()))
|
||||||
|
{
|
||||||
|
String value = paramMap.get(inputField.getName());
|
||||||
|
if(!StringUtils.hasContent(value) && inputField.getIsRequired())
|
||||||
|
{
|
||||||
|
badRequestMessages.add("Missing value for required input field " + inputField.getName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo - types?
|
||||||
|
|
||||||
|
runProcessInput.addValue(inputField.getName(), value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1233,65 +1289,6 @@ public class ApiImplementation
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public static QProcessMetaData validateProcessAndVersion(ApiInstanceMetaData apiInstanceMetaData, String version, String processApiName) throws QNotFoundException
|
|
||||||
{
|
|
||||||
QProcessMetaData process = getProcessByApiName(apiInstanceMetaData.getName(), version, processApiName);
|
|
||||||
LogPair[] logPairs = new LogPair[] { logPair("apiName", apiInstanceMetaData.getName()), logPair("version", version), logPair("processApiName", processApiName) };
|
|
||||||
|
|
||||||
if(process == null)
|
|
||||||
{
|
|
||||||
LOG.info("404 because process is null (processApiName=" + processApiName + ")", logPairs);
|
|
||||||
throw (new QNotFoundException("Could not find a process named " + processApiName + " in this api."));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(BooleanUtils.isTrue(process.getIsHidden()))
|
|
||||||
{
|
|
||||||
LOG.info("404 because process isHidden", logPairs);
|
|
||||||
throw (new QNotFoundException("Could not find a process named " + processApiName + " in this api."));
|
|
||||||
}
|
|
||||||
|
|
||||||
ApiProcessMetaDataContainer apiProcessMetaDataContainer = ApiProcessMetaDataContainer.of(process);
|
|
||||||
if(apiProcessMetaDataContainer == null)
|
|
||||||
{
|
|
||||||
LOG.info("404 because process apiProcessMetaDataContainer is null", logPairs);
|
|
||||||
throw (new QNotFoundException("Could not find a process named " + processApiName + " in this api."));
|
|
||||||
}
|
|
||||||
|
|
||||||
ApiProcessMetaData apiProcessMetaData = apiProcessMetaDataContainer.getApiProcessMetaData(apiInstanceMetaData.getName());
|
|
||||||
if(apiProcessMetaData == null)
|
|
||||||
{
|
|
||||||
LOG.info("404 because process apiProcessMetaData is null", logPairs);
|
|
||||||
throw (new QNotFoundException("Could not find a process named " + processApiName + " in this api."));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(BooleanUtils.isTrue(apiProcessMetaData.getIsExcluded()))
|
|
||||||
{
|
|
||||||
LOG.info("404 because process is excluded", logPairs);
|
|
||||||
throw (new QNotFoundException("Could not find a process named " + processApiName + " in this api."));
|
|
||||||
}
|
|
||||||
|
|
||||||
APIVersion requestApiVersion = new APIVersion(version);
|
|
||||||
List<APIVersion> supportedVersions = apiInstanceMetaData.getSupportedVersions();
|
|
||||||
if(CollectionUtils.nullSafeIsEmpty(supportedVersions) || !supportedVersions.contains(requestApiVersion))
|
|
||||||
{
|
|
||||||
LOG.info("404 because requested version is not supported", logPairs);
|
|
||||||
throw (new QNotFoundException(version + " is not a supported version in this api."));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!apiProcessMetaData.getApiVersionRange().includes(requestApiVersion))
|
|
||||||
{
|
|
||||||
LOG.info("404 because process version range does not include requested version", logPairs);
|
|
||||||
throw (new QNotFoundException(version + " is not a supported version for process " + processApiName + " in this api."));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (process);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -1333,99 +1330,6 @@ public class ApiImplementation
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private static QProcessMetaData getProcessByApiName(String apiName, String version, String processApiName)
|
|
||||||
{
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// processApiNameMap is a map of (apiName,apiVersion) => Map<String, QProcessMetaData>. //
|
|
||||||
// that is to say, a 2-level map. The first level is keyed by (apiName,apiVersion) pairs. //
|
|
||||||
// the second level is keyed by processApiNames. //
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Pair<String, String> key = new Pair<>(apiName, version);
|
|
||||||
if(processApiNameMap.get(key) == null)
|
|
||||||
{
|
|
||||||
Map<String, QProcessMetaData> map = new HashMap<>();
|
|
||||||
|
|
||||||
for(QProcessMetaData process : QContext.getQInstance().getProcesses().values())
|
|
||||||
{
|
|
||||||
ApiProcessMetaDataContainer apiProcessMetaDataContainer = ApiProcessMetaDataContainer.of(process);
|
|
||||||
if(apiProcessMetaDataContainer != null)
|
|
||||||
{
|
|
||||||
ApiProcessMetaData apiProcessMetaData = apiProcessMetaDataContainer.getApiProcessMetaData(apiName);
|
|
||||||
if(apiProcessMetaData != null)
|
|
||||||
{
|
|
||||||
String name = process.getName();
|
|
||||||
if(StringUtils.hasContent(apiProcessMetaData.getApiProcessName()))
|
|
||||||
{
|
|
||||||
name = apiProcessMetaData.getApiProcessName();
|
|
||||||
}
|
|
||||||
map.put(name, process);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processApiNameMap.put(key, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (processApiNameMap.get(key).get(processApiName));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public static ApiProcessMetaData getApiProcessMetaDataIfProcessIsInApi(ApiInstanceMetaData apiInstanceMetaData, QProcessMetaData process)
|
|
||||||
{
|
|
||||||
if(BooleanUtils.isTrue(process.getIsHidden()))
|
|
||||||
{
|
|
||||||
LOG.trace("excluding process because it is hidden (process=" + process.getName() + ")");
|
|
||||||
return (null);
|
|
||||||
}
|
|
||||||
|
|
||||||
ApiProcessMetaDataContainer apiProcessMetaDataContainer = ApiProcessMetaDataContainer.of(process);
|
|
||||||
if(apiProcessMetaDataContainer == null)
|
|
||||||
{
|
|
||||||
LOG.trace("excluding process because apiProcessMetaDataContainer is null (process=" + process.getName() + ")");
|
|
||||||
return (null);
|
|
||||||
}
|
|
||||||
|
|
||||||
ApiProcessMetaData apiProcessMetaData = apiProcessMetaDataContainer.getApiProcessMetaData(apiInstanceMetaData.getName());
|
|
||||||
if(apiProcessMetaData == null)
|
|
||||||
{
|
|
||||||
LOG.trace("excluding process because apiProcessMetaData is null (process=" + process.getName() + ")");
|
|
||||||
return (null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(BooleanUtils.isTrue(apiProcessMetaData.getIsExcluded()))
|
|
||||||
{
|
|
||||||
LOG.trace("excluding process because is excluded (process=" + process.getName() + ")");
|
|
||||||
return (null);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isProcessInAnySupportedVersions = false;
|
|
||||||
List<APIVersion> supportedVersions = apiInstanceMetaData.getSupportedVersions();
|
|
||||||
APIVersionRange apiVersionRange = apiProcessMetaData.getApiVersionRange();
|
|
||||||
for(APIVersion supportedVersion : supportedVersions)
|
|
||||||
{
|
|
||||||
if(apiVersionRange.includes(supportedVersion))
|
|
||||||
{
|
|
||||||
isProcessInAnySupportedVersions = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isProcessInAnySupportedVersions)
|
|
||||||
{
|
|
||||||
LOG.trace("excluding process because it is not in any supported versions (process=" + process.getName() + ")");
|
|
||||||
return (null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (apiProcessMetaData);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
@ -1445,4 +1349,29 @@ public class ApiImplementation
|
|||||||
return errors.stream().anyMatch(e -> (e instanceof NotFoundStatusMessage));
|
return errors.stream().anyMatch(e -> (e instanceof NotFoundStatusMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static QProcessCallback getCallback(QQueryFilter filter)
|
||||||
|
{
|
||||||
|
return new QProcessCallback()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public QQueryFilter getQueryFilter()
|
||||||
|
{
|
||||||
|
return (filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Serializable> getFieldValues(List<QFieldMetaData> fields)
|
||||||
|
{
|
||||||
|
return (Collections.emptyMap());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,11 @@ import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
|||||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
||||||
import com.kingsrook.qqq.api.model.metadata.ApiOperation;
|
import com.kingsrook.qqq.api.model.metadata.ApiOperation;
|
||||||
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.processes.ApiProcessInput;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessInputFieldsContainer;
|
||||||
|
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.ApiProcessUtils;
|
||||||
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;
|
||||||
@ -73,12 +78,14 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
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.JsonUtils;
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
|
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.Pair;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.YamlUtils;
|
import com.kingsrook.qqq.backend.core.utils.YamlUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
||||||
@ -194,6 +201,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
{
|
{
|
||||||
QInstance qInstance = QContext.getQInstance();
|
QInstance qInstance = QContext.getQInstance();
|
||||||
String version = input.getVersion();
|
String version = input.getVersion();
|
||||||
|
APIVersion apiVersion = new APIVersion(version);
|
||||||
|
|
||||||
ApiInstanceMetaDataContainer apiInstanceMetaDataContainer = ApiInstanceMetaDataContainer.of(qInstance);
|
ApiInstanceMetaDataContainer apiInstanceMetaDataContainer = ApiInstanceMetaDataContainer.of(qInstance);
|
||||||
if(apiInstanceMetaDataContainer == null)
|
if(apiInstanceMetaDataContainer == null)
|
||||||
@ -217,7 +225,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
throw new QException("Missing required input: version");
|
throw new QException("Missing required input: version");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!apiInstanceMetaData.getSupportedVersions().contains(new APIVersion(version)))
|
if(!apiInstanceMetaData.getSupportedVersions().contains(apiVersion))
|
||||||
{
|
{
|
||||||
throw (new QException("[" + version + "] is not a supported API Version."));
|
throw (new QException("[" + version + "] is not a supported API Version."));
|
||||||
}
|
}
|
||||||
@ -292,6 +300,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
// foreach table //
|
// foreach table //
|
||||||
///////////////////
|
///////////////////
|
||||||
List<QTableMetaData> tables = new ArrayList<>(qInstance.getTables().values());
|
List<QTableMetaData> tables = new ArrayList<>(qInstance.getTables().values());
|
||||||
|
Set<String> usedProcessNames = new HashSet<>();
|
||||||
tables.sort(Comparator.comparing(t -> ObjectUtils.requireNonNullElse(t.getLabel(), t.getName(), "")));
|
tables.sort(Comparator.comparing(t -> ObjectUtils.requireNonNullElse(t.getLabel(), t.getName(), "")));
|
||||||
for(QTableMetaData table : tables)
|
for(QTableMetaData table : tables)
|
||||||
{
|
{
|
||||||
@ -330,7 +339,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
}
|
}
|
||||||
|
|
||||||
APIVersionRange apiVersionRange = apiTableMetaData.getApiVersionRange();
|
APIVersionRange apiVersionRange = apiTableMetaData.getApiVersionRange();
|
||||||
if(!apiVersionRange.includes(new APIVersion(version)))
|
if(!apiVersionRange.includes(apiVersion))
|
||||||
{
|
{
|
||||||
LOG.debug("Omitting table [" + tableName + "] because its api version range [" + apiVersionRange + "] does not include this version [" + version + "]");
|
LOG.debug("Omitting table [" + tableName + "] because its api version range [" + apiVersionRange + "] does not include this version [" + version + "]");
|
||||||
continue;
|
continue;
|
||||||
@ -355,9 +364,11 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
boolean deleteEnabled = ApiOperation.DELETE.isOperationEnabled(operationProviders) && deleteCapability;
|
boolean deleteEnabled = ApiOperation.DELETE.isOperationEnabled(operationProviders) && deleteCapability;
|
||||||
boolean deleteBulkEnabled = ApiOperation.BULK_DELETE.isOperationEnabled(operationProviders) && deleteCapability;
|
boolean deleteBulkEnabled = ApiOperation.BULK_DELETE.isOperationEnabled(operationProviders) && deleteCapability;
|
||||||
|
|
||||||
if(!getEnabled && !queryByQueryStringEnabled && !insertEnabled && !insertBulkEnabled && !updateEnabled && !updateBulkEnabled && !deleteEnabled && !deleteBulkEnabled)
|
List<Pair<ApiProcessMetaData, QProcessMetaData>> apiProcessMetaDataList = getProcessesUnderTable(table, apiName, apiVersion);
|
||||||
|
|
||||||
|
if(!getEnabled && !queryByQueryStringEnabled && !insertEnabled && !insertBulkEnabled && !updateEnabled && !updateBulkEnabled && !deleteEnabled && !deleteBulkEnabled && !CollectionUtils.nullSafeHasContents(apiProcessMetaDataList))
|
||||||
{
|
{
|
||||||
LOG.debug("Omitting table [" + tableName + "] because it does not have any supported capabilities / enabled operations");
|
LOG.debug("Omitting table [" + tableName + "] because it does not have any supported capabilities / enabled operations or processes");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -687,6 +698,35 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
.withPatch(updateBulkEnabled ? bulkPatch : null)
|
.withPatch(updateBulkEnabled ? bulkPatch : null)
|
||||||
.withDelete(deleteBulkEnabled ? bulkDelete : null));
|
.withDelete(deleteBulkEnabled ? bulkDelete : null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo - need to place these differently based on something?
|
||||||
|
for(Pair<ApiProcessMetaData, QProcessMetaData> pair : CollectionUtils.nonNullList(apiProcessMetaDataList))
|
||||||
|
{
|
||||||
|
ApiProcessMetaData apiProcessMetaData = pair.getA();
|
||||||
|
QProcessMetaData processMetaData = pair.getB();
|
||||||
|
|
||||||
|
String processApiPath = ApiProcessUtils.getProcessApiPath(qInstance, processMetaData, apiProcessMetaData, apiInstanceMetaData);
|
||||||
|
Path path = generateProcessSpecPathObject(apiInstanceMetaData, apiProcessMetaData, processMetaData, ListBuilder.of(tableLabel));
|
||||||
|
openAPI.getPaths().put(basePath + processApiPath, path);
|
||||||
|
|
||||||
|
usedProcessNames.add(processMetaData.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// add non-table processes //
|
||||||
|
/////////////////////////////
|
||||||
|
List<Pair<ApiProcessMetaData, QProcessMetaData>> processesNotUnderTables = getProcessesNotUnderTables(apiName, apiVersion, usedProcessNames);
|
||||||
|
for(Pair<ApiProcessMetaData, QProcessMetaData> pair : CollectionUtils.nonNullList(processesNotUnderTables))
|
||||||
|
{
|
||||||
|
ApiProcessMetaData apiProcessMetaData = pair.getA();
|
||||||
|
QProcessMetaData processMetaData = pair.getB();
|
||||||
|
|
||||||
|
String processApiPath = ApiProcessUtils.getProcessApiPath(qInstance, processMetaData, apiProcessMetaData, apiInstanceMetaData);
|
||||||
|
Path path = generateProcessSpecPathObject(apiInstanceMetaData, apiProcessMetaData, processMetaData, ListBuilder.of(processMetaData.getLabel()));
|
||||||
|
openAPI.getPaths().put(basePath + processApiPath, path);
|
||||||
|
|
||||||
|
usedProcessNames.add(processMetaData.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
componentResponses.put("error" + HttpStatus.BAD_REQUEST.getCode(), buildStandardErrorResponse("Bad Request. Some portion of the request's content was not acceptable to the server. See error message in body for details.", "Parameter id should be given an integer value, but received string: \"Foo\""));
|
componentResponses.put("error" + HttpStatus.BAD_REQUEST.getCode(), buildStandardErrorResponse("Bad Request. Some portion of the request's content was not acceptable to the server. See error message in body for details.", "Parameter id should be given an integer value, but received string: \"Foo\""));
|
||||||
@ -710,6 +750,140 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private List<Pair<ApiProcessMetaData, QProcessMetaData>> getProcessesNotUnderTables(String apiName, APIVersion apiVersion, Set<String> usedProcessNames)
|
||||||
|
{
|
||||||
|
List<Pair<ApiProcessMetaData, QProcessMetaData>> apiProcessMetaDataList = new ArrayList<>();
|
||||||
|
for(QProcessMetaData processMetaData : CollectionUtils.nonNullMap(QContext.getQInstance().getProcesses()).values())
|
||||||
|
{
|
||||||
|
if(usedProcessNames.contains(processMetaData.getName()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiProcessMetaDataContainer apiProcessMetaDataContainer = ApiProcessMetaDataContainer.of(processMetaData);
|
||||||
|
if(apiProcessMetaDataContainer == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiProcessMetaData apiProcessMetaData = apiProcessMetaDataContainer.getApis().get(apiName);
|
||||||
|
if(apiProcessMetaData == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!apiProcessMetaData.getApiVersionRange().includes(apiVersion))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
apiProcessMetaDataList.add(Pair.of(apiProcessMetaData, processMetaData));
|
||||||
|
}
|
||||||
|
return (apiProcessMetaDataList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private Path generateProcessSpecPathObject(ApiInstanceMetaData apiInstanceMetaData, ApiProcessMetaData apiProcessMetaData, QProcessMetaData processMetaData, List<String> tags)
|
||||||
|
{
|
||||||
|
Method methodForProcess = new Method()
|
||||||
|
.withOperationId(apiProcessMetaData.getApiProcessName())
|
||||||
|
.withTags(tags)
|
||||||
|
.withSummary(processMetaData.getLabel()) // todo - add optional summary to meta data
|
||||||
|
.withDescription("Run the process named " + processMetaData.getLabel())// todo - add optional description to meta data, .withDescription()
|
||||||
|
.withSecurity(getSecurity(apiInstanceMetaData, "todo - process name"));
|
||||||
|
|
||||||
|
List<Parameter> parameters = new ArrayList<>();
|
||||||
|
ApiProcessInput apiProcessInput = apiProcessMetaData.getInput();
|
||||||
|
if(apiProcessInput != null)
|
||||||
|
{
|
||||||
|
ApiProcessInputFieldsContainer queryStringParams = apiProcessInput.getQueryStringParams();
|
||||||
|
if(queryStringParams != null)
|
||||||
|
{
|
||||||
|
for(QFieldMetaData field : CollectionUtils.nonNullList(queryStringParams.getFields()))
|
||||||
|
{
|
||||||
|
parameters.add(new Parameter()
|
||||||
|
.withName(field.getName())
|
||||||
|
// todo - add description to meta data .withDescription("Which page of results to return. Starts at 1.")
|
||||||
|
.withDescription("Value for the " + field.getLabel() + " field.")
|
||||||
|
.withIn("query")
|
||||||
|
.withRequired(field.getIsRequired())
|
||||||
|
.withSchema(new Schema().withType(getFieldType(field))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(CollectionUtils.nullSafeHasContents(parameters))
|
||||||
|
{
|
||||||
|
methodForProcess.setParameters(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo methodForProcess.withRequestBody();
|
||||||
|
|
||||||
|
// todo methodForProcess.withResponse();
|
||||||
|
|
||||||
|
methodForProcess.withResponse(HttpStatus.OK.getCode(), new Response()
|
||||||
|
.withDescription("Successfully ran the process")
|
||||||
|
.withContent(MapBuilder.of("application/json", new Content())));
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:indentation")
|
||||||
|
Path path = switch(apiProcessMetaData.getMethod())
|
||||||
|
{
|
||||||
|
case GET -> new Path().withGet(methodForProcess);
|
||||||
|
case POST -> new Path().withPost(methodForProcess);
|
||||||
|
case PUT -> new Path().withPut(methodForProcess);
|
||||||
|
case PATCH -> new Path().withPatch(methodForProcess);
|
||||||
|
case DELETE -> new Path().withDelete(methodForProcess);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private List<Pair<ApiProcessMetaData, QProcessMetaData>> getProcessesUnderTable(QTableMetaData table, String apiName, APIVersion apiVersion)
|
||||||
|
{
|
||||||
|
List<Pair<ApiProcessMetaData, QProcessMetaData>> apiProcessMetaDataList = new ArrayList<>();
|
||||||
|
for(QProcessMetaData processMetaData : CollectionUtils.nonNullMap(QContext.getQInstance().getProcesses()).values())
|
||||||
|
{
|
||||||
|
if(!table.getName().equals(processMetaData.getTableName()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiProcessMetaDataContainer apiProcessMetaDataContainer = ApiProcessMetaDataContainer.of(processMetaData);
|
||||||
|
if(apiProcessMetaDataContainer == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiProcessMetaData apiProcessMetaData = apiProcessMetaDataContainer.getApis().get(apiName);
|
||||||
|
if(apiProcessMetaData == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!apiProcessMetaData.getApiVersionRange().includes(apiVersion))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
apiProcessMetaDataList.add(Pair.of(apiProcessMetaData, processMetaData));
|
||||||
|
}
|
||||||
|
return (apiProcessMetaDataList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** written for the use-case of, generating a single table's api, but it has
|
** written for the use-case of, generating a single table's api, but it has
|
||||||
** associations that it references, so we need their schemas too - so, make
|
** associations that it references, so we need their schemas too - so, make
|
||||||
|
@ -28,11 +28,13 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.kingsrook.qqq.api.actions.ApiImplementation;
|
import com.kingsrook.qqq.api.actions.ApiImplementation;
|
||||||
@ -41,10 +43,15 @@ import com.kingsrook.qqq.api.model.APILog;
|
|||||||
import com.kingsrook.qqq.api.model.APIVersion;
|
import com.kingsrook.qqq.api.model.APIVersion;
|
||||||
import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecInput;
|
import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecInput;
|
||||||
import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecOutput;
|
import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecOutput;
|
||||||
|
import com.kingsrook.qqq.api.model.actions.HttpApiResponse;
|
||||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
||||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
||||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataProvider;
|
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataProvider;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessInput;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessInputFieldsContainer;
|
||||||
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessMetaData;
|
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.ApiProcessUtils;
|
||||||
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.HttpMethod;
|
import com.kingsrook.qqq.api.model.openapi.HttpMethod;
|
||||||
@ -80,6 +87,7 @@ import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModu
|
|||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ExceptionUtils;
|
import com.kingsrook.qqq.backend.core.utils.ExceptionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
|
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 com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||||
import com.kingsrook.qqq.backend.javalin.QJavalinAccessLogger;
|
import com.kingsrook.qqq.backend.javalin.QJavalinAccessLogger;
|
||||||
@ -91,6 +99,7 @@ import io.javalin.http.Context;
|
|||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang.BooleanUtils;
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
|
import org.json.JSONObject;
|
||||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
import static com.kingsrook.qqq.backend.javalin.QJavalinImplementation.SLOW_LOG_THRESHOLD_MS;
|
import static com.kingsrook.qqq.backend.javalin.QJavalinImplementation.SLOW_LOG_THRESHOLD_MS;
|
||||||
|
|
||||||
@ -102,6 +111,8 @@ public class QJavalinApiHandler
|
|||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(QJavalinApiHandler.class);
|
private static final QLogger LOG = QLogger.getLogger(QJavalinApiHandler.class);
|
||||||
|
|
||||||
|
private static final ApiProcessMetaDataContainer EMPTY_CONTAINER = new ApiProcessMetaDataContainer().withApis(Collections.emptyMap());
|
||||||
|
|
||||||
private static QInstance qInstance;
|
private static QInstance qInstance;
|
||||||
|
|
||||||
private static Map<String, Integer> apiLogUserIdCache = new HashMap<>();
|
private static Map<String, Integer> apiLogUserIdCache = new HashMap<>();
|
||||||
@ -174,10 +185,11 @@ public class QJavalinApiHandler
|
|||||||
///////////////////
|
///////////////////
|
||||||
for(QProcessMetaData process : qInstance.getProcesses().values())
|
for(QProcessMetaData process : qInstance.getProcesses().values())
|
||||||
{
|
{
|
||||||
ApiProcessMetaData apiProcessMetaData = ApiImplementation.getApiProcessMetaDataIfProcessIsInApi(apiInstanceMetaData, process);
|
ApiProcessMetaDataContainer apiProcessMetaDataContainer = Objects.requireNonNullElse(ApiProcessMetaDataContainer.of(process), EMPTY_CONTAINER);
|
||||||
|
ApiProcessMetaData apiProcessMetaData = apiProcessMetaDataContainer.getApis().get(apiInstanceMetaData.getName());
|
||||||
if(apiProcessMetaData != null)
|
if(apiProcessMetaData != null)
|
||||||
{
|
{
|
||||||
String path = getProcessApiPath(process, apiProcessMetaData, apiInstanceMetaData);
|
String path = ApiProcessUtils.getProcessApiPath(qInstance, process, apiProcessMetaData, apiInstanceMetaData);
|
||||||
HttpMethod method = apiProcessMetaData.getMethod();
|
HttpMethod method = apiProcessMetaData.getMethod();
|
||||||
switch(method)
|
switch(method)
|
||||||
{
|
{
|
||||||
@ -189,6 +201,8 @@ public class QJavalinApiHandler
|
|||||||
default -> throw (new QRuntimeException("Unrecognized http method [" + method + "] for process [" + process.getName() + "]"));
|
default -> throw (new QRuntimeException("Unrecognized http method [" + method + "] for process [" + process.getName() + "]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
make405sForOtherMethods(method, path);
|
||||||
|
|
||||||
if(doesProcessSupportAsync(apiInstanceMetaData, process))
|
if(doesProcessSupportAsync(apiInstanceMetaData, process))
|
||||||
{
|
{
|
||||||
ApiBuilder.get(path + "/status/{processId}", context -> getProcessStatus(context, apiInstanceMetaData));
|
ApiBuilder.get(path + "/status/{processId}", context -> getProcessStatus(context, apiInstanceMetaData));
|
||||||
@ -247,6 +261,49 @@ public class QJavalinApiHandler
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void make405sForOtherMethods(HttpMethod allowedMethod, String path)
|
||||||
|
{
|
||||||
|
if(!allowedMethod.equals(HttpMethod.GET))
|
||||||
|
{
|
||||||
|
ApiBuilder.get(path, (Context c) -> QJavalinApiHandler.return405(c, allowedMethod));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!allowedMethod.equals(HttpMethod.POST))
|
||||||
|
{
|
||||||
|
ApiBuilder.post(path, (Context c) -> QJavalinApiHandler.return405(c, allowedMethod));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!allowedMethod.equals(HttpMethod.PUT))
|
||||||
|
{
|
||||||
|
ApiBuilder.put(path, (Context c) -> QJavalinApiHandler.return405(c, allowedMethod));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!allowedMethod.equals(HttpMethod.PATCH))
|
||||||
|
{
|
||||||
|
ApiBuilder.patch(path, (Context c) -> QJavalinApiHandler.return405(c, allowedMethod));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!allowedMethod.equals(HttpMethod.DELETE))
|
||||||
|
{
|
||||||
|
ApiBuilder.delete(path, (Context c) -> QJavalinApiHandler.return405(c, allowedMethod));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static void return405(Context context, HttpMethod allowedMethod)
|
||||||
|
{
|
||||||
|
respondWithError(context, HttpStatus.Code.METHOD_NOT_ALLOWED, "This path only supports method: " + allowedMethod, newAPILog(context)); // 405
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -272,21 +329,26 @@ public class QJavalinApiHandler
|
|||||||
QJavalinAccessLogger.logStart("apiRunProcess", logPair("process", processMetaData.getName()));
|
QJavalinAccessLogger.logStart("apiRunProcess", logPair("process", processMetaData.getName()));
|
||||||
|
|
||||||
Map<String, String> parameters = new LinkedHashMap<>();
|
Map<String, String> parameters = new LinkedHashMap<>();
|
||||||
for(QFieldMetaData inputField : CollectionUtils.nonNullList(apiProcessMetaData.getInputFields()))
|
|
||||||
|
ApiProcessInput input = apiProcessMetaData.getInput();
|
||||||
|
if(input != null)
|
||||||
{
|
{
|
||||||
String value = switch(apiProcessMetaData.getMethod())
|
processProcessInputFieldsContainer(context, parameters, input.getQueryStringParams(), Context::queryParam);
|
||||||
|
processProcessInputFieldsContainer(context, parameters, input.getFormParams(), Context::formParam);
|
||||||
|
|
||||||
|
ApiProcessInputFieldsContainer objectBodyParams = input.getObjectBodyParams();
|
||||||
|
if(objectBodyParams != null)
|
||||||
{
|
{
|
||||||
case GET -> context.queryParam(inputField.getName());
|
JSONObject jsonObject = new JSONObject(context.body());
|
||||||
// todo - other methods (all from a JSON body??)
|
processProcessInputFieldsContainer(context, parameters, objectBodyParams, (ctx, name) -> jsonObject.optString(name, null));
|
||||||
default -> throw new QException("Http method " + apiLog.getMethod() + " is not yet implemented for reading parameters");
|
}
|
||||||
};
|
|
||||||
parameters.put(inputField.getName(), value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Serializable> outputRecord = ApiImplementation.runProcess(apiInstanceMetaData, version, apiProcessMetaData.getApiProcessName(), parameters);
|
HttpApiResponse response = ApiImplementation.runProcess(apiInstanceMetaData, version, apiProcessMetaData.getApiProcessName(), parameters);
|
||||||
|
context.status(response.getStatusCode().getCode());
|
||||||
|
|
||||||
QJavalinAccessLogger.logEndSuccess();
|
QJavalinAccessLogger.logEndSuccess();
|
||||||
String resultString = toJson(outputRecord);
|
String resultString = toJson(Objects.requireNonNullElse(response.getResponseBodyObject(), ""));
|
||||||
context.result(resultString);
|
context.result(resultString);
|
||||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
||||||
}
|
}
|
||||||
@ -302,10 +364,22 @@ public class QJavalinApiHandler
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private boolean doesProcessSupportAsync(ApiInstanceMetaData apiInstanceMetaData, QProcessMetaData process)
|
private static void processProcessInputFieldsContainer(Context context, Map<String, String> parameters, ApiProcessInputFieldsContainer fieldsContainer, BiFunction<Context, String, String> paramAccessor)
|
||||||
{
|
{
|
||||||
// todo - implement
|
if(fieldsContainer != null)
|
||||||
return false;
|
{
|
||||||
|
List<QFieldMetaData> fields = CollectionUtils.nonNullList(fieldsContainer.getFields());
|
||||||
|
ObjectUtils.ifNotNull(fieldsContainer.getRecordIdsField(), fields::add);
|
||||||
|
for(QFieldMetaData field : fields)
|
||||||
|
{
|
||||||
|
String queryParamValue = paramAccessor.apply(context, field.getName());
|
||||||
|
if(queryParamValue != null)
|
||||||
|
{
|
||||||
|
String backendName = ObjectUtils.requireConditionElse(field.getBackendName(), StringUtils::hasContent, field.getName());
|
||||||
|
parameters.put(backendName, queryParamValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -313,34 +387,10 @@ public class QJavalinApiHandler
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private String getProcessApiPath(QProcessMetaData process, ApiProcessMetaData apiProcessMetaData, ApiInstanceMetaData apiInstanceMetaData)
|
private boolean doesProcessSupportAsync(ApiInstanceMetaData apiInstanceMetaData, QProcessMetaData process)
|
||||||
{
|
{
|
||||||
if(StringUtils.hasContent(apiProcessMetaData.getPath()))
|
// todo - implement
|
||||||
{
|
return false;
|
||||||
return apiProcessMetaData.getPath() + "/" + apiProcessMetaData.getApiProcessName();
|
|
||||||
}
|
|
||||||
else if(StringUtils.hasContent(process.getTableName()))
|
|
||||||
{
|
|
||||||
QTableMetaData table = qInstance.getTable(process.getTableName());
|
|
||||||
String tablePathPart = table.getName();
|
|
||||||
ApiTableMetaDataContainer apiTableMetaDataContainer = ApiTableMetaDataContainer.of(table);
|
|
||||||
if(apiTableMetaDataContainer != null)
|
|
||||||
{
|
|
||||||
ApiTableMetaData apiTableMetaData = apiTableMetaDataContainer.getApis().get(apiInstanceMetaData.getName());
|
|
||||||
if(apiTableMetaData != null)
|
|
||||||
{
|
|
||||||
if(StringUtils.hasContent(apiTableMetaData.getApiTableName()))
|
|
||||||
{
|
|
||||||
tablePathPart = apiTableMetaData.getApiTableName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tablePathPart + "/" + apiProcessMetaData.getApiProcessName();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return apiProcessMetaData.getApiProcessName();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. 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 org.eclipse.jetty.http.HttpStatus;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** class to contain http api responses.
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class HttpApiResponse
|
||||||
|
{
|
||||||
|
private HttpStatus.Code statusCode;
|
||||||
|
private Serializable responseBodyObject;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Default Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public HttpApiResponse()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public HttpApiResponse(HttpStatus.Code statusCode, Serializable responseBodyObject)
|
||||||
|
{
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
this.responseBodyObject = responseBodyObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for statusCode
|
||||||
|
*******************************************************************************/
|
||||||
|
public HttpStatus.Code getStatusCode()
|
||||||
|
{
|
||||||
|
return (this.statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for statusCode
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setStatusCode(HttpStatus.Code statusCode)
|
||||||
|
{
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for statusCode
|
||||||
|
*******************************************************************************/
|
||||||
|
public HttpApiResponse withStatusCode(HttpStatus.Code statusCode)
|
||||||
|
{
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for responseBodyObject
|
||||||
|
*******************************************************************************/
|
||||||
|
public Serializable getResponseBodyObject()
|
||||||
|
{
|
||||||
|
return (this.responseBodyObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for responseBodyObject
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setResponseBodyObject(Serializable responseBodyObject)
|
||||||
|
{
|
||||||
|
this.responseBodyObject = responseBodyObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for responseBodyObject
|
||||||
|
*******************************************************************************/
|
||||||
|
public HttpApiResponse withResponseBodyObject(Serializable responseBodyObject)
|
||||||
|
{
|
||||||
|
this.responseBodyObject = responseBodyObject;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class ApiProcessInput
|
||||||
|
{
|
||||||
|
private ApiProcessInputFieldsContainer queryStringParams;
|
||||||
|
private ApiProcessInputFieldsContainer formParams;
|
||||||
|
private ApiProcessInputFieldsContainer recordBodyParams;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getRecordIdsParamName()
|
||||||
|
{
|
||||||
|
if(queryStringParams != null && queryStringParams.getRecordIdsField() != null)
|
||||||
|
{
|
||||||
|
return (queryStringParams.getRecordIdsField().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(formParams != null && formParams.getRecordIdsField() != null)
|
||||||
|
{
|
||||||
|
return (formParams.getRecordIdsField().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(recordBodyParams != null && recordBodyParams.getRecordIdsField() != null)
|
||||||
|
{
|
||||||
|
return (recordBodyParams.getRecordIdsField().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for queryStringParams
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessInputFieldsContainer getQueryStringParams()
|
||||||
|
{
|
||||||
|
return (this.queryStringParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for queryStringParams
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQueryStringParams(ApiProcessInputFieldsContainer queryStringParams)
|
||||||
|
{
|
||||||
|
this.queryStringParams = queryStringParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for queryStringParams
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessInput withQueryStringParams(ApiProcessInputFieldsContainer queryStringParams)
|
||||||
|
{
|
||||||
|
this.queryStringParams = queryStringParams;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for formParams
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessInputFieldsContainer getFormParams()
|
||||||
|
{
|
||||||
|
return (this.formParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for formParams
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setFormParams(ApiProcessInputFieldsContainer formParams)
|
||||||
|
{
|
||||||
|
this.formParams = formParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for formParams
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessInput withFormParams(ApiProcessInputFieldsContainer formParams)
|
||||||
|
{
|
||||||
|
this.formParams = formParams;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for recordBodyParams
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessInputFieldsContainer getObjectBodyParams()
|
||||||
|
{
|
||||||
|
return (this.recordBodyParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for recordBodyParams
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setRecordBodyParams(ApiProcessInputFieldsContainer recordBodyParams)
|
||||||
|
{
|
||||||
|
this.recordBodyParams = recordBodyParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for recordBodyParams
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessInput withRecordBodyParams(ApiProcessInputFieldsContainer recordBodyParams)
|
||||||
|
{
|
||||||
|
this.recordBodyParams = recordBodyParams;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,116 @@
|
|||||||
|
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class ApiProcessInputFieldsContainer
|
||||||
|
{
|
||||||
|
private QFieldMetaData recordIdsField;
|
||||||
|
private List<QFieldMetaData> fields;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessInputFieldsContainer withInferredInputFields(QProcessMetaData processMetaData)
|
||||||
|
{
|
||||||
|
fields = new ArrayList<>();
|
||||||
|
for(QStepMetaData stepMetaData : CollectionUtils.nonNullList(processMetaData.getStepList()))
|
||||||
|
{
|
||||||
|
if(stepMetaData instanceof QFrontendStepMetaData frontendStep)
|
||||||
|
{
|
||||||
|
fields.addAll(frontendStep.getInputFields());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for recordIdsField
|
||||||
|
*******************************************************************************/
|
||||||
|
public QFieldMetaData getRecordIdsField()
|
||||||
|
{
|
||||||
|
return (this.recordIdsField);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for recordIdsField
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setRecordIdsField(QFieldMetaData recordIdsField)
|
||||||
|
{
|
||||||
|
this.recordIdsField = recordIdsField;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for recordIdsField
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessInputFieldsContainer withRecordIdsField(QFieldMetaData recordIdsField)
|
||||||
|
{
|
||||||
|
this.recordIdsField = recordIdsField;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for fields
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<QFieldMetaData> getFields()
|
||||||
|
{
|
||||||
|
return (this.fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for fields
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setFields(List<QFieldMetaData> fields)
|
||||||
|
{
|
||||||
|
this.fields = fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for fields
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessInputFieldsContainer withField(QFieldMetaData field)
|
||||||
|
{
|
||||||
|
if(this.fields == null)
|
||||||
|
{
|
||||||
|
this.fields = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.fields.add(field);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for fields
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessInputFieldsContainer withFields(List<QFieldMetaData> fields)
|
||||||
|
{
|
||||||
|
this.fields = fields;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,6 @@
|
|||||||
package com.kingsrook.qqq.api.model.metadata.processes;
|
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -31,13 +30,13 @@ import com.kingsrook.qqq.api.model.APIVersionRange;
|
|||||||
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.openapi.HttpMethod;
|
import com.kingsrook.qqq.api.model.openapi.HttpMethod;
|
||||||
|
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
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.model.metadata.processes.QFrontendStepMetaData;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -54,51 +53,13 @@ public class ApiProcessMetaData
|
|||||||
private String path;
|
private String path;
|
||||||
private HttpMethod method;
|
private HttpMethod method;
|
||||||
|
|
||||||
private List<QFieldMetaData> inputFields;
|
private ApiProcessInput input;
|
||||||
private List<QFieldMetaData> outputFields;
|
private ApiProcessOutputInterface output;
|
||||||
|
|
||||||
private Map<String, QCodeReference> customizers;
|
private Map<String, QCodeReference> customizers;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public ApiProcessMetaData withInferredInputFields(QProcessMetaData processMetaData)
|
|
||||||
{
|
|
||||||
inputFields = new ArrayList<>();
|
|
||||||
for(QStepMetaData stepMetaData : CollectionUtils.nonNullList(processMetaData.getStepList()))
|
|
||||||
{
|
|
||||||
if(stepMetaData instanceof QFrontendStepMetaData frontendStep)
|
|
||||||
{
|
|
||||||
inputFields.addAll(frontendStep.getInputFields());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public ApiProcessMetaData withInferredOutputFields(QProcessMetaData processMetaData)
|
|
||||||
{
|
|
||||||
outputFields = new ArrayList<>();
|
|
||||||
for(QStepMetaData stepMetaData : CollectionUtils.nonNullList(processMetaData.getStepList()))
|
|
||||||
{
|
|
||||||
if(stepMetaData instanceof QFrontendStepMetaData frontendStep)
|
|
||||||
{
|
|
||||||
outputFields.addAll(frontendStep.getOutputFields());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -119,8 +80,7 @@ public class ApiProcessMetaData
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
public void enrich(QInstanceEnricher qInstanceEnricher, String apiName, QProcessMetaData process)
|
||||||
public void enrich(String apiName, QProcessMetaData process)
|
|
||||||
{
|
{
|
||||||
if(!StringUtils.hasContent(getApiProcessName()))
|
if(!StringUtils.hasContent(getApiProcessName()))
|
||||||
{
|
{
|
||||||
@ -129,17 +89,40 @@ public class ApiProcessMetaData
|
|||||||
|
|
||||||
if(initialVersion != null)
|
if(initialVersion != null)
|
||||||
{
|
{
|
||||||
///////////////////////////////////////////////////////////////
|
if(getOutput() instanceof ApiProcessObjectOutput outputObject)
|
||||||
// make sure all fields have at least an initial version set //
|
{
|
||||||
///////////////////////////////////////////////////////////////
|
enrichFieldList(qInstanceEnricher, apiName, outputObject.getOutputFields());
|
||||||
for(QFieldMetaData field : CollectionUtils.mergeLists(getInputFields(), getOutputFields()))
|
}
|
||||||
|
|
||||||
|
if(input != null)
|
||||||
|
{
|
||||||
|
for(ApiProcessInputFieldsContainer fieldsContainer : ListBuilder.of(input.getQueryStringParams(), input.getFormParams(), input.getObjectBodyParams()))
|
||||||
|
{
|
||||||
|
if(fieldsContainer != null)
|
||||||
|
{
|
||||||
|
enrichFieldList(qInstanceEnricher, apiName, fieldsContainer.getFields());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void enrichFieldList(QInstanceEnricher qInstanceEnricher, String apiName, List<QFieldMetaData> fields)
|
||||||
|
{
|
||||||
|
for(QFieldMetaData field : CollectionUtils.nonNullList(fields))
|
||||||
{
|
{
|
||||||
ApiFieldMetaData apiFieldMetaData = ensureFieldHasApiSupplementalMetaData(apiName, field);
|
ApiFieldMetaData apiFieldMetaData = ensureFieldHasApiSupplementalMetaData(apiName, field);
|
||||||
if(apiFieldMetaData.getInitialVersion() == null)
|
if(apiFieldMetaData.getInitialVersion() == null)
|
||||||
{
|
{
|
||||||
apiFieldMetaData.setInitialVersion(initialVersion);
|
apiFieldMetaData.setInitialVersion(initialVersion);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
qInstanceEnricher.enrichField(field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,36 +149,6 @@ public class ApiProcessMetaData
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for a single outputField
|
|
||||||
*******************************************************************************/
|
|
||||||
public ApiProcessMetaData withOutputField(QFieldMetaData outputField)
|
|
||||||
{
|
|
||||||
if(this.outputFields == null)
|
|
||||||
{
|
|
||||||
this.outputFields = new ArrayList<>();
|
|
||||||
}
|
|
||||||
this.outputFields.add(outputField);
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for a single inputField
|
|
||||||
*******************************************************************************/
|
|
||||||
public ApiProcessMetaData withInputField(QFieldMetaData inputField)
|
|
||||||
{
|
|
||||||
if(this.inputFields == null)
|
|
||||||
{
|
|
||||||
this.inputFields = new ArrayList<>();
|
|
||||||
}
|
|
||||||
this.inputFields.add(inputField);
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for initialVersion
|
** Getter for initialVersion
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -382,68 +335,6 @@ public class ApiProcessMetaData
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for inputFields
|
|
||||||
*******************************************************************************/
|
|
||||||
public List<QFieldMetaData> getInputFields()
|
|
||||||
{
|
|
||||||
return (this.inputFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for inputFields
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setInputFields(List<QFieldMetaData> inputFields)
|
|
||||||
{
|
|
||||||
this.inputFields = inputFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for inputFields
|
|
||||||
*******************************************************************************/
|
|
||||||
public ApiProcessMetaData withInputFields(List<QFieldMetaData> inputFields)
|
|
||||||
{
|
|
||||||
this.inputFields = inputFields;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for outputFields
|
|
||||||
*******************************************************************************/
|
|
||||||
public List<QFieldMetaData> getOutputFields()
|
|
||||||
{
|
|
||||||
return (this.outputFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for outputFields
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setOutputFields(List<QFieldMetaData> outputFields)
|
|
||||||
{
|
|
||||||
this.outputFields = outputFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Fluent setter for outputFields
|
|
||||||
*******************************************************************************/
|
|
||||||
public ApiProcessMetaData withOutputFields(List<QFieldMetaData> outputFields)
|
|
||||||
{
|
|
||||||
this.outputFields = outputFields;
|
|
||||||
return (this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for customizers
|
** Getter for customizers
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -493,4 +384,66 @@ public class ApiProcessMetaData
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for output
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessOutputInterface getOutput()
|
||||||
|
{
|
||||||
|
return (this.output);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for output
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setOutput(ApiProcessOutputInterface output)
|
||||||
|
{
|
||||||
|
this.output = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for output
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessMetaData withOutput(ApiProcessOutputInterface output)
|
||||||
|
{
|
||||||
|
this.output = output;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for input
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessInput getInput()
|
||||||
|
{
|
||||||
|
return (this.input);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for input
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setInput(ApiProcessInput input)
|
||||||
|
{
|
||||||
|
this.input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for input
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessMetaData withInput(ApiProcessInput input)
|
||||||
|
{
|
||||||
|
this.input = input;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.api.model.metadata.processes;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.kingsrook.qqq.api.ApiSupplementType;
|
import com.kingsrook.qqq.api.ApiSupplementType;
|
||||||
|
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QSupplementalProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QSupplementalProcessMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
@ -64,13 +65,13 @@ public class ApiProcessMetaDataContainer extends QSupplementalProcessMetaData
|
|||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Override
|
@Override
|
||||||
public void enrich(QProcessMetaData process)
|
public void enrich(QInstanceEnricher qInstanceEnricher, QProcessMetaData process)
|
||||||
{
|
{
|
||||||
super.enrich(process);
|
super.enrich(qInstanceEnricher, process);
|
||||||
|
|
||||||
for(Map.Entry<String, ApiProcessMetaData> entry : CollectionUtils.nonNullMap(apis).entrySet())
|
for(Map.Entry<String, ApiProcessMetaData> entry : CollectionUtils.nonNullMap(apis).entrySet())
|
||||||
{
|
{
|
||||||
entry.getValue().enrich(entry.getKey(), process);
|
entry.getValue().enrich(qInstanceEnricher, entry.getKey(), process);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class ApiProcessObjectOutput implements ApiProcessOutputInterface
|
||||||
|
{
|
||||||
|
private List<QFieldMetaData> outputFields;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public Serializable getOutputForProcess(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput)
|
||||||
|
{
|
||||||
|
LinkedHashMap<String, Serializable> outputMap = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
for(QFieldMetaData outputField : CollectionUtils.nonNullList(getOutputFields()))
|
||||||
|
{
|
||||||
|
outputMap.put(outputField.getName(), runProcessOutput.getValues().get(outputField.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (outputMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for outputFields
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<QFieldMetaData> getOutputFields()
|
||||||
|
{
|
||||||
|
return (this.outputFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for outputFields
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setOutputFields(List<QFieldMetaData> outputFields)
|
||||||
|
{
|
||||||
|
this.outputFields = outputFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for outputFields
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessObjectOutput withOutputFields(List<QFieldMetaData> outputFields)
|
||||||
|
{
|
||||||
|
this.outputFields = outputFields;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for a single outputField
|
||||||
|
*******************************************************************************/
|
||||||
|
public ApiProcessObjectOutput withOutputField(QFieldMetaData outputField)
|
||||||
|
{
|
||||||
|
if(this.outputFields == null)
|
||||||
|
{
|
||||||
|
this.outputFields = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.outputFields.add(outputField);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||||
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public interface ApiProcessOutputInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
Serializable getOutputForProcess(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput) throws QException;
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
default HttpStatus.Code getSuccessStatusCode(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput)
|
||||||
|
{
|
||||||
|
return (HttpStatus.Code.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryFilterLink;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryRecordLink;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
import org.apache.commons.lang.NotImplementedException;
|
||||||
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class ApiProcessSummaryListOutput implements ApiProcessOutputInterface
|
||||||
|
{
|
||||||
|
private static final QLogger LOG = QLogger.getLogger(ApiProcessSummaryListOutput.class);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public HttpStatus.Code getSuccessStatusCode(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput)
|
||||||
|
{
|
||||||
|
List<ProcessSummaryLineInterface> processSummaryLineInterfaces = (List<ProcessSummaryLineInterface>) runProcessOutput.getValues().get("processResults");
|
||||||
|
if(processSummaryLineInterfaces.isEmpty())
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// if there are no summary lines, all we can return is 204 - no content //
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
return (HttpStatus.Code.NO_CONTENT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// else if there are summary lines, we'll represent them as a 207 - multi-status //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
return (HttpStatus.Code.MULTI_STATUS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public Serializable getOutputForProcess(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput) throws QException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ArrayList<Serializable> apiOutput = new ArrayList<>();
|
||||||
|
List<ProcessSummaryLineInterface> processSummaryLineInterfaces = (List<ProcessSummaryLineInterface>) runProcessOutput.getValues().get("processResults");
|
||||||
|
for(ProcessSummaryLineInterface processSummaryLineInterface : processSummaryLineInterfaces)
|
||||||
|
{
|
||||||
|
if(processSummaryLineInterface instanceof ProcessSummaryLine processSummaryLine)
|
||||||
|
{
|
||||||
|
processSummaryLine.setCount(1);
|
||||||
|
processSummaryLine.prepareForFrontend(true);
|
||||||
|
|
||||||
|
List<Serializable> primaryKeys = processSummaryLine.getPrimaryKeys();
|
||||||
|
if(CollectionUtils.nullSafeHasContents(primaryKeys))
|
||||||
|
{
|
||||||
|
for(Serializable primaryKey : primaryKeys)
|
||||||
|
{
|
||||||
|
HashMap<String, Serializable> map = toMap(processSummaryLine);
|
||||||
|
map.put("id", primaryKey);
|
||||||
|
apiOutput.add(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
apiOutput.add(toMap(processSummaryLine));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(processSummaryLineInterface instanceof ProcessSummaryRecordLink processSummaryRecordLink)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("ProcessSummaryRecordLink handling");
|
||||||
|
}
|
||||||
|
else if(processSummaryLineInterface instanceof ProcessSummaryFilterLink processSummaryFilterLink)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("ProcessSummaryFilterLink handling");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("Unknown ProcessSummaryLineInterface handling");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (apiOutput);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.warn("Error getting api output for process", e);
|
||||||
|
throw (new QException("Error generating process output", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@SuppressWarnings("checkstyle:indentation")
|
||||||
|
private static HashMap<String, Serializable> toMap(ProcessSummaryLine processSummaryLine)
|
||||||
|
{
|
||||||
|
HashMap<String, Serializable> map = new HashMap<>();
|
||||||
|
HttpStatus.Code code = switch(processSummaryLine.getStatus())
|
||||||
|
{
|
||||||
|
case OK, WARNING, INFO -> HttpStatus.Code.OK;
|
||||||
|
case ERROR -> HttpStatus.Code.INTERNAL_SERVER_ERROR;
|
||||||
|
};
|
||||||
|
|
||||||
|
String messagePrefix = switch(processSummaryLine.getStatus())
|
||||||
|
{
|
||||||
|
case OK, INFO, ERROR -> "";
|
||||||
|
case WARNING -> "Warning: ";
|
||||||
|
};
|
||||||
|
|
||||||
|
map.put("statusCode", code.getCode());
|
||||||
|
map.put("statusText", code.getMessage());
|
||||||
|
map.put("message", messagePrefix + processSummaryLine.getMessage());
|
||||||
|
|
||||||
|
return (map);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,169 @@
|
|||||||
|
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import com.kingsrook.qqq.api.model.APIVersion;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaData;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaDataContainer;
|
||||||
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||||
|
import com.kingsrook.qqq.backend.core.logging.LogPair;
|
||||||
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
|
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.Pair;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class ApiProcessUtils
|
||||||
|
{
|
||||||
|
private static final QLogger LOG = QLogger.getLogger(ApiProcessUtils.class);
|
||||||
|
|
||||||
|
private static Map<Pair<String, String>, Map<String, QProcessMetaData>> processApiNameMap = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static Pair<ApiProcessMetaData, QProcessMetaData> getProcessMetaDataPair(ApiInstanceMetaData apiInstanceMetaData, String version, String processApiName) throws QNotFoundException
|
||||||
|
{
|
||||||
|
QProcessMetaData process = getProcessByApiName(apiInstanceMetaData.getName(), version, processApiName);
|
||||||
|
LogPair[] logPairs = new LogPair[] { logPair("apiName", apiInstanceMetaData.getName()), logPair("version", version), logPair("processApiName", processApiName) };
|
||||||
|
|
||||||
|
if(process == null)
|
||||||
|
{
|
||||||
|
LOG.info("404 because process is null (processApiName=" + processApiName + ")", logPairs);
|
||||||
|
throw (new QNotFoundException("Could not find a process named " + processApiName + " in this api."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(BooleanUtils.isTrue(process.getIsHidden()))
|
||||||
|
{
|
||||||
|
LOG.info("404 because process isHidden", logPairs);
|
||||||
|
throw (new QNotFoundException("Could not find a process named " + processApiName + " in this api."));
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiProcessMetaDataContainer apiProcessMetaDataContainer = ApiProcessMetaDataContainer.of(process);
|
||||||
|
if(apiProcessMetaDataContainer == null)
|
||||||
|
{
|
||||||
|
LOG.info("404 because process apiProcessMetaDataContainer is null", logPairs);
|
||||||
|
throw (new QNotFoundException("Could not find a process named " + processApiName + " in this api."));
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiProcessMetaData apiProcessMetaData = apiProcessMetaDataContainer.getApiProcessMetaData(apiInstanceMetaData.getName());
|
||||||
|
if(apiProcessMetaData == null)
|
||||||
|
{
|
||||||
|
LOG.info("404 because process apiProcessMetaData is null", logPairs);
|
||||||
|
throw (new QNotFoundException("Could not find a process named " + processApiName + " in this api."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(BooleanUtils.isTrue(apiProcessMetaData.getIsExcluded()))
|
||||||
|
{
|
||||||
|
LOG.info("404 because process is excluded", logPairs);
|
||||||
|
throw (new QNotFoundException("Could not find a process named " + processApiName + " in this api."));
|
||||||
|
}
|
||||||
|
|
||||||
|
APIVersion requestApiVersion = new APIVersion(version);
|
||||||
|
List<APIVersion> supportedVersions = apiInstanceMetaData.getSupportedVersions();
|
||||||
|
if(CollectionUtils.nullSafeIsEmpty(supportedVersions) || !supportedVersions.contains(requestApiVersion))
|
||||||
|
{
|
||||||
|
LOG.info("404 because requested version is not supported", logPairs);
|
||||||
|
throw (new QNotFoundException(version + " is not a supported version in this api."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!apiProcessMetaData.getApiVersionRange().includes(requestApiVersion))
|
||||||
|
{
|
||||||
|
LOG.info("404 because process version range does not include requested version", logPairs);
|
||||||
|
throw (new QNotFoundException(version + " is not a supported version for process " + processApiName + " in this api."));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Pair.of(apiProcessMetaData, process));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static QProcessMetaData getProcessByApiName(String apiName, String version, String processApiName)
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// processApiNameMap is a map of (apiName,apiVersion) => Map<String, QProcessMetaData>. //
|
||||||
|
// that is to say, a 2-level map. The first level is keyed by (apiName,apiVersion) pairs. //
|
||||||
|
// the second level is keyed by processApiNames. //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Pair<String, String> key = new Pair<>(apiName, version);
|
||||||
|
if(processApiNameMap.get(key) == null)
|
||||||
|
{
|
||||||
|
Map<String, QProcessMetaData> map = new HashMap<>();
|
||||||
|
|
||||||
|
for(QProcessMetaData process : QContext.getQInstance().getProcesses().values())
|
||||||
|
{
|
||||||
|
ApiProcessMetaDataContainer apiProcessMetaDataContainer = ApiProcessMetaDataContainer.of(process);
|
||||||
|
if(apiProcessMetaDataContainer != null)
|
||||||
|
{
|
||||||
|
ApiProcessMetaData apiProcessMetaData = apiProcessMetaDataContainer.getApiProcessMetaData(apiName);
|
||||||
|
if(apiProcessMetaData != null)
|
||||||
|
{
|
||||||
|
String name = process.getName();
|
||||||
|
if(StringUtils.hasContent(apiProcessMetaData.getApiProcessName()))
|
||||||
|
{
|
||||||
|
name = apiProcessMetaData.getApiProcessName();
|
||||||
|
}
|
||||||
|
map.put(name, process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processApiNameMap.put(key, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (processApiNameMap.get(key).get(processApiName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static String getProcessApiPath(QInstance qInstance, QProcessMetaData process, ApiProcessMetaData apiProcessMetaData, ApiInstanceMetaData apiInstanceMetaData)
|
||||||
|
{
|
||||||
|
if(StringUtils.hasContent(apiProcessMetaData.getPath()))
|
||||||
|
{
|
||||||
|
return apiProcessMetaData.getPath() + "/" + apiProcessMetaData.getApiProcessName();
|
||||||
|
}
|
||||||
|
else if(StringUtils.hasContent(process.getTableName()))
|
||||||
|
{
|
||||||
|
QTableMetaData table = qInstance.getTable(process.getTableName());
|
||||||
|
String tablePathPart = table.getName();
|
||||||
|
ApiTableMetaDataContainer apiTableMetaDataContainer = ApiTableMetaDataContainer.of(table);
|
||||||
|
if(apiTableMetaDataContainer != null)
|
||||||
|
{
|
||||||
|
ApiTableMetaData apiTableMetaData = apiTableMetaDataContainer.getApis().get(apiInstanceMetaData.getName());
|
||||||
|
if(apiTableMetaData != null)
|
||||||
|
{
|
||||||
|
if(StringUtils.hasContent(apiTableMetaData.getApiTableName()))
|
||||||
|
{
|
||||||
|
tablePathPart = apiTableMetaData.getApiTableName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tablePathPart + "/" + apiProcessMetaData.getApiProcessName();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return apiProcessMetaData.getApiProcessName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -29,8 +29,12 @@ import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
|||||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
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.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.processes.ApiProcessInput;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessInputFieldsContainer;
|
||||||
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessMetaData;
|
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.ApiProcessObjectOutput;
|
||||||
|
import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessSummaryListOutput;
|
||||||
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.HttpMethod;
|
import com.kingsrook.qqq.api.model.openapi.HttpMethod;
|
||||||
@ -72,8 +76,10 @@ import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage
|
|||||||
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
|
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
|
||||||
import com.kingsrook.qqq.backend.core.model.statusmessages.SystemErrorStatusMessage;
|
import com.kingsrook.qqq.backend.core.model.statusmessages.SystemErrorStatusMessage;
|
||||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryBackendModule;
|
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryBackendModule;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.LoadViaUpdateStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -90,6 +96,7 @@ public class TestUtils
|
|||||||
public static final String TABLE_NAME_ORDER_EXTRINSIC = "orderExtrinsic";
|
public static final String TABLE_NAME_ORDER_EXTRINSIC = "orderExtrinsic";
|
||||||
|
|
||||||
public static final String PROCESS_NAME_GET_PERSON_INFO = "getPersonInfo";
|
public static final String PROCESS_NAME_GET_PERSON_INFO = "getPersonInfo";
|
||||||
|
public static final String PROCESS_NAME_TRANSFORM_PEOPLE = "transformPeople";
|
||||||
|
|
||||||
public static final String API_NAME = "test-api";
|
public static final String API_NAME = "test-api";
|
||||||
public static final String ALTERNATIVE_API_NAME = "person-api";
|
public static final String ALTERNATIVE_API_NAME = "person-api";
|
||||||
@ -122,6 +129,7 @@ public class TestUtils
|
|||||||
|
|
||||||
qInstance.addPossibleValueSource(definePersonPossibleValueSource());
|
qInstance.addPossibleValueSource(definePersonPossibleValueSource());
|
||||||
qInstance.addProcess(defineProcessGetPersonInfo());
|
qInstance.addProcess(defineProcessGetPersonInfo());
|
||||||
|
qInstance.addProcess(defineProcessTransformPeople());
|
||||||
|
|
||||||
qInstance.setAuthentication(new Auth0AuthenticationMetaData().withType(QAuthenticationType.FULLY_ANONYMOUS).withName("anonymous"));
|
qInstance.setAuthentication(new Auth0AuthenticationMetaData().withType(QAuthenticationType.FULLY_ANONYMOUS).withName("anonymous"));
|
||||||
|
|
||||||
@ -214,12 +222,12 @@ public class TestUtils
|
|||||||
.withApiProcessMetaData(API_NAME, new ApiProcessMetaData()
|
.withApiProcessMetaData(API_NAME, new ApiProcessMetaData()
|
||||||
.withInitialVersion(CURRENT_API_VERSION)
|
.withInitialVersion(CURRENT_API_VERSION)
|
||||||
.withMethod(HttpMethod.GET)
|
.withMethod(HttpMethod.GET)
|
||||||
.withInferredInputFields(process)
|
.withInput(new ApiProcessInput()
|
||||||
.withOutputFields(ListBuilder.of(
|
.withQueryStringParams(new ApiProcessInputFieldsContainer().withInferredInputFields(process)))
|
||||||
new QFieldMetaData("density", QFieldType.DECIMAL),
|
.withOutput(new ApiProcessObjectOutput()
|
||||||
new QFieldMetaData("daysOld", QFieldType.INTEGER),
|
.withOutputField(new QFieldMetaData("density", QFieldType.DECIMAL))
|
||||||
new QFieldMetaData("nickname", QFieldType.STRING)
|
.withOutputField(new QFieldMetaData("daysOld", QFieldType.INTEGER))
|
||||||
))
|
.withOutputField(new QFieldMetaData("nickname", QFieldType.STRING)))
|
||||||
));
|
));
|
||||||
|
|
||||||
return (process);
|
return (process);
|
||||||
@ -227,6 +235,35 @@ public class TestUtils
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static QProcessMetaData defineProcessTransformPeople()
|
||||||
|
{
|
||||||
|
QProcessMetaData process = StreamedETLWithFrontendProcess.processMetaDataBuilder()
|
||||||
|
.withName(PROCESS_NAME_TRANSFORM_PEOPLE)
|
||||||
|
.withTableName(TABLE_NAME_PERSON)
|
||||||
|
.withSourceTable(TABLE_NAME_PERSON)
|
||||||
|
.withDestinationTable(TABLE_NAME_PERSON)
|
||||||
|
.withMinInputRecords(1)
|
||||||
|
.withExtractStepClass(ExtractViaQueryStep.class)
|
||||||
|
.withTransformStepClass(TransformPersonStep.class)
|
||||||
|
.withLoadStepClass(LoadViaUpdateStep.class)
|
||||||
|
.getProcessMetaData();
|
||||||
|
|
||||||
|
process.withSupplementalMetaData(new ApiProcessMetaDataContainer()
|
||||||
|
.withApiProcessMetaData(API_NAME, new ApiProcessMetaData()
|
||||||
|
.withInitialVersion(CURRENT_API_VERSION)
|
||||||
|
.withMethod(HttpMethod.POST)
|
||||||
|
.withInput(new ApiProcessInput()
|
||||||
|
.withQueryStringParams(new ApiProcessInputFieldsContainer().withRecordIdsField(new QFieldMetaData("id", QFieldType.STRING))))
|
||||||
|
.withOutput(new ApiProcessSummaryListOutput())));
|
||||||
|
|
||||||
|
return (process);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Define the in-memory backend used in standard tests
|
** Define the in-memory backend used in standard tests
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
package com.kingsrook.qqq.api;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractTransformStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.general.StandardProcessSummaryLineProducer;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class TransformPersonStep extends AbstractTransformStep
|
||||||
|
{
|
||||||
|
private ProcessSummaryLine okLine = StandardProcessSummaryLineProducer.getOkToUpdateLine();
|
||||||
|
private ProcessSummaryLine errorLine = StandardProcessSummaryLineProducer.getErrorLine();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public ArrayList<ProcessSummaryLineInterface> getProcessSummary(RunBackendStepOutput runBackendStepOutput, boolean isForResultScreen)
|
||||||
|
{
|
||||||
|
ArrayList<ProcessSummaryLineInterface> rs = new ArrayList<>();
|
||||||
|
okLine.addSelfToListIfAnyCount(rs);
|
||||||
|
errorLine.addSelfToListIfAnyCount(rs);
|
||||||
|
return (rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
|
{
|
||||||
|
for(QRecord record : runBackendStepInput.getRecords())
|
||||||
|
{
|
||||||
|
Integer id = record.getValueInteger("id");
|
||||||
|
if(id % 2 == 0)
|
||||||
|
{
|
||||||
|
okLine.incrementCountAndAddPrimaryKey(id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorLine.incrementCountAndAddPrimaryKey(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1443,9 +1443,12 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
void testProcess() throws QException
|
void testGetProcessForObject() throws QException
|
||||||
{
|
{
|
||||||
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/person/getPersonInfo?age=43&partnerPersonId=1&heightInches=72&weightPounds=220&homeTown=Chester").asString();
|
HttpResponse<String> response = Unirest.post(BASE_URL + "/api/" + VERSION + "/person/getPersonInfo").asString();
|
||||||
|
assertErrorResponse(HttpStatus.METHOD_NOT_ALLOWED_405, "This path only supports method: GET", response);
|
||||||
|
|
||||||
|
response = Unirest.get(BASE_URL + "/api/" + VERSION + "/person/getPersonInfo?age=43&partnerPersonId=1&heightInches=72&weightPounds=220&homeTown=Chester").asString();
|
||||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||||
JSONObject jsonObject = new JSONObject(response.getBody());
|
JSONObject jsonObject = new JSONObject(response.getBody());
|
||||||
System.out.println(jsonObject.toString(3));
|
System.out.println(jsonObject.toString(3));
|
||||||
@ -1453,6 +1456,33 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testPostProcessForProcessSummaryList() throws QException
|
||||||
|
{
|
||||||
|
insertSimpsons();
|
||||||
|
|
||||||
|
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/person/transformPeople").asString();
|
||||||
|
assertErrorResponse(HttpStatus.METHOD_NOT_ALLOWED_405, "This path only supports method: POST", response);
|
||||||
|
|
||||||
|
response = Unirest.post(BASE_URL + "/api/" + VERSION + "/person/transformPeople").asString();
|
||||||
|
assertErrorResponse(HttpStatus.BAD_REQUEST_400, "Records to run through this process were not specified", response);
|
||||||
|
|
||||||
|
response = Unirest.post(BASE_URL + "/api/" + VERSION + "/person/transformPeople?id=999").asString();
|
||||||
|
assertEquals(HttpStatus.NO_CONTENT_204, response.getStatus());
|
||||||
|
assertEquals("", response.getBody());
|
||||||
|
|
||||||
|
response = Unirest.post(BASE_URL + "/api/" + VERSION + "/person/transformPeople?id=1,2,3").asString();
|
||||||
|
assertEquals(HttpStatus.MULTI_STATUS_207, response.getStatus());
|
||||||
|
JSONArray jsonArray = new JSONArray(response.getBody());
|
||||||
|
assertEquals(3, jsonArray.length());
|
||||||
|
System.out.println(jsonArray.toString(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
Reference in New Issue
Block a user