mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
API Logs!
This commit is contained in:
@ -386,7 +386,7 @@ public class ScriptsMetaDataProvider
|
||||
.withSection(new QFieldSection("changeManagement", new QIcon().withName("history"), Tier.T2, List.of("commitMessage", "author")))
|
||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
|
||||
tableMetaData.getField("contents").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR));
|
||||
tableMetaData.getField("contents").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("javascript")));
|
||||
tableMetaData.getField("scriptId").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment());
|
||||
|
||||
return (tableMetaData);
|
||||
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.api.javalin;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
@ -37,6 +38,7 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.api.actions.GenerateOpenApiSpecAction;
|
||||
import com.kingsrook.qqq.api.actions.QRecordApiAdapter;
|
||||
import com.kingsrook.qqq.api.model.APILog;
|
||||
import com.kingsrook.qqq.api.model.APIVersion;
|
||||
import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecInput;
|
||||
import com.kingsrook.qqq.api.model.actions.GenerateOpenApiSpecOutput;
|
||||
@ -276,7 +278,21 @@ public class QJavalinApiHandler
|
||||
*******************************************************************************/
|
||||
private static void doPathNotFound(Context context)
|
||||
{
|
||||
handleException(context, new QNotFoundException("Could not find any resources at path " + context.path()));
|
||||
APILog apiLog = newAPILog(context);
|
||||
|
||||
try
|
||||
{
|
||||
setupSession(context, null, null);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// if we don't have a session, we won't be able to store the api log... //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
LOG.debug("No session in a 404; will not create api log", e);
|
||||
}
|
||||
|
||||
handleException(context, new QNotFoundException("Could not find any resources at path " + context.path()), apiLog);
|
||||
}
|
||||
|
||||
|
||||
@ -548,6 +564,7 @@ public class QJavalinApiHandler
|
||||
String version = context.pathParam("version");
|
||||
String tableApiName = context.pathParam("tableName");
|
||||
String primaryKey = context.pathParam("primaryKey");
|
||||
APILog apiLog = newAPILog(context);
|
||||
|
||||
try
|
||||
{
|
||||
@ -585,12 +602,64 @@ public class QJavalinApiHandler
|
||||
Map<String, Serializable> outputRecord = QRecordApiAdapter.qRecordToApiMap(record, tableName, version);
|
||||
|
||||
QJavalinAccessLogger.logEndSuccess();
|
||||
context.result(JsonUtils.toJson(outputRecord));
|
||||
String resultString = JsonUtils.toJson(outputRecord);
|
||||
context.result(resultString);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinAccessLogger.logEndFail(e);
|
||||
handleException(context, e);
|
||||
handleException(context, e, apiLog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static APILog newAPILog(Context context)
|
||||
{
|
||||
APILog apiLog = new APILog()
|
||||
.withTimestamp(Instant.now())
|
||||
.withMethod(context.req().getMethod())
|
||||
.withPath(context.path())
|
||||
.withQueryString(context.queryString())
|
||||
.withRequestBody(context.body());
|
||||
|
||||
try
|
||||
{
|
||||
apiLog.setVersion(context.pathParam("version"));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// pathParam throws if the param isn't found - in that case, just leave it null //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
return (apiLog);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void storeApiLog(APILog apiLog)
|
||||
{
|
||||
try
|
||||
{
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(APILog.TABLE_NAME);
|
||||
// todo - security fields!!!!!
|
||||
// todo - user!!!!
|
||||
insertInput.setRecords(List.of(apiLog.toQRecord()));
|
||||
new InsertAction().executeAsync(insertInput);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error storing API log", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -604,6 +673,7 @@ public class QJavalinApiHandler
|
||||
String version = context.pathParam("version");
|
||||
String tableApiName = context.pathParam("tableName");
|
||||
QQueryFilter filter = null;
|
||||
APILog apiLog = newAPILog(context);
|
||||
|
||||
try
|
||||
{
|
||||
@ -822,12 +892,14 @@ public class QJavalinApiHandler
|
||||
output.put("records", records);
|
||||
|
||||
QJavalinAccessLogger.logEndSuccess(logPair("recordCount", queryOutput.getRecords().size()), QJavalinAccessLogger.logPairIfSlow("filter", filter, SLOW_LOG_THRESHOLD_MS));
|
||||
context.result(JsonUtils.toJson(output));
|
||||
String resultString = JsonUtils.toJson(output);
|
||||
context.result(resultString);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinAccessLogger.logEndFail(e, logPair("filter", filter));
|
||||
handleException(context, e);
|
||||
handleException(context, e, apiLog);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1041,6 +1113,7 @@ public class QJavalinApiHandler
|
||||
{
|
||||
String version = context.pathParam("version");
|
||||
String tableApiName = context.pathParam("tableName");
|
||||
APILog apiLog = newAPILog(context);
|
||||
|
||||
try
|
||||
{
|
||||
@ -1090,12 +1163,14 @@ public class QJavalinApiHandler
|
||||
|
||||
QJavalinAccessLogger.logEndSuccess();
|
||||
context.status(HttpStatus.Code.CREATED.getCode());
|
||||
context.result(JsonUtils.toJson(outputRecord));
|
||||
String resultString = JsonUtils.toJson(outputRecord);
|
||||
context.result(resultString);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinAccessLogger.logEndFail(e);
|
||||
handleException(context, e);
|
||||
handleException(context, e, apiLog);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1108,6 +1183,7 @@ public class QJavalinApiHandler
|
||||
{
|
||||
String version = context.pathParam("version");
|
||||
String tableApiName = context.pathParam("tableName");
|
||||
APILog apiLog = newAPILog(context);
|
||||
|
||||
try
|
||||
{
|
||||
@ -1196,12 +1272,14 @@ public class QJavalinApiHandler
|
||||
|
||||
QJavalinAccessLogger.logEndSuccess();
|
||||
context.status(HttpStatus.Code.MULTI_STATUS.getCode());
|
||||
context.result(JsonUtils.toJson(response));
|
||||
String resultString = JsonUtils.toJson(response);
|
||||
context.result(resultString);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinAccessLogger.logEndFail(e);
|
||||
handleException(context, e);
|
||||
handleException(context, e, apiLog);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1214,6 +1292,7 @@ public class QJavalinApiHandler
|
||||
{
|
||||
String version = context.pathParam("version");
|
||||
String tableApiName = context.pathParam("tableName");
|
||||
APILog apiLog = newAPILog(context);
|
||||
|
||||
try
|
||||
{
|
||||
@ -1325,12 +1404,14 @@ public class QJavalinApiHandler
|
||||
|
||||
QJavalinAccessLogger.logEndSuccess();
|
||||
context.status(HttpStatus.Code.MULTI_STATUS.getCode());
|
||||
context.result(JsonUtils.toJson(response));
|
||||
String resultString = JsonUtils.toJson(response);
|
||||
context.result(resultString);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinAccessLogger.logEndFail(e);
|
||||
handleException(context, e);
|
||||
handleException(context, e, apiLog);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1353,6 +1434,7 @@ public class QJavalinApiHandler
|
||||
{
|
||||
String version = context.pathParam("version");
|
||||
String tableApiName = context.pathParam("tableName");
|
||||
APILog apiLog = newAPILog(context);
|
||||
|
||||
try
|
||||
{
|
||||
@ -1463,12 +1545,14 @@ public class QJavalinApiHandler
|
||||
|
||||
QJavalinAccessLogger.logEndSuccess();
|
||||
context.status(HttpStatus.Code.MULTI_STATUS.getCode());
|
||||
context.result(JsonUtils.toJson(response));
|
||||
String resultString = JsonUtils.toJson(response);
|
||||
context.result(resultString);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinAccessLogger.logEndFail(e);
|
||||
handleException(context, e);
|
||||
handleException(context, e, apiLog);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1482,6 +1566,7 @@ public class QJavalinApiHandler
|
||||
String version = context.pathParam("version");
|
||||
String tableApiName = context.pathParam("tableName");
|
||||
String primaryKey = context.pathParam("primaryKey");
|
||||
APILog apiLog = newAPILog(context);
|
||||
|
||||
try
|
||||
{
|
||||
@ -1546,11 +1631,12 @@ public class QJavalinApiHandler
|
||||
|
||||
QJavalinAccessLogger.logEndSuccess();
|
||||
context.status(HttpStatus.Code.NO_CONTENT.getCode());
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinAccessLogger.logEndFail(e);
|
||||
handleException(context, e);
|
||||
handleException(context, e, apiLog);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1564,6 +1650,7 @@ public class QJavalinApiHandler
|
||||
String version = context.pathParam("version");
|
||||
String tableApiName = context.pathParam("tableName");
|
||||
String primaryKey = context.pathParam("primaryKey");
|
||||
APILog apiLog = newAPILog(context);
|
||||
|
||||
try
|
||||
{
|
||||
@ -1599,22 +1686,33 @@ public class QJavalinApiHandler
|
||||
|
||||
QJavalinAccessLogger.logEndSuccess();
|
||||
context.status(HttpStatus.Code.NO_CONTENT.getCode());
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QJavalinAccessLogger.logEndFail(e);
|
||||
handleException(context, e);
|
||||
handleException(context, e, apiLog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void handleException(Context context, Exception e, APILog apiLog)
|
||||
{
|
||||
handleException(null, context, e, apiLog);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void handleException(Context context, Exception e)
|
||||
{
|
||||
handleException(null, context, e);
|
||||
handleException(null, context, e, null);
|
||||
}
|
||||
|
||||
|
||||
@ -1622,13 +1720,13 @@ public class QJavalinApiHandler
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void handleException(HttpStatus.Code statusCode, Context context, Exception e)
|
||||
public static void handleException(HttpStatus.Code statusCode, Context context, Exception e, APILog apiLog)
|
||||
{
|
||||
QBadRequestException badRequestException = ExceptionUtils.findClassInRootChain(e, QBadRequestException.class);
|
||||
if(badRequestException != null)
|
||||
{
|
||||
statusCode = Objects.requireNonNullElse(statusCode, HttpStatus.Code.BAD_REQUEST); // 400
|
||||
respondWithError(context, statusCode, badRequestException.getMessage());
|
||||
respondWithError(context, statusCode, badRequestException.getMessage(), apiLog);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1638,26 +1736,28 @@ public class QJavalinApiHandler
|
||||
if(userFacingException instanceof QNotFoundException)
|
||||
{
|
||||
statusCode = Objects.requireNonNullElse(statusCode, HttpStatus.Code.NOT_FOUND); // 404
|
||||
respondWithError(context, statusCode, userFacingException.getMessage());
|
||||
respondWithError(context, statusCode, userFacingException.getMessage(), apiLog);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.info("User-facing exception", e);
|
||||
statusCode = Objects.requireNonNullElse(statusCode, HttpStatus.Code.INTERNAL_SERVER_ERROR); // 500
|
||||
respondWithError(context, statusCode, userFacingException.getMessage());
|
||||
respondWithError(context, statusCode, userFacingException.getMessage(), apiLog);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(e instanceof QAuthenticationException)
|
||||
{
|
||||
respondWithError(context, HttpStatus.Code.UNAUTHORIZED, e.getMessage()); // 401
|
||||
respondWithError(context, HttpStatus.Code.UNAUTHORIZED, e.getMessage(), apiLog); // 401
|
||||
return;
|
||||
}
|
||||
|
||||
if(e instanceof QPermissionDeniedException)
|
||||
{
|
||||
respondWithError(context, HttpStatus.Code.FORBIDDEN, e.getMessage()); // 403
|
||||
respondWithError(context, HttpStatus.Code.FORBIDDEN, e.getMessage(), apiLog); // 403
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1665,7 +1765,8 @@ public class QJavalinApiHandler
|
||||
// default exception handling //
|
||||
////////////////////////////////
|
||||
LOG.warn("Exception in javalin request", e);
|
||||
respondWithError(context, HttpStatus.Code.INTERNAL_SERVER_ERROR, e.getClass().getSimpleName() + " (" + e.getMessage() + ")"); // 500
|
||||
respondWithError(context, HttpStatus.Code.INTERNAL_SERVER_ERROR, e.getClass().getSimpleName() + " (" + e.getMessage() + ")", apiLog); // 500
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1674,7 +1775,7 @@ public class QJavalinApiHandler
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void respondWithError(Context context, HttpStatus.Code statusCode, String errorMessage)
|
||||
public static void respondWithError(Context context, HttpStatus.Code statusCode, String errorMessage, APILog apiLog)
|
||||
{
|
||||
context.status(statusCode.getCode());
|
||||
|
||||
@ -1695,7 +1796,17 @@ public class QJavalinApiHandler
|
||||
///////////////////////////
|
||||
}
|
||||
|
||||
context.result(JsonUtils.toJson(Map.of("error", errorMessage)));
|
||||
String responseBody = JsonUtils.toJson(Map.of("error", errorMessage));
|
||||
context.result(responseBody);
|
||||
|
||||
if(apiLog != null)
|
||||
{
|
||||
if(QContext.getQSession() != null && QContext.getQInstance() != null)
|
||||
{
|
||||
apiLog.withStatusCode(statusCode.getCode()).withResponseBody(responseBody);
|
||||
storeApiLog(apiLog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,393 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api.model;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class APILog extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "apiLog";
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Instant timestamp;
|
||||
|
||||
@QField()
|
||||
private String method;
|
||||
|
||||
@QField()
|
||||
private Integer statusCode;
|
||||
|
||||
@QField()
|
||||
private String version;
|
||||
|
||||
@QField()
|
||||
private String path;
|
||||
|
||||
@QField()
|
||||
private String queryString;
|
||||
|
||||
@QField()
|
||||
private String requestBody;
|
||||
|
||||
@QField()
|
||||
private String responseBody;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APILog()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APILog(QRecord qRecord) throws QException
|
||||
{
|
||||
populateFromQRecord(qRecord);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APILog withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for timestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getTimestamp()
|
||||
{
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for timestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setTimestamp(Instant timestamp)
|
||||
{
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for timestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APILog withTimestamp(Instant timestamp)
|
||||
{
|
||||
this.timestamp = timestamp;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for method
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getMethod()
|
||||
{
|
||||
return method;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for method
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setMethod(String method)
|
||||
{
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for method
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APILog withMethod(String method)
|
||||
{
|
||||
this.method = method;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statusCode
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getStatusCode()
|
||||
{
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for statusCode
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setStatusCode(Integer statusCode)
|
||||
{
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for statusCode
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APILog withStatusCode(Integer statusCode)
|
||||
{
|
||||
this.statusCode = statusCode;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for version
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getVersion()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for version
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setVersion(String version)
|
||||
{
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for version
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APILog withVersion(String version)
|
||||
{
|
||||
this.version = version;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for path
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getPath()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for path
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setPath(String path)
|
||||
{
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for path
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APILog withPath(String path)
|
||||
{
|
||||
this.path = path;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryString
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getQueryString()
|
||||
{
|
||||
return queryString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryString
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setQueryString(String queryString)
|
||||
{
|
||||
this.queryString = queryString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryString
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APILog withQueryString(String queryString)
|
||||
{
|
||||
this.queryString = queryString;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for requestBody
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getRequestBody()
|
||||
{
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for requestBody
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setRequestBody(String requestBody)
|
||||
{
|
||||
this.requestBody = requestBody;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for requestBody
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APILog withRequestBody(String requestBody)
|
||||
{
|
||||
this.requestBody = requestBody;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for responseBody
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getResponseBody()
|
||||
{
|
||||
return responseBody;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for responseBody
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setResponseBody(String responseBody)
|
||||
{
|
||||
this.responseBody = responseBody;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for responseBody
|
||||
**
|
||||
*******************************************************************************/
|
||||
public APILog withResponseBody(String responseBody)
|
||||
{
|
||||
this.responseBody = responseBody;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.api.model.metadata;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import com.kingsrook.qqq.api.model.APILog;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class APILogMetaDataProvider
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void defineAll(QInstance qInstance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
defineAPILogTable(qInstance, backendName, backendDetailEnricher);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void defineAPILogTable(QInstance qInstance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
QTableMetaData tableMetaData = new QTableMetaData()
|
||||
.withName("apiLog")
|
||||
.withLabel("API Log")
|
||||
.withIcon(new QIcon().withName("data_object"))
|
||||
.withBackendName(backendName)
|
||||
.withRecordLabelFormat("%s")
|
||||
.withPrimaryKeyField("id")
|
||||
.withFieldsFromEntity(APILog.class)
|
||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id")))
|
||||
.withSection(new QFieldSection("request", new QIcon().withName("arrow_upward"), Tier.T2, List.of("method", "version", "path", "queryString", "requestBody")))
|
||||
.withSection(new QFieldSection("response", new QIcon().withName("arrow_downward"), Tier.T2, List.of("statusCode", "responseBody")))
|
||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("timestamp")))
|
||||
.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
|
||||
tableMetaData.getField("requestBody").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("json")));
|
||||
tableMetaData.getField("responseBody").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("json")));
|
||||
|
||||
tableMetaData.getField("method").withFieldAdornment(new FieldAdornment(AdornmentType.CHIP)
|
||||
.withValue(AdornmentType.ChipValues.colorValue("GET", AdornmentType.ChipValues.COLOR_INFO))
|
||||
.withValue(AdornmentType.ChipValues.colorValue("POST", AdornmentType.ChipValues.COLOR_SUCCESS))
|
||||
.withValue(AdornmentType.ChipValues.colorValue("DELETE", AdornmentType.ChipValues.COLOR_ERROR))
|
||||
.withValue(AdornmentType.ChipValues.colorValue("PATCH", AdornmentType.ChipValues.COLOR_WARNING)));
|
||||
|
||||
tableMetaData.getField("statusCode").withFieldAdornment(new FieldAdornment(AdornmentType.CHIP)
|
||||
.withValue(AdornmentType.ChipValues.colorValue(200, AdornmentType.ChipValues.COLOR_SUCCESS))
|
||||
.withValue(AdornmentType.ChipValues.colorValue(201, AdornmentType.ChipValues.COLOR_SUCCESS))
|
||||
.withValue(AdornmentType.ChipValues.colorValue(204, AdornmentType.ChipValues.COLOR_SUCCESS))
|
||||
.withValue(AdornmentType.ChipValues.colorValue(207, AdornmentType.ChipValues.COLOR_INFO))
|
||||
.withValue(AdornmentType.ChipValues.colorValue(400, AdornmentType.ChipValues.COLOR_ERROR))
|
||||
.withValue(AdornmentType.ChipValues.colorValue(401, AdornmentType.ChipValues.COLOR_ERROR))
|
||||
.withValue(AdornmentType.ChipValues.colorValue(403, AdornmentType.ChipValues.COLOR_ERROR))
|
||||
.withValue(AdornmentType.ChipValues.colorValue(404, AdornmentType.ChipValues.COLOR_ERROR))
|
||||
.withValue(AdornmentType.ChipValues.colorValue(429, AdornmentType.ChipValues.COLOR_ERROR))
|
||||
.withValue(AdornmentType.ChipValues.colorValue(500, AdornmentType.ChipValues.COLOR_ERROR)));
|
||||
|
||||
///////////////////////////////////////////
|
||||
// these are the lengths of a MySQL TEXT //
|
||||
///////////////////////////////////////////
|
||||
tableMetaData.getField("requestBody").withMaxLength(65_535).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS);
|
||||
tableMetaData.getField("responseBody").withMaxLength(65_535).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// internet doesn't agree on max-length for a URL, but let's go with ... 4K on query string //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
tableMetaData.getField("queryString").withMaxLength(4096).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS);
|
||||
|
||||
////////////////////////////////////////
|
||||
// and we expect short paths, 100 max //
|
||||
////////////////////////////////////////
|
||||
tableMetaData.getField("path").withMaxLength(100).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS);
|
||||
|
||||
if(backendDetailEnricher != null)
|
||||
{
|
||||
backendDetailEnricher.accept(tableMetaData);
|
||||
}
|
||||
|
||||
qInstance.addTable(tableMetaData);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user