mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-17 20:50:44 +00:00
CE-881 - First round of adjustments to api-middleware for rendering saved-reports:
- add path params as concept - add customizeHttpApiResponse to ApiProcessOutputInterface - add contentType and needsFormattedAsJson in HttpApiResponse
This commit is contained in:
@ -1000,6 +1000,7 @@ public class ApiImplementation
|
||||
ApiProcessInput apiProcessInput = apiProcessMetaData.getInput();
|
||||
if(apiProcessInput != null)
|
||||
{
|
||||
processProcessInputFields(paramMap, badRequestMessages, runProcessInput, apiProcessInput.getPathParams());
|
||||
processProcessInputFields(paramMap, badRequestMessages, runProcessInput, apiProcessInput.getQueryStringParams());
|
||||
processProcessInputFields(paramMap, badRequestMessages, runProcessInput, apiProcessInput.getFormParams());
|
||||
processProcessInputFields(paramMap, badRequestMessages, runProcessInput, apiProcessInput.getObjectBodyParams());
|
||||
@ -1143,7 +1144,10 @@ public class ApiImplementation
|
||||
ApiProcessOutputInterface output = apiProcessMetaData.getOutput();
|
||||
if(output != null)
|
||||
{
|
||||
return (new HttpApiResponse(output.getSuccessStatusCode(runProcessInput, runProcessOutput), output.getOutputForProcess(runProcessInput, runProcessOutput)));
|
||||
Serializable outputForProcess = output.getOutputForProcess(runProcessInput, runProcessOutput);
|
||||
HttpApiResponse httpApiResponse = new HttpApiResponse(output.getSuccessStatusCode(runProcessInput, runProcessOutput), outputForProcess);
|
||||
output.customizeHttpApiResponse(httpApiResponse, runProcessInput, runProcessOutput);
|
||||
return httpApiResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -816,7 +816,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
openAPI.getPaths().put(basePath + processApiPath, path);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// if the process can run async, then do the status checkin endpoitn //
|
||||
// if the process can run async, then do the status checkin endpoint //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
if(!ApiProcessMetaData.AsyncMode.NEVER.equals(apiProcessMetaData.getAsyncMode()))
|
||||
{
|
||||
@ -900,12 +900,27 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
String apiName = apiInstanceMetaData.getName();
|
||||
if(apiProcessInput != null)
|
||||
{
|
||||
/////////////////
|
||||
// path params //
|
||||
/////////////////
|
||||
ApiProcessInputFieldsContainer pathParams = apiProcessInput.getPathParams();
|
||||
if(pathParams != null)
|
||||
{
|
||||
for(QFieldMetaData field : CollectionUtils.nonNullList(pathParams.getFields()))
|
||||
{
|
||||
parameters.add(processFieldToParameter(apiInstanceMetaData, field).withIn("path"));
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// query string params //
|
||||
/////////////////////////
|
||||
ApiProcessInputFieldsContainer queryStringParams = apiProcessInput.getQueryStringParams();
|
||||
if(queryStringParams != null)
|
||||
{
|
||||
if(queryStringParams.getRecordIdsField() != null)
|
||||
{
|
||||
parameters.add(processFieldToParameter(apiInstanceMetaData, queryStringParams.getRecordIdsField()).withIn("query"));
|
||||
parameters.add(processFieldToParameter(apiInstanceMetaData, queryStringParams.getRecordIdsField()).withIn("path"));
|
||||
}
|
||||
|
||||
for(QFieldMetaData field : CollectionUtils.nonNullList(queryStringParams.getFields()))
|
||||
@ -914,6 +929,9 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Body as content //
|
||||
/////////////////////
|
||||
QFieldMetaData bodyField = apiProcessInput.getBodyField();
|
||||
if(bodyField != null)
|
||||
{
|
||||
@ -939,8 +957,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
content.withSchema(new Schema()
|
||||
.withDescription(bodyDescription)
|
||||
.withType("string")
|
||||
.withExample(exampleWithSingleValue.getValue())
|
||||
);
|
||||
.withExample(exampleWithSingleValue.getValue()));
|
||||
}
|
||||
|
||||
methodForProcess.withRequestBody(new RequestBody()
|
||||
@ -963,8 +980,9 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
.withIn("query")
|
||||
.withDescription("""
|
||||
Indicates if the job should be ran asynchronously.
|
||||
If false, or not specified, job is ran synchronously, and returns with response status of 207 (Multi-Status) or 204 (No Content).
|
||||
If true, request returns immediately with response status of 202 (Accepted).
|
||||
If false or not specified, then the job is ran synchronously and returns with an appropriate response status when completed.
|
||||
If true, then the request returns immediately with response status of 202 (Accepted), and a jobId in the response body,
|
||||
which can then be sent to the corresponding .../status/{jobId} endpoint to follow-up on the status of the job.
|
||||
""")
|
||||
.withExamples(MapBuilder.of(
|
||||
"false", new ExampleWithSingleValue().withValue(false).withSummary("Run the job synchronously."),
|
||||
@ -988,6 +1006,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
{
|
||||
responses.putAll(output.getSpecResponses(apiName));
|
||||
}
|
||||
|
||||
if(!ApiProcessMetaData.AsyncMode.NEVER.equals(apiProcessMetaData.getAsyncMode()))
|
||||
{
|
||||
responses.put(HttpStatus.ACCEPTED.getCode(), new Response()
|
||||
@ -1046,16 +1065,28 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
.withDescription("Get the status for a previous asynchronous call to the process named " + processMetaData.getLabel())
|
||||
.withSecurity(getSecurity(apiInstanceMetaData, processMetaData.getName()));
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// add the async input for optionally-async processes //
|
||||
////////////////////////////////////////////////////////
|
||||
methodForProcess.setParameters(ListBuilder.of(new Parameter()
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
ApiProcessInput apiProcessInput = apiProcessMetaData.getInput();
|
||||
ApiProcessInputFieldsContainer pathParams = apiProcessInput.getPathParams();
|
||||
if(pathParams != null)
|
||||
{
|
||||
for(QFieldMetaData field : CollectionUtils.nonNullList(pathParams.getFields()))
|
||||
{
|
||||
parameters.add(processFieldToParameter(apiInstanceMetaData, field).withIn("path"));
|
||||
}
|
||||
}
|
||||
|
||||
parameters.add(new Parameter()
|
||||
.withName("jobId")
|
||||
.withIn("path")
|
||||
.withRequired(true)
|
||||
.withDescription("Id of the job, as returned by the API call that started it.")
|
||||
.withSchema(new Schema().withType("string").withFormat("uuid"))
|
||||
));
|
||||
.withSchema(new Schema().withType("string").withFormat("uuid")));
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// add the async input for optionally-async processes //
|
||||
////////////////////////////////////////////////////////
|
||||
methodForProcess.setParameters(parameters);
|
||||
|
||||
//////////////////////////////////
|
||||
// build all possible responses //
|
||||
@ -1126,7 +1157,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
|
||||
{
|
||||
if(apiFieldMetaData.getExample() != null)
|
||||
{
|
||||
parameter.withExample(apiFieldMetaData.getExample());
|
||||
parameter.withExamples(Map.of("example", apiFieldMetaData.getExample()));
|
||||
}
|
||||
else if(apiFieldMetaData.getExamples() != null)
|
||||
{
|
||||
|
@ -316,6 +316,7 @@ public class QJavalinApiHandler
|
||||
ApiProcessInput input = apiProcessMetaData.getInput();
|
||||
if(input != null)
|
||||
{
|
||||
processProcessInputFieldsContainer(context, parameters, input.getPathParams(), Context::pathParam);
|
||||
processProcessInputFieldsContainer(context, parameters, input.getQueryStringParams(), Context::queryParam);
|
||||
processProcessInputFieldsContainer(context, parameters, input.getFormParams(), Context::formParam);
|
||||
|
||||
@ -347,9 +348,7 @@ public class QJavalinApiHandler
|
||||
//////////////////
|
||||
QJavalinAccessLogger.logEndSuccess();
|
||||
context.status(response.getStatusCode().getCode());
|
||||
String resultString = toJson(Objects.requireNonNullElse(response.getResponseBodyObject(), ""));
|
||||
context.result(resultString);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
||||
handleProcessResponse(context, response, apiLog);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
@ -360,6 +359,52 @@ public class QJavalinApiHandler
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void handleProcessResponse(Context context, HttpApiResponse response, APILog apiLog)
|
||||
{
|
||||
if(response.getNeedsFormattedAsJson())
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the response object says that we should format the response as json, then do so. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
String resultString = toJson(Objects.requireNonNullElse(response.getResponseBodyObject(), ""));
|
||||
context.result(resultString);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(StringUtils.hasContent(response.getContentType()))
|
||||
{
|
||||
context.contentType(response.getContentType());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// else, try to return it raw - as byte[], or String, or as a converted-to-String //
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
Serializable result = Objects.requireNonNullElse(response.getResponseBodyObject(), "");
|
||||
if(result instanceof byte[] ba)
|
||||
{
|
||||
context.result(ba);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody("Byte array of length: " + ba.length));
|
||||
}
|
||||
else if(result instanceof String s)
|
||||
{
|
||||
context.result(s);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(s));
|
||||
}
|
||||
else
|
||||
{
|
||||
String resultString = String.valueOf(result);
|
||||
context.result(resultString);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -381,9 +426,7 @@ public class QJavalinApiHandler
|
||||
//////////////////
|
||||
QJavalinAccessLogger.logEndSuccess();
|
||||
context.status(response.getStatusCode().getCode());
|
||||
String resultString = toJson(Objects.requireNonNullElse(response.getResponseBodyObject(), ""));
|
||||
context.result(resultString);
|
||||
storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString));
|
||||
handleProcessResponse(context, response, apiLog);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
|
@ -35,6 +35,14 @@ public class HttpApiResponse
|
||||
private HttpStatus.Code statusCode;
|
||||
private Serializable responseBodyObject;
|
||||
|
||||
private String contentType;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// by default - QJavalinApiHandler will format the responseBodyObject as JSON. //
|
||||
// set this field to false if you don't want it to do that (e.g., if your response is, say, a byte[]) //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
private boolean needsFormattedAsJson = true;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -119,4 +127,66 @@ public class HttpApiResponse
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for needsFormattedAsJson
|
||||
*******************************************************************************/
|
||||
public boolean getNeedsFormattedAsJson()
|
||||
{
|
||||
return (this.needsFormattedAsJson);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for needsFormattedAsJson
|
||||
*******************************************************************************/
|
||||
public void setNeedsFormattedAsJson(boolean needsFormattedAsJson)
|
||||
{
|
||||
this.needsFormattedAsJson = needsFormattedAsJson;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for needsFormattedAsJson
|
||||
*******************************************************************************/
|
||||
public HttpApiResponse withNeedsFormattedAsJson(boolean needsFormattedAsJson)
|
||||
{
|
||||
this.needsFormattedAsJson = needsFormattedAsJson;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for contentType
|
||||
*******************************************************************************/
|
||||
public String getContentType()
|
||||
{
|
||||
return (this.contentType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for contentType
|
||||
*******************************************************************************/
|
||||
public void setContentType(String contentType)
|
||||
{
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for contentType
|
||||
*******************************************************************************/
|
||||
public HttpApiResponse withContentType(String contentType)
|
||||
{
|
||||
this.contentType = contentType;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -30,6 +30,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
*******************************************************************************/
|
||||
public class ApiProcessInput
|
||||
{
|
||||
private ApiProcessInputFieldsContainer pathParams;
|
||||
private ApiProcessInputFieldsContainer queryStringParams;
|
||||
private ApiProcessInputFieldsContainer formParams;
|
||||
private ApiProcessInputFieldsContainer recordBodyParams;
|
||||
@ -44,6 +45,11 @@ public class ApiProcessInput
|
||||
*******************************************************************************/
|
||||
public String getRecordIdsParamName()
|
||||
{
|
||||
if(pathParams != null && pathParams.getRecordIdsField() != null)
|
||||
{
|
||||
return (pathParams.getRecordIdsField().getName());
|
||||
}
|
||||
|
||||
if(queryStringParams != null && queryStringParams.getRecordIdsField() != null)
|
||||
{
|
||||
return (queryStringParams.getRecordIdsField().getName());
|
||||
@ -217,4 +223,35 @@ public class ApiProcessInput
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for pathParams
|
||||
*******************************************************************************/
|
||||
public ApiProcessInputFieldsContainer getPathParams()
|
||||
{
|
||||
return (this.pathParams);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for pathParams
|
||||
*******************************************************************************/
|
||||
public void setPathParams(ApiProcessInputFieldsContainer pathParams)
|
||||
{
|
||||
this.pathParams = pathParams;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for pathParams
|
||||
*******************************************************************************/
|
||||
public ApiProcessInput withPathParams(ApiProcessInputFieldsContainer pathParams)
|
||||
{
|
||||
this.pathParams = pathParams;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ package com.kingsrook.qqq.api.model.metadata.processes;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.api.model.actions.HttpApiResponse;
|
||||
import com.kingsrook.qqq.api.model.openapi.Response;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
@ -61,4 +62,13 @@ public interface ApiProcessOutputInterface
|
||||
.withDescription("Process has been successfully executed.")
|
||||
));
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
default void customizeHttpApiResponse(HttpApiResponse httpApiResponse, RunProcessInput runProcessInput, RunProcessOutput runProcessOutput) throws QException
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -34,9 +34,11 @@ import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||
import com.kingsrook.qqq.backend.core.logging.LogPair;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.Pair;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
@ -162,9 +164,18 @@ public class ApiProcessUtils
|
||||
*******************************************************************************/
|
||||
public static String getProcessApiPath(QInstance qInstance, QProcessMetaData process, ApiProcessMetaData apiProcessMetaData, ApiInstanceMetaData apiInstanceMetaData)
|
||||
{
|
||||
StringBuilder pathParams = new StringBuilder();
|
||||
if(ObjectUtils.ifCan(() -> CollectionUtils.nullSafeHasContents(apiProcessMetaData.getInput().getPathParams().getFields())))
|
||||
{
|
||||
for(QFieldMetaData field : apiProcessMetaData.getInput().getPathParams().getFields())
|
||||
{
|
||||
pathParams.append("/{").append(field.getName()).append("}");
|
||||
}
|
||||
}
|
||||
|
||||
if(StringUtils.hasContent(apiProcessMetaData.getPath()))
|
||||
{
|
||||
return apiProcessMetaData.getPath() + "/" + apiProcessMetaData.getApiProcessName();
|
||||
return apiProcessMetaData.getPath() + "/" + apiProcessMetaData.getApiProcessName() + pathParams;
|
||||
}
|
||||
else if(StringUtils.hasContent(process.getTableName()))
|
||||
{
|
||||
@ -182,11 +193,11 @@ public class ApiProcessUtils
|
||||
}
|
||||
}
|
||||
}
|
||||
return tablePathPart + "/" + apiProcessMetaData.getApiProcessName();
|
||||
return tablePathPart + "/" + apiProcessMetaData.getApiProcessName() + pathParams;
|
||||
}
|
||||
else
|
||||
{
|
||||
return apiProcessMetaData.getApiProcessName();
|
||||
return apiProcessMetaData.getApiProcessName() + pathParams;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.api;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
@ -56,7 +57,7 @@ public class BaseTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
void baseBeforeEach()
|
||||
void baseBeforeEach() throws QException
|
||||
{
|
||||
QContext.init(TestUtils.defineInstance(), new QSession());
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ package com.kingsrook.qqq.api.javalin;
|
||||
import com.kingsrook.qqq.api.BaseTest;
|
||||
import com.kingsrook.qqq.api.TestUtils;
|
||||
import com.kingsrook.qqq.api.actions.ApiImplementation;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.PermissionLevel;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
@ -61,7 +60,7 @@ class QJavalinApiHandlerPermissionsTest extends BaseTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeAll
|
||||
static void beforeAll() throws QInstanceValidationException
|
||||
static void beforeAll() throws Exception
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
|
||||
|
@ -36,7 +36,6 @@ import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
@ -51,6 +50,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.savedreports.SavedReport;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
|
||||
@ -66,6 +67,7 @@ import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static com.kingsrook.qqq.api.TestUtils.insertPersonRecord;
|
||||
import static com.kingsrook.qqq.api.TestUtils.insertSavedReport;
|
||||
import static com.kingsrook.qqq.api.TestUtils.insertSimpsons;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
@ -95,7 +97,7 @@ class QJavalinApiHandlerTest extends BaseTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeAll
|
||||
static void beforeAll() throws QInstanceValidationException
|
||||
static void beforeAll() throws Exception
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
|
||||
@ -1404,6 +1406,54 @@ class QJavalinApiHandlerTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testGetProcessRenderSavedReport() throws QException
|
||||
{
|
||||
insertSimpsons();
|
||||
Integer reportId = insertSavedReport(new SavedReport()
|
||||
.withLabel("Person Report")
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON)
|
||||
.withQueryFilterJson(JsonUtils.toJson(new QQueryFilter()))
|
||||
.withColumnsJson("""
|
||||
{"columns":[
|
||||
{"name": "id"},
|
||||
{"name": "firstName"},
|
||||
{"name": "lastName"}
|
||||
]}
|
||||
"""));
|
||||
|
||||
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/savedReport/renderSavedReport/" + reportId + "?reportFormat=CSV").asString();
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertThat(response.getHeaders().getFirst("content-type")).contains("csv");
|
||||
assertEquals("""
|
||||
"Id","First Name","Last Name"
|
||||
"1","Homer","Simpson"
|
||||
"2","Marge","Simpson"
|
||||
"3","Bart","Simpson"
|
||||
"4","Lisa","Simpson"
|
||||
"5","Maggie","Simpson"
|
||||
""", response.getBody());
|
||||
|
||||
response = Unirest.get(BASE_URL + "/api/" + VERSION + "/savedReport/renderSavedReport/" + reportId + "?reportFormat=JSON").asString();
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertThat(response.getHeaders().getFirst("content-type")).contains("json");
|
||||
JSONArray jsonArray = new JSONArray(response.getBody());
|
||||
assertEquals(5, jsonArray.length());
|
||||
assertThat(jsonArray.getJSONObject(0).toMap())
|
||||
.hasFieldOrPropertyWithValue("id", 1)
|
||||
.hasFieldOrPropertyWithValue("firstName", "Homer")
|
||||
.hasFieldOrPropertyWithValue("lastName", "Simpson");
|
||||
|
||||
response = Unirest.get(BASE_URL + "/api/" + VERSION + "/savedReport/renderSavedReport/" + reportId + "?reportFormat=XLSX").asString();
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertThat(response.getHeaders().getFirst("content-type")).contains("openxmlformats-officedocument.spreadsheetml");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
Reference in New Issue
Block a user