mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Merge branch 'integration/sprint-27' into feature/CTLE-509-support-sending-custom-data-fields-to-deposco
This commit is contained in:
@ -39,6 +39,7 @@ import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider;
|
||||
import com.kingsrook.qqq.backend.core.state.StateProviderInterface;
|
||||
import com.kingsrook.qqq.backend.core.state.StateType;
|
||||
import com.kingsrook.qqq.backend.core.state.UUIDAndTypeStateKey;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
@ -50,6 +51,7 @@ public class AsyncJobManager
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(AsyncJobManager.class);
|
||||
|
||||
private String forcedJobUUID = null;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -69,7 +71,8 @@ public class AsyncJobManager
|
||||
*******************************************************************************/
|
||||
public <T extends Serializable> T startJob(String jobName, long timeout, TimeUnit timeUnit, AsyncJob<T> asyncJob) throws JobGoingAsyncException, QException
|
||||
{
|
||||
UUIDAndTypeStateKey uuidAndTypeStateKey = new UUIDAndTypeStateKey(UUID.randomUUID(), StateType.ASYNC_JOB_STATUS);
|
||||
UUID jobUUID = StringUtils.hasContent(forcedJobUUID) ? UUID.fromString(forcedJobUUID) : UUID.randomUUID();
|
||||
UUIDAndTypeStateKey uuidAndTypeStateKey = new UUIDAndTypeStateKey(jobUUID, StateType.ASYNC_JOB_STATUS);
|
||||
AsyncJobStatus asyncJobStatus = new AsyncJobStatus();
|
||||
asyncJobStatus.setState(AsyncJobState.RUNNING);
|
||||
getStateProvider().put(uuidAndTypeStateKey, asyncJobStatus);
|
||||
@ -205,4 +208,35 @@ public class AsyncJobManager
|
||||
jobStatus.ifPresent(asyncJobStatus -> asyncJobStatus.setCancelRequested(true));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for forcedJobUUID
|
||||
*******************************************************************************/
|
||||
public String getForcedJobUUID()
|
||||
{
|
||||
return (this.forcedJobUUID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for forcedJobUUID
|
||||
*******************************************************************************/
|
||||
public void setForcedJobUUID(String forcedJobUUID)
|
||||
{
|
||||
this.forcedJobUUID = forcedJobUUID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for forcedJobUUID
|
||||
*******************************************************************************/
|
||||
public AsyncJobManager withForcedJobUUID(String forcedJobUUID)
|
||||
{
|
||||
this.forcedJobUUID = forcedJobUUID;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -57,12 +57,13 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponen
|
||||
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.model.metadata.processes.QSupplementalProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportView;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QMiddlewareTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QSupplementalTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.delete.BulkDeleteLoadStep;
|
||||
@ -261,9 +262,9 @@ public class QInstanceEnricher
|
||||
{
|
||||
table.getFields().values().forEach(this::enrichField);
|
||||
|
||||
for(QMiddlewareTableMetaData middlewareTableMetaData : CollectionUtils.nonNullMap(table.getMiddlewareMetaData()).values())
|
||||
for(QSupplementalTableMetaData supplementalTableMetaData : CollectionUtils.nonNullMap(table.getSupplementalMetaData()).values())
|
||||
{
|
||||
middlewareTableMetaData.enrich(table);
|
||||
supplementalTableMetaData.enrich(table);
|
||||
}
|
||||
}
|
||||
|
||||
@ -367,6 +368,11 @@ public class QInstanceEnricher
|
||||
process.getStepList().forEach(this::enrichStep);
|
||||
}
|
||||
|
||||
for(QSupplementalProcessMetaData supplementalProcessMetaData : CollectionUtils.nonNullMap(process.getSupplementalMetaData()).values())
|
||||
{
|
||||
supplementalProcessMetaData.enrich(this, process);
|
||||
}
|
||||
|
||||
enrichPermissionRules(process);
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QMiddlewareInstanceMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QSupplementalInstanceMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||
@ -63,6 +63,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||
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.model.metadata.processes.QSupplementalProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.queues.SQSQueueProviderMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportField;
|
||||
@ -158,7 +159,7 @@ public class QInstanceValidator
|
||||
validateQueuesAndProviders(qInstance);
|
||||
validateJoins(qInstance);
|
||||
validateSecurityKeyTypes(qInstance);
|
||||
validateMiddlewareMetaData(qInstance);
|
||||
validateSupplementalMetaData(qInstance);
|
||||
|
||||
validateUniqueTopLevelNames(qInstance);
|
||||
}
|
||||
@ -182,11 +183,11 @@ public class QInstanceValidator
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void validateMiddlewareMetaData(QInstance qInstance)
|
||||
private void validateSupplementalMetaData(QInstance qInstance)
|
||||
{
|
||||
for(QMiddlewareInstanceMetaData middlewareInstanceMetaData : CollectionUtils.nonNullMap(qInstance.getMiddlewareMetaData()).values())
|
||||
for(QSupplementalInstanceMetaData supplementalInstanceMetaData : CollectionUtils.nonNullMap(qInstance.getSupplementalMetaData()).values())
|
||||
{
|
||||
middlewareInstanceMetaData.validate(qInstance, this);
|
||||
supplementalInstanceMetaData.validate(qInstance, this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1226,6 +1227,11 @@ public class QInstanceValidator
|
||||
}
|
||||
}
|
||||
|
||||
for(QSupplementalProcessMetaData supplementalProcessMetaData : CollectionUtils.nonNullMap(process.getSupplementalMetaData()).values())
|
||||
{
|
||||
supplementalProcessMetaData.validate(qInstance, process, this);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1703,7 +1709,7 @@ public class QInstanceValidator
|
||||
** But if it throws, add the provided message to the list of errors (and return false,
|
||||
** e.g., in case you need to stop evaluating rules to avoid exceptions).
|
||||
*******************************************************************************/
|
||||
private boolean assertNoException(UnsafeLambda unsafeLambda, String message)
|
||||
public boolean assertNoException(UnsafeLambda unsafeLambda, String message)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -1736,7 +1742,7 @@ public class QInstanceValidator
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void warn(String message)
|
||||
public void warn(String message)
|
||||
{
|
||||
if(printWarnings)
|
||||
{
|
||||
|
@ -22,8 +22,10 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions.processes;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.kingsrook.qqq.backend.core.logging.LogPair;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
@ -76,6 +78,35 @@ public class ProcessSummaryFilterLink implements ProcessSummaryLineInterface
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@JsonIgnore
|
||||
public String getFullText()
|
||||
{
|
||||
StringBuilder rs = new StringBuilder();
|
||||
|
||||
if(StringUtils.hasContent(linkPreText))
|
||||
{
|
||||
rs.append(linkPreText).append(" ");
|
||||
}
|
||||
|
||||
if(StringUtils.hasContent(linkText))
|
||||
{
|
||||
rs.append(linkText).append(" ");
|
||||
}
|
||||
|
||||
if(StringUtils.hasContent(linkPostText))
|
||||
{
|
||||
rs.append(linkPostText).append(" ");
|
||||
}
|
||||
|
||||
rs.deleteCharAt(rs.length() - 1);
|
||||
return (rs.toString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -26,6 +26,7 @@ import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.logging.LogPair;
|
||||
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
@ -395,15 +396,19 @@ public class ProcessSummaryLine implements ProcessSummaryLineInterface
|
||||
{
|
||||
if(count != null)
|
||||
{
|
||||
String baseMessage;
|
||||
if(count.equals(1))
|
||||
{
|
||||
setMessage((isPast ? getSingularPastMessage() : getSingularFutureMessage())
|
||||
+ (messageSuffix == null ? "" : messageSuffix));
|
||||
baseMessage = isPast ? getSingularPastMessage() : getSingularFutureMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
setMessage((isPast ? getPluralPastMessage() : getPluralFutureMessage())
|
||||
+ (messageSuffix == null ? "" : messageSuffix));
|
||||
baseMessage = isPast ? getPluralPastMessage() : getPluralFutureMessage();
|
||||
}
|
||||
|
||||
if(StringUtils.hasContent(baseMessage))
|
||||
{
|
||||
setMessage(baseMessage + ObjectUtils.requireConditionElse(messageSuffix, StringUtils::hasContent, ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,9 @@ package com.kingsrook.qqq.backend.core.model.actions.processes;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.kingsrook.qqq.backend.core.logging.LogPair;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
@ -64,6 +66,35 @@ public class ProcessSummaryRecordLink implements ProcessSummaryLineInterface
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@JsonIgnore
|
||||
public String getFullText()
|
||||
{
|
||||
StringBuilder rs = new StringBuilder();
|
||||
|
||||
if(StringUtils.hasContent(linkPreText))
|
||||
{
|
||||
rs.append(linkPreText).append(" ");
|
||||
}
|
||||
|
||||
if(StringUtils.hasContent(linkText))
|
||||
{
|
||||
rs.append(linkText).append(" ");
|
||||
}
|
||||
|
||||
if(StringUtils.hasContent(linkPostText))
|
||||
{
|
||||
rs.append(linkPostText).append(" ");
|
||||
}
|
||||
|
||||
rs.deleteCharAt(rs.length() - 1);
|
||||
return (rs.toString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -23,6 +23,10 @@ package com.kingsrook.qqq.backend.core.model.actions.processes;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobCallback;
|
||||
@ -31,6 +35,7 @@ import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -183,6 +188,17 @@ public class RunProcessInput extends AbstractActionInput
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RunProcessInput withValue(String fieldName, Serializable value)
|
||||
{
|
||||
this.processState.getValues().put(fieldName, value);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for values
|
||||
**
|
||||
@ -258,7 +274,7 @@ public class RunProcessInput extends AbstractActionInput
|
||||
*******************************************************************************/
|
||||
public String getValueString(String fieldName)
|
||||
{
|
||||
return ((String) getValue(fieldName));
|
||||
return (ValueUtils.getValueAsString(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
@ -269,7 +285,67 @@ public class RunProcessInput extends AbstractActionInput
|
||||
*******************************************************************************/
|
||||
public Integer getValueInteger(String fieldName)
|
||||
{
|
||||
return ((Integer) getValue(fieldName));
|
||||
return (ValueUtils.getValueAsInteger(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BigDecimal getValueBigDecimal(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsBigDecimal(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Boolean getValueBoolean(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsBoolean(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public LocalTime getValueLocalTime(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsLocalTime(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public LocalDate getValueLocalDate(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsLocalDate(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public byte[] getValueByteArray(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsByteArray(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getValueInstant(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsInstant(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
@ -23,11 +23,16 @@ package com.kingsrook.qqq.backend.core.model.actions.processes;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -122,6 +127,99 @@ public class RunProcessOutput extends AbstractActionOutput implements Serializab
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for a single field's value
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable getValue(String fieldName)
|
||||
{
|
||||
return (this.processState.getValues().get(fieldName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for a single field's value
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getValueString(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsString(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for a single field's value
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getValueInteger(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsInteger(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BigDecimal getValueBigDecimal(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsBigDecimal(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Boolean getValueBoolean(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsBoolean(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public LocalTime getValueLocalTime(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsLocalTime(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public LocalDate getValueLocalDate(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsLocalDate(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public byte[] getValueByteArray(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsByteArray(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getValueInstant(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsInstant(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for values
|
||||
**
|
||||
@ -133,6 +231,17 @@ public class RunProcessOutput extends AbstractActionOutput implements Serializab
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RunProcessOutput withValue(String fieldName, Serializable value)
|
||||
{
|
||||
this.processState.getValues().put(fieldName, value);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for values
|
||||
**
|
||||
|
@ -91,7 +91,7 @@ public class QInstance
|
||||
private Map<String, QQueueProviderMetaData> queueProviders = new LinkedHashMap<>();
|
||||
private Map<String, QQueueMetaData> queues = new LinkedHashMap<>();
|
||||
|
||||
private Map<String, QMiddlewareInstanceMetaData> middlewareMetaData = new LinkedHashMap<>();
|
||||
private Map<String, QSupplementalInstanceMetaData> supplementalMetaData = new LinkedHashMap<>();
|
||||
|
||||
private Map<String, String> environmentValues = new LinkedHashMap<>();
|
||||
private String defaultTimeZoneId = "UTC";
|
||||
@ -1083,60 +1083,60 @@ public class QInstance
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for middlewareMetaData
|
||||
** Getter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public Map<String, QMiddlewareInstanceMetaData> getMiddlewareMetaData()
|
||||
public Map<String, QSupplementalInstanceMetaData> getSupplementalMetaData()
|
||||
{
|
||||
return (this.middlewareMetaData);
|
||||
return (this.supplementalMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for middlewareMetaData
|
||||
** Getter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public QMiddlewareInstanceMetaData getMiddlewareMetaData(String type)
|
||||
public QSupplementalInstanceMetaData getSupplementalMetaData(String type)
|
||||
{
|
||||
if(this.middlewareMetaData == null)
|
||||
if(this.supplementalMetaData == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
return this.middlewareMetaData.get(type);
|
||||
return this.supplementalMetaData.get(type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for middlewareMetaData
|
||||
** Setter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public void setMiddlewareMetaData(Map<String, QMiddlewareInstanceMetaData> middlewareMetaData)
|
||||
public void setSupplementalMetaData(Map<String, QSupplementalInstanceMetaData> supplementalMetaData)
|
||||
{
|
||||
this.middlewareMetaData = middlewareMetaData;
|
||||
this.supplementalMetaData = supplementalMetaData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for middlewareMetaData
|
||||
** Fluent setter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public QInstance withMiddlewareMetaData(Map<String, QMiddlewareInstanceMetaData> middlewareMetaData)
|
||||
public QInstance withSupplementalMetaData(Map<String, QSupplementalInstanceMetaData> supplementalMetaData)
|
||||
{
|
||||
this.middlewareMetaData = middlewareMetaData;
|
||||
this.supplementalMetaData = supplementalMetaData;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for middlewareMetaData
|
||||
** Fluent setter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public QInstance withMiddlewareMetaData(QMiddlewareInstanceMetaData middlewareMetaData)
|
||||
public QInstance withSupplementalMetaData(QSupplementalInstanceMetaData supplementalMetaData)
|
||||
{
|
||||
if(this.middlewareMetaData == null)
|
||||
if(this.supplementalMetaData == null)
|
||||
{
|
||||
this.middlewareMetaData = new HashMap<>();
|
||||
this.supplementalMetaData = new HashMap<>();
|
||||
}
|
||||
this.middlewareMetaData.put(middlewareMetaData.getType(), middlewareMetaData);
|
||||
this.supplementalMetaData.put(supplementalMetaData.getType(), supplementalMetaData);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -27,9 +27,10 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Base-class for instance-level meta-data defined for a specific middleware.
|
||||
** Base-class for instance-level meta-data defined by some supplemental module, etc,
|
||||
** outside of qqq core
|
||||
*******************************************************************************/
|
||||
public abstract class QMiddlewareInstanceMetaData
|
||||
public abstract class QSupplementalInstanceMetaData
|
||||
{
|
||||
protected String type;
|
||||
|
||||
@ -58,7 +59,7 @@ public abstract class QMiddlewareInstanceMetaData
|
||||
/*******************************************************************************
|
||||
** Fluent setter for type
|
||||
*******************************************************************************/
|
||||
public QMiddlewareInstanceMetaData withType(String type)
|
||||
public QSupplementalInstanceMetaData withType(String type)
|
||||
{
|
||||
this.type = type;
|
||||
return (this);
|
@ -85,7 +85,7 @@ public class QFieldMetaData implements Cloneable
|
||||
|
||||
private List<FieldAdornment> adornments;
|
||||
|
||||
private Map<String, QMiddlewareFieldMetaData> middlewareMetaData;
|
||||
private Map<String, QSupplementalFieldMetaData> supplementalMetaData;
|
||||
|
||||
|
||||
|
||||
@ -840,60 +840,60 @@ public class QFieldMetaData implements Cloneable
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for middlewareMetaData
|
||||
** Getter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public Map<String, QMiddlewareFieldMetaData> getMiddlewareMetaData()
|
||||
public Map<String, QSupplementalFieldMetaData> getSupplementalMetaData()
|
||||
{
|
||||
return (this.middlewareMetaData);
|
||||
return (this.supplementalMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for middlewareMetaData
|
||||
** Getter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public QMiddlewareFieldMetaData getMiddlewareMetaData(String type)
|
||||
public QSupplementalFieldMetaData getSupplementalMetaData(String type)
|
||||
{
|
||||
if(this.middlewareMetaData == null)
|
||||
if(this.supplementalMetaData == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
return this.middlewareMetaData.get(type);
|
||||
return this.supplementalMetaData.get(type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for middlewareMetaData
|
||||
** Setter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public void setMiddlewareMetaData(Map<String, QMiddlewareFieldMetaData> middlewareMetaData)
|
||||
public void setSupplementalMetaData(Map<String, QSupplementalFieldMetaData> supplementalMetaData)
|
||||
{
|
||||
this.middlewareMetaData = middlewareMetaData;
|
||||
this.supplementalMetaData = supplementalMetaData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for middlewareMetaData
|
||||
** Fluent setter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public QFieldMetaData withMiddlewareMetaData(Map<String, QMiddlewareFieldMetaData> middlewareMetaData)
|
||||
public QFieldMetaData withSupplementalMetaData(Map<String, QSupplementalFieldMetaData> supplementalMetaData)
|
||||
{
|
||||
this.middlewareMetaData = middlewareMetaData;
|
||||
this.supplementalMetaData = supplementalMetaData;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for middlewareMetaData
|
||||
** Fluent setter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public QFieldMetaData withMiddlewareMetaData(QMiddlewareFieldMetaData middlewareMetaData)
|
||||
public QFieldMetaData withSupplementalMetaData(QSupplementalFieldMetaData supplementalMetaData)
|
||||
{
|
||||
if(this.middlewareMetaData == null)
|
||||
if(this.supplementalMetaData == null)
|
||||
{
|
||||
this.middlewareMetaData = new HashMap<>();
|
||||
this.supplementalMetaData = new HashMap<>();
|
||||
}
|
||||
this.middlewareMetaData.put(middlewareMetaData.getType(), middlewareMetaData);
|
||||
this.supplementalMetaData.put(supplementalMetaData.getType(), supplementalMetaData);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -23,9 +23,10 @@ package com.kingsrook.qqq.backend.core.model.metadata.fields;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Base-class for field-level meta-data defined for a specific middleware.
|
||||
** Base-class for field-level meta-data defined by some supplemental module, etc,
|
||||
** outside of qqq core
|
||||
*******************************************************************************/
|
||||
public abstract class QMiddlewareFieldMetaData
|
||||
public abstract class QSupplementalFieldMetaData
|
||||
{
|
||||
protected String type;
|
||||
|
||||
@ -54,7 +55,7 @@ public abstract class QMiddlewareFieldMetaData
|
||||
/*******************************************************************************
|
||||
** Fluent setter for type
|
||||
*******************************************************************************/
|
||||
public QMiddlewareFieldMetaData withType(String type)
|
||||
public QSupplementalFieldMetaData withType(String type)
|
||||
{
|
||||
this.type = type;
|
||||
return (this);
|
@ -54,6 +54,9 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
||||
private BasepullConfiguration basepullConfiguration;
|
||||
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 Map<String, QStepMetaData> steps; // this is the full map of possible steps
|
||||
|
||||
@ -61,6 +64,8 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
||||
|
||||
private QScheduleMetaData schedule;
|
||||
|
||||
private Map<String, QSupplementalProcessMetaData> supplementalMetaData;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -544,4 +549,126 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
||||
qInstance.addProcess(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public Map<String, QSupplementalProcessMetaData> getSupplementalMetaData()
|
||||
{
|
||||
return (this.supplementalMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public QSupplementalProcessMetaData getSupplementalMetaData(String type)
|
||||
{
|
||||
if(this.supplementalMetaData == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
return this.supplementalMetaData.get(type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public void setSupplementalMetaData(Map<String, QSupplementalProcessMetaData> supplementalMetaData)
|
||||
{
|
||||
this.supplementalMetaData = supplementalMetaData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public QProcessMetaData withSupplementalMetaData(Map<String, QSupplementalProcessMetaData> supplementalMetaData)
|
||||
{
|
||||
this.supplementalMetaData = supplementalMetaData;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public QProcessMetaData withSupplementalMetaData(QSupplementalProcessMetaData supplementalMetaData)
|
||||
{
|
||||
if(this.supplementalMetaData == null)
|
||||
{
|
||||
this.supplementalMetaData = new HashMap<>();
|
||||
}
|
||||
this.supplementalMetaData.put(supplementalMetaData.getType(), supplementalMetaData);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.processes;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Base-class for process-level meta-data defined by some supplemental module, etc,
|
||||
** outside of qqq core
|
||||
*******************************************************************************/
|
||||
public abstract class QSupplementalProcessMetaData
|
||||
{
|
||||
protected String type;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for type
|
||||
*******************************************************************************/
|
||||
public String getType()
|
||||
{
|
||||
return (this.type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for type
|
||||
*******************************************************************************/
|
||||
public void setType(String type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for type
|
||||
*******************************************************************************/
|
||||
public QSupplementalProcessMetaData withType(String type)
|
||||
{
|
||||
this.type = type;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void enrich(QInstanceEnricher qInstanceEnricher, QProcessMetaData process)
|
||||
{
|
||||
////////////////////////
|
||||
// noop in base class //
|
||||
////////////////////////
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void validate(QInstance qInstance, QProcessMetaData process, QInstanceValidator qInstanceValidator)
|
||||
{
|
||||
////////////////////////
|
||||
// noop in base class //
|
||||
////////////////////////
|
||||
}
|
||||
}
|
@ -23,9 +23,10 @@ package com.kingsrook.qqq.backend.core.model.metadata.tables;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Base-class for table-level meta-data defined for a specific middleware.
|
||||
** Base-class for table-level meta-data defined by some supplemental module, etc,
|
||||
** outside of qqq core
|
||||
*******************************************************************************/
|
||||
public abstract class QMiddlewareTableMetaData
|
||||
public abstract class QSupplementalTableMetaData
|
||||
{
|
||||
protected String type;
|
||||
|
||||
@ -54,7 +55,7 @@ public abstract class QMiddlewareTableMetaData
|
||||
/*******************************************************************************
|
||||
** Fluent setter for type
|
||||
*******************************************************************************/
|
||||
public QMiddlewareTableMetaData withType(String type)
|
||||
public QSupplementalTableMetaData withType(String type)
|
||||
{
|
||||
this.type = type;
|
||||
return (this);
|
@ -99,7 +99,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
|
||||
private CacheOf cacheOf;
|
||||
|
||||
private Map<String, QMiddlewareTableMetaData> middlewareMetaData;
|
||||
private Map<String, QSupplementalTableMetaData> supplementalMetaData;
|
||||
|
||||
private List<ExposedJoin> exposedJoins;
|
||||
|
||||
@ -1189,60 +1189,60 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for middlewareMetaData
|
||||
** Getter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public Map<String, QMiddlewareTableMetaData> getMiddlewareMetaData()
|
||||
public Map<String, QSupplementalTableMetaData> getSupplementalMetaData()
|
||||
{
|
||||
return (this.middlewareMetaData);
|
||||
return (this.supplementalMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for middlewareMetaData
|
||||
** Getter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public QMiddlewareTableMetaData getMiddlewareMetaData(String type)
|
||||
public QSupplementalTableMetaData getSupplementalMetaData(String type)
|
||||
{
|
||||
if(this.middlewareMetaData == null)
|
||||
if(this.supplementalMetaData == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
return this.middlewareMetaData.get(type);
|
||||
return this.supplementalMetaData.get(type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for middlewareMetaData
|
||||
** Setter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public void setMiddlewareMetaData(Map<String, QMiddlewareTableMetaData> middlewareMetaData)
|
||||
public void setSupplementalMetaData(Map<String, QSupplementalTableMetaData> supplementalMetaData)
|
||||
{
|
||||
this.middlewareMetaData = middlewareMetaData;
|
||||
this.supplementalMetaData = supplementalMetaData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for middlewareMetaData
|
||||
** Fluent setter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withMiddlewareMetaData(Map<String, QMiddlewareTableMetaData> middlewareMetaData)
|
||||
public QTableMetaData withSupplementalMetaData(Map<String, QSupplementalTableMetaData> supplementalMetaData)
|
||||
{
|
||||
this.middlewareMetaData = middlewareMetaData;
|
||||
this.supplementalMetaData = supplementalMetaData;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for middlewareMetaData
|
||||
** Fluent setter for supplementalMetaData
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withMiddlewareMetaData(QMiddlewareTableMetaData middlewareMetaData)
|
||||
public QTableMetaData withSupplementalMetaData(QSupplementalTableMetaData supplementalMetaData)
|
||||
{
|
||||
if(this.middlewareMetaData == null)
|
||||
if(this.supplementalMetaData == null)
|
||||
{
|
||||
this.middlewareMetaData = new HashMap<>();
|
||||
this.supplementalMetaData = new HashMap<>();
|
||||
}
|
||||
this.middlewareMetaData.put(middlewareMetaData.getType(), middlewareMetaData);
|
||||
this.supplementalMetaData.put(supplementalMetaData.getType(), supplementalMetaData);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -102,6 +102,8 @@ public class ScriptsMetaDataProvider
|
||||
{
|
||||
return (new QProcessMetaData()
|
||||
.withName(STORE_SCRIPT_REVISION_PROCESS_NAME)
|
||||
.withTableName(Script.TABLE_NAME)
|
||||
.withIsHidden(true)
|
||||
.withStepList(List.of(
|
||||
new QBackendStepMetaData()
|
||||
.withName("main")
|
||||
@ -118,6 +120,8 @@ public class ScriptsMetaDataProvider
|
||||
{
|
||||
return (new QProcessMetaData()
|
||||
.withName(TEST_SCRIPT_PROCESS_NAME)
|
||||
.withTableName(Script.TABLE_NAME)
|
||||
.withIsHidden(true)
|
||||
.withStepList(List.of(
|
||||
new QBackendStepMetaData()
|
||||
.withName("main")
|
||||
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.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)));
|
||||
}
|
||||
|
||||
throw (new QException("Could not find query filter for Extract step."));
|
||||
throw (new CouldNotFindQueryFilterForExtractStepException("Could not find query filter for Extract step."));
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,6 +27,7 @@ import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncRecordPipeLoop;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
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.RunBackendStepInput;
|
||||
@ -71,11 +72,14 @@ public class StreamedETLPreviewStep extends BaseStreamedETLStep implements Backe
|
||||
return;
|
||||
}
|
||||
|
||||
if(runBackendStepInput.getFrontendStepBehavior() != null && runBackendStepInput.getFrontendStepBehavior().equals(RunProcessInput.FrontendStepBehavior.SKIP))
|
||||
{
|
||||
LOG.debug("Skipping preview because frontend behavior is [" + RunProcessInput.FrontendStepBehavior.SKIP + "].");
|
||||
return;
|
||||
}
|
||||
//////////////////////////////
|
||||
// set up the extract steps //
|
||||
//////////////////////////////
|
||||
AbstractExtractStep extractStep = getExtractStep(runBackendStepInput);
|
||||
RecordPipe recordPipe = new RecordPipe();
|
||||
extractStep.setLimit(limit);
|
||||
extractStep.setRecordPipe(recordPipe);
|
||||
extractStep.preRun(runBackendStepInput, runBackendStepOutput);
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// if we're running inside an automation, then skip this step. //
|
||||
@ -86,17 +90,26 @@ public class StreamedETLPreviewStep extends BaseStreamedETLStep implements Backe
|
||||
return;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// set up the extract & transform steps //
|
||||
//////////////////////////////////////////
|
||||
AbstractExtractStep extractStep = getExtractStep(runBackendStepInput);
|
||||
RecordPipe recordPipe = new RecordPipe();
|
||||
extractStep.setLimit(limit);
|
||||
extractStep.setRecordPipe(recordPipe);
|
||||
extractStep.preRun(runBackendStepInput, runBackendStepOutput);
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if skipping frontend steps, skip this action - //
|
||||
// but, if inside an (ideally, only async) API call, at least do the count, so status calls can get x of y status //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(RunProcessInput.FrontendStepBehavior.SKIP.equals(runBackendStepInput.getFrontendStepBehavior()))
|
||||
{
|
||||
if(QContext.getQSession().getValue("apiVersion") != null)
|
||||
{
|
||||
countRecords(runBackendStepInput, runBackendStepOutput, extractStep);
|
||||
}
|
||||
|
||||
LOG.debug("Skipping preview because frontend behavior is [" + RunProcessInput.FrontendStepBehavior.SKIP + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
countRecords(runBackendStepInput, runBackendStepOutput, extractStep);
|
||||
|
||||
//////////////////////////////
|
||||
// setup the transform step //
|
||||
//////////////////////////////
|
||||
AbstractTransformStep transformStep = getTransformStep(runBackendStepInput);
|
||||
transformStep.preRun(runBackendStepInput, runBackendStepOutput);
|
||||
|
||||
|
@ -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,7 +22,9 @@
|
||||
package com.kingsrook.qqq.backend.core.utils;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
@ -88,4 +90,39 @@ public class ExceptionUtils
|
||||
|
||||
return (root);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static String concatenateMessagesFromChain(Exception exception)
|
||||
{
|
||||
if(exception == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
List<String> messages = new ArrayList<>();
|
||||
Throwable root = exception;
|
||||
Set<Throwable> seen = new HashSet<>();
|
||||
|
||||
do
|
||||
{
|
||||
if(StringUtils.hasContent(root.getMessage()))
|
||||
{
|
||||
messages.add(root.getMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
messages.add(root.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
seen.add(root);
|
||||
root = root.getCause();
|
||||
}
|
||||
while(root != null && !seen.contains(root));
|
||||
|
||||
return (StringUtils.join("; ", messages));
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,9 @@
|
||||
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;
|
||||
|
||||
|
||||
@ -96,4 +99,44 @@ public class ObjectUtils
|
||||
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:
|
||||
** 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;
|
||||
}
|
||||
@ -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()));
|
||||
}
|
||||
@ -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);
|
||||
return (this);
|
||||
@ -77,7 +77,7 @@ public class MapBuilder<K, V>
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<K, V> build()
|
||||
public M build()
|
||||
{
|
||||
return (this.map);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
|
||||
@ -88,6 +89,33 @@ class ExceptionUtilsTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testConcatenateMessagesFromChain()
|
||||
{
|
||||
assertNull(ExceptionUtils.concatenateMessagesFromChain(null));
|
||||
assertEquals("QException", ExceptionUtils.concatenateMessagesFromChain(new QException((String) null)));
|
||||
assertEquals("QException", ExceptionUtils.concatenateMessagesFromChain(new QException("")));
|
||||
assertEquals("foo; bar", ExceptionUtils.concatenateMessagesFromChain(new QException("foo", new QException("bar"))));
|
||||
assertEquals("foo; QException; bar", ExceptionUtils.concatenateMessagesFromChain(new QException("foo", new QException(null, new QException("bar")))));
|
||||
|
||||
MyException selfCaused = new MyException("selfCaused");
|
||||
selfCaused.setCause(selfCaused);
|
||||
assertEquals("selfCaused", ExceptionUtils.concatenateMessagesFromChain(selfCaused));
|
||||
|
||||
MyException cycle1 = new MyException("cycle1");
|
||||
MyException cycle2 = new MyException("cycle2");
|
||||
cycle1.setCause(cycle2);
|
||||
cycle2.setCause(cycle1);
|
||||
|
||||
assertEquals("cycle1; cycle2", ExceptionUtils.concatenateMessagesFromChain(cycle1));
|
||||
assertEquals("cycle2; cycle1", ExceptionUtils.concatenateMessagesFromChain(cycle2));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Test exception class - lets you set the cause, easier to create a loop.
|
||||
*******************************************************************************/
|
||||
@ -97,6 +125,9 @@ class ExceptionUtilsTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public MyException(String message)
|
||||
{
|
||||
super(message);
|
||||
@ -104,6 +135,9 @@ class ExceptionUtilsTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public MyException(Throwable cause)
|
||||
{
|
||||
super(cause);
|
||||
@ -111,6 +145,9 @@ class ExceptionUtilsTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCause(Throwable cause)
|
||||
{
|
||||
myCause = cause;
|
||||
|
@ -78,7 +78,7 @@ class MapBuilderTest
|
||||
@Test
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ package com.kingsrook.qqq.api;
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface ApiMiddlewareType
|
||||
public interface ApiSupplementType
|
||||
{
|
||||
String NAME = "api";
|
||||
|
@ -25,19 +25,39 @@ package com.kingsrook.qqq.api.actions;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.kingsrook.qqq.api.javalin.QBadRequestException;
|
||||
import com.kingsrook.qqq.api.model.APIVersion;
|
||||
import com.kingsrook.qqq.api.model.actions.HttpApiResponse;
|
||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
||||
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.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.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.PreRunApiProcessCustomizer;
|
||||
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaData;
|
||||
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaDataContainer;
|
||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobManager;
|
||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobState;
|
||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobStatus;
|
||||
import com.kingsrook.qqq.backend.core.actions.async.JobGoingAsyncException;
|
||||
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.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.tables.CountAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
@ -49,6 +69,9 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
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.actions.processes.ProcessState;
|
||||
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.actions.tables.QInputSource;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||
@ -67,8 +90,10 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
|
||||
import com.kingsrook.qqq.backend.core.model.statusmessages.NotFoundStatusMessage;
|
||||
@ -76,7 +101,9 @@ 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.QStatusMessage;
|
||||
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.ExceptionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.Pair;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
@ -87,6 +114,7 @@ import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
import static com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator.IN;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -96,9 +124,9 @@ public class ApiImplementation
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(ApiImplementation.class);
|
||||
|
||||
/////////////////////////////////////
|
||||
// key: Pair<apiName, apiVersion> //
|
||||
/////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// key: Pair<apiName, apiVersion>, value: Map<name => metaData> //
|
||||
///////////////////////////////////////////////////////////////////
|
||||
private static Map<Pair<String, String>, Map<String, QTableMetaData>> tableApiNameMap = new HashMap<>();
|
||||
|
||||
|
||||
@ -286,7 +314,7 @@ public class ApiImplementation
|
||||
}
|
||||
else
|
||||
{
|
||||
throw (new QBadRequestException("Request failed with " + badRequestMessages.size() + " reasons: " + StringUtils.join(" \n", badRequestMessages)));
|
||||
throw (new QBadRequestException("Request failed with " + badRequestMessages.size() + " reasons: " + StringUtils.join("\n", badRequestMessages)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -896,6 +924,291 @@ public class ApiImplementation
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static HttpApiResponse runProcess(ApiInstanceMetaData apiInstanceMetaData, String version, String processApiName, Map<String, String> paramMap) throws QException
|
||||
{
|
||||
Pair<ApiProcessMetaData, QProcessMetaData> pair = ApiProcessUtils.getProcessMetaDataPair(apiInstanceMetaData, version, processApiName);
|
||||
|
||||
ApiProcessMetaData apiProcessMetaData = pair.getA();
|
||||
QProcessMetaData process = pair.getB();
|
||||
String processName = process.getName();
|
||||
|
||||
List<String> badRequestMessages = new ArrayList<>();
|
||||
|
||||
String processUUID = UUID.randomUUID().toString();
|
||||
|
||||
RunProcessInput runProcessInput = new RunProcessInput();
|
||||
runProcessInput.setProcessName(processName);
|
||||
runProcessInput.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.SKIP);
|
||||
runProcessInput.setProcessUUID(processUUID);
|
||||
// todo i don't think runProcessInput.setAsyncJobCallback();
|
||||
|
||||
//////////////////////
|
||||
// map input values //
|
||||
//////////////////////
|
||||
ApiProcessInput apiProcessInput = apiProcessMetaData.getInput();
|
||||
if(apiProcessInput != null)
|
||||
{
|
||||
processProcessInputFields(paramMap, badRequestMessages, runProcessInput, apiProcessInput.getQueryStringParams());
|
||||
processProcessInputFields(paramMap, badRequestMessages, runProcessInput, apiProcessInput.getFormParams());
|
||||
processProcessInputFields(paramMap, badRequestMessages, runProcessInput, apiProcessInput.getObjectBodyParams());
|
||||
|
||||
if(apiProcessInput.getBodyField() != null)
|
||||
{
|
||||
processSingleProcessInputField(apiProcessInput.getBodyField(), paramMap, badRequestMessages, runProcessInput);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// 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(",");
|
||||
|
||||
QTableMetaData table = QContext.getQInstance().getTable(process.getTableName());
|
||||
QQueryFilter filter = new QQueryFilter(new QFilterCriteria(table.getPrimaryKeyField(), IN, Arrays.asList(ids)));
|
||||
runProcessInput.setCallback(getCallback(filter));
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
// throw if bad inputs have been noted //
|
||||
/////////////////////////////////////////
|
||||
if(!badRequestMessages.isEmpty())
|
||||
{
|
||||
if(badRequestMessages.size() == 1)
|
||||
{
|
||||
throw (new QBadRequestException(badRequestMessages.get(0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw (new QBadRequestException("Request failed with " + badRequestMessages.size() + " reasons: " + StringUtils.join("\n", badRequestMessages)));
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
// run pre-customizer, if there is one //
|
||||
/////////////////////////////////////////
|
||||
Map<String, QCodeReference> customizers = apiProcessMetaData.getCustomizers();
|
||||
if(customizers != null && customizers.containsKey(ApiProcessCustomizers.PRE_RUN.getRole()))
|
||||
{
|
||||
PreRunApiProcessCustomizer preRunCustomizer = QCodeLoader.getAdHoc(PreRunApiProcessCustomizer.class, customizers.get(ApiProcessCustomizers.PRE_RUN.getRole()));
|
||||
preRunCustomizer.preApiRun(runProcessInput);
|
||||
}
|
||||
|
||||
boolean async = false;
|
||||
if(ApiProcessMetaData.AsyncMode.ALWAYS.equals(apiProcessMetaData.getAsyncMode())
|
||||
|| (ApiProcessMetaData.AsyncMode.OPTIONAL.equals(apiProcessMetaData.getAsyncMode()) && "true".equalsIgnoreCase(paramMap.get("async"))))
|
||||
{
|
||||
async = true;
|
||||
}
|
||||
|
||||
if(async)
|
||||
{
|
||||
try
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// note - in other implementations, the process gets its own UUID (for process state to be stashed) //
|
||||
// and the job gets its own (where we check in on running/complete). //
|
||||
// but in this implementation, we want to just pass back one UUID to the caller, so make the job //
|
||||
// manager use the process's uuid as the job uuid, and all will be revealed! //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// todo? to help w/ StreamedETLPreview "should i count?" runProcessInput.setIsAsync(true);
|
||||
new AsyncJobManager().withForcedJobUUID(processUUID).startJob(processName, 0, TimeUnit.MILLISECONDS, (callback) ->
|
||||
{
|
||||
runProcessInput.setAsyncJobCallback(callback);
|
||||
return (new RunProcessAction().execute(runProcessInput));
|
||||
});
|
||||
}
|
||||
catch(JobGoingAsyncException jgae)
|
||||
{
|
||||
LinkedHashMap<String, Object> response = new LinkedHashMap<>();
|
||||
response.put("jobId", jgae.getJobUUID());
|
||||
return (new HttpApiResponse(HttpStatus.Code.ACCEPTED, response));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// passing 0 as the timeout to startJob *should* make it always throw the JGAE. But, //
|
||||
// in case it didn't, we don't have a uuid to return to the caller, so that's a fail. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
throw (new QException("Error starting asynchronous job - no job id was returned."));
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// run the process //
|
||||
/////////////////////
|
||||
RunProcessOutput runProcessOutput;
|
||||
|
||||
try
|
||||
{
|
||||
RunProcessAction runProcessAction = new RunProcessAction();
|
||||
runProcessOutput = runProcessAction.execute(runProcessInput);
|
||||
}
|
||||
catch(CouldNotFindQueryFilterForExtractStepException e)
|
||||
{
|
||||
throw (new QBadRequestException("Records to run through this process were not specified."));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
String concatenation = ExceptionUtils.concatenateMessagesFromChain(e);
|
||||
throw (new QException(concatenation, e));
|
||||
}
|
||||
|
||||
return (buildResponseAfterProcess(apiProcessMetaData, runProcessInput, runProcessOutput));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static HttpApiResponse buildResponseAfterProcess(ApiProcessMetaData apiProcessMetaData, RunProcessInput runProcessInput, RunProcessOutput runProcessOutput) throws QException
|
||||
{
|
||||
//////////////////////////////////////////
|
||||
// run post-customizer, if there is one //
|
||||
//////////////////////////////////////////
|
||||
Map<String, QCodeReference> customizers = apiProcessMetaData.getCustomizers();
|
||||
if(customizers != null && customizers.containsKey(ApiProcessCustomizers.POST_RUN.getRole()))
|
||||
{
|
||||
PostRunApiProcessCustomizer postRunCustomizer = QCodeLoader.getAdHoc(PostRunApiProcessCustomizer.class, customizers.get(ApiProcessCustomizers.POST_RUN.getRole()));
|
||||
postRunCustomizer.postApiRun(runProcessInput, runProcessOutput);
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// map output values //
|
||||
///////////////////////
|
||||
ApiProcessOutputInterface output = apiProcessMetaData.getOutput();
|
||||
if(output != null)
|
||||
{
|
||||
return (new HttpApiResponse(output.getSuccessStatusCode(runProcessInput, runProcessOutput), output.getOutputForProcess(runProcessInput, runProcessOutput)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return (new HttpApiResponse(HttpStatus.Code.NO_CONTENT, ""));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
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()))
|
||||
{
|
||||
processSingleProcessInputField(inputField, paramMap, badRequestMessages, runProcessInput);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void processSingleProcessInputField(QFieldMetaData inputField, Map<String, String> paramMap, List<String> badRequestMessages, RunProcessInput runProcessInput)
|
||||
{
|
||||
String value = paramMap.get(inputField.getName());
|
||||
|
||||
if(!StringUtils.hasContent(value) && inputField.getDefaultValue() != null)
|
||||
{
|
||||
value = ValueUtils.getValueAsString(inputField.getDefaultValue());
|
||||
}
|
||||
|
||||
if(!StringUtils.hasContent(value) && inputField.getIsRequired())
|
||||
{
|
||||
badRequestMessages.add("Missing value for required input field " + inputField.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
// todo - types?
|
||||
|
||||
runProcessInput.addValue(inputField.getName(), value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static HttpApiResponse getProcessStatus(ApiInstanceMetaData apiInstanceMetaData, String version, String apiProcessName, String jobUUID) throws QException
|
||||
{
|
||||
Optional<AsyncJobStatus> optionalJobStatus = new AsyncJobManager().getJobStatus(jobUUID);
|
||||
if(optionalJobStatus.isEmpty())
|
||||
{
|
||||
throw (new QException("Could not find status of process job: " + jobUUID));
|
||||
}
|
||||
|
||||
AsyncJobStatus jobStatus = optionalJobStatus.get();
|
||||
|
||||
// resultForCaller.put("jobStatus", jobStatus);
|
||||
LOG.debug("Job status is " + jobStatus.getState() + " for " + jobUUID);
|
||||
|
||||
if(jobStatus.getState().equals(AsyncJobState.COMPLETE))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the job is complete, get the process result from state provider, and return it //
|
||||
// this output should look like it did if the job finished synchronously!! //
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
Optional<ProcessState> processState = RunProcessAction.getState(jobUUID);
|
||||
if(processState.isPresent())
|
||||
{
|
||||
RunProcessOutput runProcessOutput = new RunProcessOutput(processState.get());
|
||||
RunProcessInput runProcessInput = new RunProcessInput();
|
||||
runProcessInput.seedFromProcessState(processState.get());
|
||||
|
||||
Pair<ApiProcessMetaData, QProcessMetaData> pair = ApiProcessUtils.getProcessMetaDataPair(apiInstanceMetaData, version, apiProcessName);
|
||||
|
||||
ApiProcessMetaData apiProcessMetaData = pair.getA();
|
||||
return (buildResponseAfterProcess(apiProcessMetaData, runProcessInput, runProcessOutput));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw (new QException("Could not find results for completed of process job: " + jobUUID));
|
||||
}
|
||||
}
|
||||
else if(jobStatus.getState().equals(AsyncJobState.ERROR))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the job had an error (e.g., a process step threw), "nicely" serialize its exception for the caller //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(jobStatus.getCaughtException() != null)
|
||||
{
|
||||
throw (new QException(jobStatus.getCaughtException()));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw (new QException("Job failed with an unspecified error."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LinkedHashMap<String, Object> response = new LinkedHashMap<>();
|
||||
response.put("jobId", jobUUID);
|
||||
response.put("message", jobStatus.getMessage());
|
||||
if(jobStatus.getCurrent() != null && jobStatus.getTotal() != null)
|
||||
{
|
||||
response.put("current", jobStatus.getCurrent());
|
||||
response.put("total", jobStatus.getTotal());
|
||||
}
|
||||
return (new HttpApiResponse(HttpStatus.Code.ACCEPTED, response));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -1078,14 +1391,14 @@ public class ApiImplementation
|
||||
ApiTableMetaDataContainer apiTableMetaDataContainer = ApiTableMetaDataContainer.of(table);
|
||||
if(apiTableMetaDataContainer == null)
|
||||
{
|
||||
LOG.info("404 because table apiMetaDataContainer is null", logPairs);
|
||||
LOG.info("404 because table apiTableMetaDataContainer is null", logPairs);
|
||||
throw (new QNotFoundException("Could not find a table named " + tableApiName + " in this api."));
|
||||
}
|
||||
|
||||
ApiTableMetaData apiTableMetaData = apiTableMetaDataContainer.getApiTableMetaData(apiInstanceMetaData.getName());
|
||||
if(apiTableMetaData == null)
|
||||
{
|
||||
LOG.info("404 because table apiMetaData is null", logPairs);
|
||||
LOG.info("404 because table apiTableMetaData is null", logPairs);
|
||||
throw (new QNotFoundException("Could not find a table named " + tableApiName + " in this api."));
|
||||
}
|
||||
|
||||
@ -1185,4 +1498,29 @@ public class ApiImplementation
|
||||
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,13 @@ import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
||||
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.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.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.tables.ApiTableMetaData;
|
||||
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaDataContainer;
|
||||
import com.kingsrook.qqq.api.model.openapi.Components;
|
||||
@ -73,16 +80,19 @@ 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.QPossibleValueSource;
|
||||
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.Capability;
|
||||
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.JsonUtils;
|
||||
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.YamlUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.HttpStatus;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
|
||||
@ -217,7 +227,8 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
throw new QException("Missing required input: version");
|
||||
}
|
||||
|
||||
if(!apiInstanceMetaData.getSupportedVersions().contains(new APIVersion(version)))
|
||||
APIVersion apiVersion = new APIVersion(version);
|
||||
if(!apiInstanceMetaData.getSupportedVersions().contains(apiVersion))
|
||||
{
|
||||
throw (new QException("[" + version + "] is not a supported API Version."));
|
||||
}
|
||||
@ -288,9 +299,12 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
.withDescription("Requested result page size")
|
||||
)));
|
||||
|
||||
///////////////////
|
||||
// foreach table //
|
||||
///////////////////
|
||||
List<Tag> tagList = new ArrayList<>();
|
||||
Set<String> usedProcessNames = new HashSet<>();
|
||||
|
||||
/////////////////////////////////////
|
||||
// foreach table (sorted by label) //
|
||||
/////////////////////////////////////
|
||||
List<QTableMetaData> tables = new ArrayList<>(qInstance.getTables().values());
|
||||
tables.sort(Comparator.comparing(t -> ObjectUtils.requireNonNullElse(t.getLabel(), t.getName(), "")));
|
||||
for(QTableMetaData table : tables)
|
||||
@ -330,7 +344,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
}
|
||||
|
||||
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 + "]");
|
||||
continue;
|
||||
@ -355,9 +369,11 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
boolean deleteEnabled = ApiOperation.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;
|
||||
}
|
||||
|
||||
@ -374,6 +390,10 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
String primaryKeyApiName = ApiFieldMetaData.getEffectiveApiFieldName(apiName, primaryKeyField);
|
||||
List<QFieldMetaData> tableApiFields = new GetTableApiFieldsAction().execute(new GetTableApiFieldsInput().withTableName(tableName).withVersion(version).withApiName(apiName)).getFields();
|
||||
|
||||
tagList.add(new Tag()
|
||||
.withName(tableLabel)
|
||||
.withDescription("Operations on the " + tableLabel + " table."));
|
||||
|
||||
///////////////////////////////
|
||||
// permissions for the table //
|
||||
///////////////////////////////
|
||||
@ -405,13 +425,6 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
// todo - handle non read/edit/insert/delete tables (e.g., w/ just 1 permission, or read/write) //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////
|
||||
// tag for this table //
|
||||
////////////////////////
|
||||
openAPI.getTags().add(new Tag()
|
||||
.withName(tableLabel)
|
||||
.withDescription("Operations on the " + tableLabel + " table."));
|
||||
|
||||
//////////////////////////////////////
|
||||
// build the schemas for this table //
|
||||
//////////////////////////////////////
|
||||
@ -687,8 +700,65 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
.withPatch(updateBulkEnabled ? bulkPatch : null)
|
||||
.withDelete(deleteBulkEnabled ? bulkDelete : null));
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
// add processes under the table //
|
||||
// do we want a unique tag for them? //
|
||||
///////////////////////////////////////
|
||||
/*
|
||||
String tableProcessesTag = tableLabel + " Processes";
|
||||
if(CollectionUtils.nullSafeHasContents(apiProcessMetaDataList))
|
||||
{
|
||||
tagList.add(new Tag()
|
||||
.withName(tableProcessesTag)
|
||||
.withDescription("Process on the " + tableLabel + " table."));
|
||||
}
|
||||
*/
|
||||
String tableProcessesTag = tableLabel;
|
||||
|
||||
for(Pair<ApiProcessMetaData, QProcessMetaData> pair : CollectionUtils.nonNullList(apiProcessMetaDataList))
|
||||
{
|
||||
ApiProcessMetaData apiProcessMetaData = pair.getA();
|
||||
QProcessMetaData processMetaData = pair.getB();
|
||||
|
||||
addProcessEndpoints(qInstance, apiInstanceMetaData, basePath, openAPI, tableProcessesTag, apiProcessMetaData, processMetaData);
|
||||
|
||||
usedProcessNames.add(processMetaData.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// add non-table processes //
|
||||
/////////////////////////////
|
||||
if(input.getTableName() == null)
|
||||
{
|
||||
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 tag = processMetaData.getLabel();
|
||||
if(doesProcessLabelNeedTheWordProcessAppended(tag))
|
||||
{
|
||||
tag += " process";
|
||||
}
|
||||
tagList.add(new Tag()
|
||||
.withName(tag)
|
||||
.withDescription(tag));
|
||||
|
||||
addProcessEndpoints(qInstance, apiInstanceMetaData, basePath, openAPI, tag, apiProcessMetaData, processMetaData);
|
||||
|
||||
usedProcessNames.add(processMetaData.getName());
|
||||
}
|
||||
}
|
||||
|
||||
tagList.sort(Comparator.comparing(Tag::getName));
|
||||
openAPI.setTags(tagList);
|
||||
|
||||
////////////////////////////
|
||||
// define standard errors //
|
||||
////////////////////////////
|
||||
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.UNAUTHORIZED.getCode(), buildStandardErrorResponse("Unauthorized. The required authentication credentials were missing or invalid.", "The required authentication credentials were missing or invalid."));
|
||||
componentResponses.put("error" + HttpStatus.FORBIDDEN.getCode(), buildStandardErrorResponse("Forbidden. You do not have permission to access the requested resource.", "You do not have permission to access the requested resource."));
|
||||
@ -710,6 +780,353 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static boolean doesProcessLabelNeedTheWordProcessAppended(String tag)
|
||||
{
|
||||
return !tag.matches("(?i).* process$");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void addProcessEndpoints(QInstance qInstance, ApiInstanceMetaData apiInstanceMetaData, String basePath, OpenAPI openAPI, String tag, ApiProcessMetaData apiProcessMetaData, QProcessMetaData processMetaData)
|
||||
{
|
||||
String processApiPath = ApiProcessUtils.getProcessApiPath(qInstance, processMetaData, apiProcessMetaData, apiInstanceMetaData);
|
||||
|
||||
///////////////////////////
|
||||
// do the process itself //
|
||||
///////////////////////////
|
||||
Path path = generateProcessSpecPathObject(apiInstanceMetaData, apiProcessMetaData, processMetaData, ListBuilder.of(tag));
|
||||
openAPI.getPaths().put(basePath + processApiPath, path);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// if the process can run async, then do the status checkin endpoitn //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
if(!ApiProcessMetaData.AsyncMode.NEVER.equals(apiProcessMetaData.getAsyncMode()))
|
||||
{
|
||||
Path statusPath = generateProcessStatusSpecPathObject(apiInstanceMetaData, apiProcessMetaData, processMetaData, ListBuilder.of(tag));
|
||||
openAPI.getPaths().put(basePath + processApiPath + "/status/{jobId}", statusPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
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)
|
||||
{
|
||||
String description = apiProcessMetaData.getDescription();
|
||||
if(!StringUtils.hasContent(description))
|
||||
{
|
||||
description = "Run the " + processMetaData.getLabel();
|
||||
if(doesProcessLabelNeedTheWordProcessAppended(description))
|
||||
{
|
||||
description += " process";
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// start defining the process //
|
||||
////////////////////////////////
|
||||
Method methodForProcess = new Method()
|
||||
.withOperationId(apiProcessMetaData.getApiProcessName())
|
||||
.withTags(tags)
|
||||
.withSummary(ObjectUtils.requireConditionElse(apiProcessMetaData.getSummary(), StringUtils::hasContent, processMetaData.getLabel()))
|
||||
.withDescription(description)
|
||||
.withSecurity(getSecurity(apiInstanceMetaData, processMetaData.getName()));
|
||||
|
||||
////////////////////////////////
|
||||
// add inputs for the process //
|
||||
////////////////////////////////
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
ApiProcessInput apiProcessInput = apiProcessMetaData.getInput();
|
||||
if(apiProcessInput != null)
|
||||
{
|
||||
ApiProcessInputFieldsContainer queryStringParams = apiProcessInput.getQueryStringParams();
|
||||
if(queryStringParams != null)
|
||||
{
|
||||
if(queryStringParams.getRecordIdsField() != null)
|
||||
{
|
||||
parameters.add(processFieldToParameter(apiInstanceMetaData, queryStringParams.getRecordIdsField()).withIn("query"));
|
||||
}
|
||||
|
||||
for(QFieldMetaData field : CollectionUtils.nonNullList(queryStringParams.getFields()))
|
||||
{
|
||||
parameters.add(processFieldToParameter(apiInstanceMetaData, field).withIn("query"));
|
||||
}
|
||||
}
|
||||
|
||||
QFieldMetaData bodyField = apiProcessInput.getBodyField();
|
||||
if(bodyField != null)
|
||||
{
|
||||
ApiFieldMetaDataContainer apiFieldMetaDataContainer = ApiFieldMetaDataContainer.ofOrNew(bodyField);
|
||||
ApiFieldMetaData apiFieldMetaData = apiFieldMetaDataContainer.getApiFieldMetaData(apiInstanceMetaData.getName());
|
||||
|
||||
String bodyDescription = "Value for the " + bodyField.getLabel();
|
||||
if(apiFieldMetaData != null && StringUtils.hasContent(apiFieldMetaData.getDescription()))
|
||||
{
|
||||
bodyDescription = apiFieldMetaData.getDescription();
|
||||
}
|
||||
|
||||
Content content = new Content();
|
||||
if(apiFieldMetaData != null && apiFieldMetaData.getExample() instanceof ExampleWithSingleValue exampleWithSingleValue)
|
||||
{
|
||||
content.withSchema(new Schema()
|
||||
.withDescription(bodyDescription)
|
||||
.withType("string")
|
||||
.withExample(exampleWithSingleValue.getValue())
|
||||
);
|
||||
}
|
||||
|
||||
methodForProcess.withRequestBody(new RequestBody()
|
||||
.withDescription(bodyDescription)
|
||||
.withRequired(bodyField.getIsRequired())
|
||||
.withContent(MapBuilder.of(apiProcessInput.getBodyFieldContentType(), content)));
|
||||
}
|
||||
|
||||
// todo - form & record body params
|
||||
// todo methodForProcess.withRequestBody();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// add the async input for optionally-async processes //
|
||||
////////////////////////////////////////////////////////
|
||||
if(ApiProcessMetaData.AsyncMode.OPTIONAL.equals(apiProcessMetaData.getAsyncMode()))
|
||||
{
|
||||
parameters.add(new Parameter()
|
||||
.withName("async")
|
||||
.withIn("query")
|
||||
.withDescription("""
|
||||
Indicates if the job should be ran asynchronously.
|
||||
If false, or not specified, job is ran synchronously, and returns with response status of 207 (Multi-Status) or 204 (No Content).
|
||||
If true, request returns immediately with response status of 202 (Accepted).
|
||||
""")
|
||||
.withExamples(MapBuilder.of(
|
||||
"false", new ExampleWithSingleValue().withValue(false).withSummary("Run the job synchronously."),
|
||||
"true", new ExampleWithSingleValue().withValue(true).withSummary("Run the job asynchronously.")
|
||||
))
|
||||
.withSchema(new Schema().withType("boolean")));
|
||||
}
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(parameters))
|
||||
{
|
||||
methodForProcess.setParameters(parameters);
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// build all possible responses //
|
||||
//////////////////////////////////
|
||||
Map<Integer, Response> responses = new LinkedHashMap<>();
|
||||
|
||||
ApiProcessOutputInterface output = apiProcessMetaData.getOutput();
|
||||
if(!ApiProcessMetaData.AsyncMode.ALWAYS.equals(apiProcessMetaData.getAsyncMode()))
|
||||
{
|
||||
responses.putAll(output.getSpecResponses(apiInstanceMetaData.getName()));
|
||||
}
|
||||
if(!ApiProcessMetaData.AsyncMode.NEVER.equals(apiProcessMetaData.getAsyncMode()))
|
||||
{
|
||||
responses.put(HttpStatus.ACCEPTED.getCode(), new Response()
|
||||
.withDescription("The process has been started asynchronously. You can call back later to check its status.")
|
||||
.withContent(MapBuilder.of(ContentType.JSON, new Content()
|
||||
.withSchema(new Schema()
|
||||
.withType("object")
|
||||
.withProperties(MapBuilder.of(
|
||||
"jobId", new Schema().withType("string").withFormat("uuid").withDescription("id of the asynchronous job")
|
||||
))
|
||||
)
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
responses.putAll(buildStandardErrorResponses(apiInstanceMetaData));
|
||||
methodForProcess.withResponses(responses);
|
||||
|
||||
@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 Path generateProcessStatusSpecPathObject(ApiInstanceMetaData apiInstanceMetaData, ApiProcessMetaData apiProcessMetaData, QProcessMetaData processMetaData, List<String> tags)
|
||||
{
|
||||
////////////////////////////////
|
||||
// start defining the process //
|
||||
////////////////////////////////
|
||||
Method methodForProcess = new Method()
|
||||
.withOperationId("getStatusFor" + StringUtils.ucFirst(apiProcessMetaData.getApiProcessName()))
|
||||
.withTags(tags)
|
||||
.withSummary("Get Status of Job: " + ObjectUtils.requireConditionElse(apiProcessMetaData.getSummary(), StringUtils::hasContent, processMetaData.getLabel()))
|
||||
.withDescription("Get the status for a previous asynchronous call to the process named " + processMetaData.getLabel())
|
||||
.withSecurity(getSecurity(apiInstanceMetaData, processMetaData.getName()));
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// add the async input for optionally-async processes //
|
||||
////////////////////////////////////////////////////////
|
||||
methodForProcess.setParameters(ListBuilder.of(new Parameter()
|
||||
.withName("jobId")
|
||||
.withIn("path")
|
||||
.withRequired(true)
|
||||
.withDescription("Id of the job, as returned by the API call that started it.")
|
||||
.withSchema(new Schema().withType("string").withFormat("uuid"))
|
||||
));
|
||||
|
||||
//////////////////////////////////
|
||||
// build all possible responses //
|
||||
//////////////////////////////////
|
||||
Map<Integer, Response> responses = new LinkedHashMap<>();
|
||||
responses.put(HttpStatus.ACCEPTED.getCode(), new Response()
|
||||
.withDescription("The process is still running. You can call back later to get its final status.")
|
||||
.withContent(MapBuilder.of(ContentType.JSON, new Content()
|
||||
.withSchema(new Schema()
|
||||
.withType("object")
|
||||
.withProperties(MapBuilder.of(
|
||||
"jobId", new Schema().withType("string").withFormat("uuid").withDescription("id of the asynchronous job")
|
||||
// todo - status??
|
||||
))
|
||||
)
|
||||
))
|
||||
);
|
||||
|
||||
ApiProcessOutputInterface output = apiProcessMetaData.getOutput();
|
||||
responses.putAll(output.getSpecResponses(apiInstanceMetaData.getName()));
|
||||
responses.putAll(buildStandardErrorResponses(apiInstanceMetaData));
|
||||
|
||||
methodForProcess.withResponses(responses);
|
||||
return (new Path().withGet(methodForProcess));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private Parameter processFieldToParameter(ApiInstanceMetaData apiInstanceMetaData, QFieldMetaData field)
|
||||
{
|
||||
ApiFieldMetaDataContainer apiFieldMetaDataContainer = ApiFieldMetaDataContainer.ofOrNew(field);
|
||||
ApiFieldMetaData apiFieldMetaData = apiFieldMetaDataContainer.getApiFieldMetaData(apiInstanceMetaData.getName());
|
||||
|
||||
String description = "Value for the " + field.getLabel() + " field.";
|
||||
if(field.getDefaultValue() != null)
|
||||
{
|
||||
description += " Default value is " + field.getDefaultValue() + ", if not given.";
|
||||
}
|
||||
|
||||
Schema fieldSchema = getFieldSchema(field, description, apiInstanceMetaData);
|
||||
|
||||
Parameter parameter = new Parameter()
|
||||
.withName(field.getName())
|
||||
.withDescription(description)
|
||||
.withRequired(field.getIsRequired())
|
||||
.withSchema(fieldSchema);
|
||||
|
||||
if(apiFieldMetaData != null)
|
||||
{
|
||||
if(apiFieldMetaData.getExample() != null)
|
||||
{
|
||||
parameter.withExample(apiFieldMetaData.getExample());
|
||||
}
|
||||
else if(apiFieldMetaData.getExamples() != null)
|
||||
{
|
||||
parameter.withExamples(apiFieldMetaData.getExamples());
|
||||
}
|
||||
}
|
||||
|
||||
return (parameter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
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
|
||||
** associations that it references, so we need their schemas too - so, make
|
||||
@ -766,7 +1183,14 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
|
||||
for(QFieldMetaData field : tableApiFields)
|
||||
{
|
||||
Schema fieldSchema = getFieldSchema(table, field);
|
||||
String fieldLabel = field.getLabel();
|
||||
if(!StringUtils.hasContent(fieldLabel))
|
||||
{
|
||||
fieldLabel = QInstanceEnricher.nameToLabel(field.getName());
|
||||
}
|
||||
|
||||
String defaultDescription = fieldLabel + " for the " + table.getLabel() + ".";
|
||||
Schema fieldSchema = getFieldSchema(field, defaultDescription, apiInstanceMetaData);
|
||||
tableFields.put(ApiFieldMetaData.getEffectiveApiFieldName(apiInstanceMetaData.getName(), field), fieldSchema);
|
||||
}
|
||||
|
||||
@ -937,20 +1361,22 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private Schema getFieldSchema(QTableMetaData table, QFieldMetaData field)
|
||||
private Schema getFieldSchema(QFieldMetaData field, String defaultDescription, ApiInstanceMetaData apiInstanceMetaData)
|
||||
{
|
||||
String fieldLabel = field.getLabel();
|
||||
if(!StringUtils.hasContent(fieldLabel))
|
||||
{
|
||||
fieldLabel = QInstanceEnricher.nameToLabel(field.getName());
|
||||
}
|
||||
ApiFieldMetaDataContainer apiFieldMetaDataContainer = ApiFieldMetaDataContainer.ofOrNew(field);
|
||||
ApiFieldMetaData apiFieldMetaData = apiFieldMetaDataContainer.getApiFieldMetaData(apiInstanceMetaData.getName());
|
||||
|
||||
String description = fieldLabel + " for the " + table.getLabel() + ".";
|
||||
String description = defaultDescription;
|
||||
if(field.getType().equals(QFieldType.BLOB))
|
||||
{
|
||||
description = "Base64 encoded " + description;
|
||||
}
|
||||
|
||||
if(apiFieldMetaData != null && StringUtils.hasContent(apiFieldMetaData.getDescription()))
|
||||
{
|
||||
description = apiFieldMetaData.getDescription();
|
||||
}
|
||||
|
||||
Schema fieldSchema = new Schema()
|
||||
.withType(getFieldType(field))
|
||||
.withFormat(getFieldFormat(field))
|
||||
@ -1163,7 +1589,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getFieldType(QFieldMetaData field)
|
||||
public static String getFieldType(QFieldMetaData field)
|
||||
{
|
||||
return (getFieldType(field.getType()));
|
||||
}
|
||||
@ -1174,7 +1600,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("checkstyle:indentation")
|
||||
private String getFieldType(QFieldType type)
|
||||
private static String getFieldType(QFieldType type)
|
||||
{
|
||||
return switch(type)
|
||||
{
|
||||
|
@ -28,10 +28,13 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.kingsrook.qqq.api.actions.ApiImplementation;
|
||||
@ -40,11 +43,18 @@ import com.kingsrook.qqq.api.model.APILog;
|
||||
import com.kingsrook.qqq.api.model.APIVersion;
|
||||
import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecInput;
|
||||
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.ApiInstanceMetaDataContainer;
|
||||
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.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.ApiTableMetaDataContainer;
|
||||
import com.kingsrook.qqq.api.model.openapi.HttpMethod;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
@ -54,6 +64,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QPermissionDeniedException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
@ -66,6 +77,8 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.authentication.Auth0AuthenticationMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.branding.QBrandingMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
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.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QUser;
|
||||
@ -74,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.ExceptionUtils;
|
||||
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.collections.MapBuilder;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinAccessLogger;
|
||||
@ -85,6 +99,7 @@ import io.javalin.http.Context;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
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.javalin.QJavalinImplementation.SLOW_LOG_THRESHOLD_MS;
|
||||
|
||||
@ -96,6 +111,8 @@ public class QJavalinApiHandler
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(QJavalinApiHandler.class);
|
||||
|
||||
private static final ApiProcessMetaDataContainer EMPTY_API_PROCESS_META_DATA_CONTAINER = new ApiProcessMetaDataContainer().withApis(Collections.emptyMap());
|
||||
|
||||
private static QInstance qInstance;
|
||||
|
||||
private static Map<String, Integer> apiLogUserIdCache = new HashMap<>();
|
||||
@ -156,10 +173,47 @@ public class QJavalinApiHandler
|
||||
////////////////////////////////////////////
|
||||
ApiBuilder.get("/", context -> doSpecHtml(context, apiInstanceMetaData));
|
||||
|
||||
///////////////////////////////////////////
|
||||
// add known paths for specs & docs page //
|
||||
///////////////////////////////////////////
|
||||
ApiBuilder.get("/openapi.yaml", context -> doSpecYaml(context, apiInstanceMetaData));
|
||||
ApiBuilder.get("/openapi.json", context -> doSpecJson(context, apiInstanceMetaData));
|
||||
ApiBuilder.get("/openapi.html", context -> doSpecHtml(context, apiInstanceMetaData));
|
||||
|
||||
///////////////////
|
||||
// add processes //
|
||||
///////////////////
|
||||
for(QProcessMetaData process : qInstance.getProcesses().values())
|
||||
{
|
||||
ApiProcessMetaDataContainer apiProcessMetaDataContainer = Objects.requireNonNullElse(ApiProcessMetaDataContainer.of(process), EMPTY_API_PROCESS_META_DATA_CONTAINER);
|
||||
ApiProcessMetaData apiProcessMetaData = apiProcessMetaDataContainer.getApis().get(apiInstanceMetaData.getName());
|
||||
|
||||
if(apiProcessMetaData != null && !BooleanUtils.isTrue(apiProcessMetaData.getIsExcluded()))
|
||||
{
|
||||
String path = ApiProcessUtils.getProcessApiPath(qInstance, process, apiProcessMetaData, apiInstanceMetaData);
|
||||
HttpMethod method = apiProcessMetaData.getMethod();
|
||||
switch(method)
|
||||
{
|
||||
case GET -> ApiBuilder.get(path, context -> runProcess(context, process, apiProcessMetaData, apiInstanceMetaData));
|
||||
case POST -> ApiBuilder.post(path, context -> runProcess(context, process, apiProcessMetaData, apiInstanceMetaData));
|
||||
case PUT -> ApiBuilder.put(path, context -> runProcess(context, process, apiProcessMetaData, apiInstanceMetaData));
|
||||
case PATCH -> ApiBuilder.patch(path, context -> runProcess(context, process, apiProcessMetaData, apiInstanceMetaData));
|
||||
case DELETE -> ApiBuilder.delete(path, context -> runProcess(context, process, apiProcessMetaData, apiInstanceMetaData));
|
||||
default -> throw (new QRuntimeException("Unrecognized http method [" + method + "] for process [" + process.getName() + "]"));
|
||||
}
|
||||
|
||||
make405sForOtherMethods(method, path);
|
||||
|
||||
if(!ApiProcessMetaData.AsyncMode.NEVER.equals(apiProcessMetaData.getAsyncMode()))
|
||||
{
|
||||
ApiBuilder.get(path + "/status/{jobId}", context -> getProcessStatus(context, process, apiProcessMetaData, apiInstanceMetaData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
// add wildcard paths for tables //
|
||||
///////////////////////////////////
|
||||
ApiBuilder.path("/{tableName}", () ->
|
||||
{
|
||||
ApiBuilder.get("/openapi.yaml", context -> doSpecYaml(context, apiInstanceMetaData));
|
||||
@ -208,6 +262,171 @@ 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
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("checkstyle:indentation")
|
||||
private void runProcess(Context context, QProcessMetaData processMetaData, ApiProcessMetaData apiProcessMetaData, ApiInstanceMetaData apiInstanceMetaData)
|
||||
{
|
||||
String version = context.pathParam("version");
|
||||
APILog apiLog = newAPILog(context);
|
||||
|
||||
try
|
||||
{
|
||||
setupSession(context, null, version, apiInstanceMetaData);
|
||||
QJavalinAccessLogger.logStart("apiRunProcess", logPair("process", processMetaData.getName()));
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// process inputs into map for api implementation //
|
||||
////////////////////////////////////////////////////
|
||||
Map<String, String> parameters = new LinkedHashMap<>();
|
||||
ApiProcessInput input = apiProcessMetaData.getInput();
|
||||
if(input != null)
|
||||
{
|
||||
processProcessInputFieldsContainer(context, parameters, input.getQueryStringParams(), Context::queryParam);
|
||||
processProcessInputFieldsContainer(context, parameters, input.getFormParams(), Context::formParam);
|
||||
|
||||
ApiProcessInputFieldsContainer objectBodyParams = input.getObjectBodyParams();
|
||||
if(objectBodyParams != null)
|
||||
{
|
||||
JSONObject jsonObject = new JSONObject(context.body());
|
||||
processProcessInputFieldsContainer(context, parameters, objectBodyParams, (ctx, name) -> jsonObject.optString(name, null));
|
||||
}
|
||||
|
||||
if(input.getBodyField() != null)
|
||||
{
|
||||
parameters.put(input.getBodyField().getName(), context.body());
|
||||
}
|
||||
}
|
||||
|
||||
if(ApiProcessMetaData.AsyncMode.OPTIONAL.equals(apiProcessMetaData.getAsyncMode()))
|
||||
{
|
||||
parameters.put("async", context.queryParam("async"));
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// run the process //
|
||||
/////////////////////
|
||||
HttpApiResponse response = ApiImplementation.runProcess(apiInstanceMetaData, version, apiProcessMetaData.getApiProcessName(), parameters);
|
||||
|
||||
//////////////////
|
||||
// log & return //
|
||||
//////////////////
|
||||
QJavalinAccessLogger.logEndSuccess();
|
||||
context.status(response.getStatusCode().getCode());
|
||||
String resultString = toJson(Objects.requireNonNullElse(response.getResponseBodyObject(), ""));
|
||||
context.result(resultString);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinAccessLogger.logEndFail(e);
|
||||
handleException(context, e, apiLog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void getProcessStatus(Context context, QProcessMetaData processMetaData, ApiProcessMetaData apiProcessMetaData, ApiInstanceMetaData apiInstanceMetaData)
|
||||
{
|
||||
String version = context.pathParam("version");
|
||||
APILog apiLog = newAPILog(context);
|
||||
|
||||
try
|
||||
{
|
||||
setupSession(context, null, version, apiInstanceMetaData);
|
||||
QJavalinAccessLogger.logStart("apiGetProcessStatus", logPair("process", processMetaData.getName()));
|
||||
|
||||
String jobId = context.pathParam("jobId");
|
||||
HttpApiResponse response = ApiImplementation.getProcessStatus(apiInstanceMetaData, version, apiProcessMetaData.getApiProcessName(), jobId);
|
||||
|
||||
//////////////////
|
||||
// log & return //
|
||||
//////////////////
|
||||
QJavalinAccessLogger.logEndSuccess();
|
||||
context.status(response.getStatusCode().getCode());
|
||||
String resultString = toJson(Objects.requireNonNullElse(response.getResponseBodyObject(), ""));
|
||||
context.result(resultString);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinAccessLogger.logEndFail(e);
|
||||
handleException(context, e, apiLog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void processProcessInputFieldsContainer(Context context, Map<String, String> parameters, ApiProcessInputFieldsContainer fieldsContainer, BiFunction<Context, String, String> paramAccessor)
|
||||
{
|
||||
if(fieldsContainer != null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -1161,7 +1380,12 @@ public class QJavalinApiHandler
|
||||
// default exception handling //
|
||||
////////////////////////////////
|
||||
LOG.warn("Exception in javalin request", e);
|
||||
respondWithError(context, HttpStatus.Code.INTERNAL_SERVER_ERROR, e.getClass().getSimpleName() + " (" + e.getMessage() + ")", apiLog); // 500
|
||||
String message = e.getMessage();
|
||||
if(!StringUtils.hasContent(message))
|
||||
{
|
||||
message = e.getClass().getSimpleName();
|
||||
}
|
||||
respondWithError(context, HttpStatus.Code.INTERNAL_SERVER_ERROR, message, apiLog); // 500
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -24,17 +24,17 @@ package com.kingsrook.qqq.api.model.metadata;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.api.ApiMiddlewareType;
|
||||
import com.kingsrook.qqq.api.ApiSupplementType;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QMiddlewareInstanceMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QSupplementalInstanceMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ApiInstanceMetaDataContainer extends QMiddlewareInstanceMetaData
|
||||
public class ApiInstanceMetaDataContainer extends QSupplementalInstanceMetaData
|
||||
{
|
||||
private Map<String, ApiInstanceMetaData> apis;
|
||||
|
||||
@ -46,7 +46,7 @@ public class ApiInstanceMetaDataContainer extends QMiddlewareInstanceMetaData
|
||||
*******************************************************************************/
|
||||
public ApiInstanceMetaDataContainer()
|
||||
{
|
||||
setType(ApiMiddlewareType.NAME);
|
||||
setType(ApiSupplementType.NAME);
|
||||
}
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ public class ApiInstanceMetaDataContainer extends QMiddlewareInstanceMetaData
|
||||
*******************************************************************************/
|
||||
public static ApiInstanceMetaDataContainer of(QInstance qInstance)
|
||||
{
|
||||
return ((ApiInstanceMetaDataContainer) qInstance.getMiddlewareMetaData(ApiMiddlewareType.NAME));
|
||||
return ((ApiInstanceMetaDataContainer) qInstance.getSupplementalMetaData(ApiSupplementType.NAME));
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,6 +22,8 @@
|
||||
package com.kingsrook.qqq.api.model.metadata.fields;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.api.model.openapi.Example;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
@ -35,10 +37,14 @@ public class ApiFieldMetaData
|
||||
private String finalVersion;
|
||||
|
||||
private String apiFieldName;
|
||||
private String description;
|
||||
|
||||
private Boolean isExcluded;
|
||||
private String replacedByFieldName;
|
||||
|
||||
private Example example;
|
||||
private Map<String, Example> examples;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -214,4 +220,97 @@ public class ApiFieldMetaData
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for description
|
||||
*******************************************************************************/
|
||||
public String getDescription()
|
||||
{
|
||||
return (this.description);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for description
|
||||
*******************************************************************************/
|
||||
public void setDescription(String description)
|
||||
{
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for description
|
||||
*******************************************************************************/
|
||||
public ApiFieldMetaData withDescription(String description)
|
||||
{
|
||||
this.description = description;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for example
|
||||
*******************************************************************************/
|
||||
public Example getExample()
|
||||
{
|
||||
return (this.example);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for example
|
||||
*******************************************************************************/
|
||||
public void setExample(Example example)
|
||||
{
|
||||
this.example = example;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for example
|
||||
*******************************************************************************/
|
||||
public ApiFieldMetaData withExample(Example example)
|
||||
{
|
||||
this.example = example;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for examples
|
||||
*******************************************************************************/
|
||||
public Map<String, Example> getExamples()
|
||||
{
|
||||
return (this.examples);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for examples
|
||||
*******************************************************************************/
|
||||
public void setExamples(Map<String, Example> examples)
|
||||
{
|
||||
this.examples = examples;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for examples
|
||||
*******************************************************************************/
|
||||
public ApiFieldMetaData withExamples(Map<String, Example> examples)
|
||||
{
|
||||
this.examples = examples;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,18 +24,20 @@ package com.kingsrook.qqq.api.model.metadata.fields;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.api.ApiMiddlewareType;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.api.ApiSupplementType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QMiddlewareFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QSupplementalFieldMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ApiFieldMetaDataContainer extends QMiddlewareFieldMetaData
|
||||
public class ApiFieldMetaDataContainer extends QSupplementalFieldMetaData
|
||||
{
|
||||
private Map<String, ApiFieldMetaData> apis;
|
||||
|
||||
private ApiFieldMetaData defaultApiFieldMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -54,7 +56,18 @@ public class ApiFieldMetaDataContainer extends QMiddlewareFieldMetaData
|
||||
*******************************************************************************/
|
||||
public static ApiFieldMetaDataContainer of(QFieldMetaData field)
|
||||
{
|
||||
return ((ApiFieldMetaDataContainer) field.getMiddlewareMetaData(ApiMiddlewareType.NAME));
|
||||
return ((ApiFieldMetaDataContainer) field.getSupplementalMetaData(ApiSupplementType.NAME));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** either get the container attached to a field - or a new one - note - the new
|
||||
** one will NOT be attached to the field!!
|
||||
*******************************************************************************/
|
||||
public static ApiFieldMetaDataContainer ofOrNew(QFieldMetaData field)
|
||||
{
|
||||
return (Objects.requireNonNullElseGet(of(field), ApiFieldMetaDataContainer::new));
|
||||
}
|
||||
|
||||
|
||||
@ -70,16 +83,16 @@ public class ApiFieldMetaDataContainer extends QMiddlewareFieldMetaData
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for apis
|
||||
** Getter the apiFieldMetaData for a specific api, or the container's default
|
||||
*******************************************************************************/
|
||||
public ApiFieldMetaData getApiFieldMetaData(String apiName)
|
||||
{
|
||||
if(this.apis == null)
|
||||
{
|
||||
return (null);
|
||||
return (defaultApiFieldMetaData);
|
||||
}
|
||||
|
||||
return (this.apis.get(apiName));
|
||||
return (this.apis.getOrDefault(apiName, defaultApiFieldMetaData));
|
||||
}
|
||||
|
||||
|
||||
@ -118,4 +131,35 @@ public class ApiFieldMetaDataContainer extends QMiddlewareFieldMetaData
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for defaultApiFieldMetaData
|
||||
*******************************************************************************/
|
||||
public ApiFieldMetaData getDefaultApiFieldMetaData()
|
||||
{
|
||||
return (this.defaultApiFieldMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for defaultApiFieldMetaData
|
||||
*******************************************************************************/
|
||||
public void setDefaultApiFieldMetaData(ApiFieldMetaData defaultApiFieldMetaData)
|
||||
{
|
||||
this.defaultApiFieldMetaData = defaultApiFieldMetaData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for defaultApiFieldMetaData
|
||||
*******************************************************************************/
|
||||
public ApiFieldMetaDataContainer withDefaultApiFieldMetaData(ApiFieldMetaData defaultApiFieldMetaData)
|
||||
{
|
||||
this.defaultApiFieldMetaData = defaultApiFieldMetaData;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.metadata.processes;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum ApiProcessCustomizers
|
||||
{
|
||||
PRE_RUN("preRun", PreRunApiProcessCustomizer.class),
|
||||
POST_RUN("postRun", PreRunApiProcessCustomizer.class);
|
||||
|
||||
private final String role;
|
||||
private final Class<?> expectedType;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
ApiProcessCustomizers(String role, Class<?> expectedType)
|
||||
{
|
||||
this.role = role;
|
||||
this.expectedType = expectedType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Get the FilesystemTableCustomer for a given role (e.g., the role used in meta-data, not
|
||||
** the enum-constant name).
|
||||
*******************************************************************************/
|
||||
public static ApiProcessCustomizers forRole(String name)
|
||||
{
|
||||
for(ApiProcessCustomizers value : values())
|
||||
{
|
||||
if(value.role.equals(name))
|
||||
{
|
||||
return (value);
|
||||
}
|
||||
}
|
||||
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for role
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getRole()
|
||||
{
|
||||
return role;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for expectedType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Class<?> getExpectedType()
|
||||
{
|
||||
return expectedType;
|
||||
}
|
||||
}
|
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ApiProcessInput
|
||||
{
|
||||
private ApiProcessInputFieldsContainer queryStringParams;
|
||||
private ApiProcessInputFieldsContainer formParams;
|
||||
private ApiProcessInputFieldsContainer recordBodyParams;
|
||||
|
||||
private QFieldMetaData bodyField;
|
||||
private String bodyFieldContentType;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for bodyField
|
||||
*******************************************************************************/
|
||||
public QFieldMetaData getBodyField()
|
||||
{
|
||||
return (this.bodyField);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for bodyField
|
||||
*******************************************************************************/
|
||||
public void setBodyField(QFieldMetaData bodyField)
|
||||
{
|
||||
this.bodyField = bodyField;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for bodyField
|
||||
*******************************************************************************/
|
||||
public ApiProcessInput withBodyField(QFieldMetaData bodyField)
|
||||
{
|
||||
this.bodyField = bodyField;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for bodyFieldContentType
|
||||
*******************************************************************************/
|
||||
public String getBodyFieldContentType()
|
||||
{
|
||||
return (this.bodyFieldContentType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for bodyFieldContentType
|
||||
*******************************************************************************/
|
||||
public void setBodyFieldContentType(String bodyFieldContentType)
|
||||
{
|
||||
this.bodyFieldContentType = bodyFieldContentType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for bodyFieldContentType
|
||||
*******************************************************************************/
|
||||
public ApiProcessInput withBodyFieldContentType(String bodyFieldContentType)
|
||||
{
|
||||
this.bodyFieldContentType = bodyFieldContentType;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
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;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** find all input fields in frontend steps of the process, and add them as fields
|
||||
** in this container.
|
||||
*******************************************************************************/
|
||||
public ApiProcessInputFieldsContainer withInferredInputFields(QProcessMetaData processMetaData)
|
||||
{
|
||||
return (withInferredInputFieldsExcluding(processMetaData, Collections.emptySet()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** find all input fields in frontend steps of the process, and add them as fields
|
||||
** in this container, unless they're in the collection to exclude.
|
||||
*******************************************************************************/
|
||||
public ApiProcessInputFieldsContainer withInferredInputFieldsExcluding(QProcessMetaData processMetaData, Collection<String> minusFieldNames)
|
||||
{
|
||||
if(fields == null)
|
||||
{
|
||||
fields = new ArrayList<>();
|
||||
}
|
||||
|
||||
for(QStepMetaData stepMetaData : CollectionUtils.nonNullList(processMetaData.getStepList()))
|
||||
{
|
||||
if(stepMetaData instanceof QFrontendStepMetaData frontendStep)
|
||||
{
|
||||
for(QFieldMetaData inputField : frontendStep.getInputFields())
|
||||
{
|
||||
if(minusFieldNames != null && !minusFieldNames.contains(inputField.getName()))
|
||||
{
|
||||
fields.add(inputField);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -0,0 +1,612 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.api.ApiSupplementType;
|
||||
import com.kingsrook.qqq.api.model.APIVersionRange;
|
||||
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
|
||||
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaDataContainer;
|
||||
import com.kingsrook.qqq.api.model.openapi.HttpMethod;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ApiProcessMetaData
|
||||
{
|
||||
private String initialVersion;
|
||||
private String finalVersion;
|
||||
|
||||
private String apiProcessName;
|
||||
private Boolean isExcluded;
|
||||
private Boolean overrideProcessIsHidden;
|
||||
|
||||
private String path;
|
||||
private HttpMethod method;
|
||||
private String summary;
|
||||
private String description;
|
||||
|
||||
private AsyncMode asyncMode = AsyncMode.OPTIONAL;
|
||||
|
||||
private ApiProcessInput input;
|
||||
private ApiProcessOutputInterface output;
|
||||
|
||||
private Map<String, QCodeReference> customizers;
|
||||
|
||||
|
||||
|
||||
public enum AsyncMode
|
||||
{
|
||||
NEVER,
|
||||
OPTIONAL,
|
||||
ALWAYS
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APIVersionRange getApiVersionRange()
|
||||
{
|
||||
if(getInitialVersion() == null)
|
||||
{
|
||||
return APIVersionRange.none();
|
||||
}
|
||||
|
||||
return (getFinalVersion() != null
|
||||
? APIVersionRange.betweenAndIncluding(getInitialVersion(), getFinalVersion())
|
||||
: APIVersionRange.afterAndIncluding(getInitialVersion()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void enrich(QInstanceEnricher qInstanceEnricher, String apiName, QProcessMetaData process)
|
||||
{
|
||||
if(!StringUtils.hasContent(getApiProcessName()))
|
||||
{
|
||||
setApiProcessName(process.getName());
|
||||
}
|
||||
|
||||
if(initialVersion != null)
|
||||
{
|
||||
if(getOutput() instanceof ApiProcessObjectOutput outputObject)
|
||||
{
|
||||
enrichFieldList(qInstanceEnricher, apiName, outputObject.getOutputFields());
|
||||
}
|
||||
|
||||
if(input != null)
|
||||
{
|
||||
for(ApiProcessInputFieldsContainer fieldsContainer : ListBuilder.of(input.getQueryStringParams(), input.getFormParams(), input.getObjectBodyParams()))
|
||||
{
|
||||
if(fieldsContainer != null)
|
||||
{
|
||||
enrichFieldList(qInstanceEnricher, apiName, fieldsContainer.getFields());
|
||||
}
|
||||
}
|
||||
if(input.getBodyField() != null)
|
||||
{
|
||||
enrichFieldList(qInstanceEnricher, apiName, List.of(input.getBodyField()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void enrichFieldList(QInstanceEnricher qInstanceEnricher, String apiName, List<QFieldMetaData> fields)
|
||||
{
|
||||
for(QFieldMetaData field : CollectionUtils.nonNullList(fields))
|
||||
{
|
||||
ApiFieldMetaData apiFieldMetaData = ensureFieldHasApiSupplementalMetaData(apiName, field);
|
||||
if(apiFieldMetaData.getInitialVersion() == null)
|
||||
{
|
||||
apiFieldMetaData.setInitialVersion(initialVersion);
|
||||
}
|
||||
|
||||
qInstanceEnricher.enrichField(field);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static ApiFieldMetaData ensureFieldHasApiSupplementalMetaData(String apiName, QFieldMetaData field)
|
||||
{
|
||||
if(field.getSupplementalMetaData(ApiSupplementType.NAME) == null)
|
||||
{
|
||||
field.withSupplementalMetaData(new ApiFieldMetaDataContainer());
|
||||
}
|
||||
|
||||
ApiFieldMetaDataContainer apiFieldMetaDataContainer = ApiFieldMetaDataContainer.of(field);
|
||||
if(apiFieldMetaDataContainer.getApiFieldMetaData(apiName) == null)
|
||||
{
|
||||
apiFieldMetaDataContainer.withApiFieldMetaData(apiName, new ApiFieldMetaData());
|
||||
}
|
||||
|
||||
return (apiFieldMetaDataContainer.getApiFieldMetaData(apiName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for initialVersion
|
||||
*******************************************************************************/
|
||||
public String getInitialVersion()
|
||||
{
|
||||
return (this.initialVersion);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for initialVersion
|
||||
*******************************************************************************/
|
||||
public void setInitialVersion(String initialVersion)
|
||||
{
|
||||
this.initialVersion = initialVersion;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for initialVersion
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData withInitialVersion(String initialVersion)
|
||||
{
|
||||
this.initialVersion = initialVersion;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for finalVersion
|
||||
*******************************************************************************/
|
||||
public String getFinalVersion()
|
||||
{
|
||||
return (this.finalVersion);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for finalVersion
|
||||
*******************************************************************************/
|
||||
public void setFinalVersion(String finalVersion)
|
||||
{
|
||||
this.finalVersion = finalVersion;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for finalVersion
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData withFinalVersion(String finalVersion)
|
||||
{
|
||||
this.finalVersion = finalVersion;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for apiProcessName
|
||||
*******************************************************************************/
|
||||
public String getApiProcessName()
|
||||
{
|
||||
return (this.apiProcessName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for apiProcessName
|
||||
*******************************************************************************/
|
||||
public void setApiProcessName(String apiProcessName)
|
||||
{
|
||||
this.apiProcessName = apiProcessName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for apiProcessName
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData withApiProcessName(String apiProcessName)
|
||||
{
|
||||
this.apiProcessName = apiProcessName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for isExcluded
|
||||
*******************************************************************************/
|
||||
public Boolean getIsExcluded()
|
||||
{
|
||||
return (this.isExcluded);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for isExcluded
|
||||
*******************************************************************************/
|
||||
public void setIsExcluded(Boolean isExcluded)
|
||||
{
|
||||
this.isExcluded = isExcluded;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for isExcluded
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData withIsExcluded(Boolean isExcluded)
|
||||
{
|
||||
this.isExcluded = isExcluded;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for method
|
||||
*******************************************************************************/
|
||||
public HttpMethod getMethod()
|
||||
{
|
||||
return (this.method);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for method
|
||||
*******************************************************************************/
|
||||
public void setMethod(HttpMethod method)
|
||||
{
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for method
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData withMethod(HttpMethod method)
|
||||
{
|
||||
this.method = method;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for path
|
||||
*******************************************************************************/
|
||||
public String getPath()
|
||||
{
|
||||
return (this.path);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for path
|
||||
*******************************************************************************/
|
||||
public void setPath(String path)
|
||||
{
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for path
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData withPath(String path)
|
||||
{
|
||||
this.path = path;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for customizers
|
||||
*******************************************************************************/
|
||||
public Map<String, QCodeReference> getCustomizers()
|
||||
{
|
||||
return (this.customizers);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for customizers
|
||||
*******************************************************************************/
|
||||
public void setCustomizers(Map<String, QCodeReference> customizers)
|
||||
{
|
||||
this.customizers = customizers;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for customizers
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData withCustomizers(Map<String, QCodeReference> customizers)
|
||||
{
|
||||
this.customizers = customizers;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData withCustomizer(String role, QCodeReference customizer)
|
||||
{
|
||||
if(this.customizers == null)
|
||||
{
|
||||
this.customizers = new HashMap<>();
|
||||
}
|
||||
|
||||
if(this.customizers.containsKey(role))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempt to add a second customizer with role [" + role + "] to apiProcess [" + apiProcessName + "]."));
|
||||
}
|
||||
this.customizers.put(role, customizer);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for summary
|
||||
*******************************************************************************/
|
||||
public String getSummary()
|
||||
{
|
||||
return (this.summary);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for summary
|
||||
*******************************************************************************/
|
||||
public void setSummary(String summary)
|
||||
{
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for summary
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData withSummary(String summary)
|
||||
{
|
||||
this.summary = summary;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for description
|
||||
*******************************************************************************/
|
||||
public String getDescription()
|
||||
{
|
||||
return (this.description);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for description
|
||||
*******************************************************************************/
|
||||
public void setDescription(String description)
|
||||
{
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for description
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData withDescription(String description)
|
||||
{
|
||||
this.description = description;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void validate(QInstance qInstance, QProcessMetaData process, QInstanceValidator qInstanceValidator, String apiName)
|
||||
{
|
||||
if(BooleanUtils.isTrue(getIsExcluded()))
|
||||
{
|
||||
/////////////////////////////////////////////////
|
||||
// no validation needed for excluded processes //
|
||||
/////////////////////////////////////////////////
|
||||
return;
|
||||
}
|
||||
|
||||
qInstanceValidator.assertCondition(getMethod() != null, "Missing a method for api process meta data for process: " + process.getName() + ", apiName: " + apiName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for asyncMode
|
||||
*******************************************************************************/
|
||||
public AsyncMode getAsyncMode()
|
||||
{
|
||||
return (this.asyncMode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for asyncMode
|
||||
*******************************************************************************/
|
||||
public void setAsyncMode(AsyncMode asyncMode)
|
||||
{
|
||||
this.asyncMode = asyncMode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for asyncMode
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData withAsyncMode(AsyncMode asyncMode)
|
||||
{
|
||||
this.asyncMode = asyncMode;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for overrideProcessIsHidden
|
||||
*******************************************************************************/
|
||||
public Boolean getOverrideProcessIsHidden()
|
||||
{
|
||||
return (this.overrideProcessIsHidden);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for overrideProcessIsHidden
|
||||
*******************************************************************************/
|
||||
public void setOverrideProcessIsHidden(Boolean overrideProcessIsHidden)
|
||||
{
|
||||
this.overrideProcessIsHidden = overrideProcessIsHidden;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for overrideProcessIsHidden
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData withOverrideProcessIsHidden(Boolean overrideProcessIsHidden)
|
||||
{
|
||||
this.overrideProcessIsHidden = overrideProcessIsHidden;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.api.ApiSupplementType;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||
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.processes.QSupplementalProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ApiProcessMetaDataContainer extends QSupplementalProcessMetaData
|
||||
{
|
||||
private Map<String, ApiProcessMetaData> apis;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaDataContainer()
|
||||
{
|
||||
setType(ApiSupplementType.NAME);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static ApiProcessMetaDataContainer of(QProcessMetaData process)
|
||||
{
|
||||
return ((ApiProcessMetaDataContainer) process.getSupplementalMetaData(ApiSupplementType.NAME));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** either get the container attached to a field - or create a new one and attach
|
||||
** it to the field, and return that.
|
||||
*******************************************************************************/
|
||||
public static ApiProcessMetaDataContainer ofOrWithNew(QProcessMetaData process)
|
||||
{
|
||||
ApiProcessMetaDataContainer apiProcessMetaDataContainer = (ApiProcessMetaDataContainer) process.getSupplementalMetaData(ApiSupplementType.NAME);
|
||||
if(apiProcessMetaDataContainer == null)
|
||||
{
|
||||
apiProcessMetaDataContainer = new ApiProcessMetaDataContainer();
|
||||
process.withSupplementalMetaData(apiProcessMetaDataContainer);
|
||||
}
|
||||
return (apiProcessMetaDataContainer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void enrich(QInstanceEnricher qInstanceEnricher, QProcessMetaData process)
|
||||
{
|
||||
super.enrich(qInstanceEnricher, process);
|
||||
|
||||
for(Map.Entry<String, ApiProcessMetaData> entry : CollectionUtils.nonNullMap(apis).entrySet())
|
||||
{
|
||||
entry.getValue().enrich(qInstanceEnricher, entry.getKey(), process);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void validate(QInstance qInstance, QProcessMetaData process, QInstanceValidator qInstanceValidator)
|
||||
{
|
||||
super.validate(qInstance, process, qInstanceValidator);
|
||||
|
||||
for(Map.Entry<String, ApiProcessMetaData> entry : CollectionUtils.nonNullMap(apis).entrySet())
|
||||
{
|
||||
entry.getValue().validate(qInstance, process, qInstanceValidator, entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for apis
|
||||
*******************************************************************************/
|
||||
public Map<String, ApiProcessMetaData> getApis()
|
||||
{
|
||||
return (this.apis);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for apis
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData getApiProcessMetaData(String apiName)
|
||||
{
|
||||
if(this.apis == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
return (this.apis.get(apiName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaData getApiProcessMetaDataOrWithNew(String apiName)
|
||||
{
|
||||
ApiProcessMetaData apiProcessMetaData = getApiProcessMetaData(apiName);
|
||||
if(apiProcessMetaData == null)
|
||||
{
|
||||
apiProcessMetaData = new ApiProcessMetaData();
|
||||
withApiProcessMetaData(apiName, apiProcessMetaData);
|
||||
}
|
||||
return (apiProcessMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for apis
|
||||
*******************************************************************************/
|
||||
public void setApis(Map<String, ApiProcessMetaData> apis)
|
||||
{
|
||||
this.apis = apis;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for apis
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaDataContainer withApis(Map<String, ApiProcessMetaData> apis)
|
||||
{
|
||||
this.apis = apis;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for apis
|
||||
*******************************************************************************/
|
||||
public ApiProcessMetaDataContainer withApiProcessMetaData(String apiName, ApiProcessMetaData apiProcessMetaData)
|
||||
{
|
||||
if(this.apis == null)
|
||||
{
|
||||
this.apis = new LinkedHashMap<>();
|
||||
}
|
||||
this.apis.put(apiName, apiProcessMetaData);
|
||||
return (this);
|
||||
}
|
||||
}
|
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.api.actions.GenerateOpenApiSpecAction;
|
||||
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
|
||||
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaDataContainer;
|
||||
import com.kingsrook.qqq.api.model.openapi.Content;
|
||||
import com.kingsrook.qqq.api.model.openapi.ExampleWithListValue;
|
||||
import com.kingsrook.qqq.api.model.openapi.ExampleWithSingleValue;
|
||||
import com.kingsrook.qqq.api.model.openapi.Response;
|
||||
import com.kingsrook.qqq.api.model.openapi.Schema;
|
||||
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;
|
||||
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
import io.javalin.http.ContentType;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ApiProcessObjectOutput implements ApiProcessOutputInterface
|
||||
{
|
||||
private List<QFieldMetaData> outputFields;
|
||||
|
||||
private String responseDescription;
|
||||
private HttpStatus.Code successResponseCode;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public HttpStatus.Code getSuccessStatusCode(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput)
|
||||
{
|
||||
return (HttpStatus.Code.OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Map<Integer, Response> getSpecResponses(String apiName)
|
||||
{
|
||||
Map<String, Schema> properties = new LinkedHashMap<>();
|
||||
for(QFieldMetaData outputField : CollectionUtils.nonNullList(outputFields))
|
||||
{
|
||||
ApiFieldMetaDataContainer apiFieldMetaDataContainer = ApiFieldMetaDataContainer.ofOrNew(outputField);
|
||||
ApiFieldMetaData apiFieldMetaData = apiFieldMetaDataContainer.getApiFieldMetaData(apiName);
|
||||
|
||||
Object example = null;
|
||||
if(apiFieldMetaData != null)
|
||||
{
|
||||
if(apiFieldMetaData.getExample() instanceof ExampleWithSingleValue exampleWithSingleValue)
|
||||
{
|
||||
example = exampleWithSingleValue.getValue();
|
||||
}
|
||||
else if(apiFieldMetaData.getExample() instanceof ExampleWithListValue exampleWithListValue)
|
||||
{
|
||||
example = exampleWithListValue.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
properties.put(outputField.getName(), new Schema()
|
||||
.withDescription(apiFieldMetaData == null ? null : apiFieldMetaData.getDescription())
|
||||
.withExample(example)
|
||||
.withNullable(!outputField.getIsRequired())
|
||||
.withType(GenerateOpenApiSpecAction.getFieldType(outputField))
|
||||
);
|
||||
}
|
||||
|
||||
return (MapBuilder.of(
|
||||
Objects.requireNonNullElse(successResponseCode, HttpStatus.Code.OK).getCode(),
|
||||
new Response()
|
||||
.withDescription(ObjectUtils.requireConditionElse(responseDescription, StringUtils::hasContent, "Process has been successfully executed."))
|
||||
.withContent(MapBuilder.of(ContentType.JSON, new Content()
|
||||
.withSchema(new Schema()
|
||||
.withType("object")
|
||||
.withProperties(properties))))
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
******************************************************************************/
|
||||
@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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for responseDescription
|
||||
*******************************************************************************/
|
||||
public String getResponseDescription()
|
||||
{
|
||||
return (this.responseDescription);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for responseDescription
|
||||
*******************************************************************************/
|
||||
public void setResponseDescription(String responseDescription)
|
||||
{
|
||||
this.responseDescription = responseDescription;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for responseDescription
|
||||
*******************************************************************************/
|
||||
public ApiProcessObjectOutput withResponseDescription(String responseDescription)
|
||||
{
|
||||
this.responseDescription = responseDescription;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for successResponseCode
|
||||
*******************************************************************************/
|
||||
public HttpStatus.Code getSuccessResponseCode()
|
||||
{
|
||||
return (this.successResponseCode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for successResponseCode
|
||||
*******************************************************************************/
|
||||
public void setSuccessResponseCode(HttpStatus.Code successResponseCode)
|
||||
{
|
||||
this.successResponseCode = successResponseCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for successResponseCode
|
||||
*******************************************************************************/
|
||||
public ApiProcessObjectOutput withSuccessResponseCode(HttpStatus.Code successResponseCode)
|
||||
{
|
||||
this.successResponseCode = successResponseCode;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.api.model.openapi.Response;
|
||||
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 com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
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.NO_CONTENT);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
default Map<Integer, Response> getSpecResponses(String apiName)
|
||||
{
|
||||
return (MapBuilder.of(
|
||||
HttpStatus.Code.NO_CONTENT.getCode(), new Response()
|
||||
.withDescription("Process has been successfully executed.")
|
||||
));
|
||||
}
|
||||
}
|
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.api.model.openapi.Content;
|
||||
import com.kingsrook.qqq.api.model.openapi.Response;
|
||||
import com.kingsrook.qqq.api.model.openapi.Schema;
|
||||
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 com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
import io.javalin.http.ContentType;
|
||||
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 Map<Integer, Response> getSpecResponses(String apiName)
|
||||
{
|
||||
Map<String, Schema> propertiesFor207Object = new LinkedHashMap<>();
|
||||
propertiesFor207Object.put("id", new Schema().withType("integer").withDescription("Id of the record whose status is being described in the object"));
|
||||
propertiesFor207Object.put("statusCode", new Schema().withType("integer").withDescription("HTTP Status code indicating the success or failure of the process on this record"));
|
||||
propertiesFor207Object.put("statusText", new Schema().withType("string").withDescription("HTTP Status text indicating the success or failure of the process on this record"));
|
||||
propertiesFor207Object.put("message", new Schema().withType("string").withDescription("Additional descriptive information about the result of the process on this record."));
|
||||
|
||||
List<Object> exampleFor207Object = ListBuilder.of(MapBuilder.of(LinkedHashMap::new)
|
||||
.with("id", 42)
|
||||
.with("statusCode", io.javalin.http.HttpStatus.OK.getCode())
|
||||
.with("statusText", io.javalin.http.HttpStatus.OK.getMessage())
|
||||
.with("message", "record was processed successfully.")
|
||||
.build(),
|
||||
MapBuilder.of(LinkedHashMap::new)
|
||||
.with("id", 47)
|
||||
.with("statusCode", io.javalin.http.HttpStatus.BAD_REQUEST.getCode())
|
||||
.with("statusText", io.javalin.http.HttpStatus.BAD_REQUEST.getMessage())
|
||||
.with("message", "error executing process on record.")
|
||||
.build());
|
||||
|
||||
return MapBuilder.of(
|
||||
HttpStatus.Code.MULTI_STATUS.getCode(), new Response()
|
||||
.withDescription("For each input record, an object describing its status may be returned.")
|
||||
.withContent(MapBuilder.of(ContentType.JSON, new Content()
|
||||
.withSchema(new Schema()
|
||||
.withType("array")
|
||||
.withItems(new Schema()
|
||||
.withType("object")
|
||||
.withProperties(propertiesFor207Object))
|
||||
.withExample(exampleFor207Object)
|
||||
)
|
||||
)),
|
||||
|
||||
HttpStatus.Code.NO_CONTENT.getCode(), new Response()
|
||||
.withDescription("If no records were found, there may be no content in the response.")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@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.pickMessage(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)
|
||||
{
|
||||
apiOutput.add(toMap(processSummaryRecordLink));
|
||||
}
|
||||
else if(processSummaryLineInterface instanceof ProcessSummaryFilterLink processSummaryFilterLink)
|
||||
{
|
||||
apiOutput.add(toMap(processSummaryFilterLink));
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private HashMap<String, Serializable> toMap(ProcessSummaryFilterLink processSummaryFilterLink)
|
||||
{
|
||||
HashMap<String, Serializable> map = initResultMapForProcessSummaryLine(processSummaryFilterLink);
|
||||
|
||||
String messagePrefix = getResultMapMessagePrefix(processSummaryFilterLink);
|
||||
map.put("message", messagePrefix + processSummaryFilterLink.getFullText());
|
||||
|
||||
return (map);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private HashMap<String, Serializable> toMap(ProcessSummaryRecordLink processSummaryRecordLink)
|
||||
{
|
||||
HashMap<String, Serializable> map = initResultMapForProcessSummaryLine(processSummaryRecordLink);
|
||||
|
||||
String messagePrefix = getResultMapMessagePrefix(processSummaryRecordLink);
|
||||
map.put("message", messagePrefix + processSummaryRecordLink.getFullText());
|
||||
|
||||
return (map);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static HashMap<String, Serializable> toMap(ProcessSummaryLine processSummaryLine)
|
||||
{
|
||||
HashMap<String, Serializable> map = initResultMapForProcessSummaryLine(processSummaryLine);
|
||||
|
||||
String messagePrefix = getResultMapMessagePrefix(processSummaryLine);
|
||||
map.put("message", messagePrefix + processSummaryLine.getMessage());
|
||||
|
||||
return (map);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static String getResultMapMessagePrefix(ProcessSummaryLineInterface processSummaryLine)
|
||||
{
|
||||
@SuppressWarnings("checkstyle:indentation")
|
||||
String messagePrefix = switch(processSummaryLine.getStatus())
|
||||
{
|
||||
case OK, INFO, ERROR -> "";
|
||||
case WARNING -> "Warning: ";
|
||||
};
|
||||
return messagePrefix;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static HashMap<String, Serializable> initResultMapForProcessSummaryLine(ProcessSummaryLineInterface processSummaryLine)
|
||||
{
|
||||
HashMap<String, Serializable> map = new HashMap<>();
|
||||
|
||||
@SuppressWarnings("checkstyle:indentation")
|
||||
HttpStatus.Code code = switch(processSummaryLine.getStatus())
|
||||
{
|
||||
case OK, WARNING, INFO -> HttpStatus.Code.OK;
|
||||
case ERROR -> HttpStatus.Code.INTERNAL_SERVER_ERROR;
|
||||
};
|
||||
|
||||
map.put("statusCode", code.getCode());
|
||||
map.put("statusText", code.getMessage());
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api.model.metadata.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."));
|
||||
}
|
||||
|
||||
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."));
|
||||
}
|
||||
|
||||
if(BooleanUtils.isTrue(process.getIsHidden()))
|
||||
{
|
||||
if(!BooleanUtils.isTrue(apiProcessMetaData.getOverrideProcessIsHidden()))
|
||||
{
|
||||
LOG.info("404 because process isHidden", 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||
|
||||
|
||||
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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface PostRunApiProcessCustomizer
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
void postApiRun(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput) throws QException;
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api.model.metadata.processes;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface PreRunApiProcessCustomizer
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
void preApiRun(RunProcessInput runProcessInput) throws QException;
|
||||
|
||||
}
|
@ -27,7 +27,7 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import com.kingsrook.qqq.api.ApiMiddlewareType;
|
||||
import com.kingsrook.qqq.api.ApiSupplementType;
|
||||
import com.kingsrook.qqq.api.model.APIVersionRange;
|
||||
import com.kingsrook.qqq.api.model.metadata.ApiOperation;
|
||||
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
|
||||
@ -81,7 +81,7 @@ public class ApiTableMetaData implements ApiOperation.EnabledOperationsProvider
|
||||
{
|
||||
for(QFieldMetaData field : table.getFields().values())
|
||||
{
|
||||
ApiFieldMetaData apiFieldMetaData = ensureFieldHasApiMiddlewareMetaData(apiName, field);
|
||||
ApiFieldMetaData apiFieldMetaData = ensureFieldHasApiSupplementalMetaData(apiName, field);
|
||||
if(apiFieldMetaData.getInitialVersion() == null)
|
||||
{
|
||||
apiFieldMetaData.setInitialVersion(initialVersion);
|
||||
@ -90,7 +90,7 @@ public class ApiTableMetaData implements ApiOperation.EnabledOperationsProvider
|
||||
|
||||
for(QFieldMetaData field : CollectionUtils.nonNullList(removedApiFields))
|
||||
{
|
||||
ApiFieldMetaData apiFieldMetaData = ensureFieldHasApiMiddlewareMetaData(apiName, field);
|
||||
ApiFieldMetaData apiFieldMetaData = ensureFieldHasApiSupplementalMetaData(apiName, field);
|
||||
if(apiFieldMetaData.getInitialVersion() == null)
|
||||
{
|
||||
apiFieldMetaData.setInitialVersion(initialVersion);
|
||||
@ -104,11 +104,11 @@ public class ApiTableMetaData implements ApiOperation.EnabledOperationsProvider
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static ApiFieldMetaData ensureFieldHasApiMiddlewareMetaData(String apiName, QFieldMetaData field)
|
||||
private static ApiFieldMetaData ensureFieldHasApiSupplementalMetaData(String apiName, QFieldMetaData field)
|
||||
{
|
||||
if(field.getMiddlewareMetaData(ApiMiddlewareType.NAME) == null)
|
||||
if(field.getSupplementalMetaData(ApiSupplementType.NAME) == null)
|
||||
{
|
||||
field.withMiddlewareMetaData(new ApiFieldMetaDataContainer());
|
||||
field.withSupplementalMetaData(new ApiFieldMetaDataContainer());
|
||||
}
|
||||
|
||||
ApiFieldMetaDataContainer apiFieldMetaDataContainer = ApiFieldMetaDataContainer.of(field);
|
||||
|
@ -24,8 +24,8 @@ package com.kingsrook.qqq.api.model.metadata.tables;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.api.ApiMiddlewareType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QMiddlewareTableMetaData;
|
||||
import com.kingsrook.qqq.api.ApiSupplementType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QSupplementalTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
@ -33,7 +33,7 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ApiTableMetaDataContainer extends QMiddlewareTableMetaData
|
||||
public class ApiTableMetaDataContainer extends QSupplementalTableMetaData
|
||||
{
|
||||
private Map<String, ApiTableMetaData> apis;
|
||||
|
||||
@ -55,7 +55,7 @@ public class ApiTableMetaDataContainer extends QMiddlewareTableMetaData
|
||||
*******************************************************************************/
|
||||
public static ApiTableMetaDataContainer of(QTableMetaData table)
|
||||
{
|
||||
return ((ApiTableMetaDataContainer) table.getMiddlewareMetaData(ApiMiddlewareType.NAME));
|
||||
return ((ApiTableMetaDataContainer) table.getSupplementalMetaData(ApiSupplementType.NAME));
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,12 +22,15 @@
|
||||
package com.kingsrook.qqq.api.model.openapi;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ExampleWithSingleValue extends Example
|
||||
{
|
||||
private String value;
|
||||
private Serializable value;
|
||||
|
||||
|
||||
|
||||
@ -46,7 +49,7 @@ public class ExampleWithSingleValue extends Example
|
||||
/*******************************************************************************
|
||||
** Getter for value
|
||||
*******************************************************************************/
|
||||
public String getValue()
|
||||
public Serializable getValue()
|
||||
{
|
||||
return (this.value);
|
||||
}
|
||||
@ -56,7 +59,7 @@ public class ExampleWithSingleValue extends Example
|
||||
/*******************************************************************************
|
||||
** Setter for value
|
||||
*******************************************************************************/
|
||||
public void setValue(String value)
|
||||
public void setValue(Serializable value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
@ -66,7 +69,7 @@ public class ExampleWithSingleValue extends Example
|
||||
/*******************************************************************************
|
||||
** Fluent setter for value
|
||||
*******************************************************************************/
|
||||
public ExampleWithSingleValue withValue(String value)
|
||||
public ExampleWithSingleValue withValue(Serializable value)
|
||||
{
|
||||
this.value = value;
|
||||
return (this);
|
||||
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api.model.openapi;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum HttpMethod
|
||||
{
|
||||
GET,
|
||||
POST,
|
||||
PUT,
|
||||
PATCH,
|
||||
DELETE
|
||||
}
|
@ -37,6 +37,7 @@ public class Parameter
|
||||
private Schema schema;
|
||||
private Boolean explode;
|
||||
private Map<String, Example> examples;
|
||||
private Example example;
|
||||
|
||||
|
||||
|
||||
@ -255,4 +256,35 @@ public class Parameter
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for example
|
||||
*******************************************************************************/
|
||||
public Example getExample()
|
||||
{
|
||||
return (this.example);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for examplee
|
||||
*******************************************************************************/
|
||||
public void setExample(Example example)
|
||||
{
|
||||
this.example = example;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for examplee
|
||||
*******************************************************************************/
|
||||
public Parameter withExample(Example example)
|
||||
{
|
||||
this.example = example;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.api.model.openapi;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.fasterxml.jackson.annotation.JsonGetter;
|
||||
@ -191,6 +192,27 @@ public class Schema
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for example
|
||||
*******************************************************************************/
|
||||
public void setExample(BigDecimal example)
|
||||
{
|
||||
this.example = example;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for example
|
||||
*******************************************************************************/
|
||||
public Schema withExample(Object example)
|
||||
{
|
||||
this.example = example;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for example
|
||||
*******************************************************************************/
|
||||
|
@ -30,6 +30,7 @@ import java.util.Map;
|
||||
import com.kingsrook.qqq.api.actions.ApiImplementation;
|
||||
import com.kingsrook.qqq.api.actions.QRecordApiAdapter;
|
||||
import com.kingsrook.qqq.api.model.APIVersion;
|
||||
import com.kingsrook.qqq.api.model.actions.HttpApiResponse;
|
||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.QCodeExecutor;
|
||||
@ -38,6 +39,9 @@ import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -277,6 +281,53 @@ public class ApiScriptUtils implements QCodeExecutorAware, Serializable
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable runProcess(String processApiName) throws QException
|
||||
{
|
||||
return (runProcess(processApiName, null));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable runProcess(String processApiName, Object params) throws QException
|
||||
{
|
||||
validateApiNameAndVersion("runProcess(" + processApiName + ")");
|
||||
|
||||
Map<String, String> paramMap = new LinkedHashMap<>();
|
||||
String paramsString = ValueUtils.getValueAsString(params);
|
||||
if(StringUtils.hasContent(paramsString))
|
||||
{
|
||||
JSONObject paramsJSON = new JSONObject(paramsString);
|
||||
for(String fieldName : paramsJSON.keySet())
|
||||
{
|
||||
paramMap.put(fieldName, paramsJSON.optString(fieldName));
|
||||
}
|
||||
}
|
||||
|
||||
HttpApiResponse httpApiResponse = ApiImplementation.runProcess(getApiInstanceMetaData(), apiVersion, processApiName, paramMap);
|
||||
return (httpApiResponse.getResponseBodyObject());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable getProcessStatus(String processApiName, String jobId) throws QException
|
||||
{
|
||||
validateApiNameAndVersion("getProcessStatus(" + processApiName + ")");
|
||||
|
||||
HttpApiResponse httpApiResponse = ApiImplementation.getProcessStatus(getApiInstanceMetaData(), apiVersion, processApiName, jobId);
|
||||
return (httpApiResponse.getResponseBodyObject());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -38,7 +38,7 @@
|
||||
show-header="false"
|
||||
allow-spec-file-download="true"
|
||||
primary-color="{primaryColor}"
|
||||
sort-endpoints-by="method"
|
||||
sort-endpoints-by="none"
|
||||
allow-authentication="true"
|
||||
persist-auth="true"
|
||||
render-style="focused"
|
||||
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class GetPersonInfoStep implements BackendStep
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||
{
|
||||
runBackendStepOutput.addValue("density", new BigDecimal("3.50"));
|
||||
runBackendStepOutput.addValue("daysOld", runBackendStepInput.getValueInteger("age") * 365);
|
||||
runBackendStepOutput.addValue("nickname", "Guy from " + runBackendStepInput.getValueString("homeTown"));
|
||||
}
|
||||
|
||||
}
|
@ -29,8 +29,15 @@ import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
|
||||
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
|
||||
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaData;
|
||||
import com.kingsrook.qqq.api.model.metadata.fields.ApiFieldMetaDataContainer;
|
||||
import com.kingsrook.qqq.api.model.metadata.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.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.ApiTableMetaDataContainer;
|
||||
import com.kingsrook.qqq.api.model.openapi.HttpMethod;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPreDeleteCustomizer;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPreInsertCustomizer;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPreUpdateCustomizer;
|
||||
@ -45,12 +52,23 @@ import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.authentication.Auth0AuthenticationMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode.HtmlWrapper;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode.WidgetHtmlLine;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PVSValueFormatAndFields;
|
||||
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.processes.NoCodeWidgetFrontendComponentMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
|
||||
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.tables.Association;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||
@ -58,6 +76,9 @@ 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.SystemErrorStatusMessage;
|
||||
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;
|
||||
|
||||
|
||||
@ -74,6 +95,9 @@ public class TestUtils
|
||||
public static final String TABLE_NAME_LINE_ITEM_EXTRINSIC = "orderLineExtrinsic";
|
||||
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_TRANSFORM_PEOPLE = "transformPeople";
|
||||
|
||||
public static final String API_NAME = "test-api";
|
||||
public static final String ALTERNATIVE_API_NAME = "person-api";
|
||||
|
||||
@ -103,9 +127,13 @@ public class TestUtils
|
||||
qInstance.addJoin(defineJoinLineItemLineItemExtrinsic());
|
||||
qInstance.addJoin(defineJoinOrderOrderExtrinsic());
|
||||
|
||||
qInstance.addPossibleValueSource(definePersonPossibleValueSource());
|
||||
qInstance.addProcess(defineProcessGetPersonInfo());
|
||||
qInstance.addProcess(defineProcessTransformPeople());
|
||||
|
||||
qInstance.setAuthentication(new Auth0AuthenticationMetaData().withType(QAuthenticationType.FULLY_ANONYMOUS).withName("anonymous"));
|
||||
|
||||
qInstance.withMiddlewareMetaData(new ApiInstanceMetaDataContainer()
|
||||
qInstance.withSupplementalMetaData(new ApiInstanceMetaDataContainer()
|
||||
.withApiInstanceMetaData(new ApiInstanceMetaData()
|
||||
.withName(API_NAME)
|
||||
.withPath("/api/")
|
||||
@ -133,6 +161,109 @@ public class TestUtils
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QPossibleValueSource definePersonPossibleValueSource()
|
||||
{
|
||||
return new QPossibleValueSource()
|
||||
.withName(TABLE_NAME_PERSON)
|
||||
.withType(QPossibleValueSourceType.TABLE)
|
||||
.withTableName(TABLE_NAME_PERSON)
|
||||
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QProcessMetaData defineProcessGetPersonInfo()
|
||||
{
|
||||
QProcessMetaData process = new QProcessMetaData()
|
||||
.withName(PROCESS_NAME_GET_PERSON_INFO)
|
||||
.withLabel("Get Person Info")
|
||||
.withTableName(TABLE_NAME_PERSON)
|
||||
.addStep(new QFrontendStepMetaData()
|
||||
.withName("enterInputs")
|
||||
.withLabel("Person Info Input")
|
||||
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.EDIT_FORM))
|
||||
|
||||
.withFormField(new QFieldMetaData("age", QFieldType.INTEGER).withIsRequired(true))
|
||||
.withFormField(new QFieldMetaData("partnerPersonId", QFieldType.INTEGER).withPossibleValueSourceName(TABLE_NAME_PERSON))
|
||||
.withFormField(new QFieldMetaData("heightInches", QFieldType.DECIMAL).withIsRequired(true))
|
||||
.withFormField(new QFieldMetaData("weightPounds", QFieldType.INTEGER).withIsRequired(true))
|
||||
.withFormField(new QFieldMetaData("homeTown", QFieldType.STRING).withIsRequired(true))
|
||||
|
||||
.withComponent(new NoCodeWidgetFrontendComponentMetaData()
|
||||
|
||||
.withOutput(new WidgetHtmlLine()
|
||||
.withWrapper(HtmlWrapper.divWithStyles(HtmlWrapper.STYLE_FLOAT_RIGHT, HtmlWrapper.STYLE_MEDIUM_CENTERED, HtmlWrapper.styleWidth("50%")))
|
||||
.withVelocityTemplate("""
|
||||
<b>Density:</b><br />$density<br/>
|
||||
"""))
|
||||
|
||||
.withOutput(new WidgetHtmlLine()
|
||||
.withVelocityTemplate("""
|
||||
<b>Days old:</b> $daysOld<br/>
|
||||
<b>Nickname:</b> $nickname<br/>
|
||||
"""))
|
||||
))
|
||||
|
||||
.addStep(new QBackendStepMetaData()
|
||||
.withName("execute")
|
||||
.withCode(new QCodeReference(GetPersonInfoStep.class)))
|
||||
|
||||
.addStep(new QFrontendStepMetaData()
|
||||
.withName("dummyStep")
|
||||
);
|
||||
|
||||
process.withSupplementalMetaData(new ApiProcessMetaDataContainer()
|
||||
.withApiProcessMetaData(API_NAME, new ApiProcessMetaData()
|
||||
.withInitialVersion(CURRENT_API_VERSION)
|
||||
.withMethod(HttpMethod.GET)
|
||||
.withInput(new ApiProcessInput()
|
||||
.withQueryStringParams(new ApiProcessInputFieldsContainer().withInferredInputFields(process)))
|
||||
.withOutput(new ApiProcessObjectOutput()
|
||||
.withOutputField(new QFieldMetaData("density", QFieldType.DECIMAL))
|
||||
.withOutputField(new QFieldMetaData("daysOld", QFieldType.INTEGER))
|
||||
.withOutputField(new QFieldMetaData("nickname", QFieldType.STRING)))
|
||||
));
|
||||
|
||||
return (process);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
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
|
||||
*******************************************************************************/
|
||||
@ -204,7 +335,7 @@ public class TestUtils
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// make some changes to this table in the "main" api (but leave it like the backend in the ALTERNATIVE_API_NAME) //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
table.withMiddlewareMetaData(new ApiTableMetaDataContainer()
|
||||
table.withSupplementalMetaData(new ApiTableMetaDataContainer()
|
||||
.withApiTableMetaData(API_NAME, new ApiTableMetaData()
|
||||
.withInitialVersion(V2022_Q4)
|
||||
|
||||
@ -212,7 +343,7 @@ public class TestUtils
|
||||
// in 2022.Q4, this table had a "shoeCount" field. but for the 2023.Q1 version, we renamed it to noOfShoes! //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
.withRemovedApiField(new QFieldMetaData("shoeCount", QFieldType.INTEGER).withDisplayFormat(DisplayFormat.COMMAS)
|
||||
.withMiddlewareMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(API_NAME,
|
||||
.withSupplementalMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(API_NAME,
|
||||
new ApiFieldMetaData().withFinalVersion(V2022_Q4).withReplacedByFieldName("noOfShoes"))))
|
||||
)
|
||||
.withApiTableMetaData(ALTERNATIVE_API_NAME, new ApiTableMetaData().withInitialVersion(V2022_Q4)));
|
||||
@ -220,18 +351,18 @@ public class TestUtils
|
||||
/////////////////////////////////////////////////////
|
||||
// change the name for this field for the main api //
|
||||
/////////////////////////////////////////////////////
|
||||
table.getField("birthDate").withMiddlewareMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(API_NAME, new ApiFieldMetaData().withApiFieldName("birthDay")));
|
||||
table.getField("birthDate").withSupplementalMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(API_NAME, new ApiFieldMetaData().withApiFieldName("birthDay")));
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// See above - we renamed this field (in the backend) for the 2023_Q1 version //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
table.getField("noOfShoes").withMiddlewareMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(API_NAME, new ApiFieldMetaData().withInitialVersion(V2023_Q1)));
|
||||
table.getField("noOfShoes").withSupplementalMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(API_NAME, new ApiFieldMetaData().withInitialVersion(V2023_Q1)));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 2 new fields - one will appear in a future version of the API, the other is always excluded //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
table.getField("cost").withMiddlewareMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(API_NAME, new ApiFieldMetaData().withInitialVersion(V2023_Q2)));
|
||||
table.getField("price").withMiddlewareMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(API_NAME, new ApiFieldMetaData().withIsExcluded(true)));
|
||||
table.getField("cost").withSupplementalMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(API_NAME, new ApiFieldMetaData().withInitialVersion(V2023_Q2)));
|
||||
table.getField("price").withSupplementalMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(API_NAME, new ApiFieldMetaData().withIsExcluded(true)));
|
||||
|
||||
return (table);
|
||||
}
|
||||
@ -248,7 +379,7 @@ public class TestUtils
|
||||
.withCustomizer(TableCustomizers.PRE_INSERT_RECORD.getRole(), new QCodeReference(OrderPreInsertCustomizer.class))
|
||||
.withCustomizer(TableCustomizers.PRE_UPDATE_RECORD.getRole(), new QCodeReference(OrderPreUpdateCustomizer.class))
|
||||
.withBackendName(MEMORY_BACKEND_NAME)
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion(V2022_Q4)))
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion(V2022_Q4)))
|
||||
.withPrimaryKeyField("id")
|
||||
.withAssociation(new Association().withName("orderLines").withAssociatedTableName(TABLE_NAME_LINE_ITEM).withJoinName("orderLineItem"))
|
||||
.withAssociation(new Association().withName("extrinsics").withAssociatedTableName(TABLE_NAME_ORDER_EXTRINSIC).withJoinName("orderOrderExtrinsic"))
|
||||
@ -271,7 +402,7 @@ public class TestUtils
|
||||
return new QTableMetaData()
|
||||
.withName(TABLE_NAME_LINE_ITEM)
|
||||
.withBackendName(MEMORY_BACKEND_NAME)
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion(V2022_Q4)))
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion(V2022_Q4)))
|
||||
.withPrimaryKeyField("id")
|
||||
.withAssociation(new Association().withName("extrinsics").withAssociatedTableName(TABLE_NAME_LINE_ITEM_EXTRINSIC).withJoinName("lineItemLineItemExtrinsic"))
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
|
||||
@ -293,7 +424,7 @@ public class TestUtils
|
||||
return new QTableMetaData()
|
||||
.withName(TABLE_NAME_LINE_ITEM_EXTRINSIC)
|
||||
.withBackendName(MEMORY_BACKEND_NAME)
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion(V2022_Q4)))
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion(V2022_Q4)))
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
|
||||
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withIsEditable(false))
|
||||
@ -313,7 +444,7 @@ public class TestUtils
|
||||
return new QTableMetaData()
|
||||
.withName(TABLE_NAME_ORDER_EXTRINSIC)
|
||||
.withBackendName(MEMORY_BACKEND_NAME)
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion(V2022_Q4)))
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion(V2022_Q4)))
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
|
||||
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withIsEditable(false))
|
||||
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api;
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -34,6 +34,7 @@ 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.QException;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
@ -120,7 +121,7 @@ class GenerateOpenApiSpecActionTest extends BaseTest
|
||||
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withInitialVersion(TestUtils.V2022_Q4))));
|
||||
|
||||
qInstance.addTable(new QTableMetaData()
|
||||
@ -129,7 +130,7 @@ class GenerateOpenApiSpecActionTest extends BaseTest
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withIsHidden(true)
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withInitialVersion(TestUtils.V2022_Q4))));
|
||||
|
||||
qInstance.addTable(new QTableMetaData()
|
||||
@ -137,7 +138,7 @@ class GenerateOpenApiSpecActionTest extends BaseTest
|
||||
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withIsExcluded(true))));
|
||||
|
||||
qInstance.addTable(new QTableMetaData()
|
||||
@ -151,7 +152,7 @@ class GenerateOpenApiSpecActionTest extends BaseTest
|
||||
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withInitialVersion(TestUtils.V2023_Q2))));
|
||||
|
||||
qInstance.addTable(new QTableMetaData()
|
||||
@ -159,7 +160,7 @@ class GenerateOpenApiSpecActionTest extends BaseTest
|
||||
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withInitialVersion(TestUtils.V2022_Q4)
|
||||
.withFinalVersion(TestUtils.V2022_Q4))));
|
||||
|
||||
@ -169,9 +170,11 @@ class GenerateOpenApiSpecActionTest extends BaseTest
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withoutCapabilities(Capability.TABLE_QUERY, Capability.TABLE_GET, Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE)
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withInitialVersion(TestUtils.V2022_Q4))));
|
||||
|
||||
new QInstanceEnricher(qInstance).enrich();
|
||||
|
||||
GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecAction().execute(new GenerateOpenApiSpecInput().withVersion(TestUtils.CURRENT_API_VERSION).withApiName(TestUtils.API_NAME));
|
||||
Set<String> apiPaths = output.getOpenAPI().getPaths().keySet();
|
||||
assertTrue(apiPaths.stream().anyMatch(s -> s.contains("/supportedTable/")));
|
||||
@ -198,10 +201,12 @@ class GenerateOpenApiSpecActionTest extends BaseTest
|
||||
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withApiTableName("externalName")
|
||||
.withInitialVersion(TestUtils.V2022_Q4))));
|
||||
|
||||
new QInstanceEnricher(qInstance).enrich();
|
||||
|
||||
GenerateOpenApiSpecOutput output = new GenerateOpenApiSpecAction().execute(new GenerateOpenApiSpecInput().withVersion(TestUtils.CURRENT_API_VERSION).withApiName(TestUtils.API_NAME));
|
||||
Set<String> apiPaths = output.getOpenAPI().getPaths().keySet();
|
||||
assertTrue(apiPaths.stream().anyMatch(s -> s.contains("/externalName/")));
|
||||
|
@ -74,11 +74,11 @@ class GetTableApiFieldsActionTest extends BaseTest
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addTable(new QTableMetaData()
|
||||
.withName(TABLE_NAME)
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion("1")))
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion("1")))
|
||||
.withField(new QFieldMetaData("a", STRING)) // inherit versionRange from the table
|
||||
.withField(new QFieldMetaData("b", STRING).withMiddlewareMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData().withInitialVersion("1"))))
|
||||
.withField(new QFieldMetaData("c", STRING).withMiddlewareMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData().withInitialVersion("2"))))
|
||||
.withField(new QFieldMetaData("d", STRING).withMiddlewareMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData().withInitialVersion("3"))))
|
||||
.withField(new QFieldMetaData("b", STRING).withSupplementalMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData().withInitialVersion("1"))))
|
||||
.withField(new QFieldMetaData("c", STRING).withSupplementalMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData().withInitialVersion("2"))))
|
||||
.withField(new QFieldMetaData("d", STRING).withSupplementalMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData().withInitialVersion("3"))))
|
||||
);
|
||||
new QInstanceEnricher(qInstance).enrich();
|
||||
|
||||
@ -98,13 +98,13 @@ class GetTableApiFieldsActionTest extends BaseTest
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addTable(new QTableMetaData()
|
||||
.withName(TABLE_NAME)
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion("1")
|
||||
.withRemovedApiField(new QFieldMetaData("c", STRING).withMiddlewareMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData().withInitialVersion("1").withFinalVersion("2"))))
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion("1")
|
||||
.withRemovedApiField(new QFieldMetaData("c", STRING).withSupplementalMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData().withInitialVersion("1").withFinalVersion("2"))))
|
||||
))
|
||||
.withField(new QFieldMetaData("a", STRING)) // inherit versionRange from the table
|
||||
.withField(new QFieldMetaData("b", STRING).withMiddlewareMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData().withInitialVersion("1"))))
|
||||
.withField(new QFieldMetaData("b", STRING).withSupplementalMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData().withInitialVersion("1"))))
|
||||
// we used to have "c" here... now it's in the removed list above!
|
||||
.withField(new QFieldMetaData("d", STRING).withMiddlewareMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData().withInitialVersion("3"))))
|
||||
.withField(new QFieldMetaData("d", STRING).withSupplementalMetaData(new ApiFieldMetaDataContainer().withApiFieldMetaData(TestUtils.API_NAME, new ApiFieldMetaData().withInitialVersion("3"))))
|
||||
);
|
||||
new QInstanceEnricher(qInstance).enrich();
|
||||
|
||||
|
@ -26,6 +26,7 @@ import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.kingsrook.qqq.api.BaseTest;
|
||||
import com.kingsrook.qqq.api.TestUtils;
|
||||
import com.kingsrook.qqq.api.model.metadata.tables.ApiTableMetaData;
|
||||
@ -50,6 +51,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.implementations.FullyAnonymousAuthenticationModule;
|
||||
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
|
||||
import io.javalin.apibuilder.EndpointGroup;
|
||||
@ -67,8 +69,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -98,7 +102,7 @@ class QJavalinApiHandlerTest extends BaseTest
|
||||
.withBackendName(TestUtils.MEMORY_BACKEND_NAME)
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData()
|
||||
.withApiTableName("externalName")
|
||||
.withInitialVersion(TestUtils.V2022_Q4))));
|
||||
|
||||
@ -1439,6 +1443,82 @@ class QJavalinApiHandlerTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testGetProcessForObject() throws QException
|
||||
{
|
||||
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").asString();
|
||||
assertErrorResponse(HttpStatus.BAD_REQUEST_400, "Request failed with 4 reasons: Missing value for required input field", 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());
|
||||
JSONObject jsonObject = new JSONObject(response.getBody());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testAsyncProcessAndGetStatus() throws QException
|
||||
{
|
||||
insertSimpsons();
|
||||
|
||||
HttpResponse<String> response = Unirest.post(BASE_URL + "/api/" + VERSION + "/person/transformPeople?id=1,2,3&async=true").asString();
|
||||
assertEquals(HttpStatus.ACCEPTED_202, response.getStatus());
|
||||
JSONObject acceptedJSON = new JSONObject(response.getBody());
|
||||
String jobId = acceptedJSON.getString("jobId");
|
||||
assertNotNull(jobId);
|
||||
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
SleepUtils.sleep(100, TimeUnit.MILLISECONDS);
|
||||
|
||||
response = Unirest.get(BASE_URL + "/api/" + VERSION + "/person/transformPeople/status/" + jobId).asString();
|
||||
if(response.getStatus() == HttpStatus.MULTI_STATUS_207)
|
||||
{
|
||||
JSONArray jsonArray = new JSONArray(response.getBody());
|
||||
assertEquals(3, jsonArray.length());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fail("Never got back a 207, after many sleeps");
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -114,11 +114,11 @@ class ApiInstanceMetaDataTest
|
||||
|
||||
qInstance.addTable(new QTableMetaData()
|
||||
.withName("myValidTable")
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion("2023.Q1"))));
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion("2023.Q1"))));
|
||||
|
||||
qInstance.addTable(new QTableMetaData()
|
||||
.withName("myInvalidTable")
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion("notAVersion"))));
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion("notAVersion"))));
|
||||
|
||||
assertValidationErrors(qInstance, makeBaselineValidApiInstanceMetaData()
|
||||
.withCurrentVersion(new APIVersion("2023.Q1"))
|
||||
@ -127,7 +127,7 @@ class ApiInstanceMetaDataTest
|
||||
|
||||
qInstance.addTable(new QTableMetaData()
|
||||
.withName("myFutureValidTable")
|
||||
.withMiddlewareMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion("2024.Q1"))));
|
||||
.withSupplementalMetaData(new ApiTableMetaDataContainer().withApiTableMetaData(TestUtils.API_NAME, new ApiTableMetaData().withInitialVersion("2024.Q1"))));
|
||||
|
||||
assertValidationErrors(qInstance, makeBaselineValidApiInstanceMetaData()
|
||||
.withCurrentVersion(new APIVersion("2023.Q1"))
|
||||
@ -195,7 +195,7 @@ class ApiInstanceMetaDataTest
|
||||
*******************************************************************************/
|
||||
private void assertValidationErrors(QInstance qInstance, ApiInstanceMetaData apiInstanceMetaData, List<String> expectedErrors)
|
||||
{
|
||||
qInstance.withMiddlewareMetaData(new ApiInstanceMetaDataContainer().withApiInstanceMetaData(apiInstanceMetaData));
|
||||
qInstance.withSupplementalMetaData(new ApiInstanceMetaDataContainer().withApiInstanceMetaData(apiInstanceMetaData));
|
||||
|
||||
QInstanceValidator validator = new QInstanceValidator();
|
||||
apiInstanceMetaData.validate(apiInstanceMetaData.getName(), qInstance, validator);
|
||||
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.api.utils;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.kingsrook.qqq.api.BaseTest;
|
||||
import com.kingsrook.qqq.api.TestUtils;
|
||||
import com.kingsrook.qqq.api.javalin.QBadRequestException;
|
||||
@ -33,9 +34,12 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static com.kingsrook.qqq.api.TestUtils.insertSimpsons;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@ -262,6 +266,87 @@ class ApiScriptUtilsTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testGetProcessForObject() throws QException
|
||||
{
|
||||
ApiScriptUtils apiScriptUtils = newDefaultApiScriptUtils();
|
||||
|
||||
assertThatThrownBy(() -> apiScriptUtils.runProcess(TestUtils.PROCESS_NAME_GET_PERSON_INFO))
|
||||
.isInstanceOf(QBadRequestException.class)
|
||||
.hasMessageContaining("Request failed with 4 reasons: Missing value for required input field");
|
||||
|
||||
Object result = apiScriptUtils.runProcess(TestUtils.PROCESS_NAME_GET_PERSON_INFO, """
|
||||
{"age": 43, "partnerPersonId": 1, "heightInches": 72, "weightPounds": 220, "homeTown": "Chester"}
|
||||
""");
|
||||
|
||||
assertThat(result).isInstanceOf(Map.class);
|
||||
Map<?, ?> resultMap = (Map<?, ?>) result;
|
||||
assertEquals(15695, resultMap.get("daysOld"));
|
||||
assertEquals("Guy from Chester", resultMap.get("nickname"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPostProcessForProcessSummaryList() throws QException
|
||||
{
|
||||
insertSimpsons();
|
||||
|
||||
ApiScriptUtils apiScriptUtils = newDefaultApiScriptUtils();
|
||||
|
||||
assertThatThrownBy(() -> apiScriptUtils.runProcess(TestUtils.PROCESS_NAME_TRANSFORM_PEOPLE, null))
|
||||
.isInstanceOf(QBadRequestException.class)
|
||||
.hasMessageContaining("Records to run through this process were not specified");
|
||||
|
||||
Serializable emptyResult = apiScriptUtils.runProcess(TestUtils.PROCESS_NAME_TRANSFORM_PEOPLE, JsonUtils.toJson(Map.of("id", 999)));
|
||||
assertThat(emptyResult).isInstanceOf(List.class);
|
||||
assertEquals(0, ((List<?>) emptyResult).size());
|
||||
|
||||
Serializable result = apiScriptUtils.runProcess(TestUtils.PROCESS_NAME_TRANSFORM_PEOPLE, JsonUtils.toJson(Map.of("id", "1,2,3")));
|
||||
assertThat(result).isInstanceOf(List.class);
|
||||
List<Map<String, Object>> resultList = (List<Map<String, Object>>) result;
|
||||
assertEquals(3, resultList.size());
|
||||
|
||||
assertThat(resultList.stream().filter(m -> m.get("id").equals(2)).findFirst()).isPresent().get().hasFieldOrPropertyWithValue("statusCode", 200);
|
||||
assertThat(resultList.stream().filter(m -> m.get("id").equals(3)).findFirst()).isPresent().get().hasFieldOrPropertyWithValue("statusCode", 500);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testAsyncProcessAndGetStatus() throws QException
|
||||
{
|
||||
insertSimpsons();
|
||||
|
||||
ApiScriptUtils apiScriptUtils = newDefaultApiScriptUtils();
|
||||
|
||||
Serializable asyncResult = apiScriptUtils.runProcess(TestUtils.PROCESS_NAME_TRANSFORM_PEOPLE, JsonUtils.toJson(Map.of("id", "1,2,3", "async", true)));
|
||||
assertThat(asyncResult).isInstanceOf(Map.class);
|
||||
String jobId = ValueUtils.getValueAsString(((Map<String, ?>) asyncResult).get("jobId"));
|
||||
assertNotNull(jobId);
|
||||
|
||||
SleepUtils.sleep(100, TimeUnit.MILLISECONDS);
|
||||
|
||||
Serializable result = apiScriptUtils.getProcessStatus(TestUtils.PROCESS_NAME_TRANSFORM_PEOPLE, jobId);
|
||||
assertThat(result).isInstanceOf(List.class);
|
||||
List<Map<String, Object>> resultList = (List<Map<String, Object>>) result;
|
||||
assertEquals(3, resultList.size());
|
||||
|
||||
assertThat(resultList.stream().filter(m -> m.get("id").equals(2)).findFirst()).isPresent().get().hasFieldOrPropertyWithValue("statusCode", 200);
|
||||
assertThat(resultList.stream().filter(m -> m.get("id").equals(3)).findFirst()).isPresent().get().hasFieldOrPropertyWithValue("statusCode", 500);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
Reference in New Issue
Block a user