mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
Merged dev into feature/bulk-upload-v2
This commit is contained in:
@ -63,12 +63,10 @@ import com.kingsrook.qqq.backend.core.actions.values.SearchPossibleValueSourceAc
|
||||
import com.kingsrook.qqq.backend.core.adapters.QInstanceAdapter;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QBadRequestException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||
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.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
@ -106,7 +104,6 @@ import com.kingsrook.qqq.backend.core.model.actions.values.SearchPossibleValueSo
|
||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerHelper;
|
||||
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.fields.AdornmentType;
|
||||
@ -123,6 +120,7 @@ import com.kingsrook.qqq.backend.core.model.statusmessages.QStatusMessage;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleDispatcher;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.implementations.Auth0AuthenticationModule;
|
||||
import com.kingsrook.qqq.backend.core.utils.ClassPathUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ExceptionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
@ -178,7 +176,8 @@ public class QJavalinImplementation
|
||||
|
||||
private static int DEFAULT_PORT = 8001;
|
||||
|
||||
private static Javalin service;
|
||||
private static Javalin service;
|
||||
private static List<EndpointGroup> endpointGroups;
|
||||
|
||||
private static long startTime = 0;
|
||||
|
||||
@ -241,8 +240,18 @@ public class QJavalinImplementation
|
||||
{
|
||||
// todo port from arg
|
||||
// todo base path from arg? - and then potentially multiple instances too (chosen based on the root path??)
|
||||
service = Javalin.create().start(port);
|
||||
service.routes(getRoutes());
|
||||
|
||||
service = Javalin.create(config ->
|
||||
{
|
||||
config.router.apiBuilder(getRoutes());
|
||||
|
||||
for(EndpointGroup endpointGroup : CollectionUtils.nonNullList(endpointGroups))
|
||||
{
|
||||
config.router.apiBuilder(endpointGroup);
|
||||
}
|
||||
}
|
||||
).start(port);
|
||||
|
||||
service.before(QJavalinImplementation::hotSwapQInstance);
|
||||
service.before((Context context) -> context.header("Content-Type", "application/json"));
|
||||
service.after(QJavalinImplementation::clearQContext);
|
||||
@ -292,7 +301,7 @@ public class QJavalinImplementation
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// clear the cache of classes in this class, so that new classes can be found //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
MetaDataProducerHelper.clearTopLevelClassCache();
|
||||
ClassPathUtils.clearTopLevelClassCache();
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// try to get a new instance from the supplier //
|
||||
@ -1014,8 +1023,7 @@ public class QJavalinImplementation
|
||||
QRecord record = getOutput.getRecord();
|
||||
if(record == null)
|
||||
{
|
||||
throw (new QNotFoundException("Could not find " + table.getLabel() + " with "
|
||||
+ table.getFields().get(table.getPrimaryKeyField()).getLabel() + " of " + primaryKey));
|
||||
throw (new QNotFoundException("Could not find " + table.getLabel() + " with " + table.getFields().get(table.getPrimaryKeyField()).getLabel() + " of " + primaryKey));
|
||||
}
|
||||
|
||||
QValueFormatter.setBlobValuesToDownloadUrls(table, List.of(record));
|
||||
@ -1078,8 +1086,7 @@ public class QJavalinImplementation
|
||||
///////////////////////////////////////////////////////
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
throw (new QNotFoundException("Could not find " + table.getLabel() + " with "
|
||||
+ table.getFields().get(table.getPrimaryKeyField()).getLabel() + " of " + primaryKey));
|
||||
throw (new QNotFoundException("Could not find " + table.getLabel() + " with " + table.getFields().get(table.getPrimaryKeyField()).getLabel() + " of " + primaryKey));
|
||||
}
|
||||
|
||||
String mimeType = null;
|
||||
@ -1727,7 +1734,7 @@ public class QJavalinImplementation
|
||||
}
|
||||
catch(QUserFacingException e)
|
||||
{
|
||||
handleException(HttpStatus.Code.BAD_REQUEST, context, e);
|
||||
QJavalinUtils.handleException(HttpStatus.Code.BAD_REQUEST, context, e);
|
||||
return null;
|
||||
}
|
||||
return reportFormat;
|
||||
@ -1816,7 +1823,7 @@ public class QJavalinImplementation
|
||||
if(CollectionUtils.nullSafeHasContents(valuesParamList))
|
||||
{
|
||||
String valuesParam = valuesParamList.get(0);
|
||||
values = JsonUtils.toObject(valuesParam, new TypeReference<>(){});
|
||||
values = JsonUtils.toObject(valuesParam, new TypeReference<>() {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1867,67 +1874,7 @@ public class QJavalinImplementation
|
||||
*******************************************************************************/
|
||||
public static void handleException(Context context, Exception e)
|
||||
{
|
||||
handleException(null, context, e);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void handleException(HttpStatus.Code statusCode, Context context, Exception e)
|
||||
{
|
||||
QUserFacingException userFacingException = ExceptionUtils.findClassInRootChain(e, QUserFacingException.class);
|
||||
if(userFacingException != null)
|
||||
{
|
||||
if(userFacingException instanceof QNotFoundException)
|
||||
{
|
||||
statusCode = Objects.requireNonNullElse(statusCode, HttpStatus.Code.NOT_FOUND); // 404
|
||||
respondWithError(context, statusCode, userFacingException.getMessage());
|
||||
}
|
||||
else if(userFacingException instanceof QBadRequestException)
|
||||
{
|
||||
statusCode = Objects.requireNonNullElse(statusCode, HttpStatus.Code.BAD_REQUEST); // 400
|
||||
respondWithError(context, statusCode, userFacingException.getMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.info("User-facing exception", e);
|
||||
statusCode = Objects.requireNonNullElse(statusCode, HttpStatus.Code.INTERNAL_SERVER_ERROR); // 500
|
||||
respondWithError(context, statusCode, userFacingException.getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(e instanceof QAuthenticationException)
|
||||
{
|
||||
respondWithError(context, HttpStatus.Code.UNAUTHORIZED, e.getMessage()); // 401
|
||||
return;
|
||||
}
|
||||
|
||||
if(e instanceof QPermissionDeniedException)
|
||||
{
|
||||
respondWithError(context, HttpStatus.Code.FORBIDDEN, e.getMessage()); // 403
|
||||
return;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// default exception handling //
|
||||
////////////////////////////////
|
||||
LOG.warn("Exception in javalin request", e);
|
||||
respondWithError(context, HttpStatus.Code.INTERNAL_SERVER_ERROR, e.getClass().getSimpleName() + " (" + e.getMessage() + ")"); // 500
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void respondWithError(Context context, HttpStatus.Code statusCode, String errorMessage)
|
||||
{
|
||||
context.status(statusCode.getCode());
|
||||
context.result(JsonUtils.toJson(Map.of("error", errorMessage)));
|
||||
QJavalinUtils.handleException(null, context, e);
|
||||
}
|
||||
|
||||
|
||||
@ -1946,7 +1893,7 @@ public class QJavalinImplementation
|
||||
** Getter for javalinMetaData
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QJavalinMetaData getJavalinMetaData()
|
||||
public static QJavalinMetaData getJavalinMetaData()
|
||||
{
|
||||
return javalinMetaData;
|
||||
}
|
||||
@ -1975,7 +1922,7 @@ public class QJavalinImplementation
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for qInstanceHotSwapSupplier
|
||||
** Getter for qInstance
|
||||
*******************************************************************************/
|
||||
public static QInstance getQInstance()
|
||||
{
|
||||
@ -1984,6 +1931,16 @@ public class QJavalinImplementation
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for qInstance
|
||||
*******************************************************************************/
|
||||
public static void setQInstance(QInstance qInstance)
|
||||
{
|
||||
QJavalinImplementation.qInstance = qInstance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -2011,4 +1968,30 @@ public class QJavalinImplementation
|
||||
{
|
||||
return (startTime);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public void addJavalinRoutes(EndpointGroup routes)
|
||||
{
|
||||
if(endpointGroups == null)
|
||||
{
|
||||
endpointGroups = new ArrayList<>();
|
||||
}
|
||||
endpointGroups.add(routes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** if restarting this class, and you want to re-run addJavalinRoutes, but
|
||||
** not create duplicates, well, you might want to call this method!
|
||||
***************************************************************************/
|
||||
public void clearJavalinRoutes()
|
||||
{
|
||||
endpointGroups = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
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;
|
||||
@ -231,13 +232,13 @@ public class QJavalinProcessHandler
|
||||
|
||||
if(inputField.getIsRequired() && !setValue)
|
||||
{
|
||||
QJavalinImplementation.respondWithError(context, HttpStatus.Code.BAD_REQUEST, "Missing query param value for required input field: [" + inputField.getName() + "]");
|
||||
QJavalinUtils.respondWithError(context, HttpStatus.Code.BAD_REQUEST, "Missing query param value for required input field: [" + inputField.getName() + "]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinImplementation.respondWithError(context, HttpStatus.Code.BAD_REQUEST, "Error processing query param [" + inputField.getName() + "]: " + e.getClass().getSimpleName() + " (" + e.getMessage() + ")");
|
||||
QJavalinUtils.respondWithError(context, HttpStatus.Code.BAD_REQUEST, "Error processing query param [" + inputField.getName() + "]: " + e.getClass().getSimpleName() + " (" + e.getMessage() + ")");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -428,7 +429,18 @@ public class QJavalinProcessHandler
|
||||
QJavalinAccessLogger.logEndSuccess();
|
||||
}
|
||||
|
||||
context.result(JsonUtils.toJson(resultForCaller));
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Note: originally we did not have this serializationInclusion:ALWAYS here - //
|
||||
// which meant that null and empty values from backend would not go to frontend, //
|
||||
// which made things like clearing out a value in a field not happen. //
|
||||
// So, this is added to get that beneficial effect. Unclear if there are any //
|
||||
// negative side-effects - but be aware. //
|
||||
// One could imagine that we'd need this to be configurable in the future? //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
context.result(JsonUtils.toJson(resultForCaller, mapper ->
|
||||
{
|
||||
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@ -449,11 +461,19 @@ public class QJavalinProcessHandler
|
||||
resultForCaller.put("values", runProcessOutput.getValues());
|
||||
runProcessOutput.getProcessState().getNextStepName().ifPresent(nextStep -> resultForCaller.put("nextStep", nextStep));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// todo - delete after all frontends look for processMetaDataAdjustment instead of updatedFrontendStepList //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
List<QFrontendStepMetaData> updatedFrontendStepList = runProcessOutput.getUpdatedFrontendStepList();
|
||||
if(updatedFrontendStepList != null)
|
||||
{
|
||||
resultForCaller.put("updatedFrontendStepList", updatedFrontendStepList);
|
||||
}
|
||||
|
||||
if(runProcessOutput.getProcessMetaDataAdjustment() != null)
|
||||
{
|
||||
resultForCaller.put("processMetaDataAdjustment", runProcessOutput.getProcessMetaDataAdjustment());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,11 +22,21 @@
|
||||
package com.kingsrook.qqq.backend.javalin;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QBadRequestException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QPermissionDeniedException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QValueException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.utils.ExceptionUtils;
|
||||
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 io.javalin.http.Context;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -34,6 +44,10 @@ import io.javalin.http.Context;
|
||||
*******************************************************************************/
|
||||
public class QJavalinUtils
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(QJavalinUtils.class);
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Returns Integer if context has a valid int query parameter by the given name,
|
||||
@ -178,4 +192,64 @@ public class QJavalinUtils
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void handleException(HttpStatus.Code statusCode, Context context, Exception e)
|
||||
{
|
||||
QUserFacingException userFacingException = ExceptionUtils.findClassInRootChain(e, QUserFacingException.class);
|
||||
if(userFacingException != null)
|
||||
{
|
||||
if(userFacingException instanceof QNotFoundException)
|
||||
{
|
||||
statusCode = Objects.requireNonNullElse(statusCode, HttpStatus.Code.NOT_FOUND); // 404
|
||||
respondWithError(context, statusCode, userFacingException.getMessage());
|
||||
}
|
||||
else if(userFacingException instanceof QBadRequestException)
|
||||
{
|
||||
statusCode = Objects.requireNonNullElse(statusCode, HttpStatus.Code.BAD_REQUEST); // 400
|
||||
respondWithError(context, statusCode, userFacingException.getMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.info("User-facing exception", e);
|
||||
statusCode = Objects.requireNonNullElse(statusCode, HttpStatus.Code.INTERNAL_SERVER_ERROR); // 500
|
||||
respondWithError(context, statusCode, userFacingException.getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(e instanceof QAuthenticationException)
|
||||
{
|
||||
respondWithError(context, HttpStatus.Code.UNAUTHORIZED, e.getMessage()); // 401
|
||||
return;
|
||||
}
|
||||
|
||||
if(e instanceof QPermissionDeniedException)
|
||||
{
|
||||
respondWithError(context, HttpStatus.Code.FORBIDDEN, e.getMessage()); // 403
|
||||
return;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// default exception handling //
|
||||
////////////////////////////////
|
||||
LOG.warn("Exception in javalin request", e);
|
||||
respondWithError(context, HttpStatus.Code.INTERNAL_SERVER_ERROR, e.getClass().getSimpleName() + " (" + e.getMessage() + ")"); // 500
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void respondWithError(Context context, HttpStatus.Code statusCode, String errorMessage)
|
||||
{
|
||||
context.status(statusCode.getCode());
|
||||
context.result(JsonUtils.toJson(Map.of("error", errorMessage)));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,529 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||
import com.kingsrook.qqq.backend.core.instances.AbstractQQQApplication;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.utils.ClassPathUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractMiddlewareVersion;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.MiddlewareVersionV1;
|
||||
import io.javalin.Javalin;
|
||||
import io.javalin.http.Context;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Second-generation qqq javalin server.
|
||||
**
|
||||
** An evolution over the original QJavalinImplementation, which both managed
|
||||
** the javalin instance itself, but also provided all of the endpoint handlers...
|
||||
** This class instead just configures & starts the server.
|
||||
**
|
||||
** Makes several setters available, to let application-developer choose what
|
||||
** standard qqq endpoints are served (e.g., frontend-material-dashboard, the
|
||||
** legacy-unversioned middleware, newer versioned-middleware, and additional qqq
|
||||
** modules or application-defined services (both provided as instances of
|
||||
** QJavalinRouteProviderInterface).
|
||||
**
|
||||
** System property `qqq.javalin.hotSwapInstance` (defaults to false), causes the
|
||||
** QInstance to be re-loaded every X millis, to avoid some server restarts while
|
||||
** doing dev.
|
||||
*******************************************************************************/
|
||||
public class QApplicationJavalinServer
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(QApplicationJavalinServer.class);
|
||||
|
||||
private final AbstractQQQApplication application;
|
||||
|
||||
private Integer port = 8000;
|
||||
private boolean serveFrontendMaterialDashboard = true;
|
||||
private boolean serveLegacyUnversionedMiddlewareAPI = true;
|
||||
private List<AbstractMiddlewareVersion> middlewareVersionList = List.of(new MiddlewareVersionV1());
|
||||
private List<QJavalinRouteProviderInterface> additionalRouteProviders = null;
|
||||
private Consumer<Javalin> javalinConfigurationCustomizer = null;
|
||||
|
||||
private long lastQInstanceHotSwapMillis;
|
||||
private long millisBetweenHotSwaps = 2500;
|
||||
private Consumer<QInstance> hotSwapCustomizer = null;
|
||||
|
||||
private Javalin service;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QApplicationJavalinServer(AbstractQQQApplication application)
|
||||
{
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public void start() throws QException
|
||||
{
|
||||
QInstance qInstance = application.defineValidatedQInstance();
|
||||
|
||||
service = Javalin.create(config ->
|
||||
{
|
||||
if(serveFrontendMaterialDashboard)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// If you have any assets to add to the web server (e.g., logos, icons) place them at //
|
||||
// src/main/resources/material-dashboard-overlay (or a directory of your choice //
|
||||
// under src/main/resources) and use this line of code to tell javalin about it. //
|
||||
// Make sure to add your app-specific directory to the javalin config before the core //
|
||||
// material-dashboard directory, so in case the same file exists in both (e.g., //
|
||||
// favicon.png), the app-specific one will be used. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
config.staticFiles.add("/material-dashboard-overlay");
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// tell javalin where to find material-dashboard static web assets //
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
config.staticFiles.add("/material-dashboard");
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// set the index page for the SPA from material dashboard //
|
||||
////////////////////////////////////////////////////////////
|
||||
config.spaRoot.addFile("/", "material-dashboard/index.html");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
// add qqq routes to the javalin service //
|
||||
///////////////////////////////////////////
|
||||
if(serveLegacyUnversionedMiddlewareAPI)
|
||||
{
|
||||
try
|
||||
{
|
||||
QJavalinImplementation qJavalinImplementation = new QJavalinImplementation(qInstance);
|
||||
config.router.apiBuilder(qJavalinImplementation.getRoutes());
|
||||
}
|
||||
catch(QInstanceValidationException e)
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// we should be pretty comfortable that this won't happen, because we've pre-validated the instance above... //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
// versioned qqq middleware routes //
|
||||
/////////////////////////////////////
|
||||
if(CollectionUtils.nullSafeHasContents(middlewareVersionList))
|
||||
{
|
||||
config.router.apiBuilder(new QMiddlewareApiSpecHandler(middlewareVersionList).defineJavalinEndpointGroup());
|
||||
for(AbstractMiddlewareVersion version : middlewareVersionList)
|
||||
{
|
||||
version.setQInstance(qInstance);
|
||||
config.router.apiBuilder(version.getJavalinEndpointGroup(qInstance));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// additional route providers (e.g., application-apis, other middlewares) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
for(QJavalinRouteProviderInterface routeProvider : CollectionUtils.nonNullList(additionalRouteProviders))
|
||||
{
|
||||
routeProvider.setQInstance(qInstance);
|
||||
config.router.apiBuilder(routeProvider.getJavalinEndpointGroup());
|
||||
}
|
||||
});
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// per system property, set the server to hot-swap the q instance before all routes //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
String hotSwapPropertyValue = System.getProperty("qqq.javalin.hotSwapInstance", "false");
|
||||
if(BooleanUtils.isTrue(ValueUtils.getValueAsBoolean(hotSwapPropertyValue)))
|
||||
{
|
||||
LOG.info("Server will hotSwap QInstance before requests every [" + millisBetweenHotSwaps + "] millis.");
|
||||
service.before(context -> hotSwapQInstance());
|
||||
}
|
||||
|
||||
service.before((Context context) -> context.header("Content-Type", "application/json"));
|
||||
service.after(QJavalinImplementation::clearQContext);
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// allow a configuration-customizer to be run //
|
||||
////////////////////////////////////////////////
|
||||
if(javalinConfigurationCustomizer != null)
|
||||
{
|
||||
javalinConfigurationCustomizer.accept(service);
|
||||
}
|
||||
|
||||
service.start(port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public void stop()
|
||||
{
|
||||
if(this.service == null)
|
||||
{
|
||||
LOG.info("Stop called, but there is no javalin service, so noop.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.service.stop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** If there's a qInstanceHotSwapSupplier, and its been a little while, replace
|
||||
** the qInstance with a new one from the supplier. Meant to be used while doing
|
||||
** development.
|
||||
*******************************************************************************/
|
||||
public void hotSwapQInstance()
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
if(now - lastQInstanceHotSwapMillis < millisBetweenHotSwaps)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lastQInstanceHotSwapMillis = now;
|
||||
|
||||
try
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// clear the cache of classes in this class, so that new classes can be found if a meta-data-producer is being used //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
ClassPathUtils.clearTopLevelClassCache();
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// try to get a new, validated instance from the application //
|
||||
///////////////////////////////////////////////////////////////
|
||||
QInstance newQInstance = application.defineValidatedQInstance();
|
||||
if(newQInstance == null)
|
||||
{
|
||||
LOG.warn("Got a null qInstance from the application.defineQInstance(). Not hot-swapping.");
|
||||
return;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// allow a hot-swap customizer to run //
|
||||
////////////////////////////////////////
|
||||
if(hotSwapCustomizer != null)
|
||||
{
|
||||
hotSwapCustomizer.accept(newQInstance);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// pass the new qInstance into all of the objects serving qqq routes //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
if(serveLegacyUnversionedMiddlewareAPI)
|
||||
{
|
||||
QJavalinImplementation.setQInstance(newQInstance);
|
||||
}
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(middlewareVersionList))
|
||||
{
|
||||
for(AbstractMiddlewareVersion spec : CollectionUtils.nonNullList(middlewareVersionList))
|
||||
{
|
||||
spec.setQInstance(newQInstance);
|
||||
}
|
||||
}
|
||||
|
||||
for(QJavalinRouteProviderInterface routeProvider : CollectionUtils.nonNullList(additionalRouteProviders))
|
||||
{
|
||||
routeProvider.setQInstance(newQInstance);
|
||||
}
|
||||
|
||||
LOG.info("Swapped qInstance");
|
||||
}
|
||||
catch(QInstanceValidationException e)
|
||||
{
|
||||
LOG.error("Validation Error while hot-swapping QInstance", e);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.error("Error hot-swapping QInstance", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for port
|
||||
*******************************************************************************/
|
||||
public Integer getPort()
|
||||
{
|
||||
return (this.port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for port
|
||||
*******************************************************************************/
|
||||
public void setPort(Integer port)
|
||||
{
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for port
|
||||
*******************************************************************************/
|
||||
public QApplicationJavalinServer withPort(Integer port)
|
||||
{
|
||||
this.port = port;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setMillisBetweenHotSwaps(long millisBetweenHotSwaps)
|
||||
{
|
||||
this.millisBetweenHotSwaps = millisBetweenHotSwaps;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for serveFrontendMaterialDashboard
|
||||
*******************************************************************************/
|
||||
public boolean getServeFrontendMaterialDashboard()
|
||||
{
|
||||
return (this.serveFrontendMaterialDashboard);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for serveFrontendMaterialDashboard
|
||||
*******************************************************************************/
|
||||
public void setServeFrontendMaterialDashboard(boolean serveFrontendMaterialDashboard)
|
||||
{
|
||||
this.serveFrontendMaterialDashboard = serveFrontendMaterialDashboard;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for serveFrontendMaterialDashboard
|
||||
*******************************************************************************/
|
||||
public QApplicationJavalinServer withServeFrontendMaterialDashboard(boolean serveFrontendMaterialDashboard)
|
||||
{
|
||||
this.serveFrontendMaterialDashboard = serveFrontendMaterialDashboard;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for serveLegacyUnversionedMiddlewareAPI
|
||||
*******************************************************************************/
|
||||
public boolean getServeLegacyUnversionedMiddlewareAPI()
|
||||
{
|
||||
return (this.serveLegacyUnversionedMiddlewareAPI);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for serveLegacyUnversionedMiddlewareAPI
|
||||
*******************************************************************************/
|
||||
public void setServeLegacyUnversionedMiddlewareAPI(boolean serveLegacyUnversionedMiddlewareAPI)
|
||||
{
|
||||
this.serveLegacyUnversionedMiddlewareAPI = serveLegacyUnversionedMiddlewareAPI;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for serveLegacyUnversionedMiddlewareAPI
|
||||
*******************************************************************************/
|
||||
public QApplicationJavalinServer withServeLegacyUnversionedMiddlewareAPI(boolean serveLegacyUnversionedMiddlewareAPI)
|
||||
{
|
||||
this.serveLegacyUnversionedMiddlewareAPI = serveLegacyUnversionedMiddlewareAPI;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for middlewareVersionList
|
||||
*******************************************************************************/
|
||||
public List<AbstractMiddlewareVersion> getMiddlewareVersionList()
|
||||
{
|
||||
return (this.middlewareVersionList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for middlewareVersionList
|
||||
*******************************************************************************/
|
||||
public void setMiddlewareVersionList(List<AbstractMiddlewareVersion> middlewareVersionList)
|
||||
{
|
||||
this.middlewareVersionList = middlewareVersionList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for middlewareVersionList
|
||||
*******************************************************************************/
|
||||
public QApplicationJavalinServer withMiddlewareVersionList(List<AbstractMiddlewareVersion> middlewareVersionList)
|
||||
{
|
||||
this.middlewareVersionList = middlewareVersionList;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for additionalRouteProviders
|
||||
*******************************************************************************/
|
||||
public List<QJavalinRouteProviderInterface> getAdditionalRouteProviders()
|
||||
{
|
||||
return (this.additionalRouteProviders);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for additionalRouteProviders
|
||||
*******************************************************************************/
|
||||
public void setAdditionalRouteProviders(List<QJavalinRouteProviderInterface> additionalRouteProviders)
|
||||
{
|
||||
this.additionalRouteProviders = additionalRouteProviders;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for additionalRouteProviders
|
||||
*******************************************************************************/
|
||||
public QApplicationJavalinServer withAdditionalRouteProviders(List<QJavalinRouteProviderInterface> additionalRouteProviders)
|
||||
{
|
||||
this.additionalRouteProviders = additionalRouteProviders;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for MILLIS_BETWEEN_HOT_SWAPS
|
||||
*******************************************************************************/
|
||||
public long getMillisBetweenHotSwaps()
|
||||
{
|
||||
return (millisBetweenHotSwaps);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for MILLIS_BETWEEN_HOT_SWAPS
|
||||
*******************************************************************************/
|
||||
public QApplicationJavalinServer withMillisBetweenHotSwaps(long millisBetweenHotSwaps)
|
||||
{
|
||||
this.millisBetweenHotSwaps = millisBetweenHotSwaps;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for hotSwapCustomizer
|
||||
*******************************************************************************/
|
||||
public Consumer<QInstance> getHotSwapCustomizer()
|
||||
{
|
||||
return (this.hotSwapCustomizer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for hotSwapCustomizer
|
||||
*******************************************************************************/
|
||||
public void setHotSwapCustomizer(Consumer<QInstance> hotSwapCustomizer)
|
||||
{
|
||||
this.hotSwapCustomizer = hotSwapCustomizer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for hotSwapCustomizer
|
||||
*******************************************************************************/
|
||||
public QApplicationJavalinServer withHotSwapCustomizer(Consumer<QInstance> hotSwapCustomizer)
|
||||
{
|
||||
this.hotSwapCustomizer = hotSwapCustomizer;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for javalinConfigurationCustomizer
|
||||
*******************************************************************************/
|
||||
public Consumer<Javalin> getJavalinConfigurationCustomizer()
|
||||
{
|
||||
return (this.javalinConfigurationCustomizer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for javalinConfigurationCustomizer
|
||||
*******************************************************************************/
|
||||
public void setJavalinConfigurationCustomizer(Consumer<Javalin> javalinConfigurationCustomizer)
|
||||
{
|
||||
this.javalinConfigurationCustomizer = javalinConfigurationCustomizer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for javalinConfigurationCustomizer
|
||||
*******************************************************************************/
|
||||
public QApplicationJavalinServer withJavalinConfigurationCustomizer(Consumer<Javalin> javalinConfigurationCustomizer)
|
||||
{
|
||||
this.javalinConfigurationCustomizer = javalinConfigurationCustomizer;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import io.javalin.apibuilder.EndpointGroup;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Interface for classes that can provide a list of endpoints to a javalin
|
||||
** server.
|
||||
*******************************************************************************/
|
||||
public interface QJavalinRouteProviderInterface
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
** For initial setup when server boots, set the qInstance - but also,
|
||||
** e.g., for development, to do a hot-swap.
|
||||
***************************************************************************/
|
||||
void setQInstance(QInstance qInstance);
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
EndpointGroup getJavalinEndpointGroup();
|
||||
|
||||
}
|
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin;
|
||||
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.YamlUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractMiddlewareVersion;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.MiddlewareVersionV1;
|
||||
import com.kingsrook.qqq.openapi.model.OpenAPI;
|
||||
import io.javalin.apibuilder.ApiBuilder;
|
||||
import io.javalin.apibuilder.EndpointGroup;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.Context;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** javalin-handler that serves both rapidoc static html/css/js files, and
|
||||
** dynamically generated openapi json/yaml, for a given list of qqq middleware
|
||||
** versions
|
||||
*******************************************************************************/
|
||||
public class QMiddlewareApiSpecHandler
|
||||
{
|
||||
private final List<AbstractMiddlewareVersion> middlewareVersionList;
|
||||
private final String basePath;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QMiddlewareApiSpecHandler(List<AbstractMiddlewareVersion> middlewareVersionList)
|
||||
{
|
||||
this(middlewareVersionList, "qqq");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QMiddlewareApiSpecHandler(List<AbstractMiddlewareVersion> middlewareVersionList, String basePath)
|
||||
{
|
||||
this.middlewareVersionList = middlewareVersionList;
|
||||
this.basePath = basePath.replaceFirst("^/+", "").replaceFirst("/+$", "");;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public EndpointGroup defineJavalinEndpointGroup()
|
||||
{
|
||||
return (() ->
|
||||
{
|
||||
ApiBuilder.get("/api/docs/js/rapidoc.min.js", (context) -> serveResource(context, "rapidoc/rapidoc-9.3.8.min.js", MapBuilder.of("Content-Type", ContentType.JAVASCRIPT)));
|
||||
ApiBuilder.get("/api/docs/css/qqq-api-styles.css", (context) -> serveResource(context, "rapidoc/rapidoc-overrides.css", MapBuilder.of("Content-Type", ContentType.CSS)));
|
||||
ApiBuilder.get("/images/qqq-api-logo.png", (context) -> serveResource(context, "images/qqq-on-crown-trans-160x80.png", MapBuilder.of("Content-Type", ContentType.IMAGE_PNG.getMimeType())));
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// default page is the current version spec //
|
||||
//////////////////////////////////////////////
|
||||
ApiBuilder.get("/" + basePath + "/", context -> doSpecHtml(context));
|
||||
ApiBuilder.get("/" + basePath + "/versions.json", context -> doVersions(context));
|
||||
|
||||
////////////////////////////////////////////
|
||||
// default page for a version is its spec //
|
||||
////////////////////////////////////////////
|
||||
for(AbstractMiddlewareVersion middlewareSpec : middlewareVersionList)
|
||||
{
|
||||
String version = middlewareSpec.getVersion();
|
||||
String versionPath = "/" + basePath + "/" + version;
|
||||
ApiBuilder.get(versionPath + "/", context -> doSpecHtml(context, version));
|
||||
|
||||
///////////////////////////////////////////
|
||||
// add known paths for specs & docs page //
|
||||
///////////////////////////////////////////
|
||||
ApiBuilder.get(versionPath + "/openapi.yaml", context -> doSpecYaml(context, version));
|
||||
ApiBuilder.get(versionPath + "/openapi.json", context -> doSpecJson(context, version));
|
||||
ApiBuilder.get(versionPath + "/openapi.html", context -> doSpecHtml(context, version));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** list the versions in this api
|
||||
*******************************************************************************/
|
||||
private void doVersions(Context context)
|
||||
{
|
||||
Map<String, Object> rs = new HashMap<>();
|
||||
|
||||
List<String> supportedVersions = middlewareVersionList.stream().map(msi -> msi.getVersion()).toList();
|
||||
String currentVersion = supportedVersions.get(supportedVersions.size() - 1);
|
||||
|
||||
rs.put("supportedVersions", supportedVersions);
|
||||
rs.put("currentVersion", currentVersion);
|
||||
|
||||
context.contentType(ContentType.APPLICATION_JSON);
|
||||
context.result(JsonUtils.toJson(rs));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void serveResource(Context context, String resourcePath, Map<String, String> headers)
|
||||
{
|
||||
InputStream resourceAsStream = QJavalinImplementation.class.getClassLoader().getResourceAsStream(resourcePath);
|
||||
for(Map.Entry<String, String> entry : CollectionUtils.nonNullMap(headers).entrySet())
|
||||
{
|
||||
context.header(entry.getKey(), entry.getValue());
|
||||
}
|
||||
context.result(resourceAsStream);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void doSpecYaml(Context context, String version)
|
||||
{
|
||||
try
|
||||
{
|
||||
OpenAPI openAPI = new MiddlewareVersionV1().generateOpenAPIModel(basePath);
|
||||
context.contentType(ContentType.APPLICATION_YAML);
|
||||
context.result(YamlUtils.toYaml(openAPI));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinImplementation.handleException(context, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void doSpecJson(Context context, String version)
|
||||
{
|
||||
try
|
||||
{
|
||||
OpenAPI openAPI = new MiddlewareVersionV1().generateOpenAPIModel(basePath);
|
||||
context.contentType(ContentType.APPLICATION_JSON);
|
||||
context.result(JsonUtils.toJson(openAPI));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinImplementation.handleException(context, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void doSpecHtml(Context context)
|
||||
{
|
||||
String version = null;
|
||||
|
||||
try
|
||||
{
|
||||
version = context.pathParam("version");
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
////////////////
|
||||
// leave null //
|
||||
////////////////
|
||||
}
|
||||
|
||||
if(!StringUtils.hasContent(version))
|
||||
{
|
||||
List<String> supportedVersions = middlewareVersionList.stream().map(msi -> msi.getVersion()).toList();
|
||||
version = supportedVersions.get(supportedVersions.size() - 1);
|
||||
}
|
||||
|
||||
doSpecHtml(context, version);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void doSpecHtml(Context context, String version)
|
||||
{
|
||||
try
|
||||
{
|
||||
//////////////////////////////////
|
||||
// read html from resource file //
|
||||
//////////////////////////////////
|
||||
InputStream resourceAsStream = QMiddlewareApiSpecHandler.class.getClassLoader().getResourceAsStream("rapidoc/rapidoc-container.html");
|
||||
String html = IOUtils.toString(resourceAsStream, StandardCharsets.UTF_8);
|
||||
|
||||
/////////////////////////////////
|
||||
// do replacements in the html //
|
||||
/////////////////////////////////
|
||||
html = html.replace("{spec-url}", "/" + basePath + "/" + version + "/openapi.json");
|
||||
html = html.replace("{version}", version);
|
||||
html = html.replace("{primaryColor}", "#444444");
|
||||
html = html.replace("{navLogoImg}", "<img id=\"navLogo\" slot=\"nav-logo\" src=\"/images/qqq-api-logo.png\" />");
|
||||
|
||||
Optional<AbstractMiddlewareVersion> middlewareSpec = middlewareVersionList.stream().filter(msi -> msi.getVersion().equals(version)).findFirst();
|
||||
if(middlewareSpec.isEmpty())
|
||||
{
|
||||
throw (new QUserFacingException("Unrecognized version: " + version));
|
||||
}
|
||||
|
||||
OpenAPI openAPI = middlewareSpec.get().generateOpenAPIModel(basePath);
|
||||
html = html.replace("{title}", openAPI.getInfo().getTitle() + " - " + version);
|
||||
|
||||
StringBuilder otherVersionOptions = new StringBuilder();
|
||||
for(AbstractMiddlewareVersion otherVersionSpec : middlewareVersionList)
|
||||
{
|
||||
otherVersionOptions.append("<option value=\"/").append(basePath).append("/").append(otherVersionSpec.getVersion()).append("/openapi.html\">").append(otherVersionSpec.getVersion()).append("</option>");
|
||||
}
|
||||
|
||||
html = html.replace("{otherVersionOptions}", otherVersionOptions.toString());
|
||||
|
||||
context.contentType(ContentType.HTML);
|
||||
context.result(html);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinImplementation.handleException(context, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.AbstractMiddlewareInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.AbstractMiddlewareOutputInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public abstract class AbstractMiddlewareExecutor<INPUT extends AbstractMiddlewareInput, OUTPUT extends AbstractMiddlewareOutputInterface>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public abstract void execute(INPUT input, OUTPUT output) throws QException;
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.AuthenticationMetaDataInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.AuthenticationMetaDataOutputInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class AuthenticationMetaDataExecutor extends AbstractMiddlewareExecutor<AuthenticationMetaDataInput, AuthenticationMetaDataOutputInterface>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void execute(AuthenticationMetaDataInput input, AuthenticationMetaDataOutputInterface output) throws QException
|
||||
{
|
||||
output.setAuthenticationMetaData(QContext.getQInstance().getAuthentication());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleDispatcher;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.implementations.Auth0AuthenticationModule;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import io.javalin.http.Context;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ExecutorSessionUtils
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(ExecutorSessionUtils.class);
|
||||
|
||||
public static final int SESSION_COOKIE_AGE = 60 * 60 * 24;
|
||||
public static final String SESSION_ID_COOKIE_NAME = "sessionId";
|
||||
public static final String API_KEY_NAME = "apiKey";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QSession setupSession(Context context, QInstance qInstance) throws QModuleDispatchException, QAuthenticationException
|
||||
{
|
||||
QAuthenticationModuleDispatcher qAuthenticationModuleDispatcher = new QAuthenticationModuleDispatcher();
|
||||
QAuthenticationModuleInterface authenticationModule = qAuthenticationModuleDispatcher.getQModule(qInstance.getAuthentication());
|
||||
|
||||
try
|
||||
{
|
||||
Map<String, String> authenticationContext = new HashMap<>();
|
||||
|
||||
String sessionIdCookieValue = context.cookie(SESSION_ID_COOKIE_NAME);
|
||||
String sessionUuidCookieValue = context.cookie(Auth0AuthenticationModule.SESSION_UUID_KEY);
|
||||
String authorizationHeaderValue = context.header("Authorization");
|
||||
String apiKeyHeaderValue = context.header("x-api-key");
|
||||
|
||||
if(StringUtils.hasContent(sessionIdCookieValue))
|
||||
{
|
||||
///////////////////////////////////////////////////////
|
||||
// sessionId - maybe used by table-based auth module //
|
||||
///////////////////////////////////////////////////////
|
||||
authenticationContext.put(SESSION_ID_COOKIE_NAME, sessionIdCookieValue);
|
||||
}
|
||||
else if(StringUtils.hasContent(sessionUuidCookieValue))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// session UUID - known to be used by auth0 module (in aug. 2023 update) //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
authenticationContext.put(Auth0AuthenticationModule.SESSION_UUID_KEY, sessionUuidCookieValue);
|
||||
}
|
||||
else if(apiKeyHeaderValue != null)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// next, look for an api key header: //
|
||||
// this will be used to look up auth0 values via an auth table //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
authenticationContext.put(API_KEY_NAME, apiKeyHeaderValue);
|
||||
}
|
||||
else if(authorizationHeaderValue != null)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// second, look for the authorization header: //
|
||||
// either with a "Basic " prefix (for a username:password pair) //
|
||||
// or with a "Bearer " prefix (for a token that can be handled the same as a sessionId cookie) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
processAuthorizationValue(authenticationContext, authorizationHeaderValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
String authorizationFormValue = context.formParam("Authorization");
|
||||
if(StringUtils.hasContent(authorizationFormValue))
|
||||
{
|
||||
processAuthorizationValue(authenticationContext, authorizationFormValue);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.info("Exception looking for Authorization formParam", e);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// put the qInstance into context - but no session yet (since, the whole point of this call is to setup the session!) //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QContext.init(qInstance, null);
|
||||
QSession session = authenticationModule.createSession(qInstance, authenticationContext);
|
||||
QContext.init(qInstance, session, null, null);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// todo - this must be moved ... not exactly sure where, but into some spec. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// String tableVariant = QJavalinUtils.getFormParamOrQueryParam(context, "tableVariant");
|
||||
// if(StringUtils.hasContent(tableVariant))
|
||||
// {
|
||||
// JSONObject variant = new JSONObject(tableVariant);
|
||||
// QContext.getQSession().setBackendVariants(MapBuilder.of(variant.getString("type"), variant.getInt("id")));
|
||||
// }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// if we got a session id cookie in, then send it back with updated cookie age //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
if(authenticationModule.usesSessionIdCookie())
|
||||
{
|
||||
context.cookie(SESSION_ID_COOKIE_NAME, session.getIdReference(), SESSION_COOKIE_AGE);
|
||||
}
|
||||
|
||||
setUserTimezoneOffsetMinutesInSession(context, session);
|
||||
setUserTimezoneInSession(context, session);
|
||||
|
||||
return (session);
|
||||
}
|
||||
catch(QAuthenticationException qae)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// if exception caught, clear out the cookie so the frontend will reauthorize //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
if(authenticationModule.usesSessionIdCookie())
|
||||
{
|
||||
context.removeCookie(SESSION_ID_COOKIE_NAME);
|
||||
}
|
||||
|
||||
throw (qae);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void processAuthorizationValue(Map<String, String> authenticationContext, String authorizationHeaderValue)
|
||||
{
|
||||
String basicPrefix = "Basic ";
|
||||
String bearerPrefix = "Bearer ";
|
||||
if(authorizationHeaderValue.startsWith(basicPrefix))
|
||||
{
|
||||
authorizationHeaderValue = authorizationHeaderValue.replaceFirst(basicPrefix, "");
|
||||
authenticationContext.put(Auth0AuthenticationModule.BASIC_AUTH_KEY, authorizationHeaderValue);
|
||||
}
|
||||
else if(authorizationHeaderValue.startsWith(bearerPrefix))
|
||||
{
|
||||
authorizationHeaderValue = authorizationHeaderValue.replaceFirst(bearerPrefix, "");
|
||||
authenticationContext.put(Auth0AuthenticationModule.ACCESS_TOKEN_KEY, authorizationHeaderValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.debug("Authorization value did not have Basic or Bearer prefix. [" + authorizationHeaderValue + "]");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void setUserTimezoneOffsetMinutesInSession(Context context, QSession session)
|
||||
{
|
||||
String userTimezoneOffsetMinutes = context.header("X-QQQ-UserTimezoneOffsetMinutes");
|
||||
if(StringUtils.hasContent(userTimezoneOffsetMinutes))
|
||||
{
|
||||
try
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// even though we're putting it in the session as a string, go through parse int, to make sure it's a valid int. //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
session.setValue(QSession.VALUE_KEY_USER_TIMEZONE_OFFSET_MINUTES, String.valueOf(Integer.parseInt(userTimezoneOffsetMinutes)));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.debug("Received non-integer value for X-QQQ-UserTimezoneOffsetMinutes header: " + userTimezoneOffsetMinutes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void setUserTimezoneInSession(Context context, QSession session)
|
||||
{
|
||||
String userTimezone = context.header("X-QQQ-UserTimezone");
|
||||
if(StringUtils.hasContent(userTimezone))
|
||||
{
|
||||
session.setValue(QSession.VALUE_KEY_USER_TIMEZONE, userTimezone);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleDispatcher;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.implementations.Auth0AuthenticationModule;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ManageSessionInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ManageSessionOutputInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ManageSessionExecutor extends AbstractMiddlewareExecutor<ManageSessionInput, ManageSessionOutputInterface>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void execute(ManageSessionInput input, ManageSessionOutputInterface output) throws QException
|
||||
{
|
||||
QAuthenticationModuleDispatcher qAuthenticationModuleDispatcher = new QAuthenticationModuleDispatcher();
|
||||
QAuthenticationModuleInterface authenticationModule = qAuthenticationModuleDispatcher.getQModule(QContext.getQInstance().getAuthentication());
|
||||
|
||||
Map<String, String> authContext = new HashMap<>();
|
||||
authContext.put(Auth0AuthenticationModule.ACCESS_TOKEN_KEY, input.getAccessToken());
|
||||
authContext.put(Auth0AuthenticationModule.DO_STORE_USER_SESSION_KEY, "true");
|
||||
|
||||
/////////////////////////////////
|
||||
// (try to) create the session //
|
||||
/////////////////////////////////
|
||||
QSession session = authenticationModule.createSession(QContext.getQInstance(), authContext);
|
||||
|
||||
//////////////////
|
||||
// build output //
|
||||
//////////////////
|
||||
output.setUuid(session.getUuid());
|
||||
|
||||
if(session.getValuesForFrontend() != null)
|
||||
{
|
||||
LinkedHashMap<String, Serializable> valuesForFrontend = new LinkedHashMap<>(session.getValuesForFrontend());
|
||||
output.setValues(valuesForFrontend);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.MetaDataInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.MetaDataOutputInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MetaDataExecutor extends AbstractMiddlewareExecutor<MetaDataInput, MetaDataOutputInterface>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void execute(MetaDataInput input, MetaDataOutputInterface output) throws QException
|
||||
{
|
||||
com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput actionInput = new com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput();
|
||||
|
||||
actionInput.setMiddlewareName(input.getMiddlewareName());
|
||||
actionInput.setMiddlewareVersion(input.getMiddlewareVersion());
|
||||
actionInput.setFrontendName(input.getFrontendName());
|
||||
actionInput.setFrontendVersion(input.getFrontendVersion());
|
||||
actionInput.setApplicationName(input.getApplicationName());
|
||||
actionInput.setApplicationVersion(input.getApplicationVersion());
|
||||
|
||||
MetaDataAction metaDataAction = new MetaDataAction();
|
||||
MetaDataOutput metaDataOutput = metaDataAction.execute(actionInput);
|
||||
output.setMetaDataOutput(metaDataOutput);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobManager;
|
||||
import com.kingsrook.qqq.backend.core.actions.async.JobGoingAsyncException;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.QProcessCallback;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QPermissionDeniedException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
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.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinAccessLogger;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessInitOrStepInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessInitOrStepOrStatusOutputInterface;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.utils.ProcessExecutorUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessInitOrStepExecutor extends AbstractMiddlewareExecutor<ProcessInitOrStepInput, ProcessInitOrStepOrStatusOutputInterface>
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(ProcessInitOrStepExecutor.class);
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** Note: implementation of the output interface here, it wants to know what
|
||||
** type it's going to be first, so, be polite and always call .setType before
|
||||
** any other setters.
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void execute(ProcessInitOrStepInput input, ProcessInitOrStepOrStatusOutputInterface output) throws QException
|
||||
{
|
||||
Exception returningException = null;
|
||||
|
||||
String processName = input.getProcessName();
|
||||
String startAfterStep = input.getStartAfterStep();
|
||||
String processUUID = input.getProcessUUID();
|
||||
|
||||
if(processUUID == null)
|
||||
{
|
||||
processUUID = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
LOG.info(startAfterStep == null ? "Initiating process [" + processName + "] [" + processUUID + "]"
|
||||
: "Resuming process [" + processName + "] [" + processUUID + "] after step [" + startAfterStep + "]");
|
||||
|
||||
try
|
||||
{
|
||||
RunProcessInput runProcessInput = new RunProcessInput();
|
||||
QContext.pushAction(runProcessInput);
|
||||
|
||||
runProcessInput.setProcessName(processName);
|
||||
runProcessInput.setFrontendStepBehavior(input.getFrontendStepBehavior());
|
||||
runProcessInput.setProcessUUID(processUUID);
|
||||
runProcessInput.setStartAfterStep(startAfterStep);
|
||||
runProcessInput.setValues(Objects.requireNonNullElseGet(input.getValues(), HashMap::new));
|
||||
|
||||
if(input.getRecordsFilter() != null)
|
||||
{
|
||||
runProcessInput.setCallback(new QProcessCallback()
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public QQueryFilter getQueryFilter()
|
||||
{
|
||||
return (input.getRecordsFilter());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public Map<String, Serializable> getFieldValues(List<QFieldMetaData> fields)
|
||||
{
|
||||
return (Collections.emptyMap());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
String reportName = ValueUtils.getValueAsString(runProcessInput.getValue("reportName"));
|
||||
QJavalinAccessLogger.logStart(startAfterStep == null ? "processInit" : "processStep", logPair("processName", processName), logPair("processUUID", processUUID),
|
||||
StringUtils.hasContent(startAfterStep) ? logPair("startAfterStep", startAfterStep) : null,
|
||||
StringUtils.hasContent(reportName) ? logPair("reportName", reportName) : null);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// important to do this check AFTER the runProcessInput is populated with values from context - //
|
||||
// e.g., in case things like a reportName are set in here //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
PermissionsHelper.checkProcessPermissionThrowing(runProcessInput, processName);
|
||||
|
||||
////////////////////////////////////////
|
||||
// run the process as an async action //
|
||||
////////////////////////////////////////
|
||||
RunProcessOutput runProcessOutput = new AsyncJobManager().startJob(processName, input.getStepTimeoutMillis(), TimeUnit.MILLISECONDS, (callback) ->
|
||||
{
|
||||
runProcessInput.setAsyncJobCallback(callback);
|
||||
return (new RunProcessAction().execute(runProcessInput));
|
||||
});
|
||||
|
||||
LOG.debug("Process result error? " + runProcessOutput.getException());
|
||||
for(QFieldMetaData outputField : QContext.getQInstance().getProcess(runProcessInput.getProcessName()).getOutputFields())
|
||||
{
|
||||
LOG.debug("Process result output value: " + outputField.getName() + ": " + runProcessOutput.getValues().get(outputField.getName()));
|
||||
}
|
||||
|
||||
ProcessExecutorUtils.serializeRunProcessResultForCaller(output, processName, runProcessOutput);
|
||||
QJavalinAccessLogger.logProcessSummary(processName, processUUID, runProcessOutput);
|
||||
}
|
||||
catch(JobGoingAsyncException jgae)
|
||||
{
|
||||
output.setType(ProcessInitOrStepOrStatusOutputInterface.Type.JOB_STARTED);
|
||||
output.setJobUUID(jgae.getJobUUID());
|
||||
}
|
||||
catch(QPermissionDeniedException | QAuthenticationException e)
|
||||
{
|
||||
throw (e);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// our other actions in here would do: handleException(context, e); //
|
||||
// which would return a 500 to the client. //
|
||||
// but - other process-step actions, they always return a 200, just with an //
|
||||
// optional error message - so - keep all of the processes consistent. //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
returningException = e;
|
||||
ProcessExecutorUtils.serializeRunProcessExceptionForCaller(output, e);
|
||||
}
|
||||
|
||||
output.setProcessUUID(processUUID);
|
||||
|
||||
if(returningException != null)
|
||||
{
|
||||
QJavalinAccessLogger.logEndFail(returningException);
|
||||
}
|
||||
else
|
||||
{
|
||||
QJavalinAccessLogger.logEndSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.metadata.ProcessMetaDataAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.ProcessMetaDataOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessMetaDataInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessMetaDataOutputInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessMetaDataExecutor extends AbstractMiddlewareExecutor<ProcessMetaDataInput, ProcessMetaDataOutputInterface>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void execute(ProcessMetaDataInput input, ProcessMetaDataOutputInterface output) throws QException
|
||||
{
|
||||
com.kingsrook.qqq.backend.core.model.actions.metadata.ProcessMetaDataInput processMetaDataInput = new com.kingsrook.qqq.backend.core.model.actions.metadata.ProcessMetaDataInput();
|
||||
|
||||
String processName = input.getProcessName();
|
||||
QProcessMetaData process = QContext.getQInstance().getProcess(processName);
|
||||
if(process == null)
|
||||
{
|
||||
throw (new QNotFoundException("Process [" + processName + "] was not found."));
|
||||
}
|
||||
PermissionsHelper.checkProcessPermissionThrowing(processMetaDataInput, processName);
|
||||
|
||||
processMetaDataInput.setProcessName(processName);
|
||||
ProcessMetaDataAction processMetaDataAction = new ProcessMetaDataAction();
|
||||
ProcessMetaDataOutput processMetaDataOutput = processMetaDataAction.execute(processMetaDataInput);
|
||||
|
||||
output.setProcessMetaData(processMetaDataOutput.getProcess());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors;
|
||||
|
||||
|
||||
import java.util.Optional;
|
||||
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.processes.RunProcessAction;
|
||||
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.ProcessState;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinAccessLogger;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessInitOrStepOrStatusOutputInterface;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessStatusInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.utils.ProcessExecutorUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessStatusExecutor extends AbstractMiddlewareExecutor<ProcessStatusInput, ProcessInitOrStepOrStatusOutputInterface>
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(ProcessStatusExecutor.class);
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** Note: implementation of the output interface here, it wants to know what
|
||||
** type it's going to be first, so, be polite and always call .setType before
|
||||
** any other setters.
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void execute(ProcessStatusInput input, ProcessInitOrStepOrStatusOutputInterface output) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
String processName = input.getProcessName();
|
||||
String processUUID = input.getProcessUUID();
|
||||
String jobUUID = input.getJobUUID();
|
||||
|
||||
LOG.debug("Request for status of process " + processUUID + ", job " + jobUUID);
|
||||
Optional<AsyncJobStatus> optionalJobStatus = new AsyncJobManager().getJobStatus(jobUUID);
|
||||
if(optionalJobStatus.isEmpty())
|
||||
{
|
||||
ProcessExecutorUtils.serializeRunProcessExceptionForCaller(output, new RuntimeException("Could not find status of process step job"));
|
||||
}
|
||||
else
|
||||
{
|
||||
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(processUUID);
|
||||
if(processState.isPresent())
|
||||
{
|
||||
RunProcessOutput runProcessOutput = new RunProcessOutput(processState.get());
|
||||
ProcessExecutorUtils.serializeRunProcessResultForCaller(output, processName, runProcessOutput);
|
||||
QJavalinAccessLogger.logProcessSummary(processName, processUUID, runProcessOutput);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessExecutorUtils.serializeRunProcessExceptionForCaller(output, new RuntimeException("Could not find results for process " + processUUID));
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
ProcessExecutorUtils.serializeRunProcessExceptionForCaller(output, jobStatus.getCaughtException());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
output.setType(ProcessInitOrStepOrStatusOutputInterface.Type.RUNNING);
|
||||
output.setMessage(jobStatus.getMessage());
|
||||
output.setCurrent(jobStatus.getCurrent());
|
||||
output.setTotal(jobStatus.getTotal());
|
||||
}
|
||||
}
|
||||
|
||||
output.setProcessUUID(processUUID);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
ProcessExecutorUtils.serializeRunProcessExceptionForCaller(output, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class AbstractMiddlewareInput
|
||||
{
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface AbstractMiddlewareOutputInterface
|
||||
{
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class AuthenticationMetaDataInput extends AbstractMiddlewareInput
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface AuthenticationMetaDataOutputInterface extends AbstractMiddlewareOutputInterface
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
void setAuthenticationMetaData(QAuthenticationMetaData qAuthenticationMetaData);
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** generic middleware input that has no fields.
|
||||
*******************************************************************************/
|
||||
public class EmptyMiddlewareInput extends AbstractMiddlewareInput
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface EmptyMiddlewareOutputInterface extends AbstractMiddlewareOutputInterface
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ManageSessionInput extends AbstractMiddlewareInput
|
||||
{
|
||||
private String accessToken;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for accessToken
|
||||
*******************************************************************************/
|
||||
public String getAccessToken()
|
||||
{
|
||||
return (this.accessToken);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for accessToken
|
||||
*******************************************************************************/
|
||||
public void setAccessToken(String accessToken)
|
||||
{
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for accessToken
|
||||
*******************************************************************************/
|
||||
public ManageSessionInput withAccessToken(String accessToken)
|
||||
{
|
||||
this.accessToken = accessToken;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface ManageSessionOutputInterface extends AbstractMiddlewareOutputInterface
|
||||
{
|
||||
/***************************************************************************
|
||||
** Setter for Uuid
|
||||
***************************************************************************/
|
||||
void setUuid(String uuid);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for values
|
||||
*******************************************************************************/
|
||||
void setValues(Map<String, Serializable> values);
|
||||
|
||||
}
|
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MetaDataInput extends AbstractMiddlewareInput
|
||||
{
|
||||
private String frontendName;
|
||||
private String frontendVersion;
|
||||
|
||||
private String middlewareName;
|
||||
private String middlewareVersion;
|
||||
|
||||
private String applicationName;
|
||||
private String applicationVersion;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for frontendName
|
||||
*******************************************************************************/
|
||||
public String getFrontendName()
|
||||
{
|
||||
return (this.frontendName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for frontendName
|
||||
*******************************************************************************/
|
||||
public void setFrontendName(String frontendName)
|
||||
{
|
||||
this.frontendName = frontendName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for frontendName
|
||||
*******************************************************************************/
|
||||
public MetaDataInput withFrontendName(String frontendName)
|
||||
{
|
||||
this.frontendName = frontendName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for frontendVersion
|
||||
*******************************************************************************/
|
||||
public String getFrontendVersion()
|
||||
{
|
||||
return (this.frontendVersion);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for frontendVersion
|
||||
*******************************************************************************/
|
||||
public void setFrontendVersion(String frontendVersion)
|
||||
{
|
||||
this.frontendVersion = frontendVersion;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for frontendVersion
|
||||
*******************************************************************************/
|
||||
public MetaDataInput withFrontendVersion(String frontendVersion)
|
||||
{
|
||||
this.frontendVersion = frontendVersion;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for middlewareName
|
||||
*******************************************************************************/
|
||||
public String getMiddlewareName()
|
||||
{
|
||||
return (this.middlewareName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for middlewareName
|
||||
*******************************************************************************/
|
||||
public void setMiddlewareName(String middlewareName)
|
||||
{
|
||||
this.middlewareName = middlewareName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for middlewareName
|
||||
*******************************************************************************/
|
||||
public MetaDataInput withMiddlewareName(String middlewareName)
|
||||
{
|
||||
this.middlewareName = middlewareName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for middlewareVersion
|
||||
*******************************************************************************/
|
||||
public String getMiddlewareVersion()
|
||||
{
|
||||
return (this.middlewareVersion);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for middlewareVersion
|
||||
*******************************************************************************/
|
||||
public void setMiddlewareVersion(String middlewareVersion)
|
||||
{
|
||||
this.middlewareVersion = middlewareVersion;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for middlewareVersion
|
||||
*******************************************************************************/
|
||||
public MetaDataInput withMiddlewareVersion(String middlewareVersion)
|
||||
{
|
||||
this.middlewareVersion = middlewareVersion;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for applicationName
|
||||
*******************************************************************************/
|
||||
public String getApplicationName()
|
||||
{
|
||||
return (this.applicationName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for applicationName
|
||||
*******************************************************************************/
|
||||
public void setApplicationName(String applicationName)
|
||||
{
|
||||
this.applicationName = applicationName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for applicationName
|
||||
*******************************************************************************/
|
||||
public MetaDataInput withApplicationName(String applicationName)
|
||||
{
|
||||
this.applicationName = applicationName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for applicationVersion
|
||||
*******************************************************************************/
|
||||
public String getApplicationVersion()
|
||||
{
|
||||
return (this.applicationVersion);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for applicationVersion
|
||||
*******************************************************************************/
|
||||
public void setApplicationVersion(String applicationVersion)
|
||||
{
|
||||
this.applicationVersion = applicationVersion;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for applicationVersion
|
||||
*******************************************************************************/
|
||||
public MetaDataInput withApplicationVersion(String applicationVersion)
|
||||
{
|
||||
this.applicationVersion = applicationVersion;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface MetaDataOutputInterface extends AbstractMiddlewareOutputInterface
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for metaDataOutput
|
||||
*******************************************************************************/
|
||||
void setMetaDataOutput(MetaDataOutput metaDataOutput);
|
||||
|
||||
}
|
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessInitOrStepInput extends AbstractMiddlewareInput
|
||||
{
|
||||
private String processName;
|
||||
private Integer stepTimeoutMillis = 3000;
|
||||
private QQueryFilter recordsFilter;
|
||||
|
||||
private Map<String, Serializable> values = new LinkedHashMap<>();
|
||||
|
||||
/////////////////////////////////////
|
||||
// used only for 'step' (not init) //
|
||||
/////////////////////////////////////
|
||||
private String processUUID;
|
||||
private String startAfterStep;
|
||||
|
||||
private RunProcessInput.FrontendStepBehavior frontendStepBehavior = RunProcessInput.FrontendStepBehavior.BREAK;
|
||||
|
||||
// todo - file??
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public enum RecordsParam
|
||||
{
|
||||
FILTER_JSON("filterJSON"),
|
||||
RECORD_IDS("recordIds");
|
||||
|
||||
|
||||
private final String value;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
RecordsParam(String value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processName
|
||||
*******************************************************************************/
|
||||
public String getProcessName()
|
||||
{
|
||||
return (this.processName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for processName
|
||||
*******************************************************************************/
|
||||
public void setProcessName(String processName)
|
||||
{
|
||||
this.processName = processName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for processName
|
||||
*******************************************************************************/
|
||||
public ProcessInitOrStepInput withProcessName(String processName)
|
||||
{
|
||||
this.processName = processName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for stepTimeoutMillis
|
||||
*******************************************************************************/
|
||||
public Integer getStepTimeoutMillis()
|
||||
{
|
||||
return (this.stepTimeoutMillis);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for stepTimeoutMillis
|
||||
*******************************************************************************/
|
||||
public void setStepTimeoutMillis(Integer stepTimeoutMillis)
|
||||
{
|
||||
this.stepTimeoutMillis = stepTimeoutMillis;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for stepTimeoutMillis
|
||||
*******************************************************************************/
|
||||
public ProcessInitOrStepInput withStepTimeoutMillis(Integer stepTimeoutMillis)
|
||||
{
|
||||
this.stepTimeoutMillis = stepTimeoutMillis;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for recordsFilter
|
||||
*******************************************************************************/
|
||||
public QQueryFilter getRecordsFilter()
|
||||
{
|
||||
return (this.recordsFilter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for recordsFilter
|
||||
*******************************************************************************/
|
||||
public void setRecordsFilter(QQueryFilter recordsFilter)
|
||||
{
|
||||
this.recordsFilter = recordsFilter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for recordsFilter
|
||||
*******************************************************************************/
|
||||
public ProcessInitOrStepInput withRecordsFilter(QQueryFilter recordsFilter)
|
||||
{
|
||||
this.recordsFilter = recordsFilter;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for values
|
||||
*******************************************************************************/
|
||||
public Map<String, Serializable> getValues()
|
||||
{
|
||||
return (this.values);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for values
|
||||
*******************************************************************************/
|
||||
public void setValues(Map<String, Serializable> values)
|
||||
{
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for values
|
||||
*******************************************************************************/
|
||||
public ProcessInitOrStepInput withValues(Map<String, Serializable> values)
|
||||
{
|
||||
this.values = values;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for frontendStepBehavior
|
||||
*******************************************************************************/
|
||||
public RunProcessInput.FrontendStepBehavior getFrontendStepBehavior()
|
||||
{
|
||||
return (this.frontendStepBehavior);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for frontendStepBehavior
|
||||
*******************************************************************************/
|
||||
public void setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior frontendStepBehavior)
|
||||
{
|
||||
this.frontendStepBehavior = frontendStepBehavior;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for frontendStepBehavior
|
||||
*******************************************************************************/
|
||||
public ProcessInitOrStepInput withFrontendStepBehavior(RunProcessInput.FrontendStepBehavior frontendStepBehavior)
|
||||
{
|
||||
this.frontendStepBehavior = frontendStepBehavior;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processUUID
|
||||
*******************************************************************************/
|
||||
public String getProcessUUID()
|
||||
{
|
||||
return (this.processUUID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for processUUID
|
||||
*******************************************************************************/
|
||||
public void setProcessUUID(String processUUID)
|
||||
{
|
||||
this.processUUID = processUUID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for processUUID
|
||||
*******************************************************************************/
|
||||
public ProcessInitOrStepInput withProcessUUID(String processUUID)
|
||||
{
|
||||
this.processUUID = processUUID;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for startAfterStep
|
||||
*******************************************************************************/
|
||||
public String getStartAfterStep()
|
||||
{
|
||||
return (this.startAfterStep);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for startAfterStep
|
||||
*******************************************************************************/
|
||||
public void setStartAfterStep(String startAfterStep)
|
||||
{
|
||||
this.startAfterStep = startAfterStep;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for startAfterStep
|
||||
*******************************************************************************/
|
||||
public ProcessInitOrStepInput withStartAfterStep(String startAfterStep)
|
||||
{
|
||||
this.startAfterStep = startAfterStep;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessMetaDataAdjustment;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface ProcessInitOrStepOrStatusOutputInterface extends AbstractMiddlewareOutputInterface
|
||||
{
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
enum Type
|
||||
{
|
||||
COMPLETE, JOB_STARTED, RUNNING, ERROR;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for type
|
||||
*******************************************************************************/
|
||||
void setType(Type type);
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for processUUID
|
||||
*******************************************************************************/
|
||||
void setProcessUUID(String processUUID);
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nextStep
|
||||
*******************************************************************************/
|
||||
void setNextStep(String nextStep);
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for values
|
||||
*******************************************************************************/
|
||||
void setValues(Map<String, Serializable> values);
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for jobUUID
|
||||
*******************************************************************************/
|
||||
void setJobUUID(String jobUUID);
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for message
|
||||
*******************************************************************************/
|
||||
void setMessage(String message);
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for current
|
||||
*******************************************************************************/
|
||||
void setCurrent(Integer current);
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for total
|
||||
*******************************************************************************/
|
||||
void setTotal(Integer total);
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for error
|
||||
*******************************************************************************/
|
||||
void setError(String error);
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for userFacingError
|
||||
*******************************************************************************/
|
||||
void setUserFacingError(String userFacingError);
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for processMetaDataAdjustment
|
||||
*******************************************************************************/
|
||||
void setProcessMetaDataAdjustment(ProcessMetaDataAdjustment processMetaDataAdjustment);
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessMetaDataInput extends AbstractMiddlewareInput
|
||||
{
|
||||
private String processName;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getProcessName()
|
||||
{
|
||||
return processName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for processName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setProcessName(String processName)
|
||||
{
|
||||
this.processName = processName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for processName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessMetaDataInput withProcessName(String processName)
|
||||
{
|
||||
this.processName = processName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendProcessMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface ProcessMetaDataOutputInterface extends AbstractMiddlewareOutputInterface
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
void setProcessMetaData(QFrontendProcessMetaData processMetaData);
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessStatusInput extends AbstractMiddlewareInput
|
||||
{
|
||||
private String processName;
|
||||
private String processUUID;
|
||||
private String jobUUID;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processName
|
||||
*******************************************************************************/
|
||||
public String getProcessName()
|
||||
{
|
||||
return (this.processName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for processName
|
||||
*******************************************************************************/
|
||||
public void setProcessName(String processName)
|
||||
{
|
||||
this.processName = processName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for processName
|
||||
*******************************************************************************/
|
||||
public ProcessStatusInput withProcessName(String processName)
|
||||
{
|
||||
this.processName = processName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processUUID
|
||||
*******************************************************************************/
|
||||
public String getProcessUUID()
|
||||
{
|
||||
return (this.processUUID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for processUUID
|
||||
*******************************************************************************/
|
||||
public void setProcessUUID(String processUUID)
|
||||
{
|
||||
this.processUUID = processUUID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for processUUID
|
||||
*******************************************************************************/
|
||||
public ProcessStatusInput withProcessUUID(String processUUID)
|
||||
{
|
||||
this.processUUID = processUUID;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for jobUUID
|
||||
*******************************************************************************/
|
||||
public String getJobUUID()
|
||||
{
|
||||
return (this.jobUUID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for jobUUID
|
||||
*******************************************************************************/
|
||||
public void setJobUUID(String jobUUID)
|
||||
{
|
||||
this.jobUUID = jobUUID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for jobUUID
|
||||
*******************************************************************************/
|
||||
public ProcessStatusInput withJobUUID(String jobUUID)
|
||||
{
|
||||
this.jobUUID = jobUUID;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface ProcessStatusOutputInterface extends AbstractMiddlewareOutputInterface
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class QueryMiddlewareInput extends AbstractMiddlewareInput
|
||||
{
|
||||
private String table;
|
||||
private QQueryFilter filter;
|
||||
private List<QueryJoin> queryJoins;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for table
|
||||
*******************************************************************************/
|
||||
public String getTable()
|
||||
{
|
||||
return (this.table);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for table
|
||||
*******************************************************************************/
|
||||
public void setTable(String table)
|
||||
{
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for table
|
||||
*******************************************************************************/
|
||||
public QueryMiddlewareInput withTable(String table)
|
||||
{
|
||||
this.table = table;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for filter
|
||||
*******************************************************************************/
|
||||
public QQueryFilter getFilter()
|
||||
{
|
||||
return (this.filter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for filter
|
||||
*******************************************************************************/
|
||||
public void setFilter(QQueryFilter filter)
|
||||
{
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for filter
|
||||
*******************************************************************************/
|
||||
public QueryMiddlewareInput withFilter(QQueryFilter filter)
|
||||
{
|
||||
this.filter = filter;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryJoins
|
||||
*******************************************************************************/
|
||||
public List<QueryJoin> getQueryJoins()
|
||||
{
|
||||
return (this.queryJoins);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryJoins
|
||||
*******************************************************************************/
|
||||
public void setQueryJoins(List<QueryJoin> queryJoins)
|
||||
{
|
||||
this.queryJoins = queryJoins;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryJoins
|
||||
*******************************************************************************/
|
||||
public QueryMiddlewareInput withQueryJoins(List<QueryJoin> queryJoins)
|
||||
{
|
||||
this.queryJoins = queryJoins;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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/>.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
** These classes are input/output wrappers for middleware executors.
|
||||
**
|
||||
** Some "empty" implementations are provided, for executors that (more likely)
|
||||
** take no inputs, or (less likely?) return no outputs.
|
||||
**
|
||||
*******************************************************************************/
|
||||
package com.kingsrook.qqq.middleware.javalin.executors.io;
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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/>.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
** This package contains (hopefully generally) api-version-agnostic classes
|
||||
** that implement the actual QQQ Middleware. That is to say, subclasses of
|
||||
** `AbstractMiddlewareExecutor`, which use classes from the `.io` subpackage
|
||||
** for I/O, to run code in a QQQ server.
|
||||
**
|
||||
** As new versions of the middleware evolve, the idea is that the spec classes
|
||||
** for new versions will be responsible for appropriately marshalling data
|
||||
** in and out of the executors, via the I/O classes, with "feature flags", etc
|
||||
** added to those input classes as needed (say if v N+1 adds a new feature,
|
||||
** then a request for v N may omit the feature-flag that turns that feature on).
|
||||
**
|
||||
** As functionality continues to evolve, the time may come when it's appropriate
|
||||
** to fork an Executor. Hypothetically, if version 5 of the QueryExecutor
|
||||
** bears very little resemblance to versions 1 through 4 (due to additional
|
||||
** pizzazz?) spawn a new QueryWithPizzazzExecutor. Of course, naming here
|
||||
** will be the hardest part (e.g., avoid NewNewQueryExecutorFinal2B...)
|
||||
*******************************************************************************/
|
||||
package com.kingsrook.qqq.middleware.javalin.executors;
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.executors.utils;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||
import com.kingsrook.qqq.backend.core.utils.ExceptionUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessInitOrStepOrStatusOutputInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessExecutorUtils
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(ProcessExecutorUtils.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Whether a step finished synchronously or asynchronously, return its data
|
||||
** to the caller the same way.
|
||||
*******************************************************************************/
|
||||
public static void serializeRunProcessResultForCaller(ProcessInitOrStepOrStatusOutputInterface processInitOrStepOutput, String processName, RunProcessOutput runProcessOutput)
|
||||
{
|
||||
processInitOrStepOutput.setType(ProcessInitOrStepOrStatusOutputInterface.Type.COMPLETE);
|
||||
|
||||
if(runProcessOutput.getException().isPresent())
|
||||
{
|
||||
////////////////////////////////////////////////////////////////
|
||||
// per code coverage, this path may never actually get hit... //
|
||||
////////////////////////////////////////////////////////////////
|
||||
serializeRunProcessExceptionForCaller(processInitOrStepOutput, runProcessOutput.getException().get());
|
||||
}
|
||||
|
||||
processInitOrStepOutput.setValues(runProcessOutput.getValues());
|
||||
// processInitOrStepOutput.setValues(getValuesForCaller(processName, runProcessOutput));
|
||||
|
||||
runProcessOutput.getProcessState().getNextStepName().ifPresent(nextStep -> processInitOrStepOutput.setNextStep(nextStep));
|
||||
|
||||
if(runProcessOutput.getProcessMetaDataAdjustment() != null)
|
||||
{
|
||||
processInitOrStepOutput.setProcessMetaDataAdjustment(runProcessOutput.getProcessMetaDataAdjustment());
|
||||
}
|
||||
}
|
||||
|
||||
// /***************************************************************************
|
||||
// ** maybe good idea here, but... to only return fields that the frontend steps
|
||||
// ** say they care about. yeah.
|
||||
// ***************************************************************************/
|
||||
// private static Map<String, Serializable> getValuesForCaller(String processName, RunProcessOutput runProcessOutput)
|
||||
// {
|
||||
// QProcessMetaData process = QContext.getQInstance().getProcess(processName);
|
||||
// Map<String, QFieldMetaData> frontendFields = new LinkedHashMap<>();
|
||||
// for(QStepMetaData step : process.getAllSteps().values())
|
||||
// {
|
||||
// if(step instanceof QFrontendStepMetaData frontendStepMetaData)
|
||||
// {
|
||||
// frontendFields.addAll(frontendStepMetaData.getAllFields());
|
||||
// }
|
||||
// else if(step instanceof QStateMachineStep stateMachineStep)
|
||||
// {
|
||||
// for(QStepMetaData subStep : stateMachineStep.getSubSteps())
|
||||
// {
|
||||
// // recur, etc
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // then, only return ones in the map, eh
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void serializeRunProcessExceptionForCaller(ProcessInitOrStepOrStatusOutputInterface processInitOrStepOutput, Exception exception)
|
||||
{
|
||||
processInitOrStepOutput.setType(ProcessInitOrStepOrStatusOutputInterface.Type.ERROR);
|
||||
|
||||
QUserFacingException userFacingException = ExceptionUtils.findClassInRootChain(exception, QUserFacingException.class);
|
||||
|
||||
if(userFacingException != null)
|
||||
{
|
||||
LOG.info("User-facing exception in process", userFacingException);
|
||||
processInitOrStepOutput.setError(userFacingException.getMessage());
|
||||
processInitOrStepOutput.setUserFacingError(userFacingException.getMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
Throwable rootException = ExceptionUtils.getRootException(exception);
|
||||
LOG.warn("Uncaught Exception in process", exception);
|
||||
processInitOrStepOutput.setError("Error message: " + rootException.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,347 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.schemabuilder;
|
||||
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIHasAdditionalProperties;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIIncludeProperties;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIListItems;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIMapKnownEntries;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIMapValueType;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIOneOf;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
import com.kingsrook.qqq.openapi.model.Type;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** This class facilitates generating OpenAPI Schema objects based on reflectively
|
||||
** reading classes and annotations
|
||||
*******************************************************************************/
|
||||
public class SchemaBuilder
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public Schema classToSchema(Class<?> c)
|
||||
{
|
||||
return classToSchema(c, c);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static class SchemaFromBuilder extends Schema
|
||||
{
|
||||
private Class<?> originalClass;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for originalClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
@JsonIgnore
|
||||
public Class<?> getOriginalClass()
|
||||
{
|
||||
return originalClass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for originalClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setOriginalClass(Class<?> originalClass)
|
||||
{
|
||||
this.originalClass = originalClass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for originalClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public SchemaFromBuilder withOriginalClass(Class<?> originalClass)
|
||||
{
|
||||
this.originalClass = originalClass;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private Schema classToSchema(Class<?> c, AnnotatedElement element)
|
||||
{
|
||||
SchemaFromBuilder schema = new SchemaFromBuilder();
|
||||
schema.setOriginalClass(c);
|
||||
|
||||
if(c.isEnum())
|
||||
{
|
||||
schema.withType(Type.STRING);
|
||||
schema.withEnumValues(Arrays.stream(c.getEnumConstants()).map(e -> String.valueOf(e)).collect(Collectors.toList()));
|
||||
}
|
||||
else if(c.equals(String.class))
|
||||
{
|
||||
schema.withType(Type.STRING);
|
||||
}
|
||||
else if(c.equals(Integer.class) || c.equals(BigDecimal.class))
|
||||
{
|
||||
schema.withType(Type.NUMBER);
|
||||
}
|
||||
else if(c.equals(Boolean.class))
|
||||
{
|
||||
schema.withType(Type.BOOLEAN);
|
||||
}
|
||||
else if(c.equals(List.class))
|
||||
{
|
||||
schema.withType(Type.ARRAY);
|
||||
// Class<?> itemType = field.getType().getTypeParameters()[0].getBounds().getClass();
|
||||
|
||||
OpenAPIListItems openAPIListItemsAnnotation = element.getAnnotation(OpenAPIListItems.class);
|
||||
if(openAPIListItemsAnnotation == null)
|
||||
{
|
||||
// todo - can this be allowed, to make a generic list? maybe.
|
||||
// throw (new QRuntimeException("A List field [" + field.getName() + "] was missing its @OpenAPIItems annotation"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(openAPIListItemsAnnotation.useRef())
|
||||
{
|
||||
schema.withItems(new Schema().withRef("#/components/schemas/" + openAPIListItemsAnnotation.value().getSimpleName()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Class<?> itemType = openAPIListItemsAnnotation.value();
|
||||
schema.withItems(classToSchema(itemType));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(c.equals(Map.class))
|
||||
{
|
||||
schema.withType(Type.OBJECT);
|
||||
|
||||
OpenAPIMapKnownEntries openAPIMapKnownEntriesAnnotation = element.getAnnotation(OpenAPIMapKnownEntries.class);
|
||||
if(openAPIMapKnownEntriesAnnotation != null)
|
||||
{
|
||||
schema.withRef("#/components/schemas/" + openAPIMapKnownEntriesAnnotation.value().getSimpleName());
|
||||
// if(openAPIMapKnownEntriesAnnotation.additionalProperties())
|
||||
// {
|
||||
// schema.withAdditionalProperties(true);
|
||||
// }
|
||||
}
|
||||
|
||||
OpenAPIMapValueType openAPIMapValueTypeAnnotation = element.getAnnotation(OpenAPIMapValueType.class);
|
||||
if(openAPIMapValueTypeAnnotation != null)
|
||||
{
|
||||
if(openAPIMapValueTypeAnnotation.useRef())
|
||||
{
|
||||
schema.withAdditionalProperties(new Schema().withRef("#/components/schemas/" + openAPIMapValueTypeAnnotation.value().getSimpleName()));
|
||||
}
|
||||
else
|
||||
{
|
||||
schema.withAdditionalProperties(classToSchema(openAPIMapValueTypeAnnotation.value()));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OpenAPIOneOf openAPIOneOfAnnotation = element.getAnnotation(OpenAPIOneOf.class);
|
||||
if(openAPIOneOfAnnotation != null)
|
||||
{
|
||||
String description = "[" + element + "]";
|
||||
List<Schema> oneOfList = processOneOfAnnotation(openAPIOneOfAnnotation, c, description);
|
||||
schema.withOneOf(oneOfList);
|
||||
}
|
||||
else
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// else, if not a one-of then assume the schema is an object, and build out its properties //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
schema.withType(Type.OBJECT);
|
||||
|
||||
Map<String, Schema> properties = new TreeMap<>();
|
||||
schema.withProperties(properties);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if we're told to includeProperties (e.g., from ancestor classes), then go find those //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
OpenAPIIncludeProperties openAPIIncludePropertiesAnnotation = c.getAnnotation(OpenAPIIncludeProperties.class);
|
||||
if(openAPIIncludePropertiesAnnotation != null)
|
||||
{
|
||||
Set<Class<?>> ancestorClasses = Arrays.stream(openAPIIncludePropertiesAnnotation.ancestorClasses()).collect(Collectors.toSet());
|
||||
Class<?> superClass = c.getSuperclass();
|
||||
do
|
||||
{
|
||||
if(ancestorClasses.contains(superClass))
|
||||
{
|
||||
addDeclaredFieldsToProperties(superClass, properties);
|
||||
addDeclaredMethodsToProperties(superClass, properties);
|
||||
}
|
||||
superClass = superClass.getSuperclass();
|
||||
}
|
||||
while(superClass != null);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// make all declared-fields and getters in the class into properties //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
addDeclaredFieldsToProperties(c, properties);
|
||||
addDeclaredMethodsToProperties(c, properties);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// now (after schema may have been replaced, e.g., in a recursive call), add more details to it //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
OpenAPIDescription openAPIDescriptionAnnotation = element.getAnnotation(OpenAPIDescription.class);
|
||||
if(openAPIDescriptionAnnotation != null)
|
||||
{
|
||||
schema.setDescription(openAPIDescriptionAnnotation.value());
|
||||
}
|
||||
|
||||
if(element.isAnnotationPresent(OpenAPIHasAdditionalProperties.class))
|
||||
{
|
||||
schema.withAdditionalProperties(true);
|
||||
}
|
||||
|
||||
return (schema);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** Getter methods with an annotation
|
||||
***************************************************************************/
|
||||
private void addDeclaredMethodsToProperties(Class<?> c, Map<String, Schema> properties)
|
||||
{
|
||||
for(Method method : c.getDeclaredMethods())
|
||||
{
|
||||
OpenAPIDescription methodDescription = method.getAnnotation(OpenAPIDescription.class);
|
||||
OpenAPIExclude openAPIExclude = method.getAnnotation(OpenAPIExclude.class);
|
||||
if(method.getName().startsWith("get") && method.getParameterCount() == 0 && methodDescription != null && openAPIExclude == null)
|
||||
{
|
||||
String name = StringUtils.lcFirst(method.getName().substring(3));
|
||||
properties.put(name, getMemberSchema(method));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void addDeclaredFieldsToProperties(Class<?> c, Map<String, Schema> properties)
|
||||
{
|
||||
for(Field declaredField : c.getDeclaredFields())
|
||||
{
|
||||
OpenAPIExclude openAPIExclude = declaredField.getAnnotation(OpenAPIExclude.class);
|
||||
if(openAPIExclude == null)
|
||||
{
|
||||
properties.put(declaredField.getName(), getMemberSchema(declaredField));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private Schema getMemberSchema(AccessibleObject member)
|
||||
{
|
||||
Class<?> type;
|
||||
if(member instanceof Field field)
|
||||
{
|
||||
type = field.getType();
|
||||
}
|
||||
else if(member instanceof Method method)
|
||||
{
|
||||
type = method.getReturnType();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw (new IllegalArgumentException("Unsupported AccessibleObject: " + member));
|
||||
}
|
||||
|
||||
return (classToSchema(type, member));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private List<Schema> processOneOfAnnotation(OpenAPIOneOf openAPIOneOfAnnotation, Class<?> type, String description)
|
||||
{
|
||||
List<Schema> oneOfList = new ArrayList<>();
|
||||
|
||||
if(openAPIOneOfAnnotation.mode().equals(OpenAPIOneOf.Mode.PERMITTED_SUBCLASSES))
|
||||
{
|
||||
Class<?>[] permittedSubclasses = type.getPermittedSubclasses();
|
||||
for(Class<?> permittedSubclass : permittedSubclasses)
|
||||
{
|
||||
oneOfList.add(classToSchema(permittedSubclass));
|
||||
}
|
||||
}
|
||||
else if(openAPIOneOfAnnotation.mode().equals(OpenAPIOneOf.Mode.SPECIFIED_LIST))
|
||||
{
|
||||
for(Class<?> oneOfClass : openAPIOneOfAnnotation.options())
|
||||
{
|
||||
oneOfList.add(classToSchema(oneOfClass));
|
||||
}
|
||||
}
|
||||
|
||||
if(oneOfList.isEmpty())
|
||||
{
|
||||
throw (new QRuntimeException("Could not find any options to use for an @OpenAPIOneOf annotation on " + description));
|
||||
}
|
||||
return oneOfList;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.schemabuilder;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Mark a class as eligible for running through the SchemaBuilder.
|
||||
**
|
||||
** Actually not really necessary, as schemaBuilder can run on any class - but
|
||||
** does provide a method that a class might use to customize how it gets
|
||||
** schemafied.
|
||||
*******************************************************************************/
|
||||
public interface ToSchema
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
default Schema toSchema()
|
||||
{
|
||||
return new SchemaBuilder().classToSchema(getClass());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.schemabuilder.annotations;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Target({ ElementType.FIELD, ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OpenAPIDescription
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
String value();
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.schemabuilder.annotations;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.EnumSet;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OpenAPIEnumSubSet
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
Class<? extends EnumSubSet<?>> value();
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
interface EnumSubSet<E extends Enum<E>>
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
EnumSet<E> getSubSet();
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.schemabuilder.annotations;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OpenAPIExclude
|
||||
{
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.schemabuilder.annotations;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OpenAPIHasAdditionalProperties
|
||||
{
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.schemabuilder.annotations;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OpenAPIIncludeProperties
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
Class<?>[] ancestorClasses() default { };
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.schemabuilder.annotations;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OpenAPIListItems
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
Class<?> value();
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
boolean useRef() default false;
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.schemabuilder.annotations;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OpenAPIMapKnownEntries
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
Class<?> value();
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
boolean useRef() default false;
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.schemabuilder.annotations;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OpenAPIMapValueType
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
Class<?> value();
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
boolean useRef() default false;
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.schemabuilder.annotations;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OpenAPIOneOf
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
Mode mode() default Mode.PERMITTED_SUBCLASSES;
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
Class<?>[] options() default { };
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
enum Mode
|
||||
{
|
||||
PERMITTED_SUBCLASSES,
|
||||
SPECIFIED_LIST
|
||||
}
|
||||
}
|
@ -0,0 +1,546 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.AbstractMiddlewareExecutor;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.ExecutorSessionUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.AbstractMiddlewareInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.AbstractMiddlewareOutputInterface;
|
||||
import com.kingsrook.qqq.openapi.model.Content;
|
||||
import com.kingsrook.qqq.openapi.model.Method;
|
||||
import com.kingsrook.qqq.openapi.model.Parameter;
|
||||
import com.kingsrook.qqq.openapi.model.RequestBody;
|
||||
import com.kingsrook.qqq.openapi.model.Response;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
import io.javalin.apibuilder.ApiBuilder;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.Context;
|
||||
import io.javalin.http.Handler;
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Base class for individual endpoint specs.
|
||||
** e.g., one path, that has one "spec" (a "Method" in openapi structure),
|
||||
** with one implementation (executor + input & output)
|
||||
*******************************************************************************/
|
||||
public abstract class AbstractEndpointSpec<
|
||||
INPUT extends AbstractMiddlewareInput,
|
||||
OUTPUT extends AbstractMiddlewareOutputInterface,
|
||||
EXECUTOR extends AbstractMiddlewareExecutor<INPUT, ? super OUTPUT>>
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(AbstractEndpointSpec.class);
|
||||
|
||||
protected QInstance qInstance;
|
||||
|
||||
private List<Parameter> memoizedRequestParameters = null;
|
||||
private RequestBody memoizedRequestBody = null;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** build the endpoint's input object from a javalin context
|
||||
***************************************************************************/
|
||||
public abstract INPUT buildInput(Context context) throws Exception;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** build the endpoint's http response (written to the javalin context) from
|
||||
** an execution output object
|
||||
***************************************************************************/
|
||||
public abstract void handleOutput(Context context, OUTPUT output) throws Exception;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** Construct a new instance of the executor class, based on type-argument
|
||||
***************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public EXECUTOR newExecutor()
|
||||
{
|
||||
Object object = newObjectFromTypeArgument(2);
|
||||
return (EXECUTOR) object;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** Construct a new instance of the output class, based on type-argument
|
||||
***************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public OUTPUT newOutput()
|
||||
{
|
||||
Object object = newObjectFromTypeArgument(1);
|
||||
return (OUTPUT) object;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** credit: https://www.baeldung.com/java-generic-type-find-class-runtime
|
||||
***************************************************************************/
|
||||
private Object newObjectFromTypeArgument(int argumentIndex)
|
||||
{
|
||||
try
|
||||
{
|
||||
Type superClass = getClass().getGenericSuperclass();
|
||||
Type actualTypeArgument = ((ParameterizedType) superClass).getActualTypeArguments()[argumentIndex];
|
||||
String className = actualTypeArgument.getTypeName().replaceAll("<.*", "");
|
||||
Class<?> aClass = Class.forName(className);
|
||||
return (aClass.getConstructor().newInstance());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QRuntimeException("Failed to reflectively create new object from type argument", e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** define a javalin route for the spec
|
||||
***************************************************************************/
|
||||
public void defineRoute(String versionBasePath)
|
||||
{
|
||||
CompleteOperation completeOperation = defineCompleteOperation();
|
||||
|
||||
String fullPath = "/qqq/" + versionBasePath + completeOperation.getPath();
|
||||
fullPath = fullPath.replaceAll("/+", "/");
|
||||
|
||||
final Handler handler = context -> serveRequest(context);
|
||||
|
||||
switch(completeOperation.getHttpMethod())
|
||||
{
|
||||
case GET -> ApiBuilder.get(fullPath, handler);
|
||||
case POST -> ApiBuilder.post(fullPath, handler);
|
||||
case PUT -> ApiBuilder.put(fullPath, handler);
|
||||
case PATCH -> ApiBuilder.patch(fullPath, handler);
|
||||
case DELETE -> ApiBuilder.delete(fullPath, handler);
|
||||
default -> throw new IllegalStateException("Unexpected value: " + completeOperation.getHttpMethod());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public OUTPUT serveRequest(Context context) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
if(isSecured())
|
||||
{
|
||||
ExecutorSessionUtils.setupSession(context, qInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
QContext.setQInstance(qInstance);
|
||||
}
|
||||
|
||||
INPUT input = buildInput(context);
|
||||
EXECUTOR executor = newExecutor();
|
||||
OUTPUT output = newOutput();
|
||||
executor.execute(input, output);
|
||||
handleOutput(context, output);
|
||||
return (output);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
handleException(context, e);
|
||||
return (null);
|
||||
}
|
||||
finally
|
||||
{
|
||||
QContext.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
protected void handleException(Context context, Exception e)
|
||||
{
|
||||
QJavalinUtils.handleException(null, context, e);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public Method defineMethod()
|
||||
{
|
||||
BasicOperation basicOperation = defineBasicOperation();
|
||||
|
||||
Method method = new Method()
|
||||
.withTag(basicOperation.getTag().getText())
|
||||
.withSummary(basicOperation.getShortSummary())
|
||||
.withDescription(basicOperation.getLongDescription())
|
||||
.withParameters(defineRequestParameters())
|
||||
.withRequestBody(defineRequestBody())
|
||||
.withResponses(defineResponses());
|
||||
|
||||
customizeMethod(method);
|
||||
|
||||
return (method);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
protected void customizeMethod(Method method)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public BasicOperation defineBasicOperation()
|
||||
{
|
||||
throw new NotImplementedException(getClass().getSimpleName() + " did not implement defineBasicOperation or defineMethod");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public CompleteOperation defineCompleteOperation()
|
||||
{
|
||||
CompleteOperation completeOperation = new CompleteOperation(defineBasicOperation());
|
||||
completeOperation.setMethod(defineMethod());
|
||||
return completeOperation;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public boolean isSecured()
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public BasicResponse defineBasicSuccessResponse()
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public List<BasicResponse> defineAdditionalBasicResponses()
|
||||
{
|
||||
return (Collections.emptyList());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public Map<Integer, Response> defineResponses()
|
||||
{
|
||||
BasicResponse standardSuccessResponse = defineBasicSuccessResponse();
|
||||
List<BasicResponse> basicResponseList = defineAdditionalBasicResponses();
|
||||
|
||||
List<BasicResponse> allBasicResponses = new ArrayList<>();
|
||||
if(standardSuccessResponse != null)
|
||||
{
|
||||
allBasicResponses.add(standardSuccessResponse);
|
||||
}
|
||||
|
||||
if(basicResponseList != null)
|
||||
{
|
||||
allBasicResponses.addAll(basicResponseList);
|
||||
}
|
||||
|
||||
Map<Integer, Response> rs = new HashMap<>();
|
||||
for(BasicResponse basicResponse : allBasicResponses)
|
||||
{
|
||||
Response responseObject = rs.computeIfAbsent(basicResponse.status().getCode(), (k) -> new Response());
|
||||
responseObject.withDescription(basicResponse.description());
|
||||
Map<String, Content> content = responseObject.getContent();
|
||||
if(content == null)
|
||||
{
|
||||
content = new HashMap<>();
|
||||
responseObject.setContent(content);
|
||||
}
|
||||
|
||||
content.put(basicResponse.contentType(), new Content()
|
||||
.withSchema(new Schema().withRefToSchema(basicResponse.schemaRefName()))
|
||||
.withExamples(basicResponse.examples())
|
||||
);
|
||||
}
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public List<Parameter> defineRequestParameters()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public RequestBody defineRequestBody()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public Map<String, Schema> defineComponentSchemas()
|
||||
{
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
protected String getRequestParam(Context context, String name)
|
||||
{
|
||||
for(Parameter parameter : CollectionUtils.nonNullList(getMemoizedRequestParameters()))
|
||||
{
|
||||
if(parameter.getName().equals(name))
|
||||
{
|
||||
String value = switch(parameter.getIn())
|
||||
{
|
||||
case "path" -> context.pathParam(parameter.getName());
|
||||
case "query" -> context.queryParam(parameter.getName());
|
||||
default -> throw new IllegalStateException("Unexpected 'in' value for parameter [" + parameter.getName() + "]: " + parameter.getIn());
|
||||
};
|
||||
|
||||
// todo - validate value vs. required?
|
||||
// todo - validate value vs. schema?
|
||||
|
||||
return (value);
|
||||
}
|
||||
}
|
||||
|
||||
RequestBody requestBody = getMemoizedRequestBody();
|
||||
if(requestBody != null)
|
||||
{
|
||||
String requestContentType = context.contentType();
|
||||
if(requestContentType != null)
|
||||
{
|
||||
requestContentType = requestContentType.toLowerCase().replaceAll(" *;.*", "");
|
||||
}
|
||||
|
||||
Content contentSpec = requestBody.getContent().get(requestContentType);
|
||||
if(contentSpec != null && "object".equals(contentSpec.getSchema().getType()))
|
||||
{
|
||||
if(contentSpec.getSchema().getProperties() != null && contentSpec.getSchema().getProperties().containsKey(name))
|
||||
{
|
||||
String value = null;
|
||||
if(ContentType.MULTIPART_FORM_DATA.getMimeType().equals(requestContentType))
|
||||
{
|
||||
value = context.formParam(name);
|
||||
}
|
||||
else if(ContentType.APPLICATION_JSON.getMimeType().equals(requestContentType))
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// avoid re-parsing the JSON object if getting multiple attributes from it //
|
||||
// by stashing it in a (request) attribute. //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
Object jsonBodyAttribute = context.attribute("jsonBody");
|
||||
JSONObject jsonObject = null;
|
||||
|
||||
if(jsonBodyAttribute instanceof JSONObject jo)
|
||||
{
|
||||
jsonObject = jo;
|
||||
}
|
||||
|
||||
if(jsonObject == null)
|
||||
{
|
||||
jsonObject = new JSONObject(context.body());
|
||||
context.attribute("jsonBody", jsonObject);
|
||||
}
|
||||
|
||||
if(jsonObject.has(name))
|
||||
{
|
||||
value = jsonObject.getString(name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.warn("Unhandled content type: " + requestContentType);
|
||||
}
|
||||
|
||||
return (value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
protected Map<String, Serializable> getRequestParamMap(Context context, String name)
|
||||
{
|
||||
String requestParam = getRequestParam(context, name);
|
||||
if(requestParam == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
JSONObject jsonObject = new JSONObject(requestParam);
|
||||
Map<String, Serializable> map = new LinkedHashMap<>();
|
||||
for(String key : jsonObject.keySet())
|
||||
{
|
||||
Object value = jsonObject.get(key);
|
||||
if(value instanceof Serializable s)
|
||||
{
|
||||
map.put(key, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw (new QRuntimeException("Non-serializable value in param map under key [" + name + "][" + key + "]: " + value.getClass().getSimpleName()));
|
||||
}
|
||||
}
|
||||
return (map);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
protected Integer getRequestParamInteger(Context context, String name)
|
||||
{
|
||||
String requestParam = getRequestParam(context, name);
|
||||
return ValueUtils.getValueAsInteger(requestParam);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** For initial setup when server boots, set the qInstance - but also,
|
||||
** e.g., for development, to do a hot-swap.
|
||||
***************************************************************************/
|
||||
public void setQInstance(QInstance qInstance)
|
||||
{
|
||||
this.qInstance = qInstance;
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// if we did a hot swap, we should clear these memoizations too //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
memoizedRequestParameters = null;
|
||||
memoizedRequestBody = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** An original implementation here was prone to race-condition-based errors:
|
||||
*
|
||||
** if(memoizedRequestParameters == null)
|
||||
** {
|
||||
** memoizedRequestParameters = CollectionUtils.nonNullList(defineRequestParameters());
|
||||
** }
|
||||
** return (memoizedRequestParameters);
|
||||
**
|
||||
** where between the defineX call and the return, if another thread cleared the
|
||||
** memoizedX field, then a null would be returned, which isn't supposed to happen.
|
||||
** Thus, this implementation which looks a bit more convoluted, but should
|
||||
** be safe(r).
|
||||
***************************************************************************/
|
||||
private List<Parameter> getMemoizedRequestParameters()
|
||||
{
|
||||
List<Parameter> rs = memoizedRequestParameters;
|
||||
if(rs == null)
|
||||
{
|
||||
rs = CollectionUtils.nonNullList(defineRequestParameters());
|
||||
memoizedRequestParameters = rs;
|
||||
}
|
||||
|
||||
return (rs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private RequestBody getMemoizedRequestBody()
|
||||
{
|
||||
RequestBody rs = memoizedRequestBody;
|
||||
if(rs == null)
|
||||
{
|
||||
rs = defineRequestBody();
|
||||
memoizedRequestBody = rs;
|
||||
}
|
||||
|
||||
return (rs);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs;
|
||||
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.utils.ClassPathUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.SchemaBuilder;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.openapi.model.Components;
|
||||
import com.kingsrook.qqq.openapi.model.Contact;
|
||||
import com.kingsrook.qqq.openapi.model.Content;
|
||||
import com.kingsrook.qqq.openapi.model.Example;
|
||||
import com.kingsrook.qqq.openapi.model.Info;
|
||||
import com.kingsrook.qqq.openapi.model.Method;
|
||||
import com.kingsrook.qqq.openapi.model.OpenAPI;
|
||||
import com.kingsrook.qqq.openapi.model.Path;
|
||||
import com.kingsrook.qqq.openapi.model.Response;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
import com.kingsrook.qqq.openapi.model.SecurityScheme;
|
||||
import com.kingsrook.qqq.openapi.model.SecuritySchemeType;
|
||||
import com.kingsrook.qqq.openapi.model.Type;
|
||||
import io.javalin.apibuilder.EndpointGroup;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Baseclass that combines multiple specs together into a single "version" of
|
||||
** the full qqq middleware.
|
||||
*******************************************************************************/
|
||||
public abstract class AbstractMiddlewareVersion
|
||||
{
|
||||
public static final QLogger LOG = QLogger.getLogger(AbstractMiddlewareVersion.class);
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public abstract String getVersion();
|
||||
|
||||
/***************************************************************************
|
||||
** hey - don't re-construct the endpoint-spec objects inside this method...
|
||||
***************************************************************************/
|
||||
public abstract List<AbstractEndpointSpec<?, ?, ?>> getEndpointSpecs();
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public EndpointGroup getJavalinEndpointGroup(QInstance qInstance)
|
||||
{
|
||||
return (() ->
|
||||
{
|
||||
for(AbstractEndpointSpec<?, ?, ?> spec : CollectionUtils.nonNullList(getEndpointSpecs()))
|
||||
{
|
||||
spec.defineRoute("/" + getVersion() + "/");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** For initial setup when server boots, set the qInstance - but also,
|
||||
** e.g., for development, to do a hot-swap.
|
||||
***************************************************************************/
|
||||
public void setQInstance(QInstance qInstance)
|
||||
{
|
||||
getEndpointSpecs().forEach(spec -> spec.setQInstance(qInstance));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public OpenAPI generateOpenAPIModel(String basePath) throws QException
|
||||
{
|
||||
List<AbstractEndpointSpec<?, ?, ?>> list = getEndpointSpecs();
|
||||
|
||||
Map<String, Path> paths = new LinkedHashMap<>();
|
||||
Map<String, Example> componentExamples = new LinkedHashMap<>();
|
||||
|
||||
Set<Class<?>> componentClasses = new HashSet<>();
|
||||
Map<String, Schema> componentSchemas = new TreeMap<>();
|
||||
buildComponentSchemasFromComponentsPackage(componentSchemas, componentClasses);
|
||||
|
||||
String sessionUuidCookieSchemeName = "sessionUuidCookie";
|
||||
SecurityScheme sessionUuidCookieScheme = new SecurityScheme()
|
||||
.withType(SecuritySchemeType.API_KEY)
|
||||
.withName("sessionUUID")
|
||||
.withIn("cookie");
|
||||
|
||||
for(AbstractEndpointSpec<?, ?, ?> spec : list)
|
||||
{
|
||||
CompleteOperation completeOperation = spec.defineCompleteOperation();
|
||||
String fullPath = ("/" + basePath + "/" + getVersion() + "/" + completeOperation.getPath()).replaceAll("/+", "/");
|
||||
Path path = paths.computeIfAbsent(fullPath, (k) -> new Path());
|
||||
Method method = completeOperation.getMethod();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if this spec is supposed to be secured, but no security has been applied, then add our default security //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(spec.isSecured() && method.getSecurity() == null)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// the N/A here refers to the lack of a 'scope' for this kind of permission //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
method.withSecurity(List.of(Map.of(sessionUuidCookieSchemeName, List.of("N/A"))));
|
||||
}
|
||||
|
||||
convertMethodSchemasToRefs(method, componentClasses);
|
||||
|
||||
switch(completeOperation.getHttpMethod())
|
||||
{
|
||||
case GET ->
|
||||
{
|
||||
warnIfPathMethodAlreadyUsed(path.getGet(), completeOperation, spec);
|
||||
path.withGet(method);
|
||||
}
|
||||
case POST ->
|
||||
{
|
||||
warnIfPathMethodAlreadyUsed(path.getPost(), completeOperation, spec);
|
||||
path.withPost(method);
|
||||
}
|
||||
case PUT ->
|
||||
{
|
||||
warnIfPathMethodAlreadyUsed(path.getPut(), completeOperation, spec);
|
||||
path.withPut(method);
|
||||
}
|
||||
case PATCH ->
|
||||
{
|
||||
warnIfPathMethodAlreadyUsed(path.getPatch(), completeOperation, spec);
|
||||
path.withPatch(method);
|
||||
}
|
||||
case DELETE ->
|
||||
{
|
||||
warnIfPathMethodAlreadyUsed(path.getDelete(), completeOperation, spec);
|
||||
path.withDelete(method);
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected value: " + completeOperation.getHttpMethod());
|
||||
}
|
||||
|
||||
for(Map.Entry<String, Schema> entry : CollectionUtils.nonNullMap(spec.defineComponentSchemas()).entrySet())
|
||||
{
|
||||
if(componentSchemas.containsKey(entry.getKey()))
|
||||
{
|
||||
LOG.warn("More than one endpoint spec defined a componentSchema named: " + entry.getKey() + ". The last one encountered (from " + spec.getClass().getSimpleName() + ") will be used.");
|
||||
}
|
||||
|
||||
componentSchemas.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
OpenAPI openAPI = new OpenAPI();
|
||||
openAPI.withInfo(new Info()
|
||||
.withVersion(getVersion())
|
||||
.withTitle("QQQ Middleware API")
|
||||
.withDescription(getDescription())
|
||||
.withContact(new Contact().withEmail("contact@kingsrook.com"))
|
||||
);
|
||||
|
||||
openAPI.withPaths(paths);
|
||||
|
||||
openAPI.withComponents(new Components()
|
||||
.withSchemas(componentSchemas)
|
||||
.withExamples(componentExamples)
|
||||
.withSecuritySchemes(Map.of(sessionUuidCookieSchemeName, sessionUuidCookieScheme))
|
||||
);
|
||||
|
||||
return openAPI;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void buildComponentSchemasFromComponentsPackage(Map<String, Schema> componentSchemas, Set<Class<?>> componentClasses) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
////////////////////////////////////////////////////
|
||||
// find all classes in the components sub-package //
|
||||
////////////////////////////////////////////////////
|
||||
String packageName = getClass().getPackageName();
|
||||
List<Class<?>> classesInPackage = ClassPathUtils.getClassesInPackage(packageName);
|
||||
for(Class<?> c : classesInPackage)
|
||||
{
|
||||
if(c.getPackageName().matches(".*\\bcomponents\\b.*"))
|
||||
{
|
||||
componentClasses.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// now that we know that full set, make any references to others schemas in those objects be via Ref //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
for(Class<?> c : componentClasses)
|
||||
{
|
||||
Object o = null;
|
||||
try
|
||||
{
|
||||
o = c.getConstructor().newInstance();
|
||||
}
|
||||
catch(Exception nsme)
|
||||
{
|
||||
///////////////////////////////////////
|
||||
// fine, assume we can't do toSchema //
|
||||
///////////////////////////////////////
|
||||
}
|
||||
|
||||
Schema schema = null;
|
||||
if(o instanceof ToSchema toSchema)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// just in case a custom implementation of toSchema is provided (e.g., to go around a wrapped object or some-such) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
schema = toSchema.toSchema();
|
||||
}
|
||||
else
|
||||
{
|
||||
schema = new SchemaBuilder().classToSchema(c);
|
||||
}
|
||||
|
||||
convertSchemaToRefs(schema, componentClasses);
|
||||
|
||||
componentSchemas.put(c.getSimpleName(), schema);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QException("Error building component schemas from components package", e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void convertMethodSchemasToRefs(Method method, Set<Class<?>> componentClasses)
|
||||
{
|
||||
for(Response response : method.getResponses().values())
|
||||
{
|
||||
for(Content content : response.getContent().values())
|
||||
{
|
||||
Schema schema = content.getSchema();
|
||||
convertSchemaToRefs(schema, componentClasses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void convertSchemaToRefs(Schema schema, Set<Class<?>> componentClasses)
|
||||
{
|
||||
if(schema.getItems() instanceof SchemaBuilder.SchemaFromBuilder itemSchemaFromBuilder && componentClasses.contains(itemSchemaFromBuilder.getOriginalClass()))
|
||||
{
|
||||
schema.getItems().withRefToSchema(itemSchemaFromBuilder.getOriginalClass().getSimpleName());
|
||||
schema.getItems().setProperties(null);
|
||||
schema.getItems().setType((Type) null);
|
||||
}
|
||||
else if(schema.getItems() != null)
|
||||
{
|
||||
convertSchemaToRefs(schema.getItems(), componentClasses);
|
||||
}
|
||||
|
||||
if(schema.getProperties() != null)
|
||||
{
|
||||
for(Schema propertySchema : schema.getProperties().values())
|
||||
{
|
||||
if(propertySchema instanceof SchemaBuilder.SchemaFromBuilder propertySchemaFromBuilder && componentClasses.contains(propertySchemaFromBuilder.getOriginalClass()))
|
||||
{
|
||||
propertySchema.withRefToSchema(propertySchemaFromBuilder.getOriginalClass().getSimpleName());
|
||||
propertySchema.setProperties(null);
|
||||
propertySchema.setType((Type) null);
|
||||
}
|
||||
else
|
||||
{
|
||||
convertSchemaToRefs(propertySchema, componentClasses);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static String getDescription()
|
||||
{
|
||||
return """
|
||||
## Intro
|
||||
This is the definition of the standard API implemented by QQQ Middleware.
|
||||
|
||||
Developers of QQQ Frontends (e.g., javascript libraries, or native applications) use this API to access
|
||||
a QQQ Backend server.
|
||||
|
||||
As such, this API itself is not concerned with any of the application-level details of any particular
|
||||
application built using QQQ. Instead, this API is all about the generic endpoints used for any application
|
||||
built on QQQ. For example, many endpoints work with a `${table}` path parameter - whose possible values
|
||||
are defined by the application - but which are not known to this API.
|
||||
|
||||
## Flow
|
||||
The typical flow of a user (as implemented in a frontend that utilizes this API) looks like:
|
||||
1. Frontend calls `.../metaData/authentication`, to know what type of authentication provider is used by the backend, and display an appropriate UI to the user for authenticating.
|
||||
2. User authenticates in frontend, as required for the authentication provider.
|
||||
3. Frontend calls `.../manageSession`, providing authentication details (e.g., an accessToken or other credentials) to the backend.
|
||||
4. The response from the `manageSession` call (assuming success), sets the `sessionUUID` Cookie, which should be included in all subsequent requests for authentication.
|
||||
5. After the user is authenticated, the frontend calls `.../metaData`, to discover the apps, tables, processes, etc, that the application is made up of (and that the authenticated user has permission to access).
|
||||
6. As the user interacts with apps, tables, process, etc, the frontend utilizes the appropriate endpoints as required.
|
||||
""";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public void warnIfPathMethodAlreadyUsed(Method existing, CompleteOperation completeOperation, AbstractEndpointSpec<?, ?, ?> spec)
|
||||
{
|
||||
if(existing != null)
|
||||
{
|
||||
LOG.warn("More than one endpoint spec for version " + getVersion() + " defined a " + completeOperation.getHttpMethod() + " at path: " + completeOperation.getPath() + ". The last one encountered (from " + spec.getClass().getSimpleName() + ") will be used.");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.openapi.model.HttpMethod;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Basic definition of an operation (e.g., an endpoint exposed in the API).
|
||||
*******************************************************************************/
|
||||
public class BasicOperation
|
||||
{
|
||||
private String path;
|
||||
private HttpMethod httpMethod;
|
||||
private TagsInterface tag;
|
||||
private String shortSummary;
|
||||
private String longDescription;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 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 BasicOperation withPath(String path)
|
||||
{
|
||||
this.path = path;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for httpMethod
|
||||
*******************************************************************************/
|
||||
public HttpMethod getHttpMethod()
|
||||
{
|
||||
return (this.httpMethod);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for httpMethod
|
||||
*******************************************************************************/
|
||||
public void setHttpMethod(HttpMethod httpMethod)
|
||||
{
|
||||
this.httpMethod = httpMethod;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for httpMethod
|
||||
*******************************************************************************/
|
||||
public BasicOperation withHttpMethod(HttpMethod httpMethod)
|
||||
{
|
||||
this.httpMethod = httpMethod;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tag
|
||||
*******************************************************************************/
|
||||
public TagsInterface getTag()
|
||||
{
|
||||
return (this.tag);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tag
|
||||
*******************************************************************************/
|
||||
public void setTag(TagsInterface tag)
|
||||
{
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tag
|
||||
*******************************************************************************/
|
||||
public BasicOperation withTag(TagsInterface tag)
|
||||
{
|
||||
this.tag = tag;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for shortSummary
|
||||
*******************************************************************************/
|
||||
public String getShortSummary()
|
||||
{
|
||||
return (this.shortSummary);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for shortSummary
|
||||
*******************************************************************************/
|
||||
public void setShortSummary(String shortSummary)
|
||||
{
|
||||
this.shortSummary = shortSummary;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for shortSummary
|
||||
*******************************************************************************/
|
||||
public BasicOperation withShortSummary(String shortSummary)
|
||||
{
|
||||
this.shortSummary = shortSummary;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for longDescription
|
||||
*******************************************************************************/
|
||||
public String getLongDescription()
|
||||
{
|
||||
return (this.longDescription);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for longDescription
|
||||
*******************************************************************************/
|
||||
public void setLongDescription(String longDescription)
|
||||
{
|
||||
this.longDescription = longDescription;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for longDescription
|
||||
*******************************************************************************/
|
||||
public BasicOperation withLongDescription(String longDescription)
|
||||
{
|
||||
this.longDescription = longDescription;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.openapi.model.Example;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.HttpStatus;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** Basic version of a response from a spec/endpoint.
|
||||
***************************************************************************/
|
||||
public record BasicResponse(String contentType, HttpStatus status, String description, String schemaRefName, Map<String, Example> examples)
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BasicResponse(String description, String schemaRefName)
|
||||
{
|
||||
this(ContentType.APPLICATION_JSON.getMimeType(), HttpStatus.OK, description, schemaRefName, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BasicResponse(String description, String schemaRefName, Map<String, Example> examples)
|
||||
{
|
||||
this(ContentType.APPLICATION_JSON.getMimeType(), HttpStatus.OK, description, schemaRefName, examples);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BasicResponse(HttpStatus status, String description, String schemaRefName)
|
||||
{
|
||||
this(ContentType.APPLICATION_JSON.getMimeType(), status, description, schemaRefName, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BasicResponse(HttpStatus status, String description, String schemaRefName, Map<String, Example> examples)
|
||||
{
|
||||
this(ContentType.APPLICATION_JSON.getMimeType(), status, description, schemaRefName, examples);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.openapi.model.Method;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Extension of a BasicOperation that adds the full openAPI Method object.
|
||||
*******************************************************************************/
|
||||
public class CompleteOperation extends BasicOperation
|
||||
{
|
||||
private Method method;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public CompleteOperation(BasicOperation basicOperation)
|
||||
{
|
||||
setPath(basicOperation.getPath());
|
||||
setHttpMethod(basicOperation.getHttpMethod());
|
||||
setTag(basicOperation.getTag());
|
||||
setLongDescription(basicOperation.getLongDescription());
|
||||
setShortSummary(basicOperation.getShortSummary());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for method
|
||||
*******************************************************************************/
|
||||
public Method getMethod()
|
||||
{
|
||||
return (this.method);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for method
|
||||
*******************************************************************************/
|
||||
public void setMethod(Method method)
|
||||
{
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for method
|
||||
*******************************************************************************/
|
||||
public CompleteOperation withMethod(Method method)
|
||||
{
|
||||
this.method = method;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface TagsInterface
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
String getText();
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1;
|
||||
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.AuthenticationMetaDataExecutor;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.AuthenticationMetaDataInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractEndpointSpec;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicOperation;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicResponse;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.AuthenticationMetaDataResponseV1;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.utils.TagsV1;
|
||||
import com.kingsrook.qqq.openapi.model.Example;
|
||||
import com.kingsrook.qqq.openapi.model.HttpMethod;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
import io.javalin.http.Context;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class AuthenticationMetaDataSpecV1 extends AbstractEndpointSpec<AuthenticationMetaDataInput, AuthenticationMetaDataResponseV1, AuthenticationMetaDataExecutor>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public BasicOperation defineBasicOperation()
|
||||
{
|
||||
return new BasicOperation()
|
||||
.withPath("/metaData/authentication")
|
||||
.withHttpMethod(HttpMethod.GET)
|
||||
.withTag(TagsV1.AUTHENTICATION)
|
||||
.withShortSummary("Get authentication metaData")
|
||||
.withLongDescription("""
|
||||
For a frontend to determine which authentication provider or mechanism to use, it should begin its lifecycle
|
||||
by requesting this metaData object, and inspecting the `type` property in the response.
|
||||
|
||||
Note that this endpoint is not secured, as its purpose is to be called as part of the workflow that results
|
||||
in a user being authenticated."""
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public boolean isSecured()
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public AuthenticationMetaDataInput buildInput(Context context) throws Exception
|
||||
{
|
||||
AuthenticationMetaDataInput input = new AuthenticationMetaDataInput();
|
||||
return (input);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public Map<String, Schema> defineComponentSchemas()
|
||||
{
|
||||
return Map.of(AuthenticationMetaDataResponseV1.class.getSimpleName(), new AuthenticationMetaDataResponseV1().toSchema());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public BasicResponse defineBasicSuccessResponse()
|
||||
{
|
||||
Map<String, Example> examples = new LinkedHashMap<>();
|
||||
examples.put("For FULLY_ANONYMOUS type", new Example()
|
||||
.withValue(new AuthenticationMetaDataResponseV1()
|
||||
.withType(QAuthenticationType.FULLY_ANONYMOUS.name())
|
||||
.withName("anonymous")));
|
||||
|
||||
examples.put("For AUTH_0 type", new Example()
|
||||
.withValue(new AuthenticationMetaDataResponseV1()
|
||||
.withType(QAuthenticationType.AUTH_0.name())
|
||||
.withName("auth0")
|
||||
.withValues(new AuthenticationMetaDataResponseV1.Auth0Values()
|
||||
.withClientId("abcdefg1234567")
|
||||
.withBaseUrl("https://myapp.auth0.com/")
|
||||
.withAudience("myapp.mydomain.com"))));
|
||||
|
||||
return new BasicResponse("Successful Response", AuthenticationMetaDataResponseV1.class.getSimpleName(), examples);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleOutput(Context context, AuthenticationMetaDataResponseV1 output) throws Exception
|
||||
{
|
||||
context.result(JsonUtils.toJson(output));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.ManageSessionExecutor;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ManageSessionInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractEndpointSpec;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicOperation;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicResponse;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.BasicErrorResponseV1;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.ManageSessionResponseV1;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.utils.ProcessSpecUtilsV1;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.utils.TagsV1;
|
||||
import com.kingsrook.qqq.openapi.model.Content;
|
||||
import com.kingsrook.qqq.openapi.model.Example;
|
||||
import com.kingsrook.qqq.openapi.model.HttpMethod;
|
||||
import com.kingsrook.qqq.openapi.model.RequestBody;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
import com.kingsrook.qqq.openapi.model.Type;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.Context;
|
||||
import io.javalin.http.HttpStatus;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ManageSessionSpecV1 extends AbstractEndpointSpec<ManageSessionInput, ManageSessionResponseV1, ManageSessionExecutor>
|
||||
{
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public BasicOperation defineBasicOperation()
|
||||
{
|
||||
return new BasicOperation()
|
||||
.withPath("/manageSession")
|
||||
.withHttpMethod(HttpMethod.POST)
|
||||
.withTag(TagsV1.AUTHENTICATION)
|
||||
.withShortSummary("Create a session")
|
||||
.withLongDescription("""
|
||||
After a frontend authenticates the user as per the requirements of the authentication provider specified by the
|
||||
`type` field in the `metaData/authentication` response, data from that authentication provider should be posted
|
||||
to this endpoint, to create a session within the QQQ application.
|
||||
|
||||
The response object will include a session identifier (`uuid`) to authenticate the user in subsequent API calls.""");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public boolean isSecured()
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public ManageSessionResponseV1 serveRequest(Context context) throws Exception
|
||||
{
|
||||
ManageSessionResponseV1 result = super.serveRequest(context);
|
||||
if(result != null)
|
||||
{
|
||||
String sessionUuid = result.getUuid();
|
||||
context.cookie(QJavalinImplementation.SESSION_UUID_COOKIE_NAME, sessionUuid, QJavalinImplementation.SESSION_COOKIE_AGE);
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public RequestBody defineRequestBody()
|
||||
{
|
||||
return new RequestBody()
|
||||
.withRequired(true)
|
||||
.withContent(MapBuilder.of(ContentType.JSON, new Content()
|
||||
.withSchema(new Schema()
|
||||
.withDescription("Data required to create the session. Specific needs may vary based on the AuthenticationModule type in the QQQ Backend.")
|
||||
.withType(Type.OBJECT)
|
||||
.withProperty("accessToken", new Schema()
|
||||
.withType(Type.STRING)
|
||||
.withDescription("An access token from a downstream authentication provider (e.g., Auth0), to use as the basis for authentication and authorization.")
|
||||
)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public ManageSessionInput buildInput(Context context) throws Exception
|
||||
{
|
||||
ManageSessionInput manageSessionInput = new ManageSessionInput();
|
||||
manageSessionInput.setAccessToken(getRequestParam(context, "accessToken"));
|
||||
return (manageSessionInput);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public BasicResponse defineBasicSuccessResponse()
|
||||
{
|
||||
Map<String, Example> examples = new LinkedHashMap<>();
|
||||
|
||||
examples.put("With no custom values", new Example().withValue(new ManageSessionResponseV1()
|
||||
.withUuid(ProcessSpecUtilsV1.EXAMPLE_PROCESS_UUID)
|
||||
));
|
||||
|
||||
examples.put("With custom values", new Example().withValue(new ManageSessionResponseV1()
|
||||
.withUuid(ProcessSpecUtilsV1.EXAMPLE_JOB_UUID)
|
||||
.withValues(MapBuilder.of(LinkedHashMap<String, Serializable>::new)
|
||||
.with("region", "US")
|
||||
.with("userCategoryId", 47)
|
||||
.build()
|
||||
)
|
||||
));
|
||||
|
||||
return new BasicResponse("Successful response - session has been created",
|
||||
ManageSessionResponseV1.class.getSimpleName(),
|
||||
examples);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public Map<String, Schema> defineComponentSchemas()
|
||||
{
|
||||
return Map.of(
|
||||
ManageSessionResponseV1.class.getSimpleName(), new ManageSessionResponseV1().toSchema(),
|
||||
BasicErrorResponseV1.class.getSimpleName(), new BasicErrorResponseV1().toSchema()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public List<BasicResponse> defineAdditionalBasicResponses()
|
||||
{
|
||||
Map<String, Example> examples = new LinkedHashMap<>();
|
||||
examples.put("Invalid token", new Example().withValue(new BasicErrorResponseV1().withError("Unable to decode access token.")));
|
||||
|
||||
return List.of(
|
||||
new BasicResponse(HttpStatus.UNAUTHORIZED,
|
||||
"Authentication error - session was not created",
|
||||
BasicErrorResponseV1.class.getSimpleName(),
|
||||
examples
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleOutput(Context context, ManageSessionResponseV1 output) throws Exception
|
||||
{
|
||||
context.result(JsonUtils.toJson(output));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1;
|
||||
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataAction;
|
||||
import com.kingsrook.qqq.backend.core.context.CapturedContext;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType;
|
||||
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.QAuthenticationMetaData;
|
||||
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.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.PermissionLevel;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
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.Capability;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSystemUserSession;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryBackendModule;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.MetaDataExecutor;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.MetaDataInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractEndpointSpec;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicOperation;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicResponse;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.MetaDataResponseV1;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.utils.TagsV1;
|
||||
import com.kingsrook.qqq.openapi.model.Example;
|
||||
import com.kingsrook.qqq.openapi.model.HttpMethod;
|
||||
import com.kingsrook.qqq.openapi.model.In;
|
||||
import com.kingsrook.qqq.openapi.model.Parameter;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
import com.kingsrook.qqq.openapi.model.Type;
|
||||
import io.javalin.http.Context;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MetaDataSpecV1 extends AbstractEndpointSpec<MetaDataInput, MetaDataResponseV1, MetaDataExecutor>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public BasicOperation defineBasicOperation()
|
||||
{
|
||||
return new BasicOperation()
|
||||
.withPath("/metaData")
|
||||
.withHttpMethod(HttpMethod.GET)
|
||||
.withTag(TagsV1.GENERAL)
|
||||
.withShortSummary("Get instance metaData")
|
||||
.withLongDescription("""
|
||||
Load the overall metadata, as is relevant to a frontend, for the entire application, with permissions applied, as per the
|
||||
authenticated user.
|
||||
|
||||
This includes:
|
||||
- Apps (both as a map of name to AppMetaData (`apps`), but also as a tree (`appTree`), for presenting
|
||||
hierarchical navigation),
|
||||
- Tables (but without all details, e.g., fields),
|
||||
- Processes (also without full details, e.g., screens),
|
||||
- Reports
|
||||
- Widgets
|
||||
- Branding
|
||||
- Help Contents
|
||||
- Environment values
|
||||
"""
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public List<Parameter> defineRequestParameters()
|
||||
{
|
||||
return List.of(
|
||||
|
||||
new Parameter()
|
||||
.withName("frontendName")
|
||||
.withDescription("""
|
||||
Name of the frontend requesting the meta-data.
|
||||
Generally a QQQ frontend library, unless a custom application frontend has been built.""")
|
||||
.withIn(In.QUERY)
|
||||
.withSchema(new Schema().withType(Type.STRING))
|
||||
.withExample("qqq-frontend-material-dashboard"),
|
||||
|
||||
new Parameter()
|
||||
.withName("frontendVersion")
|
||||
.withDescription("Version of the frontend requesting the meta-data.")
|
||||
.withIn(In.QUERY)
|
||||
.withSchema(new Schema().withType(Type.STRING))
|
||||
.withExample("0.23.0"),
|
||||
|
||||
new Parameter()
|
||||
.withName("applicationName")
|
||||
.withDescription("""
|
||||
Name of the application requesting the meta-data. e.g., an instance of a specific frontend
|
||||
(i.e., an application might be deployed with 2 different qqq-frontend-material-dashboard frontends,
|
||||
in which case this attribute allows differentiation between them).""")
|
||||
.withIn(In.QUERY)
|
||||
.withSchema(new Schema().withType(Type.STRING))
|
||||
.withExample("my-admin-web-app"),
|
||||
|
||||
new Parameter()
|
||||
.withName("applicationVersion")
|
||||
.withDescription("Version of the application requesting the meta-data.")
|
||||
.withIn(In.QUERY)
|
||||
.withSchema(new Schema().withType(Type.STRING))
|
||||
.withExample("20241021")
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public MetaDataInput buildInput(Context context) throws Exception
|
||||
{
|
||||
MetaDataInput input = new MetaDataInput();
|
||||
|
||||
input.setMiddlewareName("qqq-middleware-javalin");
|
||||
input.setMiddlewareVersion("v1");
|
||||
|
||||
input.setFrontendName(getRequestParam(context, "frontendName"));
|
||||
input.setFrontendVersion(getRequestParam(context, "frontendVersion"));
|
||||
|
||||
input.setApplicationName(getRequestParam(context, "applicationName"));
|
||||
input.setApplicationVersion(getRequestParam(context, "applicationVersion"));
|
||||
|
||||
return (input);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public Map<String, Schema> defineComponentSchemas()
|
||||
{
|
||||
return Map.of(MetaDataResponseV1.class.getSimpleName(), new MetaDataResponseV1().toSchema());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public BasicResponse defineBasicSuccessResponse()
|
||||
{
|
||||
Map<String, Example> examples = new HashMap<>();
|
||||
|
||||
QInstance exampleInstance = new QInstance();
|
||||
|
||||
exampleInstance.setAuthentication(new QAuthenticationMetaData().withName("anonymous").withType(QAuthenticationType.FULLY_ANONYMOUS));
|
||||
|
||||
QBackendMetaData exampleBackend = new QBackendMetaData()
|
||||
.withName("example")
|
||||
.withBackendType(MemoryBackendModule.class);
|
||||
exampleInstance.addBackend(exampleBackend);
|
||||
|
||||
//////////////////////////////////////
|
||||
// create stable sorting of entries //
|
||||
//////////////////////////////////////
|
||||
TreeSet<Capability> capabilities = new TreeSet<>(Comparator.comparing((Capability c) -> c.name()));
|
||||
capabilities.addAll(Capability.allReadCapabilities());
|
||||
capabilities.addAll(Capability.allWriteCapabilities());
|
||||
|
||||
QTableMetaData exampleTable = new QTableMetaData()
|
||||
.withName("person")
|
||||
.withLabel("Person")
|
||||
.withBackendName("example")
|
||||
.withPrimaryKeyField("id")
|
||||
.withIsHidden(false)
|
||||
.withIcon(new QIcon().withName("person_outline"))
|
||||
.withEnabledCapabilities(capabilities)
|
||||
.withPermissionRules(new QPermissionRules().withLevel(PermissionLevel.NOT_PROTECTED))
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER));
|
||||
exampleInstance.addTable(exampleTable);
|
||||
|
||||
QProcessMetaData exampleProcess = new QProcessMetaData()
|
||||
.withName("samplePersonProcess")
|
||||
.withLabel("Sample Person Process")
|
||||
.withTableName("person")
|
||||
.withIsHidden(false)
|
||||
.withIcon(new QIcon().withName("person_add"))
|
||||
.withPermissionRules(new QPermissionRules().withLevel(PermissionLevel.NOT_PROTECTED))
|
||||
.withStep(new QFrontendStepMetaData().withName("example"));
|
||||
exampleInstance.addProcess(exampleProcess);
|
||||
|
||||
QAppMetaData childApp = new QAppMetaData()
|
||||
.withName("childApp")
|
||||
.withLabel("Child App")
|
||||
.withIcon(new QIcon().withName("child_friendly"))
|
||||
.withPermissionRules(new QPermissionRules().withLevel(PermissionLevel.NOT_PROTECTED))
|
||||
.withChild(exampleProcess);
|
||||
exampleInstance.addApp(childApp);
|
||||
|
||||
QAppMetaData exampleApp = new QAppMetaData()
|
||||
.withName("homeApp")
|
||||
.withLabel("Home App")
|
||||
.withIcon(new QIcon().withName("home"))
|
||||
.withPermissionRules(new QPermissionRules().withLevel(PermissionLevel.NOT_PROTECTED))
|
||||
.withChild(childApp)
|
||||
.withChild(exampleTable);
|
||||
exampleInstance.addApp(exampleApp);
|
||||
|
||||
QContext.withTemporaryContext(new CapturedContext(exampleInstance, new QSystemUserSession()), () ->
|
||||
{
|
||||
try
|
||||
{
|
||||
MetaDataAction metaDataAction = new MetaDataAction();
|
||||
MetaDataOutput output = metaDataAction.execute(new com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput());
|
||||
examples.put("Example", new Example()
|
||||
.withValue(new MetaDataResponseV1()
|
||||
.withMetaDataOutput(output)
|
||||
)
|
||||
);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
examples.put("Example", new Example().withValue("Error building example: " + e.getMessage())
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return new BasicResponse("""
|
||||
Overall metadata for the application.""",
|
||||
MetaDataResponseV1.class.getSimpleName(),
|
||||
examples
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleOutput(Context context, MetaDataResponseV1 output) throws Exception
|
||||
{
|
||||
context.result(JsonUtils.toJson(output));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractEndpointSpec;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractMiddlewareVersion;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MiddlewareVersionV1 extends AbstractMiddlewareVersion
|
||||
{
|
||||
private static List<AbstractEndpointSpec<?, ?, ?>> list = new ArrayList<>();
|
||||
|
||||
static
|
||||
{
|
||||
list.add(new AuthenticationMetaDataSpecV1());
|
||||
list.add(new ManageSessionSpecV1());
|
||||
|
||||
list.add(new MetaDataSpecV1());
|
||||
|
||||
list.add(new ProcessMetaDataSpecV1());
|
||||
list.add(new ProcessInitSpecV1());
|
||||
list.add(new ProcessStepSpecV1());
|
||||
list.add(new ProcessStatusSpecV1());
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public String getVersion()
|
||||
{
|
||||
return "v1";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public List<AbstractEndpointSpec<?, ?, ?>> getEndpointSpecs()
|
||||
{
|
||||
return (list);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
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.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.ProcessInitOrStepExecutor;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessInitOrStepInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractEndpointSpec;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicOperation;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicResponse;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.ProcessInitOrStepOrStatusResponseV1;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.utils.ProcessSpecUtilsV1;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.utils.TagsV1;
|
||||
import com.kingsrook.qqq.openapi.model.Content;
|
||||
import com.kingsrook.qqq.openapi.model.Example;
|
||||
import com.kingsrook.qqq.openapi.model.HttpMethod;
|
||||
import com.kingsrook.qqq.openapi.model.In;
|
||||
import com.kingsrook.qqq.openapi.model.Parameter;
|
||||
import com.kingsrook.qqq.openapi.model.RequestBody;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
import com.kingsrook.qqq.openapi.model.Type;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.Context;
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessInitSpecV1 extends AbstractEndpointSpec<ProcessInitOrStepInput, ProcessInitOrStepOrStatusResponseV1, ProcessInitOrStepExecutor>
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(ProcessInitSpecV1.class);
|
||||
|
||||
public static int DEFAULT_ASYNC_STEP_TIMEOUT_MILLIS = 3_000;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public BasicOperation defineBasicOperation()
|
||||
{
|
||||
return new BasicOperation()
|
||||
.withPath("/processes/{processName}/init")
|
||||
.withHttpMethod(HttpMethod.POST)
|
||||
.withTag(TagsV1.PROCESSES)
|
||||
.withShortSummary("Initialize a process")
|
||||
.withLongDescription("""
|
||||
For a user to start running a process, this endpoint should be called, to start the process
|
||||
and run its first step(s) (any backend steps before the first frontend step).
|
||||
|
||||
Additional process-specific values should posted in a form param named `values`, as JSON object
|
||||
with keys defined by the process in question.
|
||||
|
||||
For a process which needs to operate on a set of records that a user selected, see
|
||||
`recordsParam`, and `recordIds` or `filterJSON`.
|
||||
|
||||
The response will include a `processUUID`, to be included in all subsequent requests relevant
|
||||
to the process.
|
||||
|
||||
Note that this request, if it takes longer than a given threshold* to complete, will return a
|
||||
a `jobUUID`, which should be sent to the `/processes/{processName}/{processUUID}/status/{jobUUID}`
|
||||
endpoint, to poll for a status update.
|
||||
|
||||
*This threshold has a default value of 3,000 ms., but can be set per-request via the form
|
||||
parameter `stepTimeoutMillis`.
|
||||
""");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public List<Parameter> defineRequestParameters()
|
||||
{
|
||||
return List.of(
|
||||
new Parameter()
|
||||
.withName("processName")
|
||||
.withDescription("Name of the process to initialize")
|
||||
.withRequired(true)
|
||||
.withSchema(new Schema().withType(Type.STRING))
|
||||
.withExample("samplePersonProcess")
|
||||
.withIn(In.PATH)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public RequestBody defineRequestBody()
|
||||
{
|
||||
return new RequestBody()
|
||||
.withContent(
|
||||
ContentType.MULTIPART_FORM_DATA.getMimeType(), new Content()
|
||||
.withSchema(new Schema()
|
||||
.withType(Type.OBJECT)
|
||||
.withProperty("values", new Schema()
|
||||
.withType(Type.OBJECT)
|
||||
.withDescription("Process-specific field names and values."))
|
||||
|
||||
.withProperty("recordsParam", new Schema()
|
||||
.withDescription("Specifies which other query-param will contain the indicator of initial records to pass in to the process.")
|
||||
.withType(Type.STRING)
|
||||
.withExample("recordIds", new Example().withValue("recordIds"))
|
||||
.withExample("filterJSON", new Example().withValue("recordIds")))
|
||||
|
||||
.withProperty("recordIds", new Schema()
|
||||
.withDescription("Comma-separated list of ids from the table this process is based on, to use as input records for the process. Needs `recordsParam=recordIds` value to be given as well.")
|
||||
.withType(Type.STRING)
|
||||
.withExample("one id", new Example().withValue("1701"))
|
||||
.withExample("multiple ids", new Example().withValue("42,47")))
|
||||
|
||||
.withProperty("filterJSON", new Schema()
|
||||
.withDescription("JSON encoded QQueryFilter object, to execute against the table this process is based on, to find input records for the process. Needs `recordsParam=filterJSON` value to be given as well.")
|
||||
.withType(Type.STRING)
|
||||
.withExample("empty filter (all records)", new Example().withValue("{}"))
|
||||
.withExample("filter by a condition", new Example().withValue(
|
||||
JsonUtils.toJson(new QQueryFilter().withCriteria(new QFilterCriteria("id", QCriteriaOperator.LESS_THAN, 10))))
|
||||
))
|
||||
|
||||
.withProperty("stepTimeoutMillis", new Schema()
|
||||
.withDescription("Optionally change the time that the server will wait for the job before letting it go asynchronous. Default value is 3000.")
|
||||
.withType(Type.INTEGER)
|
||||
.withExample("shorter timeout", new Example().withValue("500"))
|
||||
.withExample("longer timeout", new Example().withValue("60000")))
|
||||
|
||||
.withProperty("file", new Schema()
|
||||
.withType(Type.STRING)
|
||||
.withFormat("binary")
|
||||
.withDescription("A file upload, for processes which expect to be initialized with an uploaded file.")
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public ProcessInitOrStepInput buildInput(Context context) throws Exception
|
||||
{
|
||||
ProcessInitOrStepInput processInitOrStepInput = new ProcessInitOrStepInput();
|
||||
processInitOrStepInput.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
|
||||
|
||||
processInitOrStepInput.setProcessName(getRequestParam(context, "processName"));
|
||||
processInitOrStepInput.setStepTimeoutMillis(Objects.requireNonNullElse(getRequestParamInteger(context, "stepTimeoutMillis"), DEFAULT_ASYNC_STEP_TIMEOUT_MILLIS));
|
||||
processInitOrStepInput.setValues(getRequestParamMap(context, "values"));
|
||||
|
||||
String recordsParam = getRequestParam(context, "recordsParam");
|
||||
String recordIds = getRequestParam(context, "recordIds");
|
||||
String filterJSON = getRequestParam(context, "filterJSON");
|
||||
QQueryFilter initialRecordsFilter = buildProcessInitRecordsFilter(recordsParam, recordIds, filterJSON, processInitOrStepInput);
|
||||
processInitOrStepInput.setRecordsFilter(initialRecordsFilter);
|
||||
|
||||
// todo - uploaded files
|
||||
// todo - archive uploaded files?
|
||||
|
||||
return (processInitOrStepInput);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QQueryFilter buildProcessInitRecordsFilter(String recordsParam, String recordIds, String filterJSON, ProcessInitOrStepInput processInitOrStepInput) throws IOException
|
||||
{
|
||||
QProcessMetaData process = QContext.getQInstance().getProcess(processInitOrStepInput.getProcessName());
|
||||
QTableMetaData table = QContext.getQInstance().getTable(process.getTableName());
|
||||
|
||||
if(table == null)
|
||||
{
|
||||
LOG.info("No table found in process - so not building an init records filter.");
|
||||
return (null);
|
||||
}
|
||||
String primaryKeyField = table.getPrimaryKeyField();
|
||||
|
||||
if(StringUtils.hasContent(recordsParam))
|
||||
{
|
||||
return switch(recordsParam)
|
||||
{
|
||||
case "recordIds" ->
|
||||
{
|
||||
Serializable[] idStrings = recordIds.split(",");
|
||||
yield (new QQueryFilter().withCriteria(new QFilterCriteria()
|
||||
.withFieldName(primaryKeyField)
|
||||
.withOperator(QCriteriaOperator.IN)
|
||||
.withValues(Arrays.stream(idStrings).toList())));
|
||||
}
|
||||
case "filterJSON" -> (JsonUtils.toObject(filterJSON, QQueryFilter.class));
|
||||
case "filterId" -> throw (new NotImplementedException("Saved filters are not yet implemented."));
|
||||
default -> throw (new IllegalArgumentException("Unrecognized value [" + recordsParam + "] for query parameter: recordsParam"));
|
||||
};
|
||||
}
|
||||
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public BasicResponse defineBasicSuccessResponse()
|
||||
{
|
||||
return new BasicResponse("""
|
||||
State of the initialization of the job, with different fields set, based on the
|
||||
status of the task.""",
|
||||
|
||||
ProcessSpecUtilsV1.getResponseSchemaRefName(),
|
||||
ProcessSpecUtilsV1.buildResponseExample()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleOutput(Context context, ProcessInitOrStepOrStatusResponseV1 output) throws Exception
|
||||
{
|
||||
ProcessSpecUtilsV1.handleOutput(context, output);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1;
|
||||
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.ProcessMetaDataExecutor;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessMetaDataInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractEndpointSpec;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicOperation;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicResponse;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.ProcessMetaDataResponseV1;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.utils.TagsV1;
|
||||
import com.kingsrook.qqq.openapi.model.Example;
|
||||
import com.kingsrook.qqq.openapi.model.HttpMethod;
|
||||
import com.kingsrook.qqq.openapi.model.In;
|
||||
import com.kingsrook.qqq.openapi.model.Parameter;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
import com.kingsrook.qqq.openapi.model.Type;
|
||||
import io.javalin.http.Context;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessMetaDataSpecV1 extends AbstractEndpointSpec<ProcessMetaDataInput, ProcessMetaDataResponseV1, ProcessMetaDataExecutor>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public BasicOperation defineBasicOperation()
|
||||
{
|
||||
return new BasicOperation()
|
||||
.withPath("/metaData/process/{processName}")
|
||||
.withHttpMethod(HttpMethod.GET)
|
||||
.withTag(TagsV1.PROCESSES)
|
||||
.withShortSummary("Get process metaData")
|
||||
.withLongDescription("""
|
||||
Load the full metadata for a single process, including all screens (aka, frontend steps), which a frontend
|
||||
needs to display to users."""
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public List<Parameter> defineRequestParameters()
|
||||
{
|
||||
return List.of(
|
||||
new Parameter()
|
||||
.withName("processName")
|
||||
.withDescription("Name of the process to load.")
|
||||
.withRequired(true)
|
||||
.withSchema(new Schema().withType(Type.STRING))
|
||||
.withExample("samplePersonProcess")
|
||||
.withIn(In.PATH)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public ProcessMetaDataInput buildInput(Context context) throws Exception
|
||||
{
|
||||
ProcessMetaDataInput input = new ProcessMetaDataInput();
|
||||
input.setProcessName(getRequestParam(context, "processName"));
|
||||
return (input);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public Map<String, Schema> defineComponentSchemas()
|
||||
{
|
||||
return Map.of(ProcessMetaDataResponseV1.class.getSimpleName(), new ProcessMetaDataResponseV1().toSchema());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public BasicResponse defineBasicSuccessResponse()
|
||||
{
|
||||
Map<String, Example> examples = new LinkedHashMap<>();
|
||||
examples.put("TODO", new Example()
|
||||
.withValue(new ProcessMetaDataResponseV1())); // todo do
|
||||
|
||||
return new BasicResponse("""
|
||||
The full process metadata""",
|
||||
ProcessMetaDataResponseV1.class.getSimpleName(),
|
||||
examples
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleOutput(Context context, ProcessMetaDataResponseV1 output) throws Exception
|
||||
{
|
||||
context.result(JsonUtils.toJson(output.getProcessMetaData()));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.ProcessStatusExecutor;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessStatusInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractEndpointSpec;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicOperation;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicResponse;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.ProcessInitOrStepOrStatusResponseV1;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.utils.ProcessSpecUtilsV1;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.utils.TagsV1;
|
||||
import com.kingsrook.qqq.openapi.model.HttpMethod;
|
||||
import com.kingsrook.qqq.openapi.model.In;
|
||||
import com.kingsrook.qqq.openapi.model.Parameter;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
import com.kingsrook.qqq.openapi.model.Type;
|
||||
import io.javalin.http.Context;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessStatusSpecV1 extends AbstractEndpointSpec<ProcessStatusInput, ProcessInitOrStepOrStatusResponseV1, ProcessStatusExecutor>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public BasicOperation defineBasicOperation()
|
||||
{
|
||||
return new BasicOperation()
|
||||
.withPath("/processes/{processName}/{processUUID}/status/{jobUUID}")
|
||||
.withHttpMethod(HttpMethod.GET)
|
||||
.withTag(TagsV1.PROCESSES)
|
||||
.withShortSummary("Get job status")
|
||||
.withLongDescription("""
|
||||
Get the status of a running job for a process.
|
||||
|
||||
Response is the same format as for an init or step call that completed synchronously.
|
||||
"""
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public List<Parameter> defineRequestParameters()
|
||||
{
|
||||
return List.of(
|
||||
new Parameter()
|
||||
.withName("processName")
|
||||
.withDescription("Name of the process that is being ran")
|
||||
.withRequired(true)
|
||||
.withSchema(new Schema().withType(Type.STRING))
|
||||
.withExample("samplePersonProcess")
|
||||
.withIn(In.PATH),
|
||||
|
||||
new Parameter()
|
||||
.withName("processUUID")
|
||||
.withDescription("Unique identifier for this run of the process - as was returned by the `init` call.")
|
||||
.withRequired(true)
|
||||
.withSchema(new Schema().withType(Type.STRING).withFormat("uuid"))
|
||||
.withExample(ProcessSpecUtilsV1.EXAMPLE_PROCESS_UUID)
|
||||
.withIn(In.PATH),
|
||||
|
||||
new Parameter()
|
||||
.withName("jobUUID")
|
||||
.withDescription("Unique identifier for the asynchronous job being executed, as returned by an `init` or `step` call that went asynch.")
|
||||
.withRequired(true)
|
||||
.withSchema(new Schema().withType(Type.STRING).withFormat("uuid"))
|
||||
.withExample(ProcessSpecUtilsV1.EXAMPLE_JOB_UUID)
|
||||
.withIn(In.PATH)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** These aren't in the components sub-package, so they don't get auto-found.
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public Map<String, Schema> defineComponentSchemas()
|
||||
{
|
||||
return Map.of(
|
||||
ProcessSpecUtilsV1.getResponseSchemaRefName(), new ProcessInitOrStepOrStatusResponseV1().toSchema(),
|
||||
"ProcessStepComplete", new ProcessInitOrStepOrStatusResponseV1.ProcessStepComplete().toSchema(),
|
||||
"ProcessStepJobStarted", new ProcessInitOrStepOrStatusResponseV1.ProcessStepJobStarted().toSchema(),
|
||||
"ProcessStepRunning", new ProcessInitOrStepOrStatusResponseV1.ProcessStepRunning().toSchema(),
|
||||
"ProcessStepError", new ProcessInitOrStepOrStatusResponseV1.ProcessStepError().toSchema()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public ProcessStatusInput buildInput(Context context) throws Exception
|
||||
{
|
||||
ProcessStatusInput input = new ProcessStatusInput();
|
||||
input.setProcessName(getRequestParam(context, "processName"));
|
||||
input.setProcessUUID(getRequestParam(context, "processUUID"));
|
||||
input.setJobUUID(getRequestParam(context, "jobUUID"));
|
||||
return (input);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public BasicResponse defineBasicSuccessResponse()
|
||||
{
|
||||
return new BasicResponse("""
|
||||
State of the backend's running of the specified job, with different fields set,
|
||||
based on the status of the job.""",
|
||||
// new ProcessInitOrStepOrStatusResponseV1().toSchema(),
|
||||
|
||||
ProcessSpecUtilsV1.getResponseSchemaRefName(),
|
||||
ProcessSpecUtilsV1.buildResponseExample()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleOutput(Context context, ProcessInitOrStepOrStatusResponseV1 output) throws Exception
|
||||
{
|
||||
ProcessSpecUtilsV1.handleOutput(context, output);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.ProcessInitOrStepExecutor;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessInitOrStepInput;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractEndpointSpec;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicOperation;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicResponse;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.ProcessInitOrStepOrStatusResponseV1;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.utils.ProcessSpecUtilsV1;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.utils.TagsV1;
|
||||
import com.kingsrook.qqq.openapi.model.Content;
|
||||
import com.kingsrook.qqq.openapi.model.Example;
|
||||
import com.kingsrook.qqq.openapi.model.HttpMethod;
|
||||
import com.kingsrook.qqq.openapi.model.In;
|
||||
import com.kingsrook.qqq.openapi.model.Parameter;
|
||||
import com.kingsrook.qqq.openapi.model.RequestBody;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
import com.kingsrook.qqq.openapi.model.Type;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.Context;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessStepSpecV1 extends AbstractEndpointSpec<ProcessInitOrStepInput, ProcessInitOrStepOrStatusResponseV1, ProcessInitOrStepExecutor>
|
||||
{
|
||||
public static int DEFAULT_ASYNC_STEP_TIMEOUT_MILLIS = 3_000;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public BasicOperation defineBasicOperation()
|
||||
{
|
||||
return new BasicOperation()
|
||||
.withPath("/processes/{processName}/{processUUID}/step/{stepName}")
|
||||
.withHttpMethod(HttpMethod.POST)
|
||||
.withTag(TagsV1.PROCESSES)
|
||||
.withShortSummary("Run a step in a process")
|
||||
.withLongDescription("""
|
||||
To run the next step in a process, this endpoint should be called, with the `processName`
|
||||
and existing `processUUID`, as well as the step that was just completed in the frontend,
|
||||
given as `stepName`.
|
||||
|
||||
Additional process-specific values should posted in a form param named `values`, as JSON object
|
||||
with keys defined by the process in question.
|
||||
|
||||
Note that this request, if it takes longer than a given threshold* to complete, will return a
|
||||
a `jobUUID`, which should be sent to the `/processes/{processName}/{processUUID}/status/{jobUUID}`
|
||||
endpoint, to poll for a status update.
|
||||
|
||||
*This threshold has a default value of 3,000 ms., but can be set per-request via the form
|
||||
parameter `stepTimeoutMillis`.
|
||||
""");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public List<Parameter> defineRequestParameters()
|
||||
{
|
||||
return List.of(
|
||||
|
||||
new Parameter()
|
||||
.withName("processName")
|
||||
.withDescription("Name of the process to perform the step in.")
|
||||
.withRequired(true)
|
||||
.withExample("samplePersonProcess")
|
||||
.withSchema(new Schema().withType(Type.STRING))
|
||||
.withIn(In.PATH),
|
||||
|
||||
new Parameter()
|
||||
.withName("processUUID")
|
||||
.withDescription("Unique identifier for this run of the process - as was returned by the `init` call.")
|
||||
.withRequired(true)
|
||||
.withSchema(new Schema().withType(Type.STRING))
|
||||
.withExample(ProcessSpecUtilsV1.EXAMPLE_PROCESS_UUID)
|
||||
.withIn(In.PATH),
|
||||
|
||||
new Parameter()
|
||||
.withName("stepName")
|
||||
.withDescription("Name of the frontend step that the user has just completed.")
|
||||
.withRequired(true)
|
||||
.withSchema(new Schema().withType(Type.STRING))
|
||||
.withExample("inputForm")
|
||||
.withIn(In.PATH)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public RequestBody defineRequestBody()
|
||||
{
|
||||
return new RequestBody()
|
||||
.withContent(ContentType.MULTIPART_FORM_DATA.getMimeType(), new Content()
|
||||
.withSchema(new Schema()
|
||||
.withType(Type.OBJECT)
|
||||
.withProperty("values", new Schema()
|
||||
.withType(Type.OBJECT)
|
||||
.withDescription("Process-specific field names and values."))
|
||||
|
||||
.withProperty("stepTimeoutMillis", new Schema()
|
||||
.withDescription("Optionally change the time that the server will wait for the job before letting it go asynchronous. Default value is 3000.")
|
||||
.withType(Type.INTEGER)
|
||||
.withExample("shorter timeout", new Example().withValue("500"))
|
||||
.withExample("longer timeout", new Example().withValue("60000")))
|
||||
|
||||
.withProperty("file", new Schema()
|
||||
.withType(Type.STRING)
|
||||
.withFormat("binary")
|
||||
.withDescription("A file upload, for process steps which expect an uploaded file."))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public ProcessInitOrStepInput buildInput(Context context) throws Exception
|
||||
{
|
||||
ProcessInitOrStepInput processInitOrStepInput = new ProcessInitOrStepInput();
|
||||
processInitOrStepInput.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
|
||||
|
||||
processInitOrStepInput.setProcessName(getRequestParam(context, "processName"));
|
||||
processInitOrStepInput.setProcessUUID(getRequestParam(context, "processUUID"));
|
||||
processInitOrStepInput.setStartAfterStep(getRequestParam(context, "stepName"));
|
||||
processInitOrStepInput.setStepTimeoutMillis(Objects.requireNonNullElse(getRequestParamInteger(context, "stepTimeoutMillis"), DEFAULT_ASYNC_STEP_TIMEOUT_MILLIS));
|
||||
processInitOrStepInput.setValues(getRequestParamMap(context, "values"));
|
||||
|
||||
// todo - uploaded files
|
||||
// todo - archive uploaded files?
|
||||
|
||||
return (processInitOrStepInput);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public BasicResponse defineBasicSuccessResponse()
|
||||
{
|
||||
return new BasicResponse("""
|
||||
State of the backend's running of the next step(s) of the job, with different fields set,
|
||||
based on the status of the job.""",
|
||||
|
||||
ProcessSpecUtilsV1.getResponseSchemaRefName(),
|
||||
ProcessSpecUtilsV1.buildResponseExample()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleOutput(Context context, ProcessInitOrStepOrStatusResponseV1 output) throws Exception
|
||||
{
|
||||
ProcessSpecUtilsV1.handleOutput(context, output);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.authentication.Auth0AuthenticationMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.AuthenticationMetaDataOutputInterface;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIOneOf;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class AuthenticationMetaDataResponseV1 implements AuthenticationMetaDataOutputInterface, ToSchema
|
||||
{
|
||||
@OpenAPIDescription("""
|
||||
Specifier for the type of authentication module being used.
|
||||
|
||||
Frontends should use this value to determine how to prompt the user for authentication credentials.
|
||||
In addition, depending on this value, additional properties will be included in this object, as
|
||||
may be needed to complete the authorization workflow with the provider (e.g., a baseUrl, clientId,
|
||||
and audience for an OAuth type workflow).""")
|
||||
private String type;
|
||||
|
||||
@OpenAPIDescription("""
|
||||
Unique name for the authentication metaData object within the QInstance.
|
||||
""")
|
||||
private String name;
|
||||
|
||||
@OpenAPIDescription("""
|
||||
Additional values, as determined by the type of authentication provider.
|
||||
""")
|
||||
@OpenAPIOneOf()
|
||||
private Values values;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public sealed interface Values permits EmptyValues, Auth0Values
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("No additional values are used for some authentication providers.")
|
||||
public static final class EmptyValues implements Values
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Additional values used by the Auth0 type authentication provider.")
|
||||
public static final class Auth0Values implements Values
|
||||
{
|
||||
@OpenAPIDescription("ClientId for auth0")
|
||||
private String clientId;
|
||||
|
||||
@OpenAPIDescription("BaseUrl for auth0")
|
||||
private String baseUrl;
|
||||
|
||||
@OpenAPIDescription("Audience for auth0")
|
||||
private String audience;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for clientId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getClientId()
|
||||
{
|
||||
return clientId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for clientId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setClientId(String clientId)
|
||||
{
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for clientId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Auth0Values withClientId(String clientId)
|
||||
{
|
||||
this.clientId = clientId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for baseUrl
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getBaseUrl()
|
||||
{
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for baseUrl
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setBaseUrl(String baseUrl)
|
||||
{
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for baseUrl
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Auth0Values withBaseUrl(String baseUrl)
|
||||
{
|
||||
this.baseUrl = baseUrl;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for audience
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getAudience()
|
||||
{
|
||||
return audience;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for audience
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setAudience(String audience)
|
||||
{
|
||||
this.audience = audience;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for audience
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Auth0Values withAudience(String audience)
|
||||
{
|
||||
this.audience = audience;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setAuthenticationMetaData(QAuthenticationMetaData qAuthenticationMetaData)
|
||||
{
|
||||
setType(qAuthenticationMetaData.getType().name());
|
||||
setName(qAuthenticationMetaData.getName());
|
||||
|
||||
if(qAuthenticationMetaData instanceof Auth0AuthenticationMetaData auth0MetaData)
|
||||
{
|
||||
// values = new LinkedHashMap<>();
|
||||
// values.put("clientId", auth0MetaData.getClientId());
|
||||
// values.put("baseUrl", auth0MetaData.getBaseUrl());
|
||||
// values.put("audience", auth0MetaData.getAudience());
|
||||
Auth0Values auth0Values = new Auth0Values();
|
||||
values = auth0Values;
|
||||
auth0Values.setClientId(auth0MetaData.getClientId());
|
||||
auth0Values.setBaseUrl(auth0MetaData.getBaseUrl());
|
||||
auth0Values.setAudience(auth0MetaData.getAudience());
|
||||
}
|
||||
|
||||
/*
|
||||
JSONObject jsonObject = new JSONObject(JsonUtils.toJson(qAuthenticationMetaData));
|
||||
for(String key : jsonObject.keySet())
|
||||
{
|
||||
if("name".equals(key) || "type".equals(key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(values == null)
|
||||
{
|
||||
values = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
Object value = jsonObject.get(key);
|
||||
if(value instanceof Serializable s)
|
||||
{
|
||||
values.put(key, s);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 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 AuthenticationMetaDataResponseV1 withType(String type)
|
||||
{
|
||||
this.type = type;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return (this.name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for name
|
||||
*******************************************************************************/
|
||||
public AuthenticationMetaDataResponseV1 withName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for values
|
||||
*******************************************************************************/
|
||||
public Values getValues()
|
||||
{
|
||||
return (this.values);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for values
|
||||
*******************************************************************************/
|
||||
public void setValues(Values values)
|
||||
{
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for values
|
||||
*******************************************************************************/
|
||||
public AuthenticationMetaDataResponseV1 withValues(Values values)
|
||||
{
|
||||
this.values = values;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class BasicErrorResponseV1 implements ToSchema
|
||||
{
|
||||
@OpenAPIDescription("Description of the error")
|
||||
private String error;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for error
|
||||
*******************************************************************************/
|
||||
public String getError()
|
||||
{
|
||||
return (this.error);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for error
|
||||
*******************************************************************************/
|
||||
public void setError(String error)
|
||||
{
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for error
|
||||
*******************************************************************************/
|
||||
public BasicErrorResponseV1 withError(String error)
|
||||
{
|
||||
this.error = error;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ManageSessionOutputInterface;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIHasAdditionalProperties;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ManageSessionResponseV1 implements ManageSessionOutputInterface, ToSchema
|
||||
{
|
||||
@OpenAPIDescription("Unique identifier of the session. Required to be returned on subsequent requests in the sessionUUID Cookie, to prove authentication.")
|
||||
private String uuid;
|
||||
|
||||
@OpenAPIDescription("Optional object with application-defined values.")
|
||||
@OpenAPIHasAdditionalProperties()
|
||||
private Map<String, Serializable> values;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for uuid
|
||||
*******************************************************************************/
|
||||
public String getUuid()
|
||||
{
|
||||
return (this.uuid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for uuid
|
||||
*******************************************************************************/
|
||||
public void setUuid(String uuid)
|
||||
{
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for uuid
|
||||
*******************************************************************************/
|
||||
public ManageSessionResponseV1 withUuid(String uuid)
|
||||
{
|
||||
this.uuid = uuid;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for values
|
||||
*******************************************************************************/
|
||||
public Map<String, Serializable> getValues()
|
||||
{
|
||||
return (this.values);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for values
|
||||
*******************************************************************************/
|
||||
public void setValues(Map<String, Serializable> values)
|
||||
{
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for values
|
||||
*******************************************************************************/
|
||||
public ManageSessionResponseV1 withValues(Map<String, Serializable> values)
|
||||
{
|
||||
this.values = values;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendWidgetMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.MetaDataOutputInterface;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIListItems;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIMapValueType;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.components.AppMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.components.AppTreeNode;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.components.ProcessMetaDataLight;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.components.TableMetaDataLight;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MetaDataResponseV1 implements MetaDataOutputInterface, ToSchema
|
||||
{
|
||||
@OpenAPIDescription("Map of all apps within the QQQ Instance (that the user has permission to see that they exist).")
|
||||
@OpenAPIMapValueType(value = AppMetaData.class, useRef = true)
|
||||
private Map<String, AppMetaData> apps;
|
||||
|
||||
@OpenAPIDescription("Tree of apps within the QQQ Instance, sorted and organized hierarchically, for presentation to a user.")
|
||||
@OpenAPIListItems(value = AppTreeNode.class, useRef = true)
|
||||
private List<AppTreeNode> appTree;
|
||||
|
||||
@OpenAPIDescription("Map of all tables within the QQQ Instance (that the user has permission to see that they exist).")
|
||||
@OpenAPIMapValueType(value = TableMetaDataLight.class, useRef = true)
|
||||
private Map<String, TableMetaDataLight> tables;
|
||||
|
||||
@OpenAPIDescription("Map of all processes within the QQQ Instance (that the user has permission to see that they exist).")
|
||||
@OpenAPIMapValueType(value = ProcessMetaDataLight.class, useRef = true)
|
||||
private Map<String, ProcessMetaDataLight> processes;
|
||||
|
||||
@OpenAPIDescription("Map of all widgets within the QQQ Instance (that the user has permission to see that they exist).")
|
||||
@OpenAPIMapValueType(value = ProcessMetaDataLight.class, useRef = true)
|
||||
private Map<String, WidgetMetaData> widgets;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setMetaDataOutput(MetaDataOutput metaDataOutput)
|
||||
{
|
||||
apps = new HashMap<>();
|
||||
for(QFrontendAppMetaData app : CollectionUtils.nonNullMap(metaDataOutput.getApps()).values())
|
||||
{
|
||||
apps.put(app.getName(), new AppMetaData(app));
|
||||
}
|
||||
|
||||
appTree = new ArrayList<>();
|
||||
for(com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode app : CollectionUtils.nonNullList(metaDataOutput.getAppTree()))
|
||||
{
|
||||
appTree.add(new AppTreeNode(app));
|
||||
}
|
||||
|
||||
tables = new HashMap<>();
|
||||
for(QFrontendTableMetaData table : CollectionUtils.nonNullMap(metaDataOutput.getTables()).values())
|
||||
{
|
||||
tables.put(table.getName(), new TableMetaDataLight(table));
|
||||
}
|
||||
|
||||
processes = new HashMap<>();
|
||||
for(QFrontendProcessMetaData process : CollectionUtils.nonNullMap(metaDataOutput.getProcesses()).values())
|
||||
{
|
||||
processes.put(process.getName(), new ProcessMetaDataLight(process));
|
||||
}
|
||||
|
||||
widgets = new HashMap<>();
|
||||
for(QFrontendWidgetMetaData widget : CollectionUtils.nonNullMap(metaDataOutput.getWidgets()).values())
|
||||
{
|
||||
widgets.put(widget.getName(), new WidgetMetaData(widget));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for MetaDataOutput
|
||||
**
|
||||
*******************************************************************************/
|
||||
public MetaDataResponseV1 withMetaDataOutput(MetaDataOutput metaDataOutput)
|
||||
{
|
||||
setMetaDataOutput(metaDataOutput);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for apps
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, AppMetaData> getApps()
|
||||
{
|
||||
return apps;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for appTree
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<AppTreeNode> getAppTree()
|
||||
{
|
||||
return appTree;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tables
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, TableMetaDataLight> getTables()
|
||||
{
|
||||
return tables;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processes
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, ProcessMetaDataLight> getProcesses()
|
||||
{
|
||||
return processes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for widgets
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, WidgetMetaData> getWidgets()
|
||||
{
|
||||
return widgets;
|
||||
}
|
||||
}
|
@ -0,0 +1,444 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessInitOrStepOrStatusOutputInterface;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.SchemaBuilder;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIIncludeProperties;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIOneOf;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.components.FieldMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.components.FrontendStep;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.components.ProcessMetaDataAdjustment;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessInitOrStepOrStatusResponseV1 implements ProcessInitOrStepOrStatusOutputInterface, ToSchema
|
||||
{
|
||||
private TypedResponse typedResponse;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIOneOf()
|
||||
public static sealed class TypedResponse implements ToSchema permits ProcessStepComplete, ProcessStepJobStarted, ProcessStepRunning, ProcessStepError
|
||||
{
|
||||
@OpenAPIDescription("What kind of response has been received. Determines what additional fields will be set.")
|
||||
private String type;
|
||||
|
||||
@OpenAPIDescription("Unique identifier for a running instance the process.")
|
||||
private String processUUID;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for type
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processUUID
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getProcessUUID()
|
||||
{
|
||||
return processUUID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIIncludeProperties(ancestorClasses = { TypedResponse.class })
|
||||
@OpenAPIDescription("Data returned after the job is complete (whether it was synchronous, or asynchronous)")
|
||||
public static final class ProcessStepComplete extends TypedResponse
|
||||
{
|
||||
@OpenAPIDescription("Name of the next process step that needs to run (a frontend step). If there are no more steps in the process, this field will not be included. ")
|
||||
private String nextStep;
|
||||
|
||||
@OpenAPIDescription("Current values for fields used by the process.Keys are Strings, values can be any type, as determined by the application & process.")
|
||||
private Map<String, Serializable> values;
|
||||
|
||||
@OpenAPIDescription("Changes to be made to the process's metaData.")
|
||||
private ProcessMetaDataAdjustment processMetaDataAdjustment;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nextStep
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getNextStep()
|
||||
{
|
||||
return nextStep;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for values
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, Serializable> getValues()
|
||||
{
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processMetaDataAdjustment
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessMetaDataAdjustment getProcessMetaDataAdjustment()
|
||||
{
|
||||
return processMetaDataAdjustment;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIIncludeProperties(ancestorClasses = { TypedResponse.class })
|
||||
@OpenAPIDescription("In case the backend needs more time, this is a UUID of the background job that has been started.")
|
||||
public static final class ProcessStepJobStarted extends TypedResponse
|
||||
{
|
||||
@OpenAPIDescription("Unique identifier for a running step of the process. Must be passed into `status` check calls.")
|
||||
private String jobUUID;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for jobUUID
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getJobUUID()
|
||||
{
|
||||
return jobUUID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIIncludeProperties(ancestorClasses = { TypedResponse.class })
|
||||
@OpenAPIDescription("Response to a status check for a backgrounded job.")
|
||||
public static final class ProcessStepRunning extends TypedResponse
|
||||
{
|
||||
@OpenAPIDescription("Status message regarding the running process step.")
|
||||
private String message;
|
||||
|
||||
@OpenAPIDescription("Optional indicator of progress (e.g., `current` of `total`, as in (`1 of 10`).")
|
||||
private Integer current;
|
||||
|
||||
@OpenAPIDescription("Optional indicator of progress (e.g., `current` of `total`, as in (`1 of 10`).")
|
||||
private Integer total;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for message
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getMessage()
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for current
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getCurrent()
|
||||
{
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for total
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getTotal()
|
||||
{
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIIncludeProperties(ancestorClasses = { TypedResponse.class })
|
||||
@OpenAPIDescription("In case an error is thrown in the backend job.")
|
||||
public static final class ProcessStepError extends TypedResponse
|
||||
{
|
||||
@OpenAPIDescription("Exception message, in case the process step threw an error.")
|
||||
private String error;
|
||||
|
||||
@OpenAPIDescription("Optional user-facing exception message, in case the process step threw a user-facing error.")
|
||||
private String userFacingError;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for error
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getError()
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for userFacingError
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getUserFacingError()
|
||||
{
|
||||
return userFacingError;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setType(Type type)
|
||||
{
|
||||
this.typedResponse = switch(type)
|
||||
{
|
||||
case COMPLETE -> new ProcessStepComplete();
|
||||
case JOB_STARTED -> new ProcessStepJobStarted();
|
||||
case RUNNING -> new ProcessStepRunning();
|
||||
case ERROR -> new ProcessStepError();
|
||||
};
|
||||
|
||||
this.typedResponse.type = type.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setProcessUUID(String processUUID)
|
||||
{
|
||||
this.typedResponse.processUUID = processUUID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setNextStep(String nextStep)
|
||||
{
|
||||
if(this.typedResponse instanceof ProcessStepComplete complete)
|
||||
{
|
||||
complete.nextStep = nextStep;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setValues(Map<String, Serializable> values)
|
||||
{
|
||||
if(this.typedResponse instanceof ProcessStepComplete complete)
|
||||
{
|
||||
complete.values = values;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setProcessMetaDataAdjustment(com.kingsrook.qqq.backend.core.model.actions.processes.ProcessMetaDataAdjustment processMetaDataAdjustment)
|
||||
{
|
||||
if(this.typedResponse instanceof ProcessStepComplete complete)
|
||||
{
|
||||
if(processMetaDataAdjustment == null)
|
||||
{
|
||||
complete.processMetaDataAdjustment = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
complete.processMetaDataAdjustment = new ProcessMetaDataAdjustment();
|
||||
|
||||
Map<String, FieldMetaData> updatedFields = processMetaDataAdjustment.getUpdatedFields().entrySet()
|
||||
.stream().collect(Collectors.toMap(e -> e.getKey(), f -> new FieldMetaData(f.getValue())));
|
||||
complete.processMetaDataAdjustment.setUpdatedFields(updatedFields);
|
||||
|
||||
List<FrontendStep> updatedFrontendSteps = processMetaDataAdjustment.getUpdatedFrontendStepList()
|
||||
.stream().map(f -> new FrontendStep(f)).toList();
|
||||
complete.processMetaDataAdjustment.setUpdatedFrontendStepList(updatedFrontendSteps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setJobUUID(String jobUUID)
|
||||
{
|
||||
if(this.typedResponse instanceof ProcessStepJobStarted jobStarted)
|
||||
{
|
||||
jobStarted.jobUUID = jobUUID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setMessage(String message)
|
||||
{
|
||||
if(this.typedResponse instanceof ProcessStepRunning running)
|
||||
{
|
||||
running.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setCurrent(Integer current)
|
||||
{
|
||||
if(this.typedResponse instanceof ProcessStepRunning running)
|
||||
{
|
||||
running.current = current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setTotal(Integer total)
|
||||
{
|
||||
if(this.typedResponse instanceof ProcessStepRunning running)
|
||||
{
|
||||
running.total = total;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setError(String errorString)
|
||||
{
|
||||
if(this.typedResponse instanceof ProcessStepError error)
|
||||
{
|
||||
error.error = errorString;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setUserFacingError(String userFacingError)
|
||||
{
|
||||
if(this.typedResponse instanceof ProcessStepError error)
|
||||
{
|
||||
error.userFacingError = userFacingError;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public Schema toSchema()
|
||||
{
|
||||
return new SchemaBuilder().classToSchema(TypedResponse.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for typedResponse
|
||||
**
|
||||
*******************************************************************************/
|
||||
public TypedResponse getTypedResponse()
|
||||
{
|
||||
return typedResponse;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendProcessMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessMetaDataOutputInterface;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.SchemaBuilder;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.components.ProcessMetaData;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessMetaDataResponseV1 implements ProcessMetaDataOutputInterface, ToSchema
|
||||
{
|
||||
private ProcessMetaData processMetaData;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setProcessMetaData(QFrontendProcessMetaData frontendProcessMetaData)
|
||||
{
|
||||
this.processMetaData = new ProcessMetaData(frontendProcessMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public Schema toSchema()
|
||||
{
|
||||
return new SchemaBuilder().classToSchema(ProcessMetaData.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processMetaData
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessMetaData getProcessMetaData()
|
||||
{
|
||||
return processMetaData;
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendWidgetMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class WidgetMetaData
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private QFrontendWidgetMetaData wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetMetaData(QFrontendWidgetMetaData wrapped)
|
||||
{
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetMetaData()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Unique name for this widget within the QQQ Instance")
|
||||
public String getName()
|
||||
{
|
||||
return (this.wrapped.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("User-facing name for this widget")
|
||||
public String getLabel()
|
||||
{
|
||||
return (this.wrapped.getLabel());
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("The type of this widget.")
|
||||
// todo enum of the NAMES of the widget types?? or, can we just f'ing change to return the enum.name's?
|
||||
public String getType()
|
||||
{
|
||||
return this.wrapped.getType();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIListItems;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIMapKnownEntries;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIMapValueType;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public class AppMetaData implements ToSchema
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private QFrontendAppMetaData wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AppMetaData(QFrontendAppMetaData wrapped)
|
||||
{
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AppMetaData()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Unique name for this app within the QQQ Instance")
|
||||
public String getName()
|
||||
{
|
||||
return (this.wrapped.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("User-facing name for this app")
|
||||
public String getLabel()
|
||||
{
|
||||
return (this.wrapped.getLabel());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Icon to display for the app.")
|
||||
@OpenAPIMapKnownEntries(value = Icon.class, useRef = true)
|
||||
public Icon getIcon()
|
||||
{
|
||||
return (this.wrapped.getIcon() == null ? null : new Icon(this.wrapped.getIcon()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("List of widgets names that are part of this app. These strings should be keys to the widgets map in the QQQ Instance.")
|
||||
@OpenAPIListItems(value = String.class)
|
||||
public List<String> getWidgets()
|
||||
{
|
||||
return (this.wrapped.getWidgets());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("List of other apps, tables, process, and reports, which are contained within this app.")
|
||||
@OpenAPIListItems(value = AppTreeNode.class, useRef = true)
|
||||
public List<AppTreeNode> getChildren()
|
||||
{
|
||||
return (CollectionUtils.nonNullList(this.wrapped.getChildren()).stream().map(a -> new AppTreeNode(a)).toList());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Map of other apps, tables, process, and reports, which are contained within this app. Same contents as the children list, just structured as a map.")
|
||||
@OpenAPIMapValueType(value = AppTreeNode.class, useRef = true)
|
||||
public Map<String, AppTreeNode> getChildMap()
|
||||
{
|
||||
return (CollectionUtils.nonNullMap(this.wrapped.getChildMap()).entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> new AppTreeNode(e.getValue()))));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("List of sections - sub-divisions of the app, to further organize its children.")
|
||||
@OpenAPIListItems(value = AppSection.class, useRef = true) // todo local type
|
||||
public List<AppSection> getSections()
|
||||
{
|
||||
return (CollectionUtils.nonNullList(this.wrapped.getSections()).stream().map(s -> new AppSection(s)).toList());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Additional meta-data describing the app, which may not be known to the QQQ backend core module.")
|
||||
public Map<String, Object> getSupplementalAppMetaData()
|
||||
{
|
||||
return (new LinkedHashMap<>(CollectionUtils.nonNullMap(this.wrapped.getSupplementalAppMetaData())));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIListItems;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIMapKnownEntries;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public class AppSection implements ToSchema
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private QAppSection wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AppSection(QAppSection wrapped)
|
||||
{
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AppSection()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Unique (within the app) name for this section.")
|
||||
public String getName()
|
||||
{
|
||||
return (this.wrapped.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("User-facing name of the section.")
|
||||
public String getLabel()
|
||||
{
|
||||
return (this.wrapped.getLabel());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Icon to display for the section.")
|
||||
@OpenAPIMapKnownEntries(value = Icon.class, useRef = true)
|
||||
public Icon getIcon()
|
||||
{
|
||||
return (this.wrapped.getIcon() == null ? null : new Icon(this.wrapped.getIcon()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("List of table names for the section")
|
||||
@OpenAPIListItems(value = String.class)
|
||||
public List<String> getTables()
|
||||
{
|
||||
return (this.wrapped.getTables());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("List of process names for the section")
|
||||
@OpenAPIListItems(value = String.class)
|
||||
public List<String> getProcesses()
|
||||
{
|
||||
return (this.wrapped.getProcesses());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("List of report names for the section")
|
||||
@OpenAPIListItems(value = String.class)
|
||||
public List<String> getReports()
|
||||
{
|
||||
return (this.wrapped.getReports());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIListItems;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIMapKnownEntries;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public class AppTreeNode implements ToSchema
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AppTreeNode(com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode wrapped)
|
||||
{
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AppTreeNode()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("The type of node (table, process, report, app)")
|
||||
public String getType()
|
||||
{
|
||||
return (this.wrapped.getType() == null ? null : this.wrapped.getType().name());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Unique (within its type) name for this element. e.g., for type = 'table', the table's name.")
|
||||
public String getName()
|
||||
{
|
||||
return (this.wrapped.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("User-facing name of the element.")
|
||||
public String getLabel()
|
||||
{
|
||||
return (this.wrapped.getLabel());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Child elements. Only applies for type='app', which contains additional apps under it")
|
||||
@OpenAPIListItems(value = AppTreeNode.class, useRef = true)
|
||||
public List<AppTreeNode> getChildren()
|
||||
{
|
||||
return (CollectionUtils.nonNullList(this.wrapped.getChildren()).stream().map(a -> new AppTreeNode(a)).toList());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Icon to display for the item.")
|
||||
@OpenAPIMapKnownEntries(value = Icon.class, useRef = true)
|
||||
public Icon getIcon()
|
||||
{
|
||||
return (this.wrapped.getIcon() == null ? null : new Icon(this.wrapped.getIcon()));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIListItems;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class FieldMetaData implements ToSchema
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private QFieldMetaData wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public FieldMetaData(QFieldMetaData wrapped)
|
||||
{
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public FieldMetaData()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Unique name for this field within its container (table or process)")
|
||||
public String getName()
|
||||
{
|
||||
return (this.wrapped.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("User-facing name for this field")
|
||||
public String getLabel()
|
||||
{
|
||||
return (this.wrapped.getLabel());
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Data-type for this field") // todo enum
|
||||
public String getType()
|
||||
{
|
||||
return (this.wrapped.getType() == null ? null : this.wrapped.getType().name());
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Indicate if a value in this field is required.")
|
||||
public Boolean getIsRequired()
|
||||
{
|
||||
return (this.wrapped.getIsRequired());
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Indicate if user may edit the value in this field.")
|
||||
public Boolean getIsEditable()
|
||||
{
|
||||
return (this.wrapped.getIsEditable());
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Indicate if this field should be hidden from users")
|
||||
public Boolean getIsHidden()
|
||||
{
|
||||
return (this.wrapped.getIsHidden());
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Indicator of 'heavy' fields, which are not loaded by default. e.g., some blobs or long-texts")
|
||||
public Boolean getIsHeavy()
|
||||
{
|
||||
return (this.wrapped.getIsHeavy());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("C-style format specifier for displaying values in this field.")
|
||||
public String getDisplayFormat()
|
||||
{
|
||||
return (this.wrapped.getDisplayFormat());
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Default value to use in this field.")
|
||||
public String getDefaultValue()
|
||||
{
|
||||
return (this.wrapped.getDefaultValue() == null ? null : String.valueOf(this.wrapped.getDefaultValue()));
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("If this field's values should come from a possible value source, then that PVS is named here.")
|
||||
public String getPossibleValueSourceName()
|
||||
{
|
||||
return (this.wrapped.getPossibleValueSourceName());
|
||||
}
|
||||
|
||||
// todo - PVS filter!!
|
||||
|
||||
// todo - inline PVS
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("For String fields, the max length the field supports.")
|
||||
public Integer getMaxLength()
|
||||
{
|
||||
return (this.wrapped.getMaxLength());
|
||||
}
|
||||
|
||||
// todo behaviors?
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Special UI dressings to add to the field.")
|
||||
@OpenAPIListItems(value = FieldAdornment.class) // todo!
|
||||
public List<FieldAdornment> getAdornments()
|
||||
{
|
||||
return (this.wrapped.getAdornments());
|
||||
}
|
||||
|
||||
// todo help content
|
||||
|
||||
// todo supplemental...
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIMapKnownEntries;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class FrontendComponent implements ToSchema
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private QFrontendComponentMetaData wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public FrontendComponent(QFrontendComponentMetaData wrapped)
|
||||
{
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public FrontendComponent()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("The type of this component. e.g., what kind of UI element(s) should be presented to the user.")
|
||||
public QComponentType getType()
|
||||
{
|
||||
return (this.wrapped.getType());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for values
|
||||
**
|
||||
*******************************************************************************/
|
||||
@OpenAPIDescription("Name-value pairs specific to the type of component.")
|
||||
@OpenAPIMapKnownEntries(value = FrontendComponentValues.class, useRef = true)
|
||||
public Map<String, Serializable> getValues()
|
||||
{
|
||||
return (this.wrapped.getValues() == null ? null : new FrontendComponentValues(this.wrapped.getValues()).toMap());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.AbstractBlockWidgetData;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIHasAdditionalProperties;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIListItems;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@OpenAPIDescription("""
|
||||
These are the known values that can appear in the values map under a FrontendComponent, to control
|
||||
how that component should be presented to the user.
|
||||
|
||||
Note that additional properties may appear as well.
|
||||
|
||||
In addition, components are expected to use values from an active process's `values` map (e.g., as included in
|
||||
a `ProcessStepComplete` object), with the following contract between component-types and expected values:
|
||||
|
||||
- For component type=`HTML`, there will be a process value with key=`${stepName}.html` (e.g., `resultScreen.html`),
|
||||
whose value is the HTML to display on that screen.
|
||||
- For component type=`HELP_TEXT`: There will be a process value with key=`text`, whose value is the text to display on that screen.
|
||||
There may also be a process value with key=`previewText`, which, if present, can be shown before the full text is shown,
|
||||
e.g., with a toggle control to hide/show the `text` value.
|
||||
""")
|
||||
@OpenAPIHasAdditionalProperties()
|
||||
public class FrontendComponentValues implements ToSchema
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private Map<String, Serializable> wrapped;
|
||||
|
||||
@OpenAPIDescription("""
|
||||
Components of type=`WIDGET`, which do not reference a widget defined in the QQQ Instance, but instead,
|
||||
are defined as a list of blocks within a frontend step component, will have a this value set to true.""")
|
||||
private Boolean isAdHocWidget;
|
||||
|
||||
@OpenAPIDescription("""
|
||||
Components of type=`WIDGET`, which are set as `isAdHocWidget=true`, should include a list of WidgetBlocks in this value.""")
|
||||
@OpenAPIListItems(value = WidgetBlock.class, useRef = true)
|
||||
private List<WidgetBlock> blocks;
|
||||
|
||||
@OpenAPIDescription("""
|
||||
Components of type=`WIDGET`, which should render a widget defined in the QQQ instance, this value specifies
|
||||
the name of that widget. Contrast with ad-hoc widgets.
|
||||
""")
|
||||
private String widgetName;
|
||||
|
||||
@OpenAPIDescription("""
|
||||
Components of type=`EDIT_FORM` can specify a subset of field names to include. This can be used to break a form up into
|
||||
sections, by including multiple EDIT_FORM components, with different lists of `includeFieldNames`.
|
||||
""")
|
||||
@OpenAPIListItems(String.class)
|
||||
private List<String> includeFieldNames;
|
||||
|
||||
@OpenAPIDescription("""
|
||||
Components of type=`EDIT_FORM` can specify a user-facing text label to show on screen.
|
||||
""")
|
||||
private String sectionLabel;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public FrontendComponentValues(Map<String, Serializable> values)
|
||||
{
|
||||
this.wrapped = values;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public FrontendComponentValues()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public Map<String, Serializable> toMap()
|
||||
{
|
||||
if(wrapped == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
Map<String, Serializable> rs = new HashMap<>();
|
||||
for(Map.Entry<String, Serializable> entry : wrapped.entrySet())
|
||||
{
|
||||
String key = entry.getKey();
|
||||
Serializable value = entry.getValue();
|
||||
|
||||
if(key.equals("blocks"))
|
||||
{
|
||||
ArrayList<WidgetBlock> resultList = new ArrayList<>();
|
||||
|
||||
List<AbstractBlockWidgetData<?, ?, ?, ?>> sourceList = (List<AbstractBlockWidgetData<?, ?, ?, ?>>) value;
|
||||
for(AbstractBlockWidgetData<?, ?, ?, ?> abstractBlockWidgetData : sourceList)
|
||||
{
|
||||
resultList.add(new WidgetBlock(abstractBlockWidgetData));
|
||||
}
|
||||
|
||||
value = resultList;
|
||||
}
|
||||
|
||||
rs.put(key, value);
|
||||
}
|
||||
|
||||
return (rs);
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIListItems;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class FrontendStep implements ToSchema
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private QFrontendStepMetaData wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public FrontendStep(QFrontendStepMetaData wrapped)
|
||||
{
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public FrontendStep()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("The unique name for this step within its process")
|
||||
public String getName()
|
||||
{
|
||||
return (this.wrapped.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("The user-facing name for this step")
|
||||
public String getLabel()
|
||||
{
|
||||
return (this.wrapped.getLabel());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("An optional indicator of the screen format preferred by the application to be used for this screen. Different frontends may support different formats, and implement them differently.")
|
||||
public String getFormat()
|
||||
{
|
||||
return (this.wrapped.getFormat());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("The components that make up this screen")
|
||||
@OpenAPIListItems(value = FrontendComponent.class, useRef = true)
|
||||
public List<FrontendComponent> getComponents()
|
||||
{
|
||||
return (CollectionUtils.nonNullList(this.wrapped.getComponents()).stream().map(f -> new FrontendComponent(f)).toList());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Fields used as form fields (inputs) on this step/screen")
|
||||
@OpenAPIListItems(value = FieldMetaData.class, useRef = true)
|
||||
public List<FieldMetaData> getFormFields()
|
||||
{
|
||||
return (CollectionUtils.nonNullList(this.wrapped.getFormFields()).stream().map(f -> new FieldMetaData(f)).toList());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Fields used as view-only fields on this step/screen")
|
||||
@OpenAPIListItems(value = FieldMetaData.class, useRef = true)
|
||||
public List<FieldMetaData> getViewFields()
|
||||
{
|
||||
return (CollectionUtils.nonNullList(this.wrapped.getViewFields()).stream().map(f -> new FieldMetaData(f)).toList());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Fields used in record-lists shown on the step/screen.")
|
||||
@OpenAPIListItems(value = FieldMetaData.class, useRef = true)
|
||||
public List<FieldMetaData> getRecordListFields()
|
||||
{
|
||||
return (CollectionUtils.nonNullList(this.wrapped.getRecordListFields()).stream().map(f -> new FieldMetaData(f)).toList());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public class Icon implements ToSchema
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private QIcon wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Icon(QIcon wrapped)
|
||||
{
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Icon()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("A material UI icon name.")
|
||||
public String getName()
|
||||
{
|
||||
return (this.wrapped.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("A color code to use for displaying the icon")
|
||||
public String getColor()
|
||||
{
|
||||
return (this.wrapped.getColor());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("A path to an image file that can be requested from the server, to serve as the icon image instead of a material UI icon.")
|
||||
public String getPath()
|
||||
{
|
||||
return (this.wrapped.getPath());
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIIncludeProperties;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIListItems;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIIncludeProperties(ancestorClasses = ProcessMetaDataLight.class)
|
||||
public class ProcessMetaData extends ProcessMetaDataLight implements ToSchema
|
||||
{
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessMetaData(QFrontendProcessMetaData wrapped)
|
||||
{
|
||||
super(wrapped);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessMetaData()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Frontend steps (aka, Screens) for this process.")
|
||||
@OpenAPIListItems(value = FrontendStep.class, useRef = true)
|
||||
public List<FrontendStep> getFrontendSteps()
|
||||
{
|
||||
return (CollectionUtils.nonNullList(this.wrapped.getFrontendSteps()).stream().map(f -> new FrontendStep(f)).toList());
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIListItems;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIMapValueType;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessMetaDataAdjustment
|
||||
{
|
||||
@OpenAPIDescription("""
|
||||
In case the backend has changed the list of frontend steps, it will be set in this field.""")
|
||||
@OpenAPIListItems(FrontendStep.class)
|
||||
private List<FrontendStep> updatedFrontendStepList = null;
|
||||
|
||||
@OpenAPIDescription("""
|
||||
Fields whose meta-data has changed. e.g., changing a label, or required status, or inline-possible-values.""")
|
||||
@OpenAPIMapValueType(value = FieldMetaData.class, useRef = true)
|
||||
private Map<String, FieldMetaData> updatedFields = null;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for updatedFrontendStepList
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<FrontendStep> getUpdatedFrontendStepList()
|
||||
{
|
||||
return updatedFrontendStepList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for updatedFrontendStepList
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setUpdatedFrontendStepList(List<FrontendStep> updatedFrontendStepList)
|
||||
{
|
||||
this.updatedFrontendStepList = updatedFrontendStepList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for updatedFrontendStepList
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessMetaDataAdjustment withUpdatedFrontendStepList(List<FrontendStep> updatedFrontendStepList)
|
||||
{
|
||||
this.updatedFrontendStepList = updatedFrontendStepList;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for updatedFields
|
||||
*******************************************************************************/
|
||||
public Map<String, FieldMetaData> getUpdatedFields()
|
||||
{
|
||||
return (this.updatedFields);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for updatedFields
|
||||
*******************************************************************************/
|
||||
public void setUpdatedFields(Map<String, FieldMetaData> updatedFields)
|
||||
{
|
||||
this.updatedFields = updatedFields;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for updatedFields
|
||||
*******************************************************************************/
|
||||
public ProcessMetaDataAdjustment withUpdatedFields(Map<String, FieldMetaData> updatedFields)
|
||||
{
|
||||
this.updatedFields = updatedFields;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendProcessMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIMapKnownEntries;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public class ProcessMetaDataLight implements ToSchema
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
protected QFrontendProcessMetaData wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessMetaDataLight(QFrontendProcessMetaData wrapped)
|
||||
{
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessMetaDataLight()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Unique name for this process within the QQQ Instance")
|
||||
public String getName()
|
||||
{
|
||||
return (this.wrapped.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("User-facing name for this process")
|
||||
public String getLabel()
|
||||
{
|
||||
return (this.wrapped.getLabel());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("If this process is associated with a table, the table name is given here")
|
||||
public String getTableName()
|
||||
{
|
||||
return (this.wrapped.getTableName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Boolean indicator of whether the process should be shown to users or not")
|
||||
public Boolean getIsHidden()
|
||||
{
|
||||
return (this.wrapped.getIsHidden());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Indicator of the Step Flow used by the process. Possible values are: LINEAR, STATE_MACHINE.")
|
||||
public String getStepFlow()
|
||||
{
|
||||
return (this.wrapped.getStepFlow());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Icon to display for the process.")
|
||||
@OpenAPIMapKnownEntries(value = Icon.class, useRef = true)
|
||||
public Icon getIcon()
|
||||
{
|
||||
return (this.wrapped.getIcon() == null ? null : new Icon(this.wrapped.getIcon()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Boolean to indicate if the user has permission for the process.")
|
||||
public Boolean getHasPermission()
|
||||
{
|
||||
return (this.wrapped.getHasPermission());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIListItems;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIMapKnownEntries;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public class TableMetaDataLight implements ToSchema
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private QFrontendTableMetaData wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public TableMetaDataLight(QFrontendTableMetaData wrapped)
|
||||
{
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public TableMetaDataLight()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Unique name for this table within the QQQ Instance")
|
||||
public String getName()
|
||||
{
|
||||
return (this.wrapped.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("User-facing name for this table")
|
||||
public String getLabel()
|
||||
{
|
||||
return (this.wrapped.getLabel());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Boolean indicator of whether the table should be shown to users or not")
|
||||
public Boolean getIsHidden()
|
||||
{
|
||||
return (this.wrapped.getIsHidden());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Icon to display for the table")
|
||||
@OpenAPIMapKnownEntries(value = Icon.class, useRef = true)
|
||||
public Icon getIcon()
|
||||
{
|
||||
return (this.wrapped.getIcon() == null ? null : new Icon(this.wrapped.getIcon()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("List of strings describing actions that are supported by the backend application for the table.")
|
||||
@OpenAPIListItems(value = String.class) // todo - better, enum
|
||||
public List<String> getCapabilities()
|
||||
{
|
||||
return (new ArrayList<>(this.wrapped.getCapabilities()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Boolean to indicate if the user has read permission for the table.")
|
||||
public Boolean getReadPermission()
|
||||
{
|
||||
return (this.wrapped.getReadPermission());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Boolean to indicate if the user has insert permission for the table.")
|
||||
public Boolean getInsertPermission()
|
||||
{
|
||||
return (this.wrapped.getInsertPermission());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Boolean to indicate if the user has edit permission for the table.")
|
||||
public Boolean getEditPermission()
|
||||
{
|
||||
return (this.wrapped.getEditPermission());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Boolean to indicate if the user has delete permission for the table.")
|
||||
public Boolean getDeletePermission()
|
||||
{
|
||||
return (this.wrapped.getDeletePermission());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("If the table uses variants, this is the user-facing label for the table that supplies variants for this table.")
|
||||
public String getVariantTableLabel()
|
||||
{
|
||||
return (this.wrapped.getVariantTableLabel());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Help Contents for this table.") // todo describe more
|
||||
public Map<String, List<QHelpContent>> getHelpContents()
|
||||
{
|
||||
return (this.wrapped.getHelpContents());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.CompositeWidgetData;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.AbstractBlockWidgetData;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIEnumSubSet;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIListItems;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIOneOf;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class WidgetBlock implements Serializable, ToSchema
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private static final QLogger LOG = QLogger.getLogger(WidgetBlock.class);
|
||||
|
||||
@OpenAPIExclude()
|
||||
private final AbstractBlockWidgetData<?, ?, ?, ?> wrapped;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public WidgetBlock(AbstractBlockWidgetData<?, ?, ?, ?> abstractBlockWidgetData)
|
||||
{
|
||||
this.wrapped = abstractBlockWidgetData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Unique identifier for this block within it widget. Used as a key for helpContents.")
|
||||
public String getBlockId()
|
||||
{
|
||||
return (this.wrapped.getBlockId());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public enum BlockType
|
||||
{
|
||||
BUTTON,
|
||||
AUDIO,
|
||||
BIG_NUMBER,
|
||||
COMPOSITE,
|
||||
DIVIDER,
|
||||
IMAGE,
|
||||
INPUT_FIELD,
|
||||
NUMBER_ICON_BADGE,
|
||||
PROGRESS_BAR,
|
||||
// todo? REVEAL,
|
||||
TABLE_SUB_ROW_DETAIL_ROW,
|
||||
TEXT,
|
||||
UP_OR_DOWN_NUMBER
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("What type of block to render.")
|
||||
public BlockType getBlockType()
|
||||
{
|
||||
return (BlockType.valueOf(this.wrapped.getBlockTypeName()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Values to show in the block, or otherwise control its behavior. Different fields based on blockType.")
|
||||
@OpenAPIOneOf()
|
||||
public WidgetBlockValues getValues()
|
||||
{
|
||||
return (WidgetBlockValues.of(this.wrapped.getValues()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Styles to apply to the block. Different fields based on blockType.")
|
||||
@OpenAPIOneOf()
|
||||
public WidgetBlockStyles getStyles()
|
||||
{
|
||||
return (WidgetBlockStyles.of(this.wrapped.getStyles()));
|
||||
}
|
||||
|
||||
// todo link, links
|
||||
|
||||
// todo tooltip, tooltips
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Optional field name (e.g,. from a process's set of fields) to act as a 'guard' for the block - e.g., only include it in the UI if the value for this field is true")
|
||||
public String getConditional()
|
||||
{
|
||||
return (this.wrapped.getConditional());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("For COMPOSITE type blocks, a list of sub-blocks.")
|
||||
@OpenAPIListItems(value = WidgetBlock.class, useRef = true)
|
||||
public List<WidgetBlock> getSubBlocks()
|
||||
{
|
||||
if(this.wrapped instanceof CompositeWidgetData compositeWidgetData)
|
||||
{
|
||||
return (compositeWidgetData.getBlocks() == null ? null : compositeWidgetData.getBlocks().stream().map(b -> new WidgetBlock(b)).toList());
|
||||
}
|
||||
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("For COMPOSITE type blocks, optional control to make the widget appear modally")
|
||||
public CompositeWidgetData.ModalMode getModalMode()
|
||||
{
|
||||
if(this.wrapped instanceof CompositeWidgetData compositeWidgetData)
|
||||
{
|
||||
return (compositeWidgetData.getModalMode());
|
||||
}
|
||||
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static class LayoutSubSet implements OpenAPIEnumSubSet.EnumSubSet<CompositeWidgetData.Layout>
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public EnumSet<CompositeWidgetData.Layout> getSubSet()
|
||||
{
|
||||
return EnumSet.of(
|
||||
CompositeWidgetData.Layout.FLEX_COLUMN,
|
||||
CompositeWidgetData.Layout.FLEX_ROW_WRAPPED,
|
||||
CompositeWidgetData.Layout.FLEX_ROW_SPACE_BETWEEN,
|
||||
CompositeWidgetData.Layout.FLEX_ROW_CENTER,
|
||||
CompositeWidgetData.Layout.TABLE_SUB_ROW_DETAILS,
|
||||
CompositeWidgetData.Layout.BADGES_WRAPPER
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("For COMPOSITE type blocks, an indicator of how the sub-blocks should be laid out")
|
||||
@OpenAPIEnumSubSet(LayoutSubSet.class)
|
||||
public CompositeWidgetData.Layout getLayout()
|
||||
{
|
||||
if(this.wrapped instanceof CompositeWidgetData compositeWidgetData && compositeWidgetData.getLayout() != null)
|
||||
{
|
||||
CompositeWidgetData.Layout layout = compositeWidgetData.getLayout();
|
||||
if(new LayoutSubSet().getSubSet().contains(layout))
|
||||
{
|
||||
return (layout);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.info("Layout [" + layout + "] is not in the subset used by this version. It will not be returned.");
|
||||
}
|
||||
}
|
||||
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
/* todo
|
||||
private Map<String, Serializable> styleOverrides = new HashMap<>();
|
||||
private String overlayHtml;
|
||||
private Map<String, Serializable> overlayStyleOverrides = new HashMap<>();
|
||||
*/
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.audio.AudioValues;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@OpenAPIDescription("Values used for an AUDIO type widget block")
|
||||
public final class WidgetBlockAudioValues implements WidgetBlockValues
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private AudioValues wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockAudioValues(AudioValues textValues)
|
||||
{
|
||||
this.wrapped = textValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockAudioValues()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("The path to the audio file on the server")
|
||||
public String getPath()
|
||||
{
|
||||
return (this.wrapped.getPath());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Control if the file should automatically play when the block is rendered")
|
||||
public Boolean getAutoPlay()
|
||||
{
|
||||
return (this.wrapped.getAutoPlay());
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Control if on-screen controls should be shown to allow the user to control playback")
|
||||
public Boolean getShowControls()
|
||||
{
|
||||
return (this.wrapped.getShowControls());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseStyles;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public final class WidgetBlockBaseStyles implements WidgetBlockStyles
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private BaseStyles wrapped;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public WidgetBlockBaseStyles(BaseStyles baseStyles)
|
||||
{
|
||||
this.wrapped = baseStyles;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockBaseStyles()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Optional padding to apply to the block")
|
||||
public BaseStyles.Directional<String> getPadding()
|
||||
{
|
||||
return (this.wrapped.getPadding());
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("A background color to use for the block")
|
||||
public String getBackgroundColor()
|
||||
{
|
||||
return (this.wrapped.getBackgroundColor());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.button.ButtonStyles;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public final class WidgetBlockButtonStyles implements WidgetBlockStyles
|
||||
{
|
||||
|
||||
@OpenAPIExclude()
|
||||
private ButtonStyles wrapped;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public WidgetBlockButtonStyles(ButtonStyles buttonStyles)
|
||||
{
|
||||
this.wrapped = buttonStyles;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockButtonStyles()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("A Color to use for the button. May be specified as a StandardColor (one of: "
|
||||
+ "SUCCESS, WARNING, ERROR, INFO, MUTED) or an RGB code.")
|
||||
public String getColor()
|
||||
{
|
||||
return (this.wrapped.getColor());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("An optional indicator of the screen format preferred by the application to be used for this block, "
|
||||
+ "such as OUTLINED, FILLED, or TEXT. Different frontends may support different formats, and implement them differently.")
|
||||
public String getFormat()
|
||||
{
|
||||
return (this.wrapped.getFormat());
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.button.ButtonValues;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@OpenAPIDescription("Values used for a BUTTON type widget block")
|
||||
public final class WidgetBlockButtonValues implements WidgetBlockValues
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private ButtonValues wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockButtonValues(ButtonValues buttonValues)
|
||||
{
|
||||
this.wrapped = buttonValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockButtonValues()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("User-facing label to display in the button")
|
||||
public String getLabel()
|
||||
{
|
||||
return (this.wrapped.getLabel());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Code used within the app as the value submitted when the button is clicked")
|
||||
public String getActionCode()
|
||||
{
|
||||
return (this.wrapped.getActionCode());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("""
|
||||
Instructions for what should happen in the frontend (e.g., within a screen), when the button is clicked.
|
||||
|
||||
To show a modal composite block, use format: `showModal:${blockId}` (e.g., `showModal:myBlock`)
|
||||
|
||||
To hide a modal composite block, use format: `hideModal:${blockId}` (e.g., `hideModal:myBlock`)
|
||||
|
||||
To toggle visibility of a modal composite block, use format: `toggleModal:${blockId}` (e.g., `toggleModal:myBlock`)
|
||||
""")
|
||||
public String getControlCode()
|
||||
{
|
||||
return (this.wrapped.getControlCode());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("An optional icon to display before the text in the button")
|
||||
public Icon getStartIcon()
|
||||
{
|
||||
return (this.wrapped.getStartIcon() == null ? null : new Icon(this.wrapped.getStartIcon()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("An optional icon to display after the text in the button")
|
||||
public Icon getEndIcon()
|
||||
{
|
||||
return (this.wrapped.getEndIcon() == null ? null : new Icon(this.wrapped.getEndIcon()));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseStyles;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.image.ImageStyles;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public final class WidgetBlockImageStyles implements WidgetBlockStyles
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private ImageStyles wrapped;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public WidgetBlockImageStyles(ImageStyles imageStyles)
|
||||
{
|
||||
this.wrapped = imageStyles;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockImageStyles()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("A request to render the image at a specified width.")
|
||||
public String getWidth()
|
||||
{
|
||||
return (this.wrapped.getWidth());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("A request to render the image at a specified height.")
|
||||
public String getHeight()
|
||||
{
|
||||
return (this.wrapped.getHeight());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Optional padding to apply to the image")
|
||||
public BaseStyles.Directional<String> getPadding()
|
||||
{
|
||||
return (this.wrapped.getPadding());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.image.ImageValues;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@OpenAPIDescription("Values used for an IMAGE type widget block")
|
||||
public final class WidgetBlockImageValues implements WidgetBlockValues
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private ImageValues wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockImageValues(ImageValues textValues)
|
||||
{
|
||||
this.wrapped = textValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockImageValues()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("The path to the image on the server")
|
||||
public String getPath()
|
||||
{
|
||||
return (this.wrapped.getPath());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.inputfield.InputFieldValues;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@OpenAPIDescription("Values used for an INPUT_FIELD type widget block")
|
||||
public final class WidgetBlockInputFieldValues implements WidgetBlockValues
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private InputFieldValues wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockInputFieldValues(InputFieldValues textValues)
|
||||
{
|
||||
this.wrapped = textValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockInputFieldValues()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Metadata to define the field that this block controls")
|
||||
public FieldMetaData getFieldMetaData()
|
||||
{
|
||||
return (new FieldMetaData(this.wrapped.getFieldMetaData()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Indicate whether this field should auto-focus when it is rendered")
|
||||
public Boolean getAutoFocus()
|
||||
{
|
||||
return (this.wrapped.getAutoFocus());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Indicate whether the form that this field is on should be submitted when Enter is pressed")
|
||||
public Boolean getSubmitOnEnter()
|
||||
{
|
||||
return (this.wrapped.getSubmitOnEnter());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Indicate if the frontend uses a software/on-screen keyboard, if the application should try to hide it (e.g., upon auto-focus).")
|
||||
public Boolean getHideSoftKeyboard()
|
||||
{
|
||||
return (this.wrapped.getHideSoftKeyboard());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("Optional placeholder text to display in the input box.")
|
||||
public String getPlaceholder()
|
||||
{
|
||||
return (this.wrapped.getPlaceholder());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockStylesInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.base.BaseStyles;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.button.ButtonStyles;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.image.ImageStyles;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.text.TextStyles;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public sealed interface WidgetBlockStyles extends ToSchema permits
|
||||
WidgetBlockBaseStyles,
|
||||
WidgetBlockButtonStyles,
|
||||
WidgetBlockImageStyles,
|
||||
WidgetBlockTextStyles
|
||||
{
|
||||
@OpenAPIExclude
|
||||
QLogger LOG = QLogger.getLogger(WidgetBlockStyles.class);
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
static WidgetBlockStyles of(BlockStylesInterface blockStyles)
|
||||
{
|
||||
if(blockStyles == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
if(blockStyles instanceof ButtonStyles s)
|
||||
{
|
||||
return (new WidgetBlockButtonStyles(s));
|
||||
}
|
||||
else if(blockStyles instanceof ImageStyles s)
|
||||
{
|
||||
return (new WidgetBlockImageStyles(s));
|
||||
}
|
||||
else if(blockStyles instanceof TextStyles s)
|
||||
{
|
||||
return (new WidgetBlockTextStyles(s));
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// note - important for this one to be last, since it's a base class to some of the above!! //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
else if(blockStyles instanceof BaseStyles s)
|
||||
{
|
||||
return (new WidgetBlockBaseStyles(s));
|
||||
}
|
||||
|
||||
LOG.warn("Unrecognized block value type: " + blockStyles.getClass().getName());
|
||||
return (null);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.text.TextStyles;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public final class WidgetBlockTextStyles implements WidgetBlockStyles
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private TextStyles wrapped;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public WidgetBlockTextStyles(TextStyles textStyles)
|
||||
{
|
||||
this.wrapped = textStyles;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockTextStyles()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("A Color to display the text in. May be specified as a StandardColor (one of: "
|
||||
+ "SUCCESS, WARNING, ERROR, INFO, MUTED) or an RGB code.")
|
||||
public String getColor()
|
||||
{
|
||||
return (this.wrapped.getColor());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("An optional indicator of the screen format preferred by the application to be used for this block. "
|
||||
+ "Different frontends may support different formats, and implement them differently.")
|
||||
public String getFormat()
|
||||
{
|
||||
return (this.wrapped.getFormat());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("An optional indicator of the weight at which the text should be rendered. May be a named value (one of"
|
||||
+ "extralight, thin, medium, black, semibold, bold, extrabold) or a numeric, e.g., 100, 200, ..., 900")
|
||||
public String getWeight()
|
||||
{
|
||||
return (this.wrapped.getWeight());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("An optional indicator of the size at which the text should be rendered. May be a named value (one of"
|
||||
+ "largest, headline, title, body, smallest) or a numeric size - both are up to the frontend to interpret.")
|
||||
public String getSize()
|
||||
{
|
||||
return (this.wrapped.getSize());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.text.TextValues;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@OpenAPIDescription("Values used for a TEXT type widget block")
|
||||
public final class WidgetBlockTextValues implements WidgetBlockValues
|
||||
{
|
||||
@OpenAPIExclude()
|
||||
private TextValues wrapped;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockTextValues(TextValues textValues)
|
||||
{
|
||||
this.wrapped = textValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WidgetBlockTextValues()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("The text to display in the block")
|
||||
public String getText()
|
||||
{
|
||||
return (this.wrapped.getText());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("An optional icon to display before the text")
|
||||
public Icon getStartIcon()
|
||||
{
|
||||
return (this.wrapped.getStartIcon() == null ? null : new Icon(this.wrapped.getStartIcon()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@OpenAPIDescription("An optional icon to display after the text")
|
||||
public Icon getEndIcon()
|
||||
{
|
||||
return (this.wrapped.getEndIcon() == null ? null : new Icon(this.wrapped.getEndIcon()));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.responses.components;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.BlockValuesInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.audio.AudioValues;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.button.ButtonValues;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.image.ImageValues;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.inputfield.InputFieldValues;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.text.TextValues;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.ToSchema;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIExclude;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public sealed interface WidgetBlockValues extends ToSchema permits
|
||||
WidgetBlockAudioValues,
|
||||
WidgetBlockButtonValues,
|
||||
WidgetBlockImageValues,
|
||||
WidgetBlockInputFieldValues,
|
||||
WidgetBlockTextValues
|
||||
{
|
||||
@OpenAPIExclude
|
||||
QLogger LOG = QLogger.getLogger(WidgetBlockValues.class);
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
static WidgetBlockValues of(BlockValuesInterface blockValues)
|
||||
{
|
||||
if(blockValues == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
if(blockValues instanceof AudioValues v)
|
||||
{
|
||||
return (new WidgetBlockAudioValues(v));
|
||||
}
|
||||
else if(blockValues instanceof ButtonValues v)
|
||||
{
|
||||
return (new WidgetBlockButtonValues(v));
|
||||
}
|
||||
else if(blockValues instanceof ImageValues v)
|
||||
{
|
||||
return (new WidgetBlockImageValues(v));
|
||||
}
|
||||
else if(blockValues instanceof InputFieldValues v)
|
||||
{
|
||||
return (new WidgetBlockInputFieldValues(v));
|
||||
}
|
||||
else if(blockValues instanceof TextValues v)
|
||||
{
|
||||
return (new WidgetBlockTextValues(v));
|
||||
}
|
||||
|
||||
LOG.warn("Unrecognized block value type: " + blockValues.getClass().getName());
|
||||
return (null);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,281 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.utils;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessMetaDataAdjustment;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.QUploadedFile;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.blocks.AbstractBlockWidgetData;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.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.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.ProcessInitOrStepOrStatusOutputInterface;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.ProcessInitOrStepOrStatusResponseV1;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.components.WidgetBlock;
|
||||
import com.kingsrook.qqq.openapi.model.Example;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
import io.javalin.http.Context;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessSpecUtilsV1
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(ProcessSpecUtilsV1.class);
|
||||
|
||||
public static final String EXAMPLE_PROCESS_UUID = "01234567-89AB-CDEF-0123-456789ABCDEF";
|
||||
public static final String EXAMPLE_JOB_UUID = "98765432-10FE-DCBA-9876-543210FEDCBA";
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static String getResponseSchemaRefName()
|
||||
{
|
||||
return ("ProcessStepResponseV1");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static Schema buildResponseSchema()
|
||||
{
|
||||
return new Schema().withOneOf(List.of(
|
||||
new Schema().withRef("#/components/schemas/ProcessStepComplete"),
|
||||
new Schema().withRef("#/components/schemas/ProcessStepJobStarted"),
|
||||
new Schema().withRef("#/components/schemas/ProcessStepRunning"),
|
||||
new Schema().withRef("#/components/schemas/ProcessStepError")
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static LinkedHashMap<String, Example> buildResponseExample()
|
||||
{
|
||||
ProcessInitOrStepOrStatusResponseV1 completeResponse = new ProcessInitOrStepOrStatusResponseV1();
|
||||
completeResponse.setType(ProcessInitOrStepOrStatusOutputInterface.Type.COMPLETE);
|
||||
completeResponse.setProcessUUID(EXAMPLE_PROCESS_UUID);
|
||||
Map<String, Serializable> values = new LinkedHashMap<>();
|
||||
values.put("totalAge", 32768);
|
||||
values.put("firstLastName", "Aabramson");
|
||||
completeResponse.setValues(values);
|
||||
completeResponse.setNextStep("reviewScreen");
|
||||
|
||||
ProcessInitOrStepOrStatusResponseV1 completeResponseWithMetaDataAdjustment = new ProcessInitOrStepOrStatusResponseV1();
|
||||
completeResponseWithMetaDataAdjustment.setType(ProcessInitOrStepOrStatusOutputInterface.Type.COMPLETE);
|
||||
completeResponseWithMetaDataAdjustment.setProcessUUID(EXAMPLE_PROCESS_UUID);
|
||||
completeResponseWithMetaDataAdjustment.setValues(values);
|
||||
completeResponseWithMetaDataAdjustment.setNextStep("inputScreen");
|
||||
completeResponseWithMetaDataAdjustment.setProcessMetaDataAdjustment(new ProcessMetaDataAdjustment()
|
||||
.withUpdatedField(new QFieldMetaData("someField", QFieldType.STRING).withIsRequired(true))
|
||||
.withUpdatedFrontendStepList(List.of(
|
||||
new QFrontendStepMetaData()
|
||||
.withName("inputScreen")
|
||||
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.EDIT_FORM))
|
||||
.withFormField(new QFieldMetaData("someField", QFieldType.STRING)),
|
||||
new QFrontendStepMetaData()
|
||||
.withName("resultScreen")
|
||||
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.PROCESS_SUMMARY_RESULTS))
|
||||
)));
|
||||
|
||||
ProcessInitOrStepOrStatusResponseV1 jobStartedResponse = new ProcessInitOrStepOrStatusResponseV1();
|
||||
jobStartedResponse.setType(ProcessInitOrStepOrStatusOutputInterface.Type.JOB_STARTED);
|
||||
jobStartedResponse.setProcessUUID(EXAMPLE_PROCESS_UUID);
|
||||
jobStartedResponse.setJobUUID(EXAMPLE_JOB_UUID);
|
||||
|
||||
ProcessInitOrStepOrStatusResponseV1 runningResponse = new ProcessInitOrStepOrStatusResponseV1();
|
||||
runningResponse.setType(ProcessInitOrStepOrStatusOutputInterface.Type.RUNNING);
|
||||
runningResponse.setProcessUUID(EXAMPLE_PROCESS_UUID);
|
||||
runningResponse.setMessage("Processing person records");
|
||||
runningResponse.setCurrent(47);
|
||||
runningResponse.setTotal(1701);
|
||||
|
||||
ProcessInitOrStepOrStatusResponseV1 errorResponse = new ProcessInitOrStepOrStatusResponseV1();
|
||||
errorResponse.setType(ProcessInitOrStepOrStatusOutputInterface.Type.RUNNING);
|
||||
errorResponse.setProcessUUID(EXAMPLE_PROCESS_UUID);
|
||||
errorResponse.setError("Illegal Argument Exception: NaN");
|
||||
errorResponse.setUserFacingError("The process could not be completed due to invalid input.");
|
||||
|
||||
return MapBuilder.of(() -> new LinkedHashMap<String, Example>())
|
||||
.with("COMPLETE", new Example().withValue(completeResponse))
|
||||
.with("COMPLETE with metaDataAdjustment", new Example().withValue(completeResponseWithMetaDataAdjustment))
|
||||
.with("JOB_STARTED", new Example().withValue(jobStartedResponse))
|
||||
.with("RUNNING", new Example().withValue(runningResponse))
|
||||
.with("ERROR", new Example().withValue(errorResponse))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static void handleOutput(Context context, ProcessInitOrStepOrStatusResponseV1 output)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// normally, we like the JsonUtils behavior of excluding null/empty elements. //
|
||||
// but it turns out we want those in the values sub-map. //
|
||||
// so, go through a loop of object → JSON String → JSONObject → String... //
|
||||
// also - work with the TypedResponse sub-object within this response class //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
ProcessInitOrStepOrStatusResponseV1.TypedResponse typedOutput = output.getTypedResponse();
|
||||
|
||||
String outputJson = JsonUtils.toJson(typedOutput);
|
||||
JSONObject outputJsonObject = new JSONObject(outputJson);
|
||||
|
||||
if(typedOutput instanceof ProcessInitOrStepOrStatusResponseV1.ProcessStepComplete complete)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// here's where we'll handle the values map specially - note - that we'll also //
|
||||
// be mapping some specific object types into their API-versioned responses types //
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
Map<String, Serializable> values = complete.getValues();
|
||||
if(values != null)
|
||||
{
|
||||
JSONObject valuesAsJsonObject = new JSONObject();
|
||||
for(Map.Entry<String, Serializable> valueEntry : values.entrySet())
|
||||
{
|
||||
String name = valueEntry.getKey();
|
||||
Serializable value = valueEntry.getValue();
|
||||
|
||||
Serializable valueToMakeIntoJson = value;
|
||||
if(value instanceof String s)
|
||||
{
|
||||
valuesAsJsonObject.put(name, s);
|
||||
continue;
|
||||
}
|
||||
else if(value instanceof Boolean b)
|
||||
{
|
||||
valuesAsJsonObject.put(name, b);
|
||||
continue;
|
||||
}
|
||||
else if(value instanceof Number n)
|
||||
{
|
||||
valuesAsJsonObject.put(name, n);
|
||||
continue;
|
||||
}
|
||||
else if(value == null)
|
||||
{
|
||||
valuesAsJsonObject.put(name, (Object) null);
|
||||
continue;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// if there are any types that we want to make sure we send back using this API //
|
||||
// version's mapped objects, then add cases for them here, and wrap them. //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
else if(value instanceof AbstractBlockWidgetData<?, ?, ?, ?> abstractBlockWidgetData)
|
||||
{
|
||||
valueToMakeIntoJson = new WidgetBlock(abstractBlockWidgetData);
|
||||
}
|
||||
|
||||
String valueAsJsonString = JsonUtils.toJson(valueToMakeIntoJson, mapper ->
|
||||
{
|
||||
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
||||
});
|
||||
|
||||
if(valueAsJsonString.startsWith("["))
|
||||
{
|
||||
valuesAsJsonObject.put(name, new JSONArray(valueAsJsonString));
|
||||
}
|
||||
else if(valueAsJsonString.startsWith("{"))
|
||||
{
|
||||
valuesAsJsonObject.put(name, new JSONObject(valueAsJsonString));
|
||||
}
|
||||
else
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// curious, if/when this ever happens, since we should get all "primitive" types //
|
||||
// above, and everything else, I think, would be an object or an array, right? //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
valuesAsJsonObject.put(name, valueAsJsonString);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ... this might be a concept for us at some point in time - but might be better to not do as a value itself? //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Serializable backendOnlyValues = values.get("_qqqBackendOnlyValues");
|
||||
// if(backendOnlyValues instanceof String backendOnlyValuesString)
|
||||
// {
|
||||
// for(String key : backendOnlyValuesString.split(","))
|
||||
// {
|
||||
// jsonObject.remove(key);
|
||||
// }
|
||||
// jsonObject.remove("_qqqBackendOnlyValues");
|
||||
// }
|
||||
|
||||
outputJsonObject.put("values", valuesAsJsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
String json = outputJsonObject.toString(3);
|
||||
System.out.println(json);
|
||||
context.result(json);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void archiveUploadedFile(String processName, QUploadedFile qUploadedFile)
|
||||
{
|
||||
String fileName = QValueFormatter.formatDate(LocalDate.now())
|
||||
+ File.separator + processName
|
||||
+ File.separator + qUploadedFile.getFilename();
|
||||
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(QJavalinImplementation.getJavalinMetaData().getUploadedFileArchiveTableName());
|
||||
insertInput.setRecords(List.of(new QRecord()
|
||||
.withValue("fileName", fileName)
|
||||
.withValue("contents", qUploadedFile.getBytes())
|
||||
));
|
||||
|
||||
new InsertAction().executeAsync(insertInput);
|
||||
}
|
||||
}
|
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.utils;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
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.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.QueryMiddlewareInput;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
import com.kingsrook.qqq.openapi.model.Type;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class QuerySpecUtils
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static Schema defineQueryJoinsSchema()
|
||||
{
|
||||
Schema queryJoinsSchema = new Schema()
|
||||
.withType(Type.ARRAY)
|
||||
.withItems(new Schema()
|
||||
.withProperties(MapBuilder.of(
|
||||
"joinTable", new Schema()
|
||||
.withType(Type.STRING),
|
||||
"select", new Schema()
|
||||
.withType(Type.BOOLEAN),
|
||||
"type", new Schema()
|
||||
.withType(Type.STRING)
|
||||
.withEnumValues(Arrays.stream(QueryJoin.Type.values()).map(o -> o.name()).toList()),
|
||||
"alias", new Schema()
|
||||
.withType(Type.STRING),
|
||||
"baseTableOrAlias", new Schema()
|
||||
.withType(Type.STRING)
|
||||
))
|
||||
);
|
||||
return queryJoinsSchema;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static Schema defineQQueryFilterSchema()
|
||||
{
|
||||
Schema qQueryFilterSchema = new Schema()
|
||||
.withType(Type.OBJECT)
|
||||
.withExample(List.of(
|
||||
JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.LESS_THAN, 5)))
|
||||
))
|
||||
.withProperties(MapBuilder.of(
|
||||
"criteria", new Schema()
|
||||
.withType(Type.ARRAY)
|
||||
.withItems(new Schema()
|
||||
.withProperties(MapBuilder.of(
|
||||
"fieldName", new Schema()
|
||||
.withType(Type.STRING),
|
||||
"operator", new Schema()
|
||||
.withType(Type.STRING)
|
||||
.withEnumValues(Arrays.stream(QCriteriaOperator.values()).map(o -> o.name()).toList()),
|
||||
"values", new Schema()
|
||||
.withType(Type.ARRAY)
|
||||
.withItems(new Schema().withOneOf(List.of(
|
||||
new Schema().withType(Type.INTEGER),
|
||||
new Schema().withType(Type.STRING)
|
||||
)))
|
||||
))
|
||||
),
|
||||
"orderBys", new Schema()
|
||||
.withType(Type.ARRAY)
|
||||
.withItems(new Schema()
|
||||
.withProperties(MapBuilder.of(
|
||||
"fieldName", new Schema()
|
||||
.withType(Type.STRING),
|
||||
"isAscending", new Schema()
|
||||
.withType(Type.BOOLEAN)))
|
||||
),
|
||||
"booleanOperator", new Schema().withType(Type.STRING).withEnumValues(Arrays.stream(QQueryFilter.BooleanOperator.values()).map(o -> o.name()).toList()),
|
||||
"skip", new Schema().withType(Type.INTEGER),
|
||||
"limit", new Schema().withType(Type.INTEGER)
|
||||
// todo - subfilters??
|
||||
));
|
||||
return qQueryFilterSchema;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static Schema getQueryResponseSchema()
|
||||
{
|
||||
Schema schema = new Schema()
|
||||
.withDescription("Records found by the query. May be empty.")
|
||||
.withType(Type.OBJECT)
|
||||
.withProperties(MapBuilder.of(
|
||||
"records", new Schema()
|
||||
.withType(Type.ARRAY)
|
||||
.withItems(new Schema()
|
||||
.withType(Type.OBJECT)
|
||||
.withProperties(MapBuilder.of(
|
||||
"recordLabel", new Schema().withType(Type.STRING),
|
||||
"tableName", new Schema().withType(Type.STRING),
|
||||
"values", new Schema().withType(Type.OBJECT).withDescription("Keys for each field in the table"),
|
||||
"displayValues", new Schema().withType(Type.OBJECT)
|
||||
))
|
||||
)
|
||||
));
|
||||
return schema;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static QueryMiddlewareInput buildInput(Map<String, String> paramMap) throws IOException
|
||||
{
|
||||
|
||||
QQueryFilter filter = null;
|
||||
String filterParam = paramMap.get("filter");
|
||||
if(StringUtils.hasContent(filterParam))
|
||||
{
|
||||
filter = JsonUtils.toObject(filterParam, QQueryFilter.class);
|
||||
}
|
||||
|
||||
List<QueryJoin> queryJoins = null;
|
||||
String queryJoinsParam = paramMap.get("queryJoins");
|
||||
if(StringUtils.hasContent(queryJoinsParam))
|
||||
{
|
||||
queryJoins = new ArrayList<>();
|
||||
|
||||
JSONArray queryJoinsJSON = new JSONArray(queryJoinsParam);
|
||||
for(int i = 0; i < queryJoinsJSON.length(); i++)
|
||||
{
|
||||
QueryJoin queryJoin = new QueryJoin();
|
||||
queryJoins.add(queryJoin);
|
||||
|
||||
JSONObject jsonObject = queryJoinsJSON.getJSONObject(i);
|
||||
queryJoin.setJoinTable(jsonObject.optString("joinTable"));
|
||||
|
||||
if(jsonObject.has("baseTableOrAlias") && !jsonObject.isNull("baseTableOrAlias"))
|
||||
{
|
||||
queryJoin.setBaseTableOrAlias(jsonObject.optString("baseTableOrAlias"));
|
||||
}
|
||||
|
||||
if(jsonObject.has("alias") && !jsonObject.isNull("alias"))
|
||||
{
|
||||
queryJoin.setAlias(jsonObject.optString("alias"));
|
||||
}
|
||||
|
||||
queryJoin.setSelect(jsonObject.optBoolean("select"));
|
||||
|
||||
if(jsonObject.has("type") && !jsonObject.isNull("type"))
|
||||
{
|
||||
queryJoin.setType(QueryJoin.Type.valueOf(jsonObject.getString("type")));
|
||||
}
|
||||
|
||||
if(jsonObject.has("joinName") && !jsonObject.isNull("joinName"))
|
||||
{
|
||||
queryJoin.setJoinMetaData(QContext.getQInstance().getJoin(jsonObject.getString("joinName")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new QueryMiddlewareInput()
|
||||
.withTable(paramMap.get("table"))
|
||||
.withFilter(filter)
|
||||
.withQueryJoins(queryJoins);
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.specs.v1.utils;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.TagsInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum TagsV1 implements TagsInterface
|
||||
{
|
||||
AUTHENTICATION("Authentication"),
|
||||
GENERAL("General"),
|
||||
QUERY("Query"),
|
||||
INSERT("Insert"),
|
||||
UPDATE("Update"),
|
||||
DELETE("Delete"),
|
||||
PROCESSES("Processes"),
|
||||
REPORTS("Reports"),
|
||||
WIDGETS("Widgets");
|
||||
|
||||
|
||||
private final String text;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
TagsV1(String text)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for text
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getText()
|
||||
{
|
||||
return text;
|
||||
}
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.tools;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class APIUtils
|
||||
{
|
||||
public static final String PUBLISHED_API_LOCATION = "qqq-middleware-javalin/src/main/resources/openapi/";
|
||||
public static final List<String> FILE_FORMATS = List.of("json", "yaml");
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static String getPublishedAPIFile(String apiPath, String name, String fileFormat) throws Exception
|
||||
{
|
||||
String fileLocation = apiPath + "/" + name + "." + fileFormat;
|
||||
File file = new File(fileLocation);
|
||||
if(!file.exists())
|
||||
{
|
||||
throw (new Exception("Error: The file [" + file.getPath() + "] could not be found."));
|
||||
}
|
||||
|
||||
Path path = Paths.get(fileLocation);
|
||||
return (StringUtils.join("\n", Files.readAllLines(path)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** get a map representation of the yaml.
|
||||
** we'll remove things from that map, writing out specific sub-files that we know we want (e.g., per-table & process).
|
||||
** also, there are some objects we just don't care about (e.g., tags, they'll always be in lock-step with the tables).
|
||||
** then we'll write out everything else left in the map at the end.
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Map<String, Object>> splitUpYamlForPublishing(String openApiYaml) throws JsonProcessingException
|
||||
{
|
||||
Map<String, Object> apiYaml = parseYaml(openApiYaml);
|
||||
Map<String, Object> components = (Map<String, Object>) apiYaml.get("components");
|
||||
Map<String, Object> schemas = (Map<String, Object>) components.get("schemas");
|
||||
Map<String, Object> paths = (Map<String, Object>) apiYaml.remove("paths");
|
||||
apiYaml.remove("tags");
|
||||
|
||||
Map<String, Map<String, Object>> groupedPaths = new HashMap<>();
|
||||
for(Map.Entry<String, Object> entry : paths.entrySet())
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// keys here look like: apiName/apiVersion/table-or-process/<maybe-more> //
|
||||
// let's discard the apiName & version, and group under the table-or-process //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
String key = entry.getKey();
|
||||
String[] parts = key.split("/");
|
||||
String uniquePart = parts[3];
|
||||
groupedPaths.computeIfAbsent(uniquePart, k -> new TreeMap<>());
|
||||
groupedPaths.get(uniquePart).put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
for(Map.Entry<String, Map<String, Object>> entry : groupedPaths.entrySet())
|
||||
{
|
||||
String name = entry.getKey();
|
||||
Map<String, Object> subMap = entry.getValue();
|
||||
if(schemas.containsKey(name))
|
||||
{
|
||||
subMap.put("schema", schemas.remove(name));
|
||||
}
|
||||
|
||||
name += "SearchResult";
|
||||
if(schemas.containsKey(name))
|
||||
{
|
||||
subMap.put("searchResultSchema", schemas.remove(name));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// put the left-over yaml as a final entry in the map //
|
||||
////////////////////////////////////////////////////////
|
||||
groupedPaths.put("openapi", apiYaml);
|
||||
|
||||
return groupedPaths;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> parseYaml(String yaml) throws JsonProcessingException
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// need a larger limit than you get by default (and qqq's yamlUtils doens't let us customize this...) //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
LoaderOptions loaderOptions = new LoaderOptions();
|
||||
loaderOptions.setCodePointLimit(100 * 1024 * 1024); // 100 MB
|
||||
YAMLFactory yamlFactory = YAMLFactory.builder()
|
||||
.loaderOptions(loaderOptions)
|
||||
.build();
|
||||
YAMLMapper mapper = new YAMLMapper(yamlFactory);
|
||||
|
||||
mapper.findAndRegisterModules();
|
||||
return (mapper.readValue(yaml, Map.class));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static File[] listPublishedAPIFiles(String path) throws Exception
|
||||
{
|
||||
File file = new File(path);
|
||||
if(!file.exists())
|
||||
{
|
||||
throw (new Exception("Error: API Directory [" + file.getPath() + "] could not be found."));
|
||||
}
|
||||
|
||||
File[] files = file.listFiles();
|
||||
return (files);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.tools;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.Callable;
|
||||
import com.fasterxml.jackson.databind.MapperFeature;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.YamlUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractMiddlewareVersion;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.MiddlewareVersionV1;
|
||||
import com.kingsrook.qqq.openapi.model.OpenAPI;
|
||||
import picocli.CommandLine;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@CommandLine.Command(name = "publishAPI")
|
||||
public class PublishAPI implements Callable<Integer>
|
||||
{
|
||||
@CommandLine.Option(names = { "-r", "--repoRoot" })
|
||||
private String repoRoot;
|
||||
|
||||
@CommandLine.Option(names = { "--sortFileContentsForHuman" }, description = "By default, properties in the yaml are sorted alphabetically, to help with stability (for diffing). This option preserves the 'natural' order of the file, so may look a little bette for human consumption")
|
||||
private boolean sortFileContentsForHuman = false;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
// for a run from the IDE, to override args... args = new String[] { "-r", "/Users/dkelkhoff/git/kingsrook/qqq/" };
|
||||
int exitCode = new CommandLine(new PublishAPI()).execute(args);
|
||||
System.exit(exitCode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Integer call() throws Exception
|
||||
{
|
||||
AbstractMiddlewareVersion middlewareVersion = new MiddlewareVersionV1();
|
||||
|
||||
if(!StringUtils.hasContent(repoRoot))
|
||||
{
|
||||
throw (new QException("Repo root argument was not given."));
|
||||
}
|
||||
|
||||
if(!new File(repoRoot).exists())
|
||||
{
|
||||
throw (new QException("Repo root directory [" + repoRoot + "] was not found."));
|
||||
}
|
||||
|
||||
String allApisPath = repoRoot + "/" + APIUtils.PUBLISHED_API_LOCATION + "/";
|
||||
if(!new File(allApisPath).exists())
|
||||
{
|
||||
throw (new QException("APIs directory [" + allApisPath + "] was not found."));
|
||||
}
|
||||
|
||||
File versionDirectory = new File(allApisPath + middlewareVersion.getVersion() + "/");
|
||||
if(!versionDirectory.exists())
|
||||
{
|
||||
if(!versionDirectory.mkdirs())
|
||||
{
|
||||
// CTEngCliUtils.printError("Error: An error occurred creating directory [" + apiDirectory.getPath() + "].");
|
||||
System.err.println("Error: An error occurred creating directory [" + versionDirectory.getPath() + "].");
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// build the openapi spec - then run it through a "grouping" function, which will make several //
|
||||
// subsets of it (e.g., grouped by table mostly) - then we'll write out each such file //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
OpenAPI openAPI = middlewareVersion.generateOpenAPIModel("qqq");
|
||||
String yaml = YamlUtils.toYaml(openAPI, mapper ->
|
||||
{
|
||||
if(sortFileContentsForHuman)
|
||||
{
|
||||
////////////////////////////////////////////////
|
||||
// this is actually the default mapper config //
|
||||
////////////////////////////////////////////////
|
||||
}
|
||||
else
|
||||
{
|
||||
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
|
||||
}
|
||||
});
|
||||
|
||||
writeFile(yaml, versionDirectory, "openapi.yaml");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// if we want to split up by some paths, components, we could use a version of this //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Map<String, Map<String, Object>> groupedPaths = APIUtils.splitUpYamlForPublishing(yaml);
|
||||
// for(String name : groupedPaths.keySet())
|
||||
// {
|
||||
// writeFile(groupedPaths.get(name), versionDirectory, name + ".yaml");
|
||||
// }
|
||||
// CTEngCliUtils.printSuccess("Files for [" + apiInstanceMetaData.getName() + "] [" + apiVersion + "] have been successfully published.");
|
||||
// System.out.println("Files for [" + middlewareVersion.getClass().getSimpleName() + "] have been successfully published.");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void writeFile(String yaml, File directory, String fileBaseName) throws IOException
|
||||
{
|
||||
String yamlFileName = directory.getAbsolutePath() + "/" + fileBaseName;
|
||||
Path yamlPath = Paths.get(yamlFileName);
|
||||
Files.write(yamlPath, yaml.getBytes());
|
||||
System.out.println("Wrote [" + yamlPath + "]");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.tools;
|
||||
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import com.fasterxml.jackson.databind.MapperFeature;
|
||||
import com.kingsrook.qqq.backend.core.utils.YamlUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractMiddlewareVersion;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.MiddlewareVersionV1;
|
||||
import com.kingsrook.qqq.openapi.model.OpenAPI;
|
||||
import picocli.CommandLine;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@CommandLine.Command(name = "validateApiVersions")
|
||||
public class ValidateAPIVersions implements Callable<Integer>
|
||||
{
|
||||
@CommandLine.Option(names = { "-r", "--repoRoot" })
|
||||
String repoRoot;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
// for a run from the IDE, to override args... args = new String[] { "-r", "/Users/dkelkhoff/git/kingsrook/qqq/" };
|
||||
int exitCode = new CommandLine(new ValidateAPIVersions()).execute(args);
|
||||
System.out.println("Exiting with code: " + exitCode);
|
||||
System.exit(exitCode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Integer call() throws Exception
|
||||
{
|
||||
String fileFormat = "yaml";
|
||||
boolean hadErrors = false;
|
||||
List<String> errorHeaders = new ArrayList<>();
|
||||
|
||||
List<AbstractMiddlewareVersion> specList = List.of(new MiddlewareVersionV1());
|
||||
|
||||
for(AbstractMiddlewareVersion middlewareVersion : specList)
|
||||
{
|
||||
String version = middlewareVersion.getVersion();
|
||||
boolean hadErrorsThisVersion = false;
|
||||
|
||||
//////////
|
||||
// todo //
|
||||
//////////
|
||||
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// // if this api version is in the list of "future" versions, consider it a "beta" and don't do any validation //
|
||||
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// for(APIVersion futureAPIVersion : apiInstanceMetaData.getFutureVersions())
|
||||
// {
|
||||
// if(apiVersion.equals(futureAPIVersion))
|
||||
// {
|
||||
// continue versionLoop;
|
||||
// }
|
||||
// }
|
||||
|
||||
try
|
||||
{
|
||||
////////////////////////////////////////////////////////////
|
||||
// list current files - so we can tell if all get diff'ed //
|
||||
////////////////////////////////////////////////////////////
|
||||
Set<String> existingFileNames = new HashSet<>();
|
||||
String versionPath = repoRoot + "/" + APIUtils.PUBLISHED_API_LOCATION + "/" + version + "/";
|
||||
versionPath = versionPath.replaceAll("/+", "/");
|
||||
for(File file : APIUtils.listPublishedAPIFiles(versionPath))
|
||||
{
|
||||
existingFileNames.add(file.getPath().replaceFirst(versionPath, ""));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// generate a new spec based on current code in codebase //
|
||||
///////////////////////////////////////////////////////////
|
||||
OpenAPI openAPI = middlewareVersion.generateOpenAPIModel("qqq");
|
||||
String yaml = YamlUtils.toYaml(openAPI, mapper ->
|
||||
{
|
||||
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
|
||||
});
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// get the published API file - then diff it to what we just wrote //
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
String publishedAPI = APIUtils.getPublishedAPIFile(versionPath, "openapi", fileFormat);
|
||||
|
||||
String newFileName = "/tmp/" + version + "-new." + fileFormat;
|
||||
String publishedFileName = "/tmp/" + version + "-published." + fileFormat;
|
||||
Files.write(Path.of(newFileName), yaml.getBytes());
|
||||
Files.write(Path.of(publishedFileName), publishedAPI.getBytes());
|
||||
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
String[] commands = { "diff", "-w", publishedFileName, newFileName };
|
||||
Process proc = rt.exec(commands);
|
||||
|
||||
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
|
||||
|
||||
StringBuilder diffOutput = new StringBuilder();
|
||||
String s;
|
||||
while((s = stdInput.readLine()) != null)
|
||||
{
|
||||
diffOutput.append(s).append("\n");
|
||||
}
|
||||
|
||||
if(!"".contentEquals(diffOutput))
|
||||
{
|
||||
String message = "Error: Differences were found in openapi.yaml file between the published docs and the newly generated file for API Version [" + version + "].";
|
||||
errorHeaders.add(message);
|
||||
System.out.println(message);
|
||||
System.out.println(diffOutput);
|
||||
hadErrors = true;
|
||||
hadErrorsThisVersion = true;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// if we want to split up by some paths, components, we could use a version of this //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
/*
|
||||
Map<String, Map<String, Object>> groupedPaths = APIUtils.splitUpYamlForPublishing(yaml);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// for each of the groupings (e.g., files), compare to what was previously published //
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
for(Map.Entry<String, Map<String, Object>> entry : groupedPaths.entrySet())
|
||||
{
|
||||
try
|
||||
{
|
||||
String name = entry.getKey();
|
||||
String newFileToDiff = YamlUtils.toYaml(entry.getValue());
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// get the published API file - then diff it to what we just wrote //
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
String publishedAPI = APIUtils.getPublishedAPIFile(versionPath, name, fileFormat);
|
||||
existingFileNames.remove(name + "." + fileFormat);
|
||||
|
||||
String newFileName = "/tmp/" + version + "-new." + fileFormat;
|
||||
String publishedFileName = "/tmp/" + version + "-published." + fileFormat;
|
||||
Files.write(Path.of(newFileName), newFileToDiff.getBytes());
|
||||
Files.write(Path.of(publishedFileName), publishedAPI.getBytes());
|
||||
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
String[] commands = { "diff", "-w", publishedFileName, newFileName };
|
||||
Process proc = rt.exec(commands);
|
||||
|
||||
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
|
||||
|
||||
StringBuilder diffOutput = new StringBuilder();
|
||||
String s;
|
||||
while((s = stdInput.readLine()) != null)
|
||||
{
|
||||
diffOutput.append(s).append("\n");
|
||||
}
|
||||
|
||||
if(!"".contentEquals(diffOutput))
|
||||
{
|
||||
String message = "Error: Differences were found in file [" + name + "] between the published docs and the newly generated " + fileFormat + " file for API Version [" + version + "].";
|
||||
errorHeaders.add(message);
|
||||
System.out.println(message);
|
||||
System.out.println(diffOutput);
|
||||
hadErrors = true;
|
||||
hadErrorsThisVersion = true;
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
errorHeaders.add(e.getMessage());
|
||||
System.out.println(e.getMessage());
|
||||
hadErrors = true;
|
||||
hadErrorsThisVersion = true;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// if any existing files weren't evaluated in the loop above, then that's an error //
|
||||
// e.g., someone removed a thing that was previously in the API //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
if(!existingFileNames.isEmpty())
|
||||
{
|
||||
hadErrors = true;
|
||||
hadErrorsThisVersion = true;
|
||||
for(String existingFileName : existingFileNames)
|
||||
{
|
||||
String message = "Error: Previously published file [" + existingFileName + "] was not found in the current OpenAPI object for API Version [" + version + "].";
|
||||
errorHeaders.add(message);
|
||||
System.out.println(message);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
errorHeaders.add(e.getMessage());
|
||||
System.out.println(e.getMessage());
|
||||
hadErrors = true;
|
||||
hadErrorsThisVersion = true;
|
||||
}
|
||||
|
||||
if(!hadErrorsThisVersion)
|
||||
{
|
||||
System.out.println("Success: No differences were found between the published docs and the newly generated " + fileFormat + " file for API Version [" + version + "].");
|
||||
}
|
||||
}
|
||||
|
||||
if(!errorHeaders.isEmpty())
|
||||
{
|
||||
System.out.println("\nError summary:");
|
||||
errorHeaders.forEach(e -> System.out.println(" - " + e));
|
||||
}
|
||||
|
||||
return (hadErrors ? 1 : 0);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.tools.codegenerators;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
class ExecutorCodeGenerator
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
String qqqDir = "/Users/dkelkhoff/git/kingsrook/qqq/";
|
||||
new ExecutorCodeGenerator().writeAllFiles(qqqDir, "ProcessMetaData"); // don't include "Executor" on the end.
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
//noinspection CallToPrintStackTrace
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void writeOne(String fullPath, String content) throws IOException
|
||||
{
|
||||
File file = new File(fullPath);
|
||||
File directory = file.getParentFile();
|
||||
|
||||
if(!directory.exists())
|
||||
{
|
||||
throw (new RuntimeException("Directory for: " + fullPath + " does not exists, and I refuse to mkdir (do it yourself and/or fix your arguments)."));
|
||||
}
|
||||
|
||||
if(file.exists())
|
||||
{
|
||||
throw (new RuntimeException("File at: " + fullPath + " already exists, and I refuse to overwrite files."));
|
||||
}
|
||||
|
||||
System.out.println("Writing: " + file);
|
||||
FileUtils.writeStringToFile(file, content, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
void writeAllFiles(String rootPath, String baseName) throws IOException
|
||||
{
|
||||
if(baseName.endsWith("Executor"))
|
||||
{
|
||||
throw new IllegalArgumentException("Base name must not end with 'Executor'.");
|
||||
}
|
||||
|
||||
String basePath = rootPath + "qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/";
|
||||
writeOne(basePath + "executors/" + baseName + "Executor.java", makeExecutor(baseName));
|
||||
writeOne(basePath + "executors/io/" + baseName + "Input.java", makeInput(baseName));
|
||||
writeOne(basePath + "executors/io/" + baseName + "OutputInterface.java", makeOutputInterface(baseName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private String makeExecutor(String baseName)
|
||||
{
|
||||
return """
|
||||
package com.kingsrook.qqq.middleware.javalin.executors;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.${baseName}Input;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.${baseName}OutputInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ${baseName}Executor extends AbstractMiddlewareExecutor<${baseName}Input, ${baseName}OutputInterface>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void execute(${baseName}Input input, ${baseName}OutputInterface output) throws QException
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
""".replaceAll("\\$\\{baseName}", baseName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private String makeInput(String baseName)
|
||||
{
|
||||
return """
|
||||
package com.kingsrook.qqq.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ${baseName}Input extends AbstractMiddlewareInput
|
||||
{
|
||||
|
||||
}
|
||||
""".replaceAll("\\$\\{baseName}", baseName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private String makeOutputInterface(String baseName)
|
||||
{
|
||||
return """
|
||||
package com.kingsrook.qqq.middleware.javalin.executors.io;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface ${baseName}OutputInterface extends AbstractMiddlewareOutputInterface
|
||||
{
|
||||
|
||||
}
|
||||
""".replaceAll("\\$\\{baseName}", baseName);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.middleware.javalin.tools.codegenerators;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
class SpecCodeGenerator
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
String qqqDir = "/Users/dkelkhoff/git/kingsrook/qqq/";
|
||||
|
||||
/////////////////
|
||||
// normal case //
|
||||
/////////////////
|
||||
new SpecCodeGenerator().writeAllFiles(qqqDir, "v1", "ProcessMetaData");
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// if the executor isn't named the same as the spec (e.g., reused executors) //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// new SpecCodeGenerator().writeAllFiles(qqqDir, "v1", "ProcessInsert", "ProcessInsertOrSetp");
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
//noinspection CallToPrintStackTrace
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void writeOne(String fullPath, String content) throws IOException
|
||||
{
|
||||
File file = new File(fullPath);
|
||||
File directory = file.getParentFile();
|
||||
|
||||
if(!directory.exists())
|
||||
{
|
||||
throw (new RuntimeException("Directory for: " + fullPath + " does not exists, and I refuse to mkdir (do it yourself and/or fix your arguments)."));
|
||||
}
|
||||
|
||||
if(file.exists())
|
||||
{
|
||||
throw (new RuntimeException("File at: " + fullPath + " already exists, and I refuse to overwrite files."));
|
||||
}
|
||||
|
||||
System.out.println("Writing: " + file);
|
||||
FileUtils.writeStringToFile(file, content, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
void writeAllFiles(String rootPath, String version, String baseName) throws IOException
|
||||
{
|
||||
writeAllFiles(rootPath, version, baseName, baseName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void writeAllFiles(String rootPath, String version, String baseName, String executorBaseName) throws IOException
|
||||
{
|
||||
String basePath = rootPath + "qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/";
|
||||
writeOne(basePath + "specs/" + version.toLowerCase() + "/" + baseName + "Spec" + version.toUpperCase() + ".java", makeSpec(version, baseName, executorBaseName));
|
||||
writeOne(basePath + "specs/" + version.toLowerCase() + "/responses/" + baseName + "Response" + version.toUpperCase() + ".java", makeResponse(version, baseName, executorBaseName));
|
||||
|
||||
System.out.println();
|
||||
System.out.println("Hey - You probably want to add a line like:");
|
||||
System.out.println(" list.add(new " + baseName + "Spec" + version.toUpperCase() + "());");
|
||||
System.out.println("In:");
|
||||
System.out.println(" MiddlewareVersion" + version.toUpperCase() + ".java");
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private String makeSpec(String version, String baseName, String executorBaseName)
|
||||
{
|
||||
return """
|
||||
package com.kingsrook.qqq.middleware.javalin.specs.${version.toLowerCase};
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.${executorBaseName}Executor;
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.${executorBaseName}Input;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractEndpointSpec;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicOperation;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.BasicResponse;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.${version.toLowerCase}.responses.${executorBaseName}Response${version.toUpperCase};
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.${version.toLowerCase}.utils.Tags${version.toUpperCase};
|
||||
import com.kingsrook.qqq.openapi.model.Content;
|
||||
import com.kingsrook.qqq.openapi.model.Example;
|
||||
import com.kingsrook.qqq.openapi.model.HttpMethod;
|
||||
import com.kingsrook.qqq.openapi.model.In;
|
||||
import com.kingsrook.qqq.openapi.model.Parameter;
|
||||
import com.kingsrook.qqq.openapi.model.RequestBody;
|
||||
import com.kingsrook.qqq.openapi.model.Schema;
|
||||
import com.kingsrook.qqq.openapi.model.Type;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.Context;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ${baseName}Spec${version.toUpperCase} extends AbstractEndpointSpec<${executorBaseName}Input, ${baseName}Response${version.toUpperCase}, ${executorBaseName}Executor>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public BasicOperation defineBasicOperation()
|
||||
{
|
||||
return new BasicOperation()
|
||||
.withPath(TODO)
|
||||
.withHttpMethod(HttpMethod.TODO)
|
||||
.withTag(Tags${version.toUpperCase}.TODO)
|
||||
.withShortSummary(TODO)
|
||||
.withLongDescription(""\"
|
||||
TODO""\"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public List<Parameter> defineRequestParameters()
|
||||
{
|
||||
return List.of(
|
||||
new Parameter()
|
||||
.withName(TODO)
|
||||
.withDescription(TODO)
|
||||
.withRequired(TODO)
|
||||
.withSchema(new Schema().withType(Type.TODO))
|
||||
.withExamples(Map.of("TODO", new Example().withValue(TODO)))
|
||||
.withIn(In.TODO)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public RequestBody defineRequestBody()
|
||||
{
|
||||
return new RequestBody()
|
||||
.withContent(Map.of(
|
||||
ContentType.TODO.getMimeType(), new Content()
|
||||
.withSchema(new Schema()
|
||||
.withType(Type.TODO)
|
||||
.withProperties(Map.of(
|
||||
"TODO", new Schema()
|
||||
))
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public ${executorBaseName}Input buildInput(Context context) throws Exception
|
||||
{
|
||||
${executorBaseName}Input input = new ${executorBaseName}Input();
|
||||
input.setTODO
|
||||
return (input);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public BasicResponse defineBasicSuccessResponse()
|
||||
{
|
||||
Map<String, Example> examples = Map.of(
|
||||
|
||||
"TODO", new Example()
|
||||
.withValue(new ${baseName}Response${version.toUpperCase}()
|
||||
.withTODO
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
return new BasicResponse(""\"
|
||||
TODO""\",
|
||||
|
||||
new ${baseName}Response${version.toUpperCase}().toSchema(),
|
||||
examples
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void handleOutput(Context context, ${baseName}Response${version.toUpperCase} output) throws Exception
|
||||
{
|
||||
context.result(JsonUtils.toJson(output));
|
||||
}
|
||||
|
||||
}
|
||||
"""
|
||||
.replaceAll("\\$\\{version.toLowerCase}", version.toLowerCase())
|
||||
.replaceAll("\\$\\{version.toUpperCase}", version.toUpperCase())
|
||||
.replaceAll("\\$\\{executorBaseName}", executorBaseName)
|
||||
.replaceAll("\\$\\{baseName}", baseName)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private String makeResponse(String version, String baseName, String executorBaseName)
|
||||
{
|
||||
return """
|
||||
package com.kingsrook.qqq.middleware.javalin.specs.${version.toLowerCase}.responses;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.middleware.javalin.executors.io.${executorBaseName}OutputInterface;
|
||||
import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.ToSchema;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ${baseName}Response${version.toUpperCase} implements ${executorBaseName}OutputInterface, ToSchema
|
||||
{
|
||||
@OpenAPIDescription(TODO)
|
||||
private String TODO;
|
||||
|
||||
TODO gsw
|
||||
}
|
||||
"""
|
||||
.replaceAll("\\$\\{version.toLowerCase}", version.toLowerCase())
|
||||
.replaceAll("\\$\\{version.toUpperCase}", version.toUpperCase())
|
||||
.replaceAll("\\$\\{executorBaseName}", executorBaseName)
|
||||
.replaceAll("\\$\\{baseName}", baseName)
|
||||
;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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/>.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
** These classes are meant as tools to be executed manually by a developer,
|
||||
** to create other new classes (since there's a bit of boilerplate, innit?)
|
||||
**
|
||||
*******************************************************************************/
|
||||
package com.kingsrook.qqq.middleware.javalin.tools.codegenerators;
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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/>.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
** This tools path - is for non-production code - rather, development and CI/CD
|
||||
** tools.
|
||||
**
|
||||
*******************************************************************************/
|
||||
package com.kingsrook.qqq.middleware.javalin.tools;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user