QQQ-21 feedback from code review

This commit is contained in:
2022-07-12 09:18:01 -05:00
parent 492ee321a1
commit 2cb7eef679
5 changed files with 104 additions and 48 deletions

View File

@ -53,7 +53,7 @@
<dependency> <dependency>
<groupId>com.kingsrook.qqq</groupId> <groupId>com.kingsrook.qqq</groupId>
<artifactId>qqq-backend-core</artifactId> <artifactId>qqq-backend-core</artifactId>
<version>0.1.0-20220711.141150-7</version> <version>0.1.0-20220712.140206-8</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.kingsrook.qqq</groupId> <groupId>com.kingsrook.qqq</groupId>

View File

@ -98,13 +98,14 @@ public class QJavalinImplementation
private static final int SESSION_COOKIE_AGE = 60 * 60 * 24; private static final int SESSION_COOKIE_AGE = 60 * 60 * 24;
protected static QInstance qInstance; static QInstance qInstance;
private static int DEFAULT_PORT = 8001; private static int DEFAULT_PORT = 8001;
private static Javalin service; private static Javalin service;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -153,6 +154,7 @@ public class QJavalinImplementation
} }
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -363,6 +365,9 @@ public class QJavalinImplementation
setupSession(context, queryRequest); setupSession(context, queryRequest);
queryRequest.setTableName(tableName); queryRequest.setTableName(tableName);
// todo - validate that the primary key is of the proper type (e.g,. not a string for an id field)
// and throw a 400-series error (tell the user bad-request), rather than, we're doing a 500 (server error)
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// setup a filter for the primaryKey = the path-pram // // setup a filter for the primaryKey = the path-pram //
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////

View File

@ -53,8 +53,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.state.StateType;
import com.kingsrook.qqq.backend.core.state.UUIDAndTypeStateKey;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.ExceptionUtils; import com.kingsrook.qqq.backend.core.utils.ExceptionUtils;
import com.kingsrook.qqq.backend.core.utils.JsonUtils; import com.kingsrook.qqq.backend.core.utils.JsonUtils;
@ -145,29 +143,27 @@ public class QJavalinProcessHandler
runProcessRequest.setStartAfterStep(startAfterStep); runProcessRequest.setStartAfterStep(startAfterStep);
populateRunProcessRequestWithValuesFromContext(context, runProcessRequest); populateRunProcessRequestWithValuesFromContext(context, runProcessRequest);
try ////////////////////////////////////////
// run the process as an async action //
////////////////////////////////////////
Integer timeout = getTimeoutMillis(context);
RunProcessResult runProcessResult = new AsyncJobManager().startJob(timeout, TimeUnit.MILLISECONDS, (callback) ->
{ {
//////////////////////////////////////// runProcessRequest.setAsyncJobCallback(callback);
// run the process as an async action // return (new RunProcessAction().execute(runProcessRequest));
//////////////////////////////////////// });
Integer timeout = getTimeoutMillis(context);
RunProcessResult runProcessResult = new AsyncJobManager().startJob(timeout, TimeUnit.MILLISECONDS, (callback) ->
{
runProcessRequest.setAsyncJobCallback(callback);
return (new RunProcessAction().execute(runProcessRequest));
});
LOG.info("Process result error? " + runProcessResult.getException()); LOG.info("Process result error? " + runProcessResult.getException());
for(QFieldMetaData outputField : QJavalinImplementation.qInstance.getProcess(runProcessRequest.getProcessName()).getOutputFields()) for(QFieldMetaData outputField : QJavalinImplementation.qInstance.getProcess(runProcessRequest.getProcessName()).getOutputFields())
{
LOG.info("Process result output value: " + outputField.getName() + ": " + runProcessResult.getValues().get(outputField.getName()));
}
serializeRunProcessResultForCaller(resultForCaller, runProcessResult);
}
catch(JobGoingAsyncException jgae)
{ {
resultForCaller.put("jobUUID", jgae.getJobUUID()); LOG.info("Process result output value: " + outputField.getName() + ": " + runProcessResult.getValues().get(outputField.getName()));
} }
serializeRunProcessResultForCaller(resultForCaller, runProcessResult);
}
catch(JobGoingAsyncException jgae)
{
resultForCaller.put("jobUUID", jgae.getJobUUID());
} }
catch(Exception e) catch(Exception e)
{ {
@ -199,7 +195,7 @@ public class QJavalinProcessHandler
serializeRunProcessExceptionForCaller(resultForCaller, runProcessResult.getException().get()); serializeRunProcessExceptionForCaller(resultForCaller, runProcessResult.getException().get());
} }
resultForCaller.put("values", runProcessResult.getValues()); resultForCaller.put("values", runProcessResult.getValues());
runProcessResult.getProcessState().getNextStepName().ifPresent(lastStep -> resultForCaller.put("nextStep", lastStep)); runProcessResult.getProcessState().getNextStepName().ifPresent(nextStep -> resultForCaller.put("nextStep", nextStep));
} }
@ -297,13 +293,13 @@ public class QJavalinProcessHandler
{ {
case "recordIds": case "recordIds":
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
Serializable[] idStrings = context.queryParam(recordsParam).split(","); Serializable[] idStrings = paramValue.split(",");
return (new QQueryFilter().withCriteria(new QFilterCriteria() return (new QQueryFilter().withCriteria(new QFilterCriteria()
.withFieldName(primaryKeyField) .withFieldName(primaryKeyField)
.withOperator(QCriteriaOperator.IN) .withOperator(QCriteriaOperator.IN)
.withValues(Arrays.stream(idStrings).toList()))); .withValues(Arrays.stream(idStrings).toList())));
case "filterJSON": case "filterJSON":
return (JsonUtils.toObject(context.queryParam(recordsParam), QQueryFilter.class)); return (JsonUtils.toObject(paramValue, QQueryFilter.class));
case "filterId": case "filterId":
// return (JsonUtils.toObject(context.queryParam(recordsParam), QQueryFilter.class)); // return (JsonUtils.toObject(context.queryParam(recordsParam), QQueryFilter.class));
throw (new NotImplementedException("Saved filters are not yet implemented.")); throw (new NotImplementedException("Saved filters are not yet implemented."));
@ -335,19 +331,20 @@ public class QJavalinProcessHandler
*******************************************************************************/ *******************************************************************************/
public static void processStatus(Context context) public static void processStatus(Context context)
{ {
Map<String, Object> resultForCaller = new HashMap<>();
String processUUID = context.pathParam("processUUID"); String processUUID = context.pathParam("processUUID");
String jobUUID = context.pathParam("jobUUID"); String jobUUID = context.pathParam("jobUUID");
LOG.info("Request for status of job " + jobUUID); LOG.info("Request for status of process " + processUUID + ", job " + jobUUID);
Optional<AsyncJobStatus> optionalJobStatus = new AsyncJobManager().getJobStatus(jobUUID); Optional<AsyncJobStatus> optionalJobStatus = new AsyncJobManager().getJobStatus(jobUUID);
if(optionalJobStatus.isEmpty()) if(optionalJobStatus.isEmpty())
{ {
QJavalinImplementation.handleException(context, new RuntimeException("Could not find status of process step job")); serializeRunProcessExceptionForCaller(resultForCaller, new RuntimeException("Could not find status of process step job"));
} }
else else
{ {
Map<String, Object> resultForCaller = new HashMap<>(); AsyncJobStatus jobStatus = optionalJobStatus.get();
AsyncJobStatus jobStatus = optionalJobStatus.get();
resultForCaller.put("jobStatus", jobStatus); resultForCaller.put("jobStatus", jobStatus);
LOG.info("Job status is " + jobStatus.getState() + " for " + jobUUID); LOG.info("Job status is " + jobStatus.getState() + " for " + jobUUID);
@ -358,7 +355,7 @@ public class QJavalinProcessHandler
// if the job is complete, get the process result from state provider, and return it // // if the job is complete, get the process result from state provider, and return it //
// this output should look like it did if the job finished synchronously!! // // this output should look like it did if the job finished synchronously!! //
/////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////
Optional<ProcessState> processState = RunProcessAction.getStateProvider().get(ProcessState.class, new UUIDAndTypeStateKey(UUID.fromString(processUUID), StateType.PROCESS_STATUS)); Optional<ProcessState> processState = RunProcessAction.getState(processUUID);
if(processState.isPresent()) if(processState.isPresent())
{ {
RunProcessResult runProcessResult = new RunProcessResult(processState.get()); RunProcessResult runProcessResult = new RunProcessResult(processState.get());
@ -366,7 +363,7 @@ public class QJavalinProcessHandler
} }
else else
{ {
QJavalinImplementation.handleException(context, new RuntimeException("Could not find process results")); serializeRunProcessExceptionForCaller(resultForCaller, new RuntimeException("Could not find results for process " + processUUID));
} }
} }
else if(jobStatus.getState().equals(AsyncJobState.ERROR)) else if(jobStatus.getState().equals(AsyncJobState.ERROR))
@ -379,9 +376,9 @@ public class QJavalinProcessHandler
serializeRunProcessExceptionForCaller(resultForCaller, jobStatus.getCaughtException()); serializeRunProcessExceptionForCaller(resultForCaller, jobStatus.getCaughtException());
} }
} }
context.result(JsonUtils.toJson(resultForCaller));
} }
context.result(JsonUtils.toJson(resultForCaller));
} }
@ -397,7 +394,9 @@ public class QJavalinProcessHandler
Integer skip = Objects.requireNonNullElse(QJavalinImplementation.integerQueryParam(context, "skip"), 0); Integer skip = Objects.requireNonNullElse(QJavalinImplementation.integerQueryParam(context, "skip"), 0);
Integer limit = Objects.requireNonNullElse(QJavalinImplementation.integerQueryParam(context, "limit"), 20); Integer limit = Objects.requireNonNullElse(QJavalinImplementation.integerQueryParam(context, "limit"), 20);
Optional<ProcessState> optionalProcessState = RunProcessAction.getStateProvider().get(ProcessState.class, new UUIDAndTypeStateKey(UUID.fromString(processUUID), StateType.PROCESS_STATUS)); // todo - potential optimization - if a future state provider could take advantage of it,
// we might pass the skip & limit in to a method that fetch just those 'n' rows from state, rather than the whole thing?
Optional<ProcessState> optionalProcessState = RunProcessAction.getState(processUUID);
if(optionalProcessState.isEmpty()) if(optionalProcessState.isEmpty())
{ {
throw (new Exception("Could not find process results.")); throw (new Exception("Could not find process results."));
@ -411,7 +410,7 @@ public class QJavalinProcessHandler
} }
Map<String, Object> resultForCaller = new HashMap<>(); Map<String, Object> resultForCaller = new HashMap<>();
List<QRecord> recordPage = CollectionUtils.safelyGetPage(records, skip, limit); List<QRecord> recordPage = CollectionUtils.safelyGetPage(records, skip, limit);
resultForCaller.put("records", recordPage); resultForCaller.put("records", recordPage);
context.result(JsonUtils.toJson(resultForCaller)); context.result(JsonUtils.toJson(resultForCaller));
} }

View File

@ -47,7 +47,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
*******************************************************************************/ *******************************************************************************/
class QJavalinProcessHandlerTest extends QJavalinTestBase class QJavalinProcessHandlerTest extends QJavalinTestBase
{ {
private static final int MORE_THAN_TIMEOUT = 1000; private static final int MORE_THAN_TIMEOUT = 500;
private static final int LESS_THAN_TIMEOUT = 50; private static final int LESS_THAN_TIMEOUT = 50;
@ -96,8 +96,9 @@ class QJavalinProcessHandlerTest extends QJavalinTestBase
assertEquals(200, response.getStatus()); assertEquals(200, response.getStatus());
JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody()); JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
assertNotNull(jsonObject); assertNotNull(jsonObject);
String processUUID = jsonObject.getString("processUUID");
// todo - once we know how to get records from a process, add that call getProcessRecords(processUUID, 2);
} }
@ -120,8 +121,45 @@ class QJavalinProcessHandlerTest extends QJavalinTestBase
assertEquals(200, response.getStatus()); assertEquals(200, response.getStatus());
JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody()); JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
assertNotNull(jsonObject); assertNotNull(jsonObject);
String processUUID = jsonObject.getString("processUUID");
// todo - once we know how to get records from a process, add that call getProcessRecords(processUUID, 3);
}
/*******************************************************************************
**
*******************************************************************************/
private JSONObject getProcessRecords(String processUUID, int expectedNoOfRecords)
{
return (getProcessRecords(processUUID, expectedNoOfRecords, 0, 20));
}
/*******************************************************************************
**
*******************************************************************************/
private JSONObject getProcessRecords(String processUUID, int expectedNoOfRecords, int skip, int limit)
{
HttpResponse<String> response;
JSONObject jsonObject;
response = Unirest.get(BASE_URL + "/processes/greet/" + processUUID + "/records?skip=" + skip + "&limit=" + limit).asString();
jsonObject = JsonUtils.toJSONObject(response.getBody());
assertNotNull(jsonObject);
if(expectedNoOfRecords == 0)
{
assertFalse(jsonObject.has("records"));
}
else
{
assertTrue(jsonObject.has("records"));
JSONArray records = jsonObject.getJSONArray("records");
assertEquals(expectedNoOfRecords, records.length());
}
return (jsonObject);
} }
@ -133,7 +171,7 @@ class QJavalinProcessHandlerTest extends QJavalinTestBase
@Test @Test
public void test_processGreetInitWithQueryValues() public void test_processGreetInitWithQueryValues()
{ {
HttpResponse<String> response = Unirest.get(BASE_URL + "/processes/greet/init?recordsParam=recordIds&recordIds=2,3&greetingPrefix=Hey&greetingSuffix=Jude").asString(); HttpResponse<String> response = Unirest.post(BASE_URL + "/processes/greet/init?recordsParam=recordIds&recordIds=2,3&greetingPrefix=Hey&greetingSuffix=Jude").asString();
assertEquals(200, response.getStatus()); assertEquals(200, response.getStatus());
JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody()); JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
assertNotNull(jsonObject); assertNotNull(jsonObject);
@ -321,7 +359,7 @@ class QJavalinProcessHandlerTest extends QJavalinTestBase
/******************************************************************************* /*******************************************************************************
** every time a process step (sync or async) has gone async, expect what the ** every time a process step (or init) has gone async, expect what the
** response should look like ** response should look like
*******************************************************************************/ *******************************************************************************/
private JSONObject assertProcessStepWentAsyncResponse(HttpResponse<String> response) private JSONObject assertProcessStepWentAsyncResponse(HttpResponse<String> response)
@ -412,16 +450,30 @@ class QJavalinProcessHandlerTest extends QJavalinTestBase
assertNotNull(jsonObject); assertNotNull(jsonObject);
String processUUID = jsonObject.getString("processUUID"); String processUUID = jsonObject.getString("processUUID");
response = Unirest.get(BASE_URL + "/processes/greet/" + processUUID + "/records").asString(); jsonObject = getProcessRecords(processUUID, 2);
jsonObject = JsonUtils.toJSONObject(response.getBody()); JSONArray records = jsonObject.getJSONArray("records");
assertNotNull(jsonObject);
assertTrue(jsonObject.has("records"));
JSONArray records = jsonObject.getJSONArray("records");
assertEquals(2, records.length());
JSONObject record0 = records.getJSONObject(0); JSONObject record0 = records.getJSONObject(0);
JSONObject values = record0.getJSONObject("values"); JSONObject values = record0.getJSONObject("values");
assertTrue(values.has("id")); assertTrue(values.has("id"));
assertTrue(values.has("firstName")); assertTrue(values.has("firstName"));
} }
/*******************************************************************************
** test getting records back from a process with skip & Limit
**
*******************************************************************************/
@Test
public void test_processRecordsSkipAndLimit()
{
HttpResponse<String> response = Unirest.get(BASE_URL + "/processes/greet/init?recordsParam=recordIds&recordIds=1,2,3,4,5").asString();
JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
String processUUID = jsonObject.getString("processUUID");
getProcessRecords(processUUID, 5);
getProcessRecords(processUUID, 1, 4, 5);
getProcessRecords(processUUID, 0, 5, 5);
}
} }

View File

@ -47,7 +47,7 @@ public class QJavalinTestBase
public static void beforeAll() public static void beforeAll()
{ {
qJavalinImplementation = new QJavalinImplementation(TestUtils.defineInstance()); qJavalinImplementation = new QJavalinImplementation(TestUtils.defineInstance());
QJavalinProcessHandler.setAsyncStepTimeoutMillis(500); QJavalinProcessHandler.setAsyncStepTimeoutMillis(250);
qJavalinImplementation.startJavalinServer(PORT); qJavalinImplementation.startJavalinServer(PORT);
} }