mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
Initial checkin of lambda module
This commit is contained in:
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.lambda;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import com.amazonaws.services.lambda.runtime.Context;
|
||||
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.lambda.model.QLambdaRequest;
|
||||
import com.kingsrook.qqq.lambda.model.QLambdaResponse;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QQQ base class for lambda handlers. Meant to be sub-classed, where QQQ apps
|
||||
** can just override `handleJsonRequest`, and avoid seeing the lambda-ness of
|
||||
** lambda.
|
||||
**
|
||||
** Such subclasses can then have easy standalone unit tests - just testing their
|
||||
** logic, and not the lambda-ness.
|
||||
*******************************************************************************/
|
||||
public class QBasicLambdaHandler implements RequestStreamHandler
|
||||
{
|
||||
private static Logger LOG; // = LogManager.getLogger(QBasicLambdaHandler.class);
|
||||
|
||||
private Context context;
|
||||
|
||||
@SuppressWarnings("checkstyle:MemberName")
|
||||
protected final QLambdaResponse GENERIC_SERVER_ERROR = new QLambdaResponse(500, "Internal Server Error");
|
||||
protected final QLambdaResponse OK = new QLambdaResponse(200);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Entrypoint from AWS Lambda.
|
||||
*******************************************************************************/
|
||||
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException
|
||||
{
|
||||
this.context = context;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// tell log4j to use a config that won't fail when running in lambda //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
Configurator.initialize(null, "qqq-lambda-log4j2.xml");
|
||||
|
||||
String requestId = "unknown";
|
||||
try
|
||||
{
|
||||
String input = IOUtils.toString(inputStream, "UTF-8");
|
||||
log("Full Input: " + input);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// parse the input as json - then pull parts out of it that we know to look for //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
JSONObject inputJsonObject;
|
||||
try
|
||||
{
|
||||
inputJsonObject = JsonUtils.toJSONObject(input);
|
||||
}
|
||||
catch(JSONException je)
|
||||
{
|
||||
writeResponse(outputStream, requestId, new QLambdaResponse(400, "Unable to parse input as JSON: " + je.getMessage()));
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject headers = inputJsonObject.optJSONObject("headers");
|
||||
String contentType = headers != null ? headers.optString("content-type") : null;
|
||||
String path = inputJsonObject.optString("rawPath");
|
||||
String queryString = inputJsonObject.optString("rawQueryString");
|
||||
JSONObject requestContext = inputJsonObject.optJSONObject("requestContext");
|
||||
requestId = requestContext.optString("requestId");
|
||||
|
||||
if("application/json".equals(contentType))
|
||||
{
|
||||
String body = inputJsonObject.optString("body");
|
||||
|
||||
JSONObject bodyJsonObject;
|
||||
try
|
||||
{
|
||||
bodyJsonObject = JsonUtils.toJSONObject(body);
|
||||
}
|
||||
catch(JSONException je)
|
||||
{
|
||||
writeResponse(outputStream, requestId, new QLambdaResponse(400, "Unable to parse request body as JSON: " + je.getMessage()));
|
||||
return;
|
||||
}
|
||||
|
||||
QLambdaResponse response = handleJsonRequest(new QLambdaRequest(headers, path, queryString, bodyJsonObject));
|
||||
writeResponse(outputStream, requestId, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeResponse(outputStream, requestId, new QLambdaResponse(400, "Unsupported content-type: " + contentType));
|
||||
}
|
||||
}
|
||||
catch(QUserFacingException ufe)
|
||||
{
|
||||
writeResponse(outputStream, requestId, new QLambdaResponse(400, "Error handing request: " + ufe.getMessage()));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
writeResponse(outputStream, requestId, new QLambdaResponse(500, "Uncaught error handing request: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Meant to be overridden by subclasses, to provide functionality, if needed.
|
||||
*******************************************************************************/
|
||||
protected QLambdaResponse handleJsonRequest(QLambdaRequest request) throws QException
|
||||
{
|
||||
log(this.getClass().getSimpleName() + " did not override handleJsonRequest - so noop and return 200.");
|
||||
return (OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Write to the cloudwatch logs.
|
||||
*******************************************************************************/
|
||||
protected void log(String message)
|
||||
{
|
||||
if(message == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.context != null && this.context.getLogger() != null)
|
||||
{
|
||||
context.getLogger().log(message + (message.endsWith("\n") ? "" : "\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
initLOG();
|
||||
LOG.info(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected void log(Throwable e)
|
||||
{
|
||||
if(e == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.context != null && this.context.getLogger() != null)
|
||||
{
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
log(sw + "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
initLOG();
|
||||
LOG.warn("Exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** So, when running inside lambda, we cannot
|
||||
*******************************************************************************/
|
||||
private void initLOG()
|
||||
{
|
||||
if(LOG == null)
|
||||
{
|
||||
LOG = LogManager.getLogger(QBasicLambdaHandler.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void writeResponse(OutputStream outputStream, String requestId, QLambdaResponse response) throws IOException
|
||||
{
|
||||
QLambdaResponse.Body body = response.getBody();
|
||||
body.setRequestId(requestId);
|
||||
|
||||
outputStream.write(JsonUtils.toJson(body).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.lambda.examples;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.lambda.QBasicLambdaHandler;
|
||||
import com.kingsrook.qqq.lambda.model.QLambdaRequest;
|
||||
import com.kingsrook.qqq.lambda.model.QLambdaResponse;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ExampleLambdaHandler extends QBasicLambdaHandler
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
protected QLambdaResponse handleJsonRequest(QLambdaRequest request) throws QException
|
||||
{
|
||||
log("In subclass: " + getClass().getSimpleName());
|
||||
return (OK);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.lambda.model;
|
||||
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QQQ abstraction over an AWS Lambda Request.
|
||||
*******************************************************************************/
|
||||
public class QLambdaRequest
|
||||
{
|
||||
private JSONObject headers;
|
||||
private String path;
|
||||
private String queryString;
|
||||
private JSONObject body;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QLambdaRequest(JSONObject headers, String path, String queryString, JSONObject body)
|
||||
{
|
||||
this.headers = headers;
|
||||
this.path = path;
|
||||
this.queryString = queryString;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for headers
|
||||
**
|
||||
*******************************************************************************/
|
||||
public JSONObject getHeaders()
|
||||
{
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for path
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getPath()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryString
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getQueryString()
|
||||
{
|
||||
return queryString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for body
|
||||
**
|
||||
*******************************************************************************/
|
||||
public JSONObject getBody()
|
||||
{
|
||||
return body;
|
||||
}
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.lambda.model;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QQQ abstraction over an AWS Lambda Response.
|
||||
*******************************************************************************/
|
||||
public class QLambdaResponse
|
||||
{
|
||||
private int statusCode;
|
||||
private boolean isBase64Encoded = false;
|
||||
private Map<String, String> headers = Map.of("Content-Type", "application/json");
|
||||
private Body body;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QLambdaResponse(int statusCode, Body body)
|
||||
{
|
||||
this.statusCode = statusCode;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QLambdaResponse(int statusCode, String errorMessage)
|
||||
{
|
||||
this.statusCode = statusCode;
|
||||
this.body = new Body(errorMessage);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QLambdaResponse(int statusCode)
|
||||
{
|
||||
this.statusCode = statusCode;
|
||||
this.body = new Body();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QLambdaResponse(int statusCode, boolean isBase64Encoded, Map<String, String> headers, Body body)
|
||||
{
|
||||
this.statusCode = statusCode;
|
||||
this.isBase64Encoded = isBase64Encoded;
|
||||
this.headers = headers;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statusCode
|
||||
**
|
||||
*******************************************************************************/
|
||||
public int getStatusCode()
|
||||
{
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for isBase64Encoded
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getIsBase64Encoded()
|
||||
{
|
||||
return isBase64Encoded;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for headers
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, String> getHeaders()
|
||||
{
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for body
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Body getBody()
|
||||
{
|
||||
return body;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class Body
|
||||
{
|
||||
private String requestId;
|
||||
private String errorMessage;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Body()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Body(String errorMessage)
|
||||
{
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Body(String requestId, String errorMessage)
|
||||
{
|
||||
this.requestId = requestId;
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for requestId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setRequestId(String requestId)
|
||||
{
|
||||
this.requestId = requestId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for requestId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getRequestId()
|
||||
{
|
||||
return requestId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for errorMessage
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getErrorMessage()
|
||||
{
|
||||
return errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration>
|
||||
<Properties>
|
||||
<Property name="LOG_PATTERN">%date{ISO8601} | %relative | %level | %threadName{1} | %logger{1}.%method | %message%n</Property>
|
||||
</Properties>
|
||||
<Appenders>
|
||||
<Console name="SystemOutAppender" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="${LOG_PATTERN}"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger name="org.apache.log4j.xml" additivity="false">
|
||||
</Logger>
|
||||
<Root level="all">
|
||||
<AppenderRef ref="SystemOutAppender"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.lambda;
|
||||
|
||||
|
||||
import com.amazonaws.services.lambda.runtime.ClientContext;
|
||||
import com.amazonaws.services.lambda.runtime.CognitoIdentity;
|
||||
import com.amazonaws.services.lambda.runtime.Context;
|
||||
import com.amazonaws.services.lambda.runtime.LambdaLogger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MockContext implements Context
|
||||
{
|
||||
@Override
|
||||
public String getAwsRequestId()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getLogGroupName()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getLogStreamName()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getFunctionName()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getFunctionVersion()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getInvokedFunctionArn()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public CognitoIdentity getIdentity()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public ClientContext getClientContext()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getRemainingTimeInMillis()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getMemoryLimitInMB()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public LambdaLogger getLogger()
|
||||
{
|
||||
return new LambdaLogger()
|
||||
{
|
||||
@Override
|
||||
public void log(String s)
|
||||
{
|
||||
System.out.println(s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void log(byte[] bytes)
|
||||
{
|
||||
System.out.println(bytes);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,354 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.lambda;
|
||||
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import com.amazonaws.services.lambda.runtime.Context;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.lambda.model.QLambdaRequest;
|
||||
import com.kingsrook.qqq.lambda.model.QLambdaResponse;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for com.kingsrook.qqq.lambda.AnotherHandler
|
||||
*******************************************************************************/
|
||||
class QBasicLambdaHandlerTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testSuccess() throws IOException
|
||||
{
|
||||
String inputString = getSuccessInputString();
|
||||
String outputString = runHandleRequest(inputString);
|
||||
JSONObject outputJson = JsonUtils.toJSONObject(outputString);
|
||||
assertTrue(outputJson.has("requestId"));
|
||||
assertFalse(outputJson.has("errorMessage"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNonJsonInput() throws IOException
|
||||
{
|
||||
String inputString = getNonJsonInputString();
|
||||
String outputString = runHandleRequest(inputString);
|
||||
JSONObject outputJson = JsonUtils.toJSONObject(outputString);
|
||||
assertTrue(outputJson.has("errorMessage"));
|
||||
assertThat(outputJson.getString("errorMessage")).contains("Unable to parse input as JSON");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNonJsonBody() throws IOException
|
||||
{
|
||||
String inputString = getNonJsonBodyString();
|
||||
String outputString = runHandleRequest(inputString);
|
||||
JSONObject outputJson = JsonUtils.toJSONObject(outputString);
|
||||
assertTrue(outputJson.has("errorMessage"));
|
||||
assertThat(outputJson.getString("errorMessage")).contains("Unable to parse request body as JSON");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNonJsonContentType() throws IOException
|
||||
{
|
||||
String inputString = getNonJsonContentTypeString();
|
||||
String outputString = runHandleRequest(inputString);
|
||||
JSONObject outputJson = JsonUtils.toJSONObject(outputString);
|
||||
assertTrue(outputJson.has("errorMessage"));
|
||||
assertThat(outputJson.getString("errorMessage")).contains("Unsupported content-type:");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testLogException() throws IOException
|
||||
{
|
||||
InputStream inputStream = new ByteArrayInputStream(getSuccessInputString().getBytes());
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
Context context = new MockContext();
|
||||
|
||||
new QBasicLambdaHandler()
|
||||
{
|
||||
@Override
|
||||
protected QLambdaResponse handleJsonRequest(QLambdaRequest request) throws QException
|
||||
{
|
||||
log(new QException("Test Exception"));
|
||||
return (OK);
|
||||
}
|
||||
}.handleRequest(inputStream, outputStream, context);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String runHandleRequest(String inputString) throws IOException
|
||||
{
|
||||
InputStream inputStream = new ByteArrayInputStream(inputString.getBytes());
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
Context context = new MockContext();
|
||||
new QBasicLambdaHandler().handleRequest(inputStream, outputStream, context);
|
||||
String outputString = outputStream.toString(StandardCharsets.UTF_8);
|
||||
System.out.println(outputString);
|
||||
return outputString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testResponseJson()
|
||||
{
|
||||
String json = JsonUtils.toPrettyJson(new QLambdaResponse(400, "Testing an error"));
|
||||
System.out.println(json);
|
||||
|
||||
json = JsonUtils.toPrettyJson(new QLambdaResponse(200, false, Map.of("Content-Type", "application/json", "X-my-header", "1234"),
|
||||
new QLambdaResponse.Body(UUID.randomUUID().toString(), null)));
|
||||
System.out.println(json);
|
||||
|
||||
QLambdaResponse qLambdaResponse = new QLambdaResponse(201);
|
||||
qLambdaResponse.getBody().setRequestId(UUID.randomUUID().toString());
|
||||
json = JsonUtils.toPrettyJson(qLambdaResponse);
|
||||
System.out.println(json);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testHandleRequest() throws QException
|
||||
{
|
||||
QLambdaResponse response = new QBasicLambdaHandler().handleJsonRequest(new QLambdaRequest(new JSONObject(), "/", "", new JSONObject()));
|
||||
assertEquals(200, response.getStatusCode());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getTemplateInputString()
|
||||
{
|
||||
return ("""
|
||||
""");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getNonJsonContentTypeString()
|
||||
{
|
||||
return ("""
|
||||
{
|
||||
"version": "2.0",
|
||||
"routeKey": "$default",
|
||||
"rawPath": "/",
|
||||
"rawQueryString": "",
|
||||
"headers": {
|
||||
"content-length": "21",
|
||||
"x-amzn-tls-cipher-suite": "ECDHE-RSA-AES128-GCM-SHA256",
|
||||
"x-amzn-tls-version": "TLSv1.2",
|
||||
"x-amzn-trace-id": "Root=1-63483527-78c1f53c4710848602d961f0",
|
||||
"x-forwarded-proto": "https",
|
||||
"host": "ge74ifwcbvykmlp7rmqd2a7a4e0hhkmm.lambda-url.us-east-1.on.aws",
|
||||
"x-forwarded-port": "443",
|
||||
"content-type": "text/xml",
|
||||
"x-forwarded-for": "24.217.225.229",
|
||||
"accept": "*/*",
|
||||
"user-agent": "curl/7.79.1"
|
||||
},
|
||||
"requestContext": {
|
||||
"accountId": "anonymous",
|
||||
"apiId": "ge74ifwcbvykmlp7rmqd2a7a4e0hhkmm",
|
||||
"domainName": "ge74ifwcbvykmlp7rmqd2a7a4e0hhkmm.lambda-url.us-east-1.on.aws",
|
||||
"domainPrefix": "ge74ifwcbvykmlp7rmqd2a7a4e0hhkmm",
|
||||
"http": {
|
||||
"method": "POST",
|
||||
"path": "/",
|
||||
"protocol": "HTTP/1.1",
|
||||
"sourceIp": "24.217.225.229",
|
||||
"userAgent": "curl/7.79.1"
|
||||
},
|
||||
"requestId": "4c11e825-79a3-4583-ba6e-438fc5d9ec91",
|
||||
"routeKey": "$default",
|
||||
"stage": "$default",
|
||||
"time": "13/Oct/2022:15:56:23 +0000",
|
||||
"timeEpoch": 1665676583934
|
||||
},
|
||||
"body": "<?xml ...>",
|
||||
"isBase64Encoded": false
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getNonJsonBodyString()
|
||||
{
|
||||
return ("""
|
||||
{
|
||||
"version": "2.0",
|
||||
"routeKey": "$default",
|
||||
"rawPath": "/",
|
||||
"rawQueryString": "",
|
||||
"headers": {
|
||||
"content-length": "21",
|
||||
"x-amzn-tls-cipher-suite": "ECDHE-RSA-AES128-GCM-SHA256",
|
||||
"x-amzn-tls-version": "TLSv1.2",
|
||||
"x-amzn-trace-id": "Root=1-63483527-78c1f53c4710848602d961f0",
|
||||
"x-forwarded-proto": "https",
|
||||
"host": "ge74ifwcbvykmlp7rmqd2a7a4e0hhkmm.lambda-url.us-east-1.on.aws",
|
||||
"x-forwarded-port": "443",
|
||||
"content-type": "application/json",
|
||||
"x-forwarded-for": "24.217.225.229",
|
||||
"accept": "*/*",
|
||||
"user-agent": "curl/7.79.1"
|
||||
},
|
||||
"requestContext": {
|
||||
"accountId": "anonymous",
|
||||
"apiId": "ge74ifwcbvykmlp7rmqd2a7a4e0hhkmm",
|
||||
"domainName": "ge74ifwcbvykmlp7rmqd2a7a4e0hhkmm.lambda-url.us-east-1.on.aws",
|
||||
"domainPrefix": "ge74ifwcbvykmlp7rmqd2a7a4e0hhkmm",
|
||||
"http": {
|
||||
"method": "POST",
|
||||
"path": "/",
|
||||
"protocol": "HTTP/1.1",
|
||||
"sourceIp": "24.217.225.229",
|
||||
"userAgent": "curl/7.79.1"
|
||||
},
|
||||
"requestId": "4c11e825-79a3-4583-ba6e-438fc5d9ec91",
|
||||
"routeKey": "$default",
|
||||
"stage": "$default",
|
||||
"time": "13/Oct/2022:15:56:23 +0000",
|
||||
"timeEpoch": 1665676583934
|
||||
},
|
||||
"body": "not json.",
|
||||
"isBase64Encoded": false
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getNonJsonInputString()
|
||||
{
|
||||
return ("""
|
||||
not json.
|
||||
""");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getSuccessInputString()
|
||||
{
|
||||
return ("""
|
||||
{
|
||||
"version": "2.0",
|
||||
"routeKey": "$default",
|
||||
"rawPath": "/",
|
||||
"rawQueryString": "",
|
||||
"headers": {
|
||||
"content-length": "21",
|
||||
"x-amzn-tls-cipher-suite": "ECDHE-RSA-AES128-GCM-SHA256",
|
||||
"x-amzn-tls-version": "TLSv1.2",
|
||||
"x-amzn-trace-id": "Root=1-63483527-78c1f53c4710848602d961f0",
|
||||
"x-forwarded-proto": "https",
|
||||
"host": "ge74ifwcbvykmlp7rmqd2a7a4e0hhkmm.lambda-url.us-east-1.on.aws",
|
||||
"x-forwarded-port": "443",
|
||||
"content-type": "application/json",
|
||||
"x-forwarded-for": "24.217.225.229",
|
||||
"accept": "*/*",
|
||||
"user-agent": "curl/7.79.1"
|
||||
},
|
||||
"requestContext": {
|
||||
"accountId": "anonymous",
|
||||
"apiId": "ge74ifwcbvykmlp7rmqd2a7a4e0hhkmm",
|
||||
"domainName": "ge74ifwcbvykmlp7rmqd2a7a4e0hhkmm.lambda-url.us-east-1.on.aws",
|
||||
"domainPrefix": "ge74ifwcbvykmlp7rmqd2a7a4e0hhkmm",
|
||||
"http": {
|
||||
"method": "POST",
|
||||
"path": "/",
|
||||
"protocol": "HTTP/1.1",
|
||||
"sourceIp": "24.217.225.229",
|
||||
"userAgent": "curl/7.79.1"
|
||||
},
|
||||
"requestId": "4c11e825-79a3-4583-ba6e-438fc5d9ec91",
|
||||
"routeKey": "$default",
|
||||
"stage": "$default",
|
||||
"time": "13/Oct/2022:15:56:23 +0000",
|
||||
"timeEpoch": 1665676583934
|
||||
},
|
||||
"body": "{\\n \\"test\\": \\"event\\"\\n}",
|
||||
"isBase64Encoded": false
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.lambda.examples;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.lambda.model.QLambdaRequest;
|
||||
import com.kingsrook.qqq.lambda.model.QLambdaResponse;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for ExampleLambdaHandler
|
||||
*******************************************************************************/
|
||||
class ExampleLambdaHandlerTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
QLambdaResponse qLambdaResponse = new ExampleLambdaHandler().handleJsonRequest(new QLambdaRequest(new JSONObject(), "", "", new JSONObject()));
|
||||
assertEquals(200, qLambdaResponse.getStatusCode());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
--
|
||||
-- QQQ - Low-code Application Framework for Engineers.
|
||||
-- Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
-- 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
-- contact@kingsrook.com
|
||||
-- https://github.com/Kingsrook/
|
||||
--
|
||||
-- This program is free software: you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU Affero General Public License as
|
||||
-- published by the Free Software Foundation, either version 3 of the
|
||||
-- License, or (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU Affero General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU Affero General Public License
|
||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS person;
|
||||
CREATE TABLE person
|
||||
(
|
||||
id INT AUTO_INCREMENT,
|
||||
create_date TIMESTAMP DEFAULT now(),
|
||||
modify_date TIMESTAMP DEFAULT now(),
|
||||
|
||||
first_name VARCHAR(80) NOT NULL,
|
||||
last_name VARCHAR(80) NOT NULL,
|
||||
birth_date DATE,
|
||||
email VARCHAR(250) NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (1, 'Darin', 'Kelkhoff', '1980-05-31', 'darin.kelkhoff@gmail.com');
|
||||
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (2, 'James', 'Maes', '1980-05-15', 'jmaes@mmltholdings.com');
|
||||
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (3, 'Tim', 'Chamberlain', '1976-05-28', 'tchamberlain@mmltholdings.com');
|
||||
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (4, 'Tyler', 'Samples', '1990-01-01', 'tsamples@mmltholdings.com');
|
||||
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (5, 'Garret', 'Richardson', '1981-01-01', 'grichardson@mmltholdings.com');
|
Reference in New Issue
Block a user