mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-17 20:50:44 +00:00
Javalin process-based custom router; javalin meta-data to define routers
This commit is contained in:
@ -22,9 +22,11 @@
|
||||
package com.kingsrook.qqq.backend.javalin;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QSupplementalInstanceMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.metadata.JavalinRouteProviderMetaData;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
|
||||
@ -46,7 +48,8 @@ public class QJavalinMetaData implements QSupplementalInstanceMetaData
|
||||
private Integer queryWithoutLimitDefault = 1000;
|
||||
private Level queryWithoutLimitLogLevel = Level.INFO;
|
||||
|
||||
// todo - list of objects with hosted path, file-system paths
|
||||
private List<JavalinRouteProviderMetaData> routeProviders;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
@ -278,4 +281,35 @@ public class QJavalinMetaData implements QSupplementalInstanceMetaData
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for routeProviders
|
||||
*******************************************************************************/
|
||||
public List<JavalinRouteProviderMetaData> getRouteProviders()
|
||||
{
|
||||
return (this.routeProviders);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for routeProviders
|
||||
*******************************************************************************/
|
||||
public void setRouteProviders(List<JavalinRouteProviderMetaData> routeProviders)
|
||||
{
|
||||
this.routeProviders = routeProviders;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for routeProviders
|
||||
*******************************************************************************/
|
||||
public QJavalinMetaData withRouteProviders(List<JavalinRouteProviderMetaData> routeProviders)
|
||||
{
|
||||
this.routeProviders = routeProviders;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,8 +32,13 @@ 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.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.metadata.JavalinRouteProviderMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.routeproviders.ProcessBasedRouter;
|
||||
import com.kingsrook.qqq.middleware.javalin.routeproviders.SimpleFileSystemDirectoryRouter;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.AbstractMiddlewareVersion;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.MiddlewareVersionV1;
|
||||
import io.javalin.Javalin;
|
||||
@ -99,6 +104,12 @@ public class QApplicationJavalinServer
|
||||
{
|
||||
QInstance qInstance = application.defineValidatedQInstance();
|
||||
|
||||
QJavalinMetaData qJavalinMetaData = QJavalinMetaData.of(qInstance);
|
||||
if(qJavalinMetaData != null)
|
||||
{
|
||||
addRouteProvidersFromMetaData(qJavalinMetaData);
|
||||
}
|
||||
|
||||
service = Javalin.create(config ->
|
||||
{
|
||||
if(serveFrontendMaterialDashboard)
|
||||
@ -205,6 +216,35 @@ public class QApplicationJavalinServer
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void addRouteProvidersFromMetaData(QJavalinMetaData qJavalinMetaData) throws QException
|
||||
{
|
||||
if(qJavalinMetaData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for(JavalinRouteProviderMetaData routeProviderMetaData : CollectionUtils.nonNullList(qJavalinMetaData.getRouteProviders()))
|
||||
{
|
||||
if(StringUtils.hasContent(routeProviderMetaData.getProcessName()) && StringUtils.hasContent(routeProviderMetaData.getHostedPath()))
|
||||
{
|
||||
withAdditionalRouteProvider(new ProcessBasedRouter(routeProviderMetaData));
|
||||
}
|
||||
else if(StringUtils.hasContent(routeProviderMetaData.getFileSystemPath()) && StringUtils.hasContent(routeProviderMetaData.getHostedPath()))
|
||||
{
|
||||
withAdditionalRouteProvider(new SimpleFileSystemDirectoryRouter(routeProviderMetaData));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw (new QException("Error processing route provider - does not have sufficient fields set."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
|
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.metadata;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QMetaDataObject;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class JavalinRouteProviderMetaData implements QMetaDataObject
|
||||
{
|
||||
private String hostedPath;
|
||||
|
||||
private String fileSystemPath;
|
||||
private String processName;
|
||||
|
||||
private List<String> methods;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public JavalinRouteProviderMetaData()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for hostedPath
|
||||
*******************************************************************************/
|
||||
public String getHostedPath()
|
||||
{
|
||||
return (this.hostedPath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for hostedPath
|
||||
*******************************************************************************/
|
||||
public void setHostedPath(String hostedPath)
|
||||
{
|
||||
this.hostedPath = hostedPath;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for hostedPath
|
||||
*******************************************************************************/
|
||||
public JavalinRouteProviderMetaData withHostedPath(String hostedPath)
|
||||
{
|
||||
this.hostedPath = hostedPath;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fileSystemPath
|
||||
*******************************************************************************/
|
||||
public String getFileSystemPath()
|
||||
{
|
||||
return (this.fileSystemPath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fileSystemPath
|
||||
*******************************************************************************/
|
||||
public void setFileSystemPath(String fileSystemPath)
|
||||
{
|
||||
this.fileSystemPath = fileSystemPath;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fileSystemPath
|
||||
*******************************************************************************/
|
||||
public JavalinRouteProviderMetaData withFileSystemPath(String fileSystemPath)
|
||||
{
|
||||
this.fileSystemPath = fileSystemPath;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 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 JavalinRouteProviderMetaData withProcessName(String processName)
|
||||
{
|
||||
this.processName = processName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for methods
|
||||
*******************************************************************************/
|
||||
public List<String> getMethods()
|
||||
{
|
||||
return (this.methods);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for methods
|
||||
*******************************************************************************/
|
||||
public void setMethods(List<String> methods)
|
||||
{
|
||||
this.methods = methods;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for methods
|
||||
*******************************************************************************/
|
||||
public JavalinRouteProviderMetaData withMethods(List<String> methods)
|
||||
{
|
||||
this.methods = methods;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.routeproviders;
|
||||
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
||||
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.processes.RunProcessOutput;
|
||||
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.QJavalinImplementation;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.QJavalinRouteProviderInterface;
|
||||
import com.kingsrook.qqq.middleware.javalin.metadata.JavalinRouteProviderMetaData;
|
||||
import io.javalin.apibuilder.ApiBuilder;
|
||||
import io.javalin.apibuilder.EndpointGroup;
|
||||
import io.javalin.http.Context;
|
||||
import io.javalin.http.HttpStatus;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ProcessBasedRouter implements QJavalinRouteProviderInterface
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(ProcessBasedRouter.class);
|
||||
|
||||
private final String hostedPath;
|
||||
private final String processName;
|
||||
private final List<String> methods;
|
||||
private QInstance qInstance;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouter(String hostedPath, String processName)
|
||||
{
|
||||
this(hostedPath, processName, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public ProcessBasedRouter(JavalinRouteProviderMetaData routeProvider)
|
||||
{
|
||||
this(routeProvider.getHostedPath(), routeProvider.getProcessName(), routeProvider.getMethods());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ProcessBasedRouter(String hostedPath, String processName, List<String> methods)
|
||||
{
|
||||
this.hostedPath = hostedPath;
|
||||
this.processName = processName;
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(methods))
|
||||
{
|
||||
this.methods = methods;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.methods = List.of("GET");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setQInstance(QInstance qInstance)
|
||||
{
|
||||
this.qInstance = qInstance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public EndpointGroup getJavalinEndpointGroup()
|
||||
{
|
||||
return (() ->
|
||||
{
|
||||
for(String method : methods)
|
||||
{
|
||||
switch(method.toLowerCase())
|
||||
{
|
||||
case "get" -> ApiBuilder.get(hostedPath, this::handleRequest);
|
||||
case "post" -> ApiBuilder.post(hostedPath, this::handleRequest);
|
||||
case "put" -> ApiBuilder.put(hostedPath, this::handleRequest);
|
||||
case "patch" -> ApiBuilder.patch(hostedPath, this::handleRequest);
|
||||
case "delete" -> ApiBuilder.delete(hostedPath, this::handleRequest);
|
||||
default -> throw (new IllegalArgumentException("Unrecognized method: " + method));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void handleRequest(Context context)
|
||||
{
|
||||
RunProcessInput input = new RunProcessInput();
|
||||
input.setProcessName(processName);
|
||||
|
||||
try
|
||||
{
|
||||
QJavalinImplementation.setupSession(context, input);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
context.header("WWW-Authenticate", "Basic realm=\"Access to this QQQ site\"");
|
||||
context.status(HttpStatus.UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
boolean authorized = false;
|
||||
String authorization = context.header("Authorization");
|
||||
if(authorization != null && authorization.matches("^Basic .+"))
|
||||
{
|
||||
String base64Authorization = authorization.substring("Basic ".length());
|
||||
String decoded = new String(Base64.getDecoder().decode(base64Authorization), StandardCharsets.UTF_8);
|
||||
String[] parts = decoded.split(":", 2);
|
||||
|
||||
QAuthenticationModuleDispatcher qAuthenticationModuleDispatcher = new QAuthenticationModuleDispatcher();
|
||||
QAuthenticationModuleInterface authenticationModule = qAuthenticationModuleDispatcher.getQModule(qInstance.getAuthentication());
|
||||
}
|
||||
|
||||
if(!authorized)
|
||||
{
|
||||
}
|
||||
|
||||
// todo - not always system-user session!!
|
||||
QContext.init(this.qInstance, new QSystemUserSession());
|
||||
*/
|
||||
|
||||
try
|
||||
{
|
||||
LOG.info("Running [" + processName + "] to serve [" + context.path() + "]...");
|
||||
|
||||
/////////////////////
|
||||
// run the process //
|
||||
/////////////////////
|
||||
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.SKIP);
|
||||
input.addValue("path", context.path());
|
||||
input.addValue("method", context.method());
|
||||
input.addValue("pathParams", new HashMap<>(context.pathParamMap()));
|
||||
input.addValue("queryParams", new HashMap<>(context.queryParamMap()));
|
||||
input.addValue("formParams", new HashMap<>(context.formParamMap()));
|
||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(input);
|
||||
|
||||
/////////////////
|
||||
// status code //
|
||||
/////////////////
|
||||
Integer statusCode = runProcessOutput.getValueInteger("statusCode");
|
||||
if(statusCode != null)
|
||||
{
|
||||
context.status(statusCode);
|
||||
}
|
||||
|
||||
/////////////////
|
||||
// headers map //
|
||||
/////////////////
|
||||
Serializable headers = runProcessOutput.getValue("responseHeaders");
|
||||
if(headers instanceof Map headersMap)
|
||||
{
|
||||
for(Object key : headersMap.keySet())
|
||||
{
|
||||
context.header(ValueUtils.getValueAsString(key), ValueUtils.getValueAsString(headersMap.get(key)));
|
||||
}
|
||||
}
|
||||
|
||||
// todo - make the inputStream available to the process
|
||||
// maybe via the callback object??? input.setCallback(new QProcessCallback() {});
|
||||
// context.resultInputStream();
|
||||
|
||||
///////////////////
|
||||
// response body //
|
||||
///////////////////
|
||||
Serializable response = runProcessOutput.getValue("response");
|
||||
if(response instanceof String s)
|
||||
{
|
||||
context.result(s);
|
||||
}
|
||||
else if(response instanceof byte[] ba)
|
||||
{
|
||||
context.result(ba);
|
||||
}
|
||||
else if(response instanceof InputStream is)
|
||||
{
|
||||
context.result(is);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.result(ValueUtils.getValueAsString(response));
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinUtils.handleException(null, context, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
QContext.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -24,6 +24,7 @@ package com.kingsrook.qqq.middleware.javalin.routeproviders;
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.middleware.javalin.QJavalinRouteProviderInterface;
|
||||
import com.kingsrook.qqq.middleware.javalin.metadata.JavalinRouteProviderMetaData;
|
||||
import io.javalin.config.JavalinConfig;
|
||||
import io.javalin.http.staticfiles.Location;
|
||||
import io.javalin.http.staticfiles.StaticFileConfig;
|
||||
@ -52,6 +53,16 @@ public class SimpleFileSystemDirectoryRouter implements QJavalinRouteProviderInt
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public SimpleFileSystemDirectoryRouter(JavalinRouteProviderMetaData routeProvider)
|
||||
{
|
||||
this(routeProvider.getHostedPath(), routeProvider.getFileSystemPath());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
|
Reference in New Issue
Block a user