diff --git a/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/actions/ApiImplementation.java b/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/actions/ApiImplementation.java index 56444250..84137b7d 100644 --- a/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/actions/ApiImplementation.java +++ b/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/actions/ApiImplementation.java @@ -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 { diff --git a/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/actions/GenerateOpenApiSpecAction.java b/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/actions/GenerateOpenApiSpecAction.java index a4ad7378..5644e4f2 100644 --- a/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/actions/GenerateOpenApiSpecAction.java +++ b/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/actions/GenerateOpenApiSpecAction.java @@ -816,7 +816,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction 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 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; } } diff --git a/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/BaseTest.java b/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/BaseTest.java index 2d7fd5b3..e77c0119 100644 --- a/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/BaseTest.java +++ b/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/BaseTest.java @@ -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()); } diff --git a/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/javalin/QJavalinApiHandlerPermissionsTest.java b/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/javalin/QJavalinApiHandlerPermissionsTest.java index 680c850d..c884d47c 100644 --- a/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/javalin/QJavalinApiHandlerPermissionsTest.java +++ b/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/javalin/QJavalinApiHandlerPermissionsTest.java @@ -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(); diff --git a/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/javalin/QJavalinApiHandlerTest.java b/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/javalin/QJavalinApiHandlerTest.java index f495f51c..8407541b 100644 --- a/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/javalin/QJavalinApiHandlerTest.java +++ b/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/javalin/QJavalinApiHandlerTest.java @@ -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 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"); + } + + + /******************************************************************************* ** *******************************************************************************/