From 264b436a62b87c1483977da905390b046b383052 Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Thu, 20 Feb 2020 00:39:51 +0200 Subject: [PATCH 01/18] Support querying workflows from both main tables and archive. --- .../internal/dao/WorkflowInstanceDao.java | 60 ++++++++++++------- .../internal/workflow/StateExecutionImpl.java | 1 + .../service/WorkflowInstanceService.java | 24 +++++++- .../instance/QueryWorkflowInstances.java | 19 ++++++ .../workflow/instance/WorkflowInstance.java | 18 ++++++ .../internal/dao/WorkflowInstanceDaoTest.java | 7 ++- .../java/io/nflow/rest/v1/ResourceBase.java | 7 ++- .../v1/jaxrs/WorkflowInstanceResource.java | 10 ++-- .../jaxrs/WorkflowInstanceResourceTest.java | 28 +++++---- .../springweb/WorkflowInstanceResource.java | 10 ++-- 10 files changed, 136 insertions(+), 48 deletions(-) diff --git a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java index c13f31e8b..3243b1da4 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java +++ b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java @@ -4,6 +4,8 @@ import static io.nflow.engine.internal.dao.DaoUtil.getInt; import static io.nflow.engine.internal.dao.DaoUtil.getLong; import static io.nflow.engine.internal.dao.DaoUtil.toTimestamp; +import static io.nflow.engine.internal.dao.TablePrefix.ARCHIVE; +import static io.nflow.engine.internal.dao.TablePrefix.MAIN; import static io.nflow.engine.workflow.instance.WorkflowInstance.WorkflowInstanceStatus.created; import static io.nflow.engine.workflow.instance.WorkflowInstance.WorkflowInstanceStatus.executing; import static io.nflow.engine.workflow.instance.WorkflowInstance.WorkflowInstanceStatus.inProgress; @@ -14,6 +16,7 @@ import static java.util.Collections.sort; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toList; +import static java.util.stream.Stream.concat; import static java.util.stream.Stream.empty; import static org.apache.commons.lang3.StringUtils.abbreviate; import static org.apache.commons.lang3.StringUtils.join; @@ -505,23 +508,27 @@ private boolean addExpectedStatesToQueryAndUpdate(StringBuilder sql, long workfl } public WorkflowInstance getWorkflowInstance(long id, Set includes, Long maxActions) { - String sql = "select * from nflow_workflow where id = ?"; + return getWorkflowInstance(id, includes, maxActions, MAIN); + } + + public WorkflowInstance getWorkflowInstance(long id, Set includes, Long maxActions, TablePrefix tablePrefix) { + String sql = "select * from " + tablePrefix.nameOf("workflow") + " where id = ?"; WorkflowInstance instance = jdbc.queryForObject(sql, new WorkflowInstanceRowMapper(), id).build(); if (includes.contains(WorkflowInstanceInclude.CURRENT_STATE_VARIABLES)) { - fillState(instance); + fillState(instance, tablePrefix); } if (includes.contains(WorkflowInstanceInclude.CHILD_WORKFLOW_IDS)) { - fillChildWorkflowIds(instance); + fillChildWorkflowIds(instance, tablePrefix); } if (includes.contains(WorkflowInstanceInclude.ACTIONS)) { - fillActions(instance, includes.contains(WorkflowInstanceInclude.ACTION_STATE_VARIABLES), maxActions); + fillActions(instance, includes.contains(WorkflowInstanceInclude.ACTION_STATE_VARIABLES), maxActions, tablePrefix); } return instance; } - private void fillState(final WorkflowInstance instance) { - jdbc.query("select outside.state_key, outside.state_value from nflow_workflow_state outside inner join " - + "(select workflow_id, max(action_id) action_id, state_key from nflow_workflow_state where workflow_id = ? group by workflow_id, state_key) inside " + private void fillState(final WorkflowInstance instance, TablePrefix tablePrefix) { + jdbc.query("select outside.state_key, outside.state_value from " + tablePrefix.nameOf("workflow_state") + " outside inner join " + + "(select workflow_id, max(action_id) action_id, state_key from " + tablePrefix.nameOf("workflow_state") + " where workflow_id = ? group by workflow_id, state_key) inside " + "on outside.workflow_id = inside.workflow_id and outside.action_id = inside.action_id and outside.state_key = inside.state_key", rs -> { instance.stateVariables.put(rs.getString(1), rs.getString(2)); @@ -626,7 +633,6 @@ public List queryWorkflowInstances(QueryWorkflowInstances quer } public Stream queryWorkflowInstancesAsStream(QueryWorkflowInstances query) { - String sql = "select * from nflow_workflow "; List conditions = new ArrayList<>(); MapSqlParameterSource params = new MapSqlParameterSource(); conditions.add(executorInfo.getExecutorGroupCondition()); @@ -665,25 +671,35 @@ public Stream queryWorkflowInstancesAsStream(QueryWorkflowInst } conditions.add("executor_group = :executor_group"); params.addValue("executor_group", executorInfo.getExecutorGroup()); - sql += " where " + collectionToDelimitedString(conditions, " and ") + " order by id desc"; - sql = sqlVariants.limit(sql, getMaxResults(query.maxResults)); + String sqlQuery = " where " + collectionToDelimitedString(conditions, " and ") + " order by id desc"; + + long maxResults = getMaxResults(query.maxResults); + String sql = sqlVariants.limit("select * from " + MAIN.nameOf("workflow") + sqlQuery, maxResults); + List results = namedJdbc.query(sql, params, new WorkflowInstanceRowMapper()); + maxResults -= results.size(); + Stream resultStream = results.stream(); + + if (query.queryArchive && maxResults > 0) { + sql = sqlVariants.limit("select * from " + ARCHIVE.nameOf("workflow") + sqlQuery, maxResults); + resultStream = concat(resultStream, namedJdbc.query(sql, params, new WorkflowInstanceRowMapper()).stream() + .peek(builder -> builder.setArchived(true))); + } - Stream ret = namedJdbc.query(sql, params, new WorkflowInstanceRowMapper()).stream() - .map(WorkflowInstance.Builder::build); + Stream ret = resultStream.map(WorkflowInstance.Builder::build); if (query.includeCurrentStateVariables) { - ret = ret.peek(instance -> fillState(instance)); + ret = ret.peek(instance -> fillState(instance, instance.isArchived ? ARCHIVE : MAIN)); } if (query.includeActions) { - ret = ret.peek(instance -> fillActions(instance, query.includeActionStateVariables, query.maxActions)); + ret = ret.peek(instance -> fillActions(instance, query.includeActionStateVariables, query.maxActions, instance.isArchived ? ARCHIVE : MAIN)); } if (query.includeChildWorkflows) { - ret = ret.peek(instance -> fillChildWorkflowIds(instance)); + ret = ret.peek(instance -> fillChildWorkflowIds(instance, instance.isArchived ? ARCHIVE : MAIN)); } return ret; } - private void fillChildWorkflowIds(final WorkflowInstance instance) { - jdbc.query("select parent_action_id, id from nflow_workflow where parent_workflow_id = ?", rs -> { + private void fillChildWorkflowIds(final WorkflowInstance instance, TablePrefix tablePrefix) { + jdbc.query("select parent_action_id, id from " + tablePrefix.nameOf("workflow") + " where parent_workflow_id = ?", rs -> { long parentActionId = rs.getLong(1); long childWorkflowInstanceId = rs.getLong(2); List children = instance.childWorkflows.computeIfAbsent(parentActionId, k -> new ArrayList<>()); @@ -698,11 +714,11 @@ private long getMaxResults(Long maxResults) { return min(maxResults, workflowInstanceQueryMaxResults); } - private void fillActions(WorkflowInstance instance, boolean includeStateVariables, Long maxActions) { - Map> actionStates = includeStateVariables ? fetchActionStateVariables(instance) + private void fillActions(WorkflowInstance instance, boolean includeStateVariables, Long maxActions, TablePrefix tablePrefix) { + Map> actionStates = includeStateVariables ? fetchActionStateVariables(instance, tablePrefix) : EMPTY_ACTION_STATE_MAP; String sql = sqlVariants.limit( - "select nflow_workflow_action.* from nflow_workflow_action where workflow_id = ? order by id desc", + "select nflow_workflow_action.* from " + tablePrefix.nameOf("workflow_action") + " where workflow_id = ? order by id desc", getMaxActions(maxActions)); instance.actions.addAll(jdbc.query(sql, new WorkflowInstanceActionRowMapper(sqlVariants, actionStates), instance.id)); } @@ -714,8 +730,8 @@ private long getMaxActions(Long maxActions) { return min(maxActions, workflowInstanceQueryMaxActions); } - private Map> fetchActionStateVariables(WorkflowInstance instance) { - return jdbc.query("select * from nflow_workflow_state where workflow_id = ? order by action_id, state_key asc", + private Map> fetchActionStateVariables(WorkflowInstance instance, TablePrefix tablePrefix) { + return jdbc.query("select * from " + tablePrefix.nameOf("workflow_state") + " where workflow_id = ? order by action_id, state_key asc", new WorkflowActionStateRowMapper(), instance.id); } diff --git a/nflow-engine/src/main/java/io/nflow/engine/internal/workflow/StateExecutionImpl.java b/nflow-engine/src/main/java/io/nflow/engine/internal/workflow/StateExecutionImpl.java index 79b52d583..8cb811351 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/internal/workflow/StateExecutionImpl.java +++ b/nflow-engine/src/main/java/io/nflow/engine/internal/workflow/StateExecutionImpl.java @@ -1,6 +1,7 @@ package io.nflow.engine.internal.workflow; import static java.util.Collections.unmodifiableList; +import static java.util.stream.Collectors.toList; import static org.joda.time.DateTime.now; import static org.slf4j.LoggerFactory.getLogger; import static org.springframework.util.Assert.notNull; diff --git a/nflow-engine/src/main/java/io/nflow/engine/service/WorkflowInstanceService.java b/nflow-engine/src/main/java/io/nflow/engine/service/WorkflowInstanceService.java index 2952d9468..5ff36dbf4 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/service/WorkflowInstanceService.java +++ b/nflow-engine/src/main/java/io/nflow/engine/service/WorkflowInstanceService.java @@ -1,6 +1,8 @@ package io.nflow.engine.service; import static java.util.Collections.emptySet; +import static io.nflow.engine.internal.dao.TablePrefix.ARCHIVE; +import static io.nflow.engine.internal.dao.TablePrefix.MAIN; import static org.slf4j.LoggerFactory.getLogger; import static org.springframework.util.StringUtils.isEmpty; @@ -57,12 +59,32 @@ public WorkflowInstanceService(WorkflowInstanceDao workflowInstanceDao, Workflow */ public WorkflowInstance getWorkflowInstance(long id, Set includes, Long maxActions) { try { - return workflowInstanceDao.getWorkflowInstance(id, includes, maxActions); + return getWorkflowInstance(id, includes, maxActions, false); } catch (EmptyResultDataAccessException e) { throw new NflowNotFoundException("Workflow instance", id, e); } } + /** + * Return the workflow instance matching the given id. + * @param id Workflow instance id. + * @param includes Set of properties to be loaded. + * @param maxActions Maximum number of actions to be loaded. + * @param queryArchive Query archive if not found from main tables. + * @return The workflow instance + * @throws EmptyResultDataAccessException if not found + */ + public WorkflowInstance getWorkflowInstance(long id, Set includes, Long maxActions, boolean queryArchive) { + try { + return workflowInstanceDao.getWorkflowInstance(id, includes, maxActions, MAIN); + } catch (EmptyResultDataAccessException ex) { + if (queryArchive) { + return workflowInstanceDao.getWorkflowInstance(id, includes, maxActions, ARCHIVE); + } + throw new NflowNotFoundException("Workflow instance", id, ex); + } + } + /** * Insert the workflow instance to the database and return the id of the * instance. If the instance already exists, return the id of the existing diff --git a/nflow-engine/src/main/java/io/nflow/engine/workflow/instance/QueryWorkflowInstances.java b/nflow-engine/src/main/java/io/nflow/engine/workflow/instance/QueryWorkflowInstances.java index c298c81f7..874af6691 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/workflow/instance/QueryWorkflowInstances.java +++ b/nflow-engine/src/main/java/io/nflow/engine/workflow/instance/QueryWorkflowInstances.java @@ -85,6 +85,11 @@ public class QueryWorkflowInstances extends ModelObject { */ public final Long maxActions; + /** + * When set also the workflow archive will be queried. + */ + public final boolean queryArchive; + QueryWorkflowInstances(Builder builder) { this.ids = new ArrayList<>(builder.ids); this.types = new ArrayList<>(builder.types); @@ -100,6 +105,7 @@ public class QueryWorkflowInstances extends ModelObject { this.includeChildWorkflows = builder.includeChildWorkflows; this.maxResults = builder.maxResults; this.maxActions = builder.maxActions; + this.queryArchive = builder.queryArchive; } /** @@ -120,6 +126,7 @@ public static class Builder { boolean includeChildWorkflows; Long maxResults; Long maxActions; + boolean queryArchive; /** * Create a workflow instance query builder. @@ -142,6 +149,7 @@ public Builder(QueryWorkflowInstances copy) { this.includeChildWorkflows = copy.includeChildWorkflows; this.maxResults = copy.maxResults; this.maxActions = copy.maxActions; + this.queryArchive = copy.queryArchive; } /** * Add identifiers to query parameters. @@ -287,6 +295,17 @@ public Builder setMaxActions(Long maxActions) { return this; } + /** + * If true the workflow instance archive is also searched. + * + * @param queryArchive True if archive should also be queried. + * @return this. + */ + public Builder setQueryArchive(boolean queryArchive) { + this.queryArchive = queryArchive; + return this; + } + /** * Create the workflow instance query object. * diff --git a/nflow-engine/src/main/java/io/nflow/engine/workflow/instance/WorkflowInstance.java b/nflow-engine/src/main/java/io/nflow/engine/workflow/instance/WorkflowInstance.java index 7344c28b1..2e13c06a5 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/workflow/instance/WorkflowInstance.java +++ b/nflow-engine/src/main/java/io/nflow/engine/workflow/instance/WorkflowInstance.java @@ -148,6 +148,11 @@ public enum WorkflowInstanceStatus { */ public final Optional signal; + /** + * True if this instance is archived. + */ + public final boolean isArchived; + /** * Child workflow instance IDs created by this workflow instance, grouped by instance action ID. */ @@ -178,6 +183,7 @@ public enum WorkflowInstanceStatus { this.started = builder.started; this.executorGroup = builder.executorGroup; this.signal = builder.signal; + this.isArchived = builder.isArchived; this.mapper = builder.mapper; } @@ -251,6 +257,7 @@ public static class Builder { DateTime started; DateTime modified; String executorGroup; + boolean isArchived; Optional signal = Optional.empty(); ObjectStringMapper mapper; @@ -295,6 +302,7 @@ public Builder(WorkflowInstance copy) { this.started = copy.started; this.executorGroup = copy.executorGroup; this.signal = copy.signal; + this.isArchived = copy.isArchived; this.mapper = copy.mapper; } @@ -561,6 +569,16 @@ public Builder setSignal(Optional signal) { return this; } + /** + * Set the workflow source. + * @param isArchived True if this workflow is archived. + * @return this. + */ + public Builder setArchived(boolean isArchived) { + this.isArchived = isArchived; + return this; + } + /** * Create the workflow instance object. * @return The workflow instance. diff --git a/nflow-engine/src/test/java/io/nflow/engine/internal/dao/WorkflowInstanceDaoTest.java b/nflow-engine/src/test/java/io/nflow/engine/internal/dao/WorkflowInstanceDaoTest.java index dee500250..4669e29c3 100644 --- a/nflow-engine/src/test/java/io/nflow/engine/internal/dao/WorkflowInstanceDaoTest.java +++ b/nflow-engine/src/test/java/io/nflow/engine/internal/dao/WorkflowInstanceDaoTest.java @@ -1,5 +1,6 @@ package io.nflow.engine.internal.dao; +import static io.nflow.engine.internal.dao.TablePrefix.MAIN; import static io.nflow.engine.service.WorkflowInstanceInclude.CHILD_WORKFLOW_IDS; import static io.nflow.engine.service.WorkflowInstanceInclude.CURRENT_STATE_VARIABLES; import static io.nflow.engine.workflow.instance.WorkflowInstance.WorkflowInstanceStatus.created; @@ -100,7 +101,7 @@ public void roundTripTest() { WorkflowInstance i1 = constructWorkflowInstanceBuilder().build(); i1.stateVariables.put("a", "1"); long id = dao.insertWorkflowInstance(i1); - WorkflowInstance i2 = dao.getWorkflowInstance(id, EnumSet.allOf(WorkflowInstanceInclude.class), null); + WorkflowInstance i2 = dao.getWorkflowInstance(id, EnumSet.allOf(WorkflowInstanceInclude.class), null, MAIN); assertThat(i2.id, notNullValue()); assertThat(i2.created, notNullValue()); assertThat(i2.modified, notNullValue()); @@ -145,7 +146,9 @@ public void queryWorkflowInstanceWithAllConditions() { .setIncludeCurrentStateVariables(true) // .setIncludeChildWorkflows(true) // .setMaxResults(1L) // - .setMaxActions(1L).build(); + .setMaxActions(1L) // + .setQueryArchive(true) // + .build(); List l = dao.queryWorkflowInstances(q); assertThat(l.size(), is(1)); checkSameWorkflowInfo(child, l.get(0)); diff --git a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java index 46cf98329..d0285e0c3 100644 --- a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java +++ b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java @@ -136,7 +136,7 @@ public boolean updateWorkflowInstance(final long id, public Stream listWorkflowInstances(final List ids, final List types, final Long parentWorkflowId, final Long parentActionId, final List states, final List statuses, final String businessKey, final String externalId, final String include, - final Long maxResults, final Long maxActions, final WorkflowInstanceService workflowInstances, + final Long maxResults, final Long maxActions, boolean queryArchive, final WorkflowInstanceService workflowInstances, final ListWorkflowInstanceConverter listWorkflowConverter) { Set includeStrings = parseIncludeStrings(include).collect(toSet()); QueryWorkflowInstances q = new QueryWorkflowInstances.Builder() // @@ -153,6 +153,7 @@ public Stream listWorkflowInstances(final List instances = workflowInstances.listWorkflowInstancesAsStream(q); Set parseIncludeEnums = parseIncludeEnums(include); @@ -168,11 +169,11 @@ private Stream parseIncludeStrings(String include) { return Stream.of(trimToEmpty(include).split(",")); } - public ListWorkflowInstanceResponse fetchWorkflowInstance(final long id, final String include, final Long maxActions, + public ListWorkflowInstanceResponse fetchWorkflowInstance(final long id, final String include, final Long maxActions, boolean queryArchive, final WorkflowInstanceService workflowInstances, final ListWorkflowInstanceConverter listWorkflowConverter) throws EmptyResultDataAccessException { Set includes = parseIncludeEnums(include); - WorkflowInstance instance = workflowInstances.getWorkflowInstance(id, includes, maxActions); + WorkflowInstance instance = workflowInstances.getWorkflowInstance(id, includes, maxActions, queryArchive); return listWorkflowConverter.convert(instance, includes); } diff --git a/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java b/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java index 179b03a7b..7be4e7054 100644 --- a/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java +++ b/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java @@ -122,9 +122,10 @@ public Response updateWorkflowInstance(@ApiParam("Internal id for workflow insta @SuppressFBWarnings(value = "LEST_LOST_EXCEPTION_STACK_TRACE", justification = "The empty result exception contains no useful information") public Response fetchWorkflowInstance(@ApiParam("Internal id for workflow instance") @PathParam("id") long id, @QueryParam("include") @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, - @QueryParam("maxActions") @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions) { + @QueryParam("maxActions") @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, + @QueryParam("queryArchive") @ApiParam("Query also the archive") Boolean queryArchive) { return handleExceptions( - () -> ok(super.fetchWorkflowInstance(id, include, maxActions, workflowInstances, listWorkflowConverter))); + () -> ok(super.fetchWorkflowInstance(id, include, maxActions, ofNullable(queryArchive).orElse(true), workflowInstances, listWorkflowConverter))); } @GET @@ -139,9 +140,10 @@ public Response listWorkflowInstances(@QueryParam("id") @ApiParam("Internal id o @QueryParam("externalId") @ApiParam("External id for workflow instance") String externalId, @QueryParam("include") @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @QueryParam("maxResults") @ApiParam("Maximum number of workflow instances to be returned") Long maxResults, - @QueryParam("maxActions") @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions) { + @QueryParam("maxActions") @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, + @QueryParam("queryArchive") @ApiParam("Query also the archive") Boolean queryArchive) { return handleExceptions(() -> ok(super.listWorkflowInstances(ids, types, parentWorkflowId, parentActionId, states, statuses, - businessKey, externalId, include, maxResults, maxActions, workflowInstances, listWorkflowConverter).iterator())); + businessKey, externalId, include, maxResults, maxActions, ofNullable(queryArchive).orElse(true), workflowInstances, listWorkflowConverter).iterator())); } @PUT diff --git a/nflow-rest-api-jax-rs/src/test/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResourceTest.java b/nflow-rest-api-jax-rs/src/test/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResourceTest.java index a921839d9..9aa9d1490 100644 --- a/nflow-rest-api-jax-rs/src/test/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResourceTest.java +++ b/nflow-rest-api-jax-rs/src/test/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResourceTest.java @@ -184,7 +184,7 @@ public void whenUpdatingStateVariablesUpdateWorkflowInstanceWorks() { @Test public void listWorkflowInstancesWorks() { resource.listWorkflowInstances(asList(42L), asList("type"), 99L, 88L, asList("state"), - asList(WorkflowInstanceStatus.created), "businessKey", "externalId", "", null, null); + asList(WorkflowInstanceStatus.created), "businessKey", "externalId", "", null, null, true); verify(workflowInstances).listWorkflowInstancesAsStream((QueryWorkflowInstances) argThat(allOf( hasField("ids", contains(42L)), hasField("types", contains("type")), @@ -199,14 +199,16 @@ public void listWorkflowInstancesWorks() { hasField("includeActionStateVariables", equalTo(false)), hasField("includeChildWorkflows", equalTo(false)), hasField("maxResults", equalTo(null)), - hasField("maxActions", equalTo(null))))); + hasField("maxActions", equalTo(null)), + hasField("queryArchive", equalTo(true)) + ))); } @Test public void listWorkflowInstancesWorksWithAllIncludes() { resource.listWorkflowInstances(asList(42L), asList("type"), 99L, 88L, asList("state"), asList(WorkflowInstanceStatus.created, WorkflowInstanceStatus.executing), - "businessKey", "externalId", "actions,currentStateVariables,actionStateVariables,childWorkflows", 1L, 1L); + "businessKey", "externalId", "actions,currentStateVariables,actionStateVariables,childWorkflows", 1L, 1L, false); verify(workflowInstances).listWorkflowInstancesAsStream((QueryWorkflowInstances) argThat(allOf( hasField("ids", contains(42L)), hasField("types", contains("type")), @@ -221,14 +223,16 @@ public void listWorkflowInstancesWorksWithAllIncludes() { hasField("includeActionStateVariables", equalTo(true)), hasField("includeChildWorkflows", equalTo(true)), hasField("maxResults", equalTo(1L)), - hasField("maxActions", equalTo(1L))))); + hasField("maxActions", equalTo(1L)), + hasField("queryArchive", equalTo(false)) + ))); } @Test public void fetchingNonExistingWorkflowReturnsNotFound() { - when(workflowInstances.getWorkflowInstance(42, emptySet(), null)) + when(workflowInstances.getWorkflowInstance(42, emptySet(), null, true)) .thenThrow(new NflowNotFoundException("Workflow instance", 42, new Exception())); - try (Response response = resource.fetchWorkflowInstance(42, null, null)) { + try (Response response = resource.fetchWorkflowInstance(42, null, null, true)) { assertThat(response.getStatus(), is(equalTo(NOT_FOUND.getStatusCode()))); assertThat(response.readEntity(ErrorResponse.class).error, is(equalTo("Workflow instance 42 not found"))); } @@ -238,11 +242,11 @@ public void fetchingNonExistingWorkflowReturnsNotFound() { @Test public void fetchingExistingWorkflowWorks() { WorkflowInstance instance = mock(WorkflowInstance.class); - when(workflowInstances.getWorkflowInstance(42, emptySet(), null)).thenReturn(instance); + when(workflowInstances.getWorkflowInstance(42, emptySet(), null, false)).thenReturn(instance); ListWorkflowInstanceResponse resp = mock(ListWorkflowInstanceResponse.class); when(listWorkflowConverter.convert(eq(instance), any(Set.class))).thenReturn(resp); - ListWorkflowInstanceResponse result = resource.fetchWorkflowInstance(42, null, null).readEntity(ListWorkflowInstanceResponse.class); - verify(workflowInstances).getWorkflowInstance(42, emptySet(), null); + ListWorkflowInstanceResponse result = resource.fetchWorkflowInstance(42, null, null, false).readEntity(ListWorkflowInstanceResponse.class); + verify(workflowInstances).getWorkflowInstance(42, emptySet(), null, false); assertEquals(resp, result); } @@ -251,12 +255,12 @@ public void fetchingExistingWorkflowWorks() { public void fetchingExistingWorkflowWorksWithAllIncludes() { WorkflowInstance instance = mock(WorkflowInstance.class); EnumSet includes = EnumSet.allOf(WorkflowInstanceInclude.class); - when(workflowInstances.getWorkflowInstance(42, includes, 10L)).thenReturn(instance); + when(workflowInstances.getWorkflowInstance(42, includes, 10L, false)).thenReturn(instance); ListWorkflowInstanceResponse resp = mock(ListWorkflowInstanceResponse.class); when(listWorkflowConverter.convert(eq(instance), any(Set.class))).thenReturn(resp); ListWorkflowInstanceResponse result = resource.fetchWorkflowInstance(42, - "actions,currentStateVariables,actionStateVariables,childWorkflows", 10L).readEntity(ListWorkflowInstanceResponse.class); - verify(workflowInstances).getWorkflowInstance(42, includes, 10L); + "actions,currentStateVariables,actionStateVariables,childWorkflows", 10L, false).readEntity(ListWorkflowInstanceResponse.class); + verify(workflowInstances).getWorkflowInstance(42, includes, 10L, false); assertEquals(resp, result); } diff --git a/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java b/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java index 6908c8ae7..7400d078e 100644 --- a/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java +++ b/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java @@ -109,9 +109,10 @@ public ResponseEntity updateWorkflowInstance(@ApiParam("Internal id for workf @SuppressFBWarnings(value = "LEST_LOST_EXCEPTION_STACK_TRACE", justification = "The empty result exception contains no useful information") public ResponseEntity fetchWorkflowInstance(@ApiParam("Internal id for workflow instance") @PathVariable("id") long id, @RequestParam(value = "include", required = false) @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, - @RequestParam(value = "maxActions", required = false) @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions) { + @RequestParam(value = "maxActions", required = false) @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, + @RequestParam(value = "queryArchive", required = false, defaultValue = "true") @ApiParam("Query also the archive") boolean queryArchive) { return handleExceptions( - () -> ok(super.fetchWorkflowInstance(id, include, maxActions, this.workflowInstances, this.listWorkflowConverter))); + () -> ok(super.fetchWorkflowInstance(id, include, maxActions, queryArchive, this.workflowInstances, this.listWorkflowConverter))); } @GetMapping @@ -127,10 +128,11 @@ public ResponseEntity listWorkflowInstances( @RequestParam(value = "externalId", required = false) @ApiParam("External id for workflow instance") String externalId, @RequestParam(value = "include", required = false) @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @RequestParam(value = "maxResults", required = false) @ApiParam("Maximum number of workflow instances to be returned") Long maxResults, - @RequestParam(value = "maxActions", required = false) @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions) { + @RequestParam(value = "maxActions", required = false) @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, + @RequestParam(value = "queryArchive", required = false, defaultValue = "true") @ApiParam("Query also the archive") boolean queryArchive) { return handleExceptions( () -> ok(super.listWorkflowInstances(ids, types, parentWorkflowId, parentActionId, states, statuses, businessKey, - externalId, include, maxResults, maxActions, this.workflowInstances, this.listWorkflowConverter).iterator())); + externalId, include, maxResults, maxActions, queryArchive, this.workflowInstances, this.listWorkflowConverter).iterator())); } @PutMapping(path = "/{id}/signal", consumes = APPLICATION_JSON_VALUE) From 6c56e0c67f399cb460613a180c9525eab5af9af5 Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Thu, 20 Feb 2020 11:19:47 +0200 Subject: [PATCH 02/18] Child workflows can be in different tables than the parent workflow --- .../internal/dao/WorkflowInstanceDao.java | 55 ++++++++++++------- .../service/WorkflowInstanceService.java | 15 ++--- .../internal/dao/WorkflowInstanceDaoTest.java | 2 +- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java index 3243b1da4..ef45fe41f 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java +++ b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java @@ -15,6 +15,7 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.sort; import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static java.util.stream.Stream.concat; import static java.util.stream.Stream.empty; @@ -45,6 +46,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.stream.Collectors; import java.util.stream.Stream; import javax.inject.Inject; @@ -508,25 +510,33 @@ private boolean addExpectedStatesToQueryAndUpdate(StringBuilder sql, long workfl } public WorkflowInstance getWorkflowInstance(long id, Set includes, Long maxActions) { - return getWorkflowInstance(id, includes, maxActions, MAIN); + return getWorkflowInstance(id, includes, maxActions, false); } - public WorkflowInstance getWorkflowInstance(long id, Set includes, Long maxActions, TablePrefix tablePrefix) { - String sql = "select * from " + tablePrefix.nameOf("workflow") + " where id = ?"; + public WorkflowInstance getWorkflowInstance(long id, Set includes, Long maxActions, boolean queryArchive) { + String sql = "select *, 0 as archived from " + MAIN.nameOf("workflow") + " where id = ?"; + if (queryArchive) { + sql += "union all select *, 1 as archived from " + ARCHIVE.nameOf("workflow") + " where id = ?"; + } WorkflowInstance instance = jdbc.queryForObject(sql, new WorkflowInstanceRowMapper(), id).build(); if (includes.contains(WorkflowInstanceInclude.CURRENT_STATE_VARIABLES)) { - fillState(instance, tablePrefix); + fillState(instance); } if (includes.contains(WorkflowInstanceInclude.CHILD_WORKFLOW_IDS)) { - fillChildWorkflowIds(instance, tablePrefix); + fillChildWorkflowIds(instance, queryArchive); } if (includes.contains(WorkflowInstanceInclude.ACTIONS)) { - fillActions(instance, includes.contains(WorkflowInstanceInclude.ACTION_STATE_VARIABLES), maxActions, tablePrefix); + fillActions(instance, includes.contains(WorkflowInstanceInclude.ACTION_STATE_VARIABLES), maxActions); } return instance; } - private void fillState(final WorkflowInstance instance, TablePrefix tablePrefix) { + private TablePrefix sourceTable(WorkflowInstance instance) { + return instance.isArchived ? ARCHIVE : MAIN; + } + + private void fillState(final WorkflowInstance instance) { + TablePrefix tablePrefix = sourceTable(instance); jdbc.query("select outside.state_key, outside.state_value from " + tablePrefix.nameOf("workflow_state") + " outside inner join " + "(select workflow_id, max(action_id) action_id, state_key from " + tablePrefix.nameOf("workflow_state") + " where workflow_id = ? group by workflow_id, state_key) inside " + "on outside.workflow_id = inside.workflow_id and outside.action_id = inside.action_id and outside.state_key = inside.state_key", @@ -674,32 +684,34 @@ public Stream queryWorkflowInstancesAsStream(QueryWorkflowInst String sqlQuery = " where " + collectionToDelimitedString(conditions, " and ") + " order by id desc"; long maxResults = getMaxResults(query.maxResults); - String sql = sqlVariants.limit("select * from " + MAIN.nameOf("workflow") + sqlQuery, maxResults); + String sql = sqlVariants.limit("select *, 0 as archived from " + MAIN.nameOf("workflow") + sqlQuery, maxResults); List results = namedJdbc.query(sql, params, new WorkflowInstanceRowMapper()); maxResults -= results.size(); Stream resultStream = results.stream(); if (query.queryArchive && maxResults > 0) { - sql = sqlVariants.limit("select * from " + ARCHIVE.nameOf("workflow") + sqlQuery, maxResults); - resultStream = concat(resultStream, namedJdbc.query(sql, params, new WorkflowInstanceRowMapper()).stream() - .peek(builder -> builder.setArchived(true))); + sql = sqlVariants.limit("select *, 1 as archived from " + ARCHIVE.nameOf("workflow") + sqlQuery, maxResults); + resultStream = concat(resultStream, namedJdbc.query(sql, params, new WorkflowInstanceRowMapper()).stream()); } Stream ret = resultStream.map(WorkflowInstance.Builder::build); if (query.includeCurrentStateVariables) { - ret = ret.peek(instance -> fillState(instance, instance.isArchived ? ARCHIVE : MAIN)); + ret = ret.peek(instance -> fillState(instance)); } if (query.includeActions) { - ret = ret.peek(instance -> fillActions(instance, query.includeActionStateVariables, query.maxActions, instance.isArchived ? ARCHIVE : MAIN)); + ret = ret.peek(instance -> fillActions(instance, query.includeActionStateVariables, query.maxActions)); } if (query.includeChildWorkflows) { - ret = ret.peek(instance -> fillChildWorkflowIds(instance, instance.isArchived ? ARCHIVE : MAIN)); + ret = ret.peek(instance -> fillChildWorkflowIds(instance, query.queryArchive)); } return ret; } - private void fillChildWorkflowIds(final WorkflowInstance instance, TablePrefix tablePrefix) { - jdbc.query("select parent_action_id, id from " + tablePrefix.nameOf("workflow") + " where parent_workflow_id = ?", rs -> { + private void fillChildWorkflowIds(final WorkflowInstance instance, boolean queryArchive) { + Stream tables = queryArchive ? Stream.of(MAIN, ARCHIVE) : Stream.of(MAIN); + String sql = tables.map(tablePrefix -> "select parent_action_id, id from " + tablePrefix.nameOf("workflow") + " where parent_workflow_id = ?") + .collect(joining(" union all ")); + jdbc.query(sql, rs -> { long parentActionId = rs.getLong(1); long childWorkflowInstanceId = rs.getLong(2); List children = instance.childWorkflows.computeIfAbsent(parentActionId, k -> new ArrayList<>()); @@ -714,9 +726,10 @@ private long getMaxResults(Long maxResults) { return min(maxResults, workflowInstanceQueryMaxResults); } - private void fillActions(WorkflowInstance instance, boolean includeStateVariables, Long maxActions, TablePrefix tablePrefix) { - Map> actionStates = includeStateVariables ? fetchActionStateVariables(instance, tablePrefix) + private void fillActions(WorkflowInstance instance, boolean includeStateVariables, Long maxActions) { + Map> actionStates = includeStateVariables ? fetchActionStateVariables(instance) : EMPTY_ACTION_STATE_MAP; + TablePrefix tablePrefix = sourceTable(instance); String sql = sqlVariants.limit( "select nflow_workflow_action.* from " + tablePrefix.nameOf("workflow_action") + " where workflow_id = ? order by id desc", getMaxActions(maxActions)); @@ -730,7 +743,8 @@ private long getMaxActions(Long maxActions) { return min(maxActions, workflowInstanceQueryMaxActions); } - private Map> fetchActionStateVariables(WorkflowInstance instance, TablePrefix tablePrefix) { + private Map> fetchActionStateVariables(WorkflowInstance instance) { + TablePrefix tablePrefix = sourceTable(instance); return jdbc.query("select * from " + tablePrefix.nameOf("workflow_state") + " where workflow_id = ? order by action_id, state_key asc", new WorkflowActionStateRowMapper(), instance.id); } @@ -792,7 +806,8 @@ public WorkflowInstance.Builder mapRow(ResultSet rs, int rowNum) throws SQLExcep .setModified(sqlVariants.getDateTime(rs, "modified")) // .setStartedIfNotSet(sqlVariants.getDateTime(rs, "started")) // .setExecutorGroup(rs.getString("executor_group")) // - .setSignal(ofNullable(getInt(rs, "workflow_signal"))); + .setSignal(ofNullable(getInt(rs, "workflow_signal"))) // + .setArchived(rs.getBoolean("archived")); } } diff --git a/nflow-engine/src/main/java/io/nflow/engine/service/WorkflowInstanceService.java b/nflow-engine/src/main/java/io/nflow/engine/service/WorkflowInstanceService.java index 5ff36dbf4..46ef0f47a 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/service/WorkflowInstanceService.java +++ b/nflow-engine/src/main/java/io/nflow/engine/service/WorkflowInstanceService.java @@ -58,11 +58,7 @@ public WorkflowInstanceService(WorkflowInstanceDao workflowInstanceDao, Workflow * @throws NflowNotFoundException If workflow instance is not found. */ public WorkflowInstance getWorkflowInstance(long id, Set includes, Long maxActions) { - try { - return getWorkflowInstance(id, includes, maxActions, false); - } catch (EmptyResultDataAccessException e) { - throw new NflowNotFoundException("Workflow instance", id, e); - } + return getWorkflowInstance(id, includes, maxActions, false); } /** @@ -76,12 +72,9 @@ public WorkflowInstance getWorkflowInstance(long id, Set includes, Long maxActions, boolean queryArchive) { try { - return workflowInstanceDao.getWorkflowInstance(id, includes, maxActions, MAIN); - } catch (EmptyResultDataAccessException ex) { - if (queryArchive) { - return workflowInstanceDao.getWorkflowInstance(id, includes, maxActions, ARCHIVE); - } - throw new NflowNotFoundException("Workflow instance", id, ex); + return workflowInstanceDao.getWorkflowInstance(id, includes, maxActions, queryArchive); + } catch (EmptyResultDataAccessException e) { + throw new NflowNotFoundException("Workflow instance", id, e); } } diff --git a/nflow-engine/src/test/java/io/nflow/engine/internal/dao/WorkflowInstanceDaoTest.java b/nflow-engine/src/test/java/io/nflow/engine/internal/dao/WorkflowInstanceDaoTest.java index 4669e29c3..748fdc574 100644 --- a/nflow-engine/src/test/java/io/nflow/engine/internal/dao/WorkflowInstanceDaoTest.java +++ b/nflow-engine/src/test/java/io/nflow/engine/internal/dao/WorkflowInstanceDaoTest.java @@ -101,7 +101,7 @@ public void roundTripTest() { WorkflowInstance i1 = constructWorkflowInstanceBuilder().build(); i1.stateVariables.put("a", "1"); long id = dao.insertWorkflowInstance(i1); - WorkflowInstance i2 = dao.getWorkflowInstance(id, EnumSet.allOf(WorkflowInstanceInclude.class), null, MAIN); + WorkflowInstance i2 = dao.getWorkflowInstance(id, EnumSet.allOf(WorkflowInstanceInclude.class), null); assertThat(i2.id, notNullValue()); assertThat(i2.created, notNullValue()); assertThat(i2.modified, notNullValue()); From 1aec3edee679a7412426ad20801fb7b70d952ec1 Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Thu, 20 Feb 2020 12:39:53 +0200 Subject: [PATCH 03/18] Remove unused import --- .../io/nflow/engine/internal/workflow/StateExecutionImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/nflow-engine/src/main/java/io/nflow/engine/internal/workflow/StateExecutionImpl.java b/nflow-engine/src/main/java/io/nflow/engine/internal/workflow/StateExecutionImpl.java index 8cb811351..79b52d583 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/internal/workflow/StateExecutionImpl.java +++ b/nflow-engine/src/main/java/io/nflow/engine/internal/workflow/StateExecutionImpl.java @@ -1,7 +1,6 @@ package io.nflow.engine.internal.workflow; import static java.util.Collections.unmodifiableList; -import static java.util.stream.Collectors.toList; import static org.joda.time.DateTime.now; import static org.slf4j.LoggerFactory.getLogger; import static org.springframework.util.Assert.notNull; From c93aa8db29827aa834eb0cb6b5cd531ab63da72b Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Sat, 22 Feb 2020 11:25:18 +0200 Subject: [PATCH 04/18] Stream query workflows results out of rest api. (#382) * Stream results out of rest api, lowering latency to first item. The main table query is still done synchronously, but additional queries are done on the stream lowering latency * Performance is only lightly improved due to lower memory usage --- .../rest/v1/converter/ListWorkflowDefinitionConverter.java | 4 +--- .../io/nflow/rest/v1/msg/ListWorkflowDefinitionResponse.java | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/converter/ListWorkflowDefinitionConverter.java b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/converter/ListWorkflowDefinitionConverter.java index c1b7c65b0..9b351220c 100644 --- a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/converter/ListWorkflowDefinitionConverter.java +++ b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/converter/ListWorkflowDefinitionConverter.java @@ -37,9 +37,7 @@ public ListWorkflowDefinitionResponse convert(AbstractWorkflowDefinition> entry : definition.getAllowedTransitions().entrySet()) { State state = states.get(entry.getKey()); - for(String targetState : entry.getValue()) { - state.transitions.add(targetState); - } + state.transitions.addAll(entry.getValue()); } for (Entry entry : definition.getFailureTransitions().entrySet()) { State state = states.get(entry.getKey()); diff --git a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/ListWorkflowDefinitionResponse.java b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/ListWorkflowDefinitionResponse.java index 1798f480f..d49541ed8 100644 --- a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/ListWorkflowDefinitionResponse.java +++ b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/ListWorkflowDefinitionResponse.java @@ -64,7 +64,7 @@ public static class TransitionDelays extends ModelObject { } - public static class Signal { + public static class Signal extends ModelObject { @ApiModelProperty(value = "Signal value", required = true) public int value; From bd31416cec5744234128a17fa9a9b77e6957260e Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Thu, 20 Feb 2020 00:39:51 +0200 Subject: [PATCH 05/18] Support querying workflows from both main tables and archive. Stream workflow query results out of rest api, which makes it faster --- .../java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java | 3 +-- .../java/io/nflow/engine/service/WorkflowInstanceService.java | 2 -- .../io/nflow/engine/internal/dao/WorkflowInstanceDaoTest.java | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java index ef45fe41f..ecabfadd4 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java +++ b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java @@ -46,7 +46,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.stream.Collectors; import java.util.stream.Stream; import javax.inject.Inject; @@ -516,7 +515,7 @@ public WorkflowInstance getWorkflowInstance(long id, Set includes, Long maxActions, boolean queryArchive) { String sql = "select *, 0 as archived from " + MAIN.nameOf("workflow") + " where id = ?"; if (queryArchive) { - sql += "union all select *, 1 as archived from " + ARCHIVE.nameOf("workflow") + " where id = ?"; + sql += " union all select *, 1 as archived from " + ARCHIVE.nameOf("workflow") + " where id = ?"; } WorkflowInstance instance = jdbc.queryForObject(sql, new WorkflowInstanceRowMapper(), id).build(); if (includes.contains(WorkflowInstanceInclude.CURRENT_STATE_VARIABLES)) { diff --git a/nflow-engine/src/main/java/io/nflow/engine/service/WorkflowInstanceService.java b/nflow-engine/src/main/java/io/nflow/engine/service/WorkflowInstanceService.java index 46ef0f47a..9a3042546 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/service/WorkflowInstanceService.java +++ b/nflow-engine/src/main/java/io/nflow/engine/service/WorkflowInstanceService.java @@ -1,8 +1,6 @@ package io.nflow.engine.service; import static java.util.Collections.emptySet; -import static io.nflow.engine.internal.dao.TablePrefix.ARCHIVE; -import static io.nflow.engine.internal.dao.TablePrefix.MAIN; import static org.slf4j.LoggerFactory.getLogger; import static org.springframework.util.StringUtils.isEmpty; diff --git a/nflow-engine/src/test/java/io/nflow/engine/internal/dao/WorkflowInstanceDaoTest.java b/nflow-engine/src/test/java/io/nflow/engine/internal/dao/WorkflowInstanceDaoTest.java index 748fdc574..32ac51a54 100644 --- a/nflow-engine/src/test/java/io/nflow/engine/internal/dao/WorkflowInstanceDaoTest.java +++ b/nflow-engine/src/test/java/io/nflow/engine/internal/dao/WorkflowInstanceDaoTest.java @@ -1,6 +1,5 @@ package io.nflow.engine.internal.dao; -import static io.nflow.engine.internal.dao.TablePrefix.MAIN; import static io.nflow.engine.service.WorkflowInstanceInclude.CHILD_WORKFLOW_IDS; import static io.nflow.engine.service.WorkflowInstanceInclude.CURRENT_STATE_VARIABLES; import static io.nflow.engine.workflow.instance.WorkflowInstance.WorkflowInstanceStatus.created; From 1ef3c423d84e0be15496754916ebc86c49ee6070 Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Sat, 22 Feb 2020 12:50:12 +0200 Subject: [PATCH 06/18] Expose archived bit over rest api --- .../rest/v1/converter/ListWorkflowInstanceConverter.java | 2 ++ .../io/nflow/rest/v1/msg/ListWorkflowInstanceResponse.java | 3 +++ .../rest/v1/converter/ListWorkflowInstanceConverterTest.java | 4 +++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverter.java b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverter.java index adfb913c6..2f109adc9 100644 --- a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverter.java +++ b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverter.java @@ -1,5 +1,6 @@ package io.nflow.rest.v1.converter; +import static java.lang.Boolean.TRUE; import static org.springframework.util.CollectionUtils.isEmpty; import java.io.IOException; @@ -50,6 +51,7 @@ public ListWorkflowInstanceResponse convert(WorkflowInstance instance, Set(); for (WorkflowInstanceAction action : instance.actions) { diff --git a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/ListWorkflowInstanceResponse.java b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/ListWorkflowInstanceResponse.java index 68a79e5cd..22da6aa01 100644 --- a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/ListWorkflowInstanceResponse.java +++ b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/ListWorkflowInstanceResponse.java @@ -76,4 +76,7 @@ public class ListWorkflowInstanceResponse extends ModelObject { @ApiModelProperty("Current signal value") public Integer signal; + @ApiModelProperty("True if the instance is in the archive") + public Boolean archived; + } diff --git a/nflow-rest-api-common/src/test/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverterTest.java b/nflow-rest-api-common/src/test/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverterTest.java index 3f273344c..300d96c63 100644 --- a/nflow-rest-api-common/src/test/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverterTest.java +++ b/nflow-rest-api-common/src/test/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverterTest.java @@ -94,6 +94,7 @@ public void convertWithActionsWorks() throws IOException { assertThat(resp.started, is(i.started)); assertThat(resp.retries, is(i.retries)); assertThat(resp.signal, is(i.signal.get())); + assertThat(resp.archived, nullValue()); assertThat(resp.actions, contains(reflectEquals( new Action(a.id, a.type.name(), a.state, a.stateText, a.retryNo, a.executionStart, a.executionEnd, a.executorId)))); } @@ -112,7 +113,7 @@ public void convertWithActionStateVariablesWorks() throws IOException { .setBusinessKey("businessKey").setParentWorkflowId(942L).setParentActionId(842L).setExternalId("externalId") .setState("cState").setStateText("cState desc").setNextActivation(now()).setActions(asList(a)) .setCreated(now().minusMinutes(1)).setCreated(now().minusHours(2)).setModified(now().minusHours(1)).setRetries(42) - .setSignal(Optional.empty()).build(); + .setSignal(Optional.empty()).setArchived(true).build(); JsonNode node1 = mock(JsonNode.class); JsonNode nodeQuux = mock(JsonNode.class); @@ -143,6 +144,7 @@ public void convertWithActionStateVariablesWorks() throws IOException { assertThat(resp.started, is(i.started)); assertThat(resp.retries, is(i.retries)); assertThat(resp.signal, is(nullValue())); + assertThat(resp.archived, is(true)); assertThat(resp.actions, contains(reflectEquals(new Action(a.id, a.type.name(), a.state, a.stateText, a.retryNo, a.executionStart, a.executionEnd, a.executorId, expectedStateVariables)))); } From 6ce607a08ff97b7302bbda03a6b121a9bb2f30cf Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Sun, 23 Feb 2020 14:23:53 +0200 Subject: [PATCH 07/18] Rename field --- .../rest/v1/converter/ListWorkflowInstanceConverter.java | 2 +- .../io/nflow/rest/v1/msg/ListWorkflowInstanceResponse.java | 2 +- .../rest/v1/converter/ListWorkflowInstanceConverterTest.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverter.java b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverter.java index 2f109adc9..00dc926ab 100644 --- a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverter.java +++ b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverter.java @@ -51,7 +51,7 @@ public ListWorkflowInstanceResponse convert(WorkflowInstance instance, Set(); for (WorkflowInstanceAction action : instance.actions) { diff --git a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/ListWorkflowInstanceResponse.java b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/ListWorkflowInstanceResponse.java index 22da6aa01..668476f7c 100644 --- a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/ListWorkflowInstanceResponse.java +++ b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/ListWorkflowInstanceResponse.java @@ -77,6 +77,6 @@ public class ListWorkflowInstanceResponse extends ModelObject { public Integer signal; @ApiModelProperty("True if the instance is in the archive") - public Boolean archived; + public Boolean isArchived; } diff --git a/nflow-rest-api-common/src/test/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverterTest.java b/nflow-rest-api-common/src/test/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverterTest.java index 300d96c63..1ca2df18a 100644 --- a/nflow-rest-api-common/src/test/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverterTest.java +++ b/nflow-rest-api-common/src/test/java/io/nflow/rest/v1/converter/ListWorkflowInstanceConverterTest.java @@ -94,7 +94,7 @@ public void convertWithActionsWorks() throws IOException { assertThat(resp.started, is(i.started)); assertThat(resp.retries, is(i.retries)); assertThat(resp.signal, is(i.signal.get())); - assertThat(resp.archived, nullValue()); + assertThat(resp.isArchived, nullValue()); assertThat(resp.actions, contains(reflectEquals( new Action(a.id, a.type.name(), a.state, a.stateText, a.retryNo, a.executionStart, a.executionEnd, a.executorId)))); } @@ -144,7 +144,7 @@ public void convertWithActionStateVariablesWorks() throws IOException { assertThat(resp.started, is(i.started)); assertThat(resp.retries, is(i.retries)); assertThat(resp.signal, is(nullValue())); - assertThat(resp.archived, is(true)); + assertThat(resp.isArchived, is(true)); assertThat(resp.actions, contains(reflectEquals(new Action(a.id, a.type.name(), a.state, a.stateText, a.retryNo, a.executionStart, a.executionEnd, a.executorId, expectedStateVariables)))); } From 88545ebfff55a242a0a4476d657aea32143c60b9 Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Sun, 23 Feb 2020 14:25:03 +0200 Subject: [PATCH 08/18] Change default state of queryArchive in rest api, and instead make explorer specify it explicitly --- nflow-explorer/src/app/search/criteriaModel.js | 1 + nflow-explorer/src/app/services/WorkflowService.js | 2 +- .../java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java | 4 ++-- .../io/nflow/rest/v1/springweb/WorkflowInstanceResource.java | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/nflow-explorer/src/app/search/criteriaModel.js b/nflow-explorer/src/app/search/criteriaModel.js index 6d7222f65..f6cdcea9e 100644 --- a/nflow-explorer/src/app/search/criteriaModel.js +++ b/nflow-explorer/src/app/search/criteriaModel.js @@ -26,6 +26,7 @@ q.type = _.result(self.model.definition, 'type'); q.state = _.result(self.model.state, 'id'); + q.queryArchive = true; _.defaults(q, _.omit(self.model, ['definition', 'state'])); return omitNonValues(q); } diff --git a/nflow-explorer/src/app/services/WorkflowService.js b/nflow-explorer/src/app/services/WorkflowService.js index 6c48f28da..4d3eaddc6 100644 --- a/nflow-explorer/src/app/services/WorkflowService.js +++ b/nflow-explorer/src/app/services/WorkflowService.js @@ -15,7 +15,7 @@ function get(workflowId) { return RestHelper.get({ - path: '/v1/workflow-instance/id/' + workflowId + '?include=actions,currentStateVariables,actionStateVariables' + path: '/v1/workflow-instance/id/' + workflowId + '?include=actions,currentStateVariables,actionStateVariables&queryArchive=true' }); } diff --git a/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java b/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java index 7be4e7054..4a646f0fe 100644 --- a/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java +++ b/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java @@ -125,7 +125,7 @@ public Response fetchWorkflowInstance(@ApiParam("Internal id for workflow instan @QueryParam("maxActions") @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, @QueryParam("queryArchive") @ApiParam("Query also the archive") Boolean queryArchive) { return handleExceptions( - () -> ok(super.fetchWorkflowInstance(id, include, maxActions, ofNullable(queryArchive).orElse(true), workflowInstances, listWorkflowConverter))); + () -> ok(super.fetchWorkflowInstance(id, include, maxActions, ofNullable(queryArchive).orElse(false), workflowInstances, listWorkflowConverter))); } @GET @@ -143,7 +143,7 @@ public Response listWorkflowInstances(@QueryParam("id") @ApiParam("Internal id o @QueryParam("maxActions") @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, @QueryParam("queryArchive") @ApiParam("Query also the archive") Boolean queryArchive) { return handleExceptions(() -> ok(super.listWorkflowInstances(ids, types, parentWorkflowId, parentActionId, states, statuses, - businessKey, externalId, include, maxResults, maxActions, ofNullable(queryArchive).orElse(true), workflowInstances, listWorkflowConverter).iterator())); + businessKey, externalId, include, maxResults, maxActions, ofNullable(queryArchive).orElse(false), workflowInstances, listWorkflowConverter).iterator())); } @PUT diff --git a/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java b/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java index 7400d078e..31880ffbd 100644 --- a/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java +++ b/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java @@ -110,7 +110,7 @@ public ResponseEntity updateWorkflowInstance(@ApiParam("Internal id for workf public ResponseEntity fetchWorkflowInstance(@ApiParam("Internal id for workflow instance") @PathVariable("id") long id, @RequestParam(value = "include", required = false) @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @RequestParam(value = "maxActions", required = false) @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, - @RequestParam(value = "queryArchive", required = false, defaultValue = "true") @ApiParam("Query also the archive") boolean queryArchive) { + @RequestParam(value = "queryArchive", required = false, defaultValue = "false") @ApiParam("Query also the archive") boolean queryArchive) { return handleExceptions( () -> ok(super.fetchWorkflowInstance(id, include, maxActions, queryArchive, this.workflowInstances, this.listWorkflowConverter))); } @@ -129,7 +129,7 @@ public ResponseEntity listWorkflowInstances( @RequestParam(value = "include", required = false) @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @RequestParam(value = "maxResults", required = false) @ApiParam("Maximum number of workflow instances to be returned") Long maxResults, @RequestParam(value = "maxActions", required = false) @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, - @RequestParam(value = "queryArchive", required = false, defaultValue = "true") @ApiParam("Query also the archive") boolean queryArchive) { + @RequestParam(value = "queryArchive", required = false, defaultValue = "false") @ApiParam("Query also the archive") boolean queryArchive) { return handleExceptions( () -> ok(super.listWorkflowInstances(ids, types, parentWorkflowId, parentActionId, states, statuses, businessKey, externalId, include, maxResults, maxActions, queryArchive, this.workflowInstances, this.listWorkflowConverter).iterator())); From 3b5d6085be3ed55a978e0208aae2b2fcfb806d69 Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Sun, 23 Feb 2020 14:27:20 +0200 Subject: [PATCH 09/18] Fix test --- .../io/nflow/engine/service/WorkflowInstanceServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nflow-engine/src/test/java/io/nflow/engine/service/WorkflowInstanceServiceTest.java b/nflow-engine/src/test/java/io/nflow/engine/service/WorkflowInstanceServiceTest.java index 998338e41..79478df21 100644 --- a/nflow-engine/src/test/java/io/nflow/engine/service/WorkflowInstanceServiceTest.java +++ b/nflow-engine/src/test/java/io/nflow/engine/service/WorkflowInstanceServiceTest.java @@ -80,7 +80,7 @@ public void getWorkflowInstance() { WorkflowInstance instance = Mockito.mock(WorkflowInstance.class); @SuppressWarnings("unchecked") Set includes = Mockito.mock(Set.class); - when(workflowInstanceDao.getWorkflowInstance(42, includes, 10L)).thenReturn(instance); + when(workflowInstanceDao.getWorkflowInstance(42, includes, 10L, false)).thenReturn(instance); assertEquals(instance, service.getWorkflowInstance(42, includes, 10L)); } From 77c25f6e3309f69eb42b0b7ab386eb1fa4d8cabd Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Sun, 23 Feb 2020 22:30:26 +0200 Subject: [PATCH 10/18] Fix tests --- nflow-explorer/test/spec/search/criteriaModel.js | 16 ++++++++-------- nflow-explorer/test/spec/search/searchForm.js | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/nflow-explorer/test/spec/search/criteriaModel.js b/nflow-explorer/test/spec/search/criteriaModel.js index 909aec67d..b11c84910 100644 --- a/nflow-explorer/test/spec/search/criteriaModel.js +++ b/nflow-explorer/test/spec/search/criteriaModel.js @@ -57,35 +57,35 @@ describe('Service: CriteriaModel', function () { describe('toQuery', function () { it('sets definition type when available', function () { actualModel.definition = definitions[0]; - expect(CriteriaModel.toQuery()).toEqual({type: 'foo'}); + expect(CriteriaModel.toQuery()).toEqual({type: 'foo', queryArchive: true}); delete actualModel.definition.type; - expect(CriteriaModel.toQuery()).toEqual({}); + expect(CriteriaModel.toQuery()).toEqual({queryArchive: true}); delete actualModel.definition; - expect(CriteriaModel.toQuery()).toEqual({}); + expect(CriteriaModel.toQuery()).toEqual({queryArchive: true}); }); it('sets state id', function () { actualModel.state = definitions[0].states[0]; - expect(CriteriaModel.toQuery()).toEqual({state: 'bar'}); + expect(CriteriaModel.toQuery()).toEqual({state: 'bar', queryArchive: true}); delete actualModel.state.id; - expect(CriteriaModel.toQuery()).toEqual({}); + expect(CriteriaModel.toQuery()).toEqual({queryArchive: true}); delete actualModel.state; - expect(CriteriaModel.toQuery()).toEqual({}); + expect(CriteriaModel.toQuery()).toEqual({queryArchive: true}); }); it('null values are omitted', function () { actualModel.foo = null; - expect(CriteriaModel.toQuery()).toEqual({}); + expect(CriteriaModel.toQuery()).toEqual({queryArchive: true}); }); it('undefined values are omitted', function () { actualModel.foo = undefined; - expect(CriteriaModel.toQuery()).toEqual({}); + expect(CriteriaModel.toQuery()).toEqual({queryArchive: true}); }); }); diff --git a/nflow-explorer/test/spec/search/searchForm.js b/nflow-explorer/test/spec/search/searchForm.js index 44912d61b..31b562306 100644 --- a/nflow-explorer/test/spec/search/searchForm.js +++ b/nflow-explorer/test/spec/search/searchForm.js @@ -71,7 +71,7 @@ describe('Directive: searchForm', function () { $httpBackend = _$httpBackend_; CriteriaModel.model = { foo: 'bar' }; - url = config.nflowUrl + '/v1/workflow-instance?foo=bar'; + url = config.nflowUrl + '/v1/workflow-instance?foo=bar&queryArchive=true'; })); afterEach(function() { From 64e4bea0140da3caf51e95c416acfaf25ae688ea Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Sun, 23 Feb 2020 23:22:44 +0200 Subject: [PATCH 11/18] Add missing double arguments to jdbc union --- .../nflow/engine/internal/dao/WorkflowInstanceDao.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java index ecabfadd4..91262991f 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java +++ b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java @@ -514,10 +514,12 @@ public WorkflowInstance getWorkflowInstance(long id, Set includes, Long maxActions, boolean queryArchive) { String sql = "select *, 0 as archived from " + MAIN.nameOf("workflow") + " where id = ?"; + Object[] args = new Object[]{id}; if (queryArchive) { sql += " union all select *, 1 as archived from " + ARCHIVE.nameOf("workflow") + " where id = ?"; + args = new Object[]{id, id}; } - WorkflowInstance instance = jdbc.queryForObject(sql, new WorkflowInstanceRowMapper(), id).build(); + WorkflowInstance instance = jdbc.queryForObject(sql, args, new WorkflowInstanceRowMapper()).build(); if (includes.contains(WorkflowInstanceInclude.CURRENT_STATE_VARIABLES)) { fillState(instance); } @@ -710,12 +712,13 @@ private void fillChildWorkflowIds(final WorkflowInstance instance, boolean query Stream tables = queryArchive ? Stream.of(MAIN, ARCHIVE) : Stream.of(MAIN); String sql = tables.map(tablePrefix -> "select parent_action_id, id from " + tablePrefix.nameOf("workflow") + " where parent_workflow_id = ?") .collect(joining(" union all ")); - jdbc.query(sql, rs -> { + Object[] args = queryArchive ? new Object[]{instance.id, instance.id} : new Object[]{instance.id}; + jdbc.query(sql, args, rs -> { long parentActionId = rs.getLong(1); long childWorkflowInstanceId = rs.getLong(2); List children = instance.childWorkflows.computeIfAbsent(parentActionId, k -> new ArrayList<>()); children.add(childWorkflowInstanceId); - }, instance.id); + }); } private long getMaxResults(Long maxResults) { From 935494255986f848ce365e8f228ac6f986c7a747 Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Sat, 18 Apr 2020 21:03:28 +0300 Subject: [PATCH 12/18] Add back change descriptions --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5561b10bb..6e0cc813b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,15 @@ ## 7.2.1-SNAPSHOT (future release) **Highlights** +- Support for querying archived workflow instances **Details** +- `nflow-engine` + - Query interfaces allow to request also scanning of archived workflow instances +- `nflow-rest-api-jax-rs` and `nflow-rest-api-spring-web` + - Support for querying also archived workflow instances when passing `queryArchive=true` query parameter. +- `nflow-explorer` + - Query also archived workflow instances by default. Configurable in `config.js`. ## 7.2.0 (2020-04-27) From 94f8e1ece1e5d12490a601a0d9cb59c0ccfe0579 Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Sat, 18 Apr 2020 21:11:21 +0300 Subject: [PATCH 13/18] Replace hardcoded wait with loop --- .../io/nflow/tests/MaintenanceWorkflowTest.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/nflow-tests/src/test/java/io/nflow/tests/MaintenanceWorkflowTest.java b/nflow-tests/src/test/java/io/nflow/tests/MaintenanceWorkflowTest.java index 85c816083..bf83edcd3 100644 --- a/nflow-tests/src/test/java/io/nflow/tests/MaintenanceWorkflowTest.java +++ b/nflow-tests/src/test/java/io/nflow/tests/MaintenanceWorkflowTest.java @@ -14,7 +14,10 @@ import static org.joda.time.Period.seconds; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Set; import javax.ws.rs.NotFoundException; @@ -75,7 +78,17 @@ public void createWorkflows() { @Test @Order(4) public void waitForCleanup() throws InterruptedException { - SECONDS.sleep(6); + Set waiting = new HashSet<>(ids); + for (int i=0; i<10 && !waiting.isEmpty(); ++i) { + SECONDS.sleep(1); + for (Iterator it = waiting.iterator(); it.hasNext(); ) { + try { + getWorkflowInstance(it.next()); + } catch (NotFoundException ex) { + it.remove(); + } + } + } } @Test From 197d815d57fa97ec8bc72a73a05bbb65c1ec8948 Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Mon, 27 Apr 2020 14:59:45 +0300 Subject: [PATCH 14/18] Review fixes --- CHANGELOG.md | 8 ++++---- .../io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java | 2 +- .../nflow/rest/v1/springweb/WorkflowInstanceResource.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e0cc813b..17d1772b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,15 @@ ## 7.2.1-SNAPSHOT (future release) **Highlights** -- Support for querying archived workflow instances +- Support for querying archived workflow instances. **Details** - `nflow-engine` - - Query interfaces allow to request also scanning of archived workflow instances + - Query interfaces allow to request searching of archived workflow instances. - `nflow-rest-api-jax-rs` and `nflow-rest-api-spring-web` - - Support for querying also archived workflow instances when passing `queryArchive=true` query parameter. + - Support for querying archived workflow instances when passing `queryArchive=true` query parameter. - `nflow-explorer` - - Query also archived workflow instances by default. Configurable in `config.js`. + - Query and show archived workflow instances by default. Configurable in `config.js`. ## 7.2.0 (2020-04-27) diff --git a/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java b/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java index 4a646f0fe..993940312 100644 --- a/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java +++ b/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java @@ -123,7 +123,7 @@ public Response updateWorkflowInstance(@ApiParam("Internal id for workflow insta public Response fetchWorkflowInstance(@ApiParam("Internal id for workflow instance") @PathParam("id") long id, @QueryParam("include") @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @QueryParam("maxActions") @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, - @QueryParam("queryArchive") @ApiParam("Query also the archive") Boolean queryArchive) { + @QueryParam("queryArchive") @ApiParam("Query also the archive if not found from prod tables") Boolean queryArchive) { return handleExceptions( () -> ok(super.fetchWorkflowInstance(id, include, maxActions, ofNullable(queryArchive).orElse(false), workflowInstances, listWorkflowConverter))); } diff --git a/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java b/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java index 31880ffbd..cf103b3e3 100644 --- a/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java +++ b/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java @@ -110,7 +110,7 @@ public ResponseEntity updateWorkflowInstance(@ApiParam("Internal id for workf public ResponseEntity fetchWorkflowInstance(@ApiParam("Internal id for workflow instance") @PathVariable("id") long id, @RequestParam(value = "include", required = false) @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @RequestParam(value = "maxActions", required = false) @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, - @RequestParam(value = "queryArchive", required = false, defaultValue = "false") @ApiParam("Query also the archive") boolean queryArchive) { + @RequestParam(value = "queryArchive", required = false, defaultValue = "false") @ApiParam("Query also the archive if not found from prod tables") boolean queryArchive) { return handleExceptions( () -> ok(super.fetchWorkflowInstance(id, include, maxActions, queryArchive, this.workflowInstances, this.listWorkflowConverter))); } From 939f727e8faa9432bbb77de060b7f183c2409e2d Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Mon, 27 Apr 2020 15:04:50 +0300 Subject: [PATCH 15/18] More wording improvements --- .../java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java | 4 ++-- .../io/nflow/rest/v1/springweb/WorkflowInstanceResource.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java b/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java index 993940312..9e139d886 100644 --- a/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java +++ b/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java @@ -123,7 +123,7 @@ public Response updateWorkflowInstance(@ApiParam("Internal id for workflow insta public Response fetchWorkflowInstance(@ApiParam("Internal id for workflow instance") @PathParam("id") long id, @QueryParam("include") @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @QueryParam("maxActions") @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, - @QueryParam("queryArchive") @ApiParam("Query also the archive if not found from prod tables") Boolean queryArchive) { + @QueryParam("queryArchive") @ApiParam("Query also the archive if not found from main tables") Boolean queryArchive) { return handleExceptions( () -> ok(super.fetchWorkflowInstance(id, include, maxActions, ofNullable(queryArchive).orElse(false), workflowInstances, listWorkflowConverter))); } @@ -141,7 +141,7 @@ public Response listWorkflowInstances(@QueryParam("id") @ApiParam("Internal id o @QueryParam("include") @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @QueryParam("maxResults") @ApiParam("Maximum number of workflow instances to be returned") Long maxResults, @QueryParam("maxActions") @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, - @QueryParam("queryArchive") @ApiParam("Query also the archive") Boolean queryArchive) { + @QueryParam("queryArchive") @ApiParam("Query also the archive if not enough results found from main tables") Boolean queryArchive) { return handleExceptions(() -> ok(super.listWorkflowInstances(ids, types, parentWorkflowId, parentActionId, states, statuses, businessKey, externalId, include, maxResults, maxActions, ofNullable(queryArchive).orElse(false), workflowInstances, listWorkflowConverter).iterator())); } diff --git a/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java b/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java index cf103b3e3..33ddfe5bb 100644 --- a/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java +++ b/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java @@ -110,7 +110,7 @@ public ResponseEntity updateWorkflowInstance(@ApiParam("Internal id for workf public ResponseEntity fetchWorkflowInstance(@ApiParam("Internal id for workflow instance") @PathVariable("id") long id, @RequestParam(value = "include", required = false) @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @RequestParam(value = "maxActions", required = false) @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, - @RequestParam(value = "queryArchive", required = false, defaultValue = "false") @ApiParam("Query also the archive if not found from prod tables") boolean queryArchive) { + @RequestParam(value = "queryArchive", required = false, defaultValue = "false") @ApiParam("Query also the archive if not found from main tables") boolean queryArchive) { return handleExceptions( () -> ok(super.fetchWorkflowInstance(id, include, maxActions, queryArchive, this.workflowInstances, this.listWorkflowConverter))); } @@ -129,7 +129,7 @@ public ResponseEntity listWorkflowInstances( @RequestParam(value = "include", required = false) @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @RequestParam(value = "maxResults", required = false) @ApiParam("Maximum number of workflow instances to be returned") Long maxResults, @RequestParam(value = "maxActions", required = false) @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, - @RequestParam(value = "queryArchive", required = false, defaultValue = "false") @ApiParam("Query also the archive") boolean queryArchive) { + @RequestParam(value = "queryArchive", required = false, defaultValue = "false") @ApiParam("Query also the archive if not enough results found from main tables") boolean queryArchive) { return handleExceptions( () -> ok(super.listWorkflowInstances(ids, types, parentWorkflowId, parentActionId, states, statuses, businessKey, externalId, include, maxResults, maxActions, queryArchive, this.workflowInstances, this.listWorkflowConverter).iterator())); From 1647be9f390fccc2da0cc9e044f2e841a5029dcf Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Mon, 27 Apr 2020 15:12:05 +0300 Subject: [PATCH 16/18] Move default query-archived state to constant(s) in base class --- CHANGELOG.md | 6 +++--- .../src/main/java/io/nflow/rest/v1/ResourceBase.java | 6 ++++-- .../io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java | 4 ++-- .../nflow/rest/v1/springweb/WorkflowInstanceResource.java | 4 ++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17d1772b0..d68903acb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,11 @@ **Details** - `nflow-engine` - - Query interfaces allow to request searching of archived workflow instances. + - Query interfaces allow to request searching of archived workflow instances if not enough matches found from main tables. - `nflow-rest-api-jax-rs` and `nflow-rest-api-spring-web` - - Support for querying archived workflow instances when passing `queryArchive=true` query parameter. + - Support for querying archived workflow instances when passing `queryArchive=true` query parameter if not enough matches found from main tables. - `nflow-explorer` - - Query and show archived workflow instances by default. Configurable in `config.js`. + - Query and show archived workflow instances by default if not enough matches found from main tables. Configurable in `config.js`. ## 7.2.0 (2020-04-27) diff --git a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java index d0285e0c3..f633ba403 100644 --- a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java +++ b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java @@ -68,10 +68,12 @@ public abstract class ResourceBase { new SimpleEntry<>(actionStateVariables, WorkflowInstanceInclude.ACTION_STATE_VARIABLES), new SimpleEntry<>(childWorkflows, WorkflowInstanceInclude.CHILD_WORKFLOW_IDS)) .collect(toMap(Entry::getKey, Entry::getValue))); + protected static final String QUERY_ARCHIVED_DEFAULT_S = "false"; + protected static final boolean QUERY_ARCHIVED_DEFAULT = Boolean.parseBoolean(QUERY_ARCHIVED_DEFAULT_S); public List listWorkflowDefinitions(final List types, - final WorkflowDefinitionService workflowDefinitions, final ListWorkflowDefinitionConverter converter, - final WorkflowDefinitionDao workflowDefinitionDao) { + final WorkflowDefinitionService workflowDefinitions, final ListWorkflowDefinitionConverter converter, + final WorkflowDefinitionDao workflowDefinitionDao) { List> definitions = workflowDefinitions.getWorkflowDefinitions(); Set reqTypes = new HashSet<>(types); Set foundTypes = new HashSet<>(); diff --git a/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java b/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java index 9e139d886..e52b1d379 100644 --- a/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java +++ b/nflow-rest-api-jax-rs/src/main/java/io/nflow/rest/v1/jaxrs/WorkflowInstanceResource.java @@ -125,7 +125,7 @@ public Response fetchWorkflowInstance(@ApiParam("Internal id for workflow instan @QueryParam("maxActions") @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, @QueryParam("queryArchive") @ApiParam("Query also the archive if not found from main tables") Boolean queryArchive) { return handleExceptions( - () -> ok(super.fetchWorkflowInstance(id, include, maxActions, ofNullable(queryArchive).orElse(false), workflowInstances, listWorkflowConverter))); + () -> ok(super.fetchWorkflowInstance(id, include, maxActions, ofNullable(queryArchive).orElse(QUERY_ARCHIVED_DEFAULT), workflowInstances, listWorkflowConverter))); } @GET @@ -143,7 +143,7 @@ public Response listWorkflowInstances(@QueryParam("id") @ApiParam("Internal id o @QueryParam("maxActions") @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, @QueryParam("queryArchive") @ApiParam("Query also the archive if not enough results found from main tables") Boolean queryArchive) { return handleExceptions(() -> ok(super.listWorkflowInstances(ids, types, parentWorkflowId, parentActionId, states, statuses, - businessKey, externalId, include, maxResults, maxActions, ofNullable(queryArchive).orElse(false), workflowInstances, listWorkflowConverter).iterator())); + businessKey, externalId, include, maxResults, maxActions, ofNullable(queryArchive).orElse(QUERY_ARCHIVED_DEFAULT), workflowInstances, listWorkflowConverter).iterator())); } @PUT diff --git a/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java b/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java index 33ddfe5bb..38b81aa35 100644 --- a/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java +++ b/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java @@ -110,7 +110,7 @@ public ResponseEntity updateWorkflowInstance(@ApiParam("Internal id for workf public ResponseEntity fetchWorkflowInstance(@ApiParam("Internal id for workflow instance") @PathVariable("id") long id, @RequestParam(value = "include", required = false) @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @RequestParam(value = "maxActions", required = false) @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, - @RequestParam(value = "queryArchive", required = false, defaultValue = "false") @ApiParam("Query also the archive if not found from main tables") boolean queryArchive) { + @RequestParam(value = "queryArchive", required = false, defaultValue = QUERY_ARCHIVED_DEFAULT_S) @ApiParam("Query also the archive if not found from main tables") boolean queryArchive) { return handleExceptions( () -> ok(super.fetchWorkflowInstance(id, include, maxActions, queryArchive, this.workflowInstances, this.listWorkflowConverter))); } @@ -129,7 +129,7 @@ public ResponseEntity listWorkflowInstances( @RequestParam(value = "include", required = false) @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @RequestParam(value = "maxResults", required = false) @ApiParam("Maximum number of workflow instances to be returned") Long maxResults, @RequestParam(value = "maxActions", required = false) @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, - @RequestParam(value = "queryArchive", required = false, defaultValue = "false") @ApiParam("Query also the archive if not enough results found from main tables") boolean queryArchive) { + @RequestParam(value = "queryArchive", required = false, defaultValue = QUERY_ARCHIVED_DEFAULT_S) @ApiParam("Query also the archive if not enough results found from main tables") boolean queryArchive) { return handleExceptions( () -> ok(super.listWorkflowInstances(ids, types, parentWorkflowId, parentActionId, states, statuses, businessKey, externalId, include, maxResults, maxActions, queryArchive, this.workflowInstances, this.listWorkflowConverter).iterator())); From ee682d1b5ed57f7fcff0a833acf5d3782ab4ad93 Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Mon, 27 Apr 2020 15:20:01 +0300 Subject: [PATCH 17/18] Review refactorings --- .../nflow/engine/internal/dao/WorkflowInstanceDao.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java index 91262991f..a97581c7a 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java +++ b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java @@ -514,10 +514,10 @@ public WorkflowInstance getWorkflowInstance(long id, Set includes, Long maxActions, boolean queryArchive) { String sql = "select *, 0 as archived from " + MAIN.nameOf("workflow") + " where id = ?"; - Object[] args = new Object[]{id}; + Object[] args = new Object[]{ id }; if (queryArchive) { sql += " union all select *, 1 as archived from " + ARCHIVE.nameOf("workflow") + " where id = ?"; - args = new Object[]{id, id}; + args = new Object[]{ id, id }; } WorkflowInstance instance = jdbc.queryForObject(sql, args, new WorkflowInstanceRowMapper()).build(); if (includes.contains(WorkflowInstanceInclude.CURRENT_STATE_VARIABLES)) { @@ -682,16 +682,16 @@ public Stream queryWorkflowInstancesAsStream(QueryWorkflowInst } conditions.add("executor_group = :executor_group"); params.addValue("executor_group", executorInfo.getExecutorGroup()); - String sqlQuery = " where " + collectionToDelimitedString(conditions, " and ") + " order by id desc"; + String whereCondition = " where " + collectionToDelimitedString(conditions, " and ") + " order by id desc"; long maxResults = getMaxResults(query.maxResults); - String sql = sqlVariants.limit("select *, 0 as archived from " + MAIN.nameOf("workflow") + sqlQuery, maxResults); + String sql = sqlVariants.limit("select *, 0 as archived from " + MAIN.nameOf("workflow") + whereCondition, maxResults); List results = namedJdbc.query(sql, params, new WorkflowInstanceRowMapper()); maxResults -= results.size(); Stream resultStream = results.stream(); if (query.queryArchive && maxResults > 0) { - sql = sqlVariants.limit("select *, 1 as archived from " + ARCHIVE.nameOf("workflow") + sqlQuery, maxResults); + sql = sqlVariants.limit("select *, 1 as archived from " + ARCHIVE.nameOf("workflow") + whereCondition, maxResults); resultStream = concat(resultStream, namedJdbc.query(sql, params, new WorkflowInstanceRowMapper()).stream()); } From 4fa6a7d072eba3c6eb2261a12c104d394b52cb92 Mon Sep 17 00:00:00 2001 From: Mikko Tiihonen Date: Mon, 27 Apr 2020 23:10:49 +0300 Subject: [PATCH 18/18] Review fixes --- .../src/main/java/io/nflow/rest/v1/ResourceBase.java | 8 ++++---- .../nflow/rest/v1/springweb/WorkflowInstanceResource.java | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java index f633ba403..a4b9e6822 100644 --- a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java +++ b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java @@ -68,12 +68,12 @@ public abstract class ResourceBase { new SimpleEntry<>(actionStateVariables, WorkflowInstanceInclude.ACTION_STATE_VARIABLES), new SimpleEntry<>(childWorkflows, WorkflowInstanceInclude.CHILD_WORKFLOW_IDS)) .collect(toMap(Entry::getKey, Entry::getValue))); - protected static final String QUERY_ARCHIVED_DEFAULT_S = "false"; - protected static final boolean QUERY_ARCHIVED_DEFAULT = Boolean.parseBoolean(QUERY_ARCHIVED_DEFAULT_S); + protected static final String QUERY_ARCHIVED_DEFAULT_STR = "false"; + protected static final boolean QUERY_ARCHIVED_DEFAULT = Boolean.parseBoolean(QUERY_ARCHIVED_DEFAULT_STR); public List listWorkflowDefinitions(final List types, - final WorkflowDefinitionService workflowDefinitions, final ListWorkflowDefinitionConverter converter, - final WorkflowDefinitionDao workflowDefinitionDao) { + final WorkflowDefinitionService workflowDefinitions, final ListWorkflowDefinitionConverter converter, + final WorkflowDefinitionDao workflowDefinitionDao) { List> definitions = workflowDefinitions.getWorkflowDefinitions(); Set reqTypes = new HashSet<>(types); Set foundTypes = new HashSet<>(); diff --git a/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java b/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java index 38b81aa35..d6a986772 100644 --- a/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java +++ b/nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/WorkflowInstanceResource.java @@ -110,7 +110,7 @@ public ResponseEntity updateWorkflowInstance(@ApiParam("Internal id for workf public ResponseEntity fetchWorkflowInstance(@ApiParam("Internal id for workflow instance") @PathVariable("id") long id, @RequestParam(value = "include", required = false) @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @RequestParam(value = "maxActions", required = false) @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, - @RequestParam(value = "queryArchive", required = false, defaultValue = QUERY_ARCHIVED_DEFAULT_S) @ApiParam("Query also the archive if not found from main tables") boolean queryArchive) { + @RequestParam(value = "queryArchive", required = false, defaultValue = QUERY_ARCHIVED_DEFAULT_STR) @ApiParam("Query also the archive if not found from main tables") boolean queryArchive) { return handleExceptions( () -> ok(super.fetchWorkflowInstance(id, include, maxActions, queryArchive, this.workflowInstances, this.listWorkflowConverter))); } @@ -129,7 +129,7 @@ public ResponseEntity listWorkflowInstances( @RequestParam(value = "include", required = false) @ApiParam(value = INCLUDE_PARAM_DESC, allowableValues = INCLUDE_PARAM_VALUES, allowMultiple = true) String include, @RequestParam(value = "maxResults", required = false) @ApiParam("Maximum number of workflow instances to be returned") Long maxResults, @RequestParam(value = "maxActions", required = false) @ApiParam("Maximum number of actions returned for each workflow instance") Long maxActions, - @RequestParam(value = "queryArchive", required = false, defaultValue = QUERY_ARCHIVED_DEFAULT_S) @ApiParam("Query also the archive if not enough results found from main tables") boolean queryArchive) { + @RequestParam(value = "queryArchive", required = false, defaultValue = QUERY_ARCHIVED_DEFAULT_STR) @ApiParam("Query also the archive if not enough results found from main tables") boolean queryArchive) { return handleExceptions( () -> ok(super.listWorkflowInstances(ids, types, parentWorkflowId, parentActionId, states, statuses, businessKey, externalId, include, maxResults, maxActions, queryArchive, this.workflowInstances, this.listWorkflowConverter).iterator()));