diff --git a/CHANGELOG.md b/CHANGELOG.md index bf841beab..d64e807f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,13 @@ ## 7.2.3-SNAPSHOT (future release) **Highlights** +- Support updating workflow instance business key. **Details** +- `nflow-engine` + - `WorkflowInstanceService.updateWorkflowInstance` can now be used to update business key of the workflow instance. +- `nflow-rest-api-common` + - `UpdateWorkflowInstanceRequest.businessKey` field was added to support updating workflow instance business key via REST API. - `nflow-explorer` - Dependency updates: - urijs 1.19.5 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 aa8d20651..c2bc16bf2 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 @@ -467,6 +467,10 @@ public boolean updateNotRunningWorkflowInstance(WorkflowInstance instance) { vars.add("status = " + sqlVariants.workflowStatus()); args.add(instance.status.name()); } + if (instance.businessKey != null) { + vars.add("business_key = ?"); + args.add(instance.businessKey); + } String sql = "update nflow_workflow set " + join(vars, ", ") + " where id = ? and executor_id is null"; args.add(instance.id); return jdbc.update(sql, args.toArray()) == 1; 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 3afe1c18a..2c15e4d36 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 @@ -108,9 +108,7 @@ public void roundTripTest() { @Test public void queryNonExistingWorkflowThrowsException() { - assertThrows(EmptyResultDataAccessException.class, () -> - dao.getWorkflowInstance(-42, emptySet(), null) - ); + assertThrows(EmptyResultDataAccessException.class, () -> dao.getWorkflowInstance(-42, emptySet(), null)); } @Test @@ -382,6 +380,7 @@ public void processRow(ResultSet rs) throws SQLException { assertThat(rs.getString("state"), is(instance.state)); assertThat(rs.getTimestamp("next_activation").getTime(), is(instance.nextActivation.getMillis())); assertThat(rs.getString("state_text"), is("modified")); + assertThat(rs.getString("business_key"), is(instance.businessKey)); } }); } @@ -401,6 +400,7 @@ public void processRow(ResultSet rs) throws SQLException { assertThat(rs.getString("state"), is("manualState")); assertThat(rs.getTimestamp("next_activation").getTime(), is(instance.nextActivation.getMillis())); assertThat(rs.getString("state_text"), is("modified")); + assertThat(rs.getString("business_key"), is(instance.businessKey)); } }); } @@ -421,6 +421,27 @@ public void processRow(ResultSet rs) throws SQLException { assertThat(rs.getString("state"), is(instance.state)); assertThat(rs.getTimestamp("next_activation").getTime(), is(tomorrow.getMillis())); assertThat(rs.getString("state_text"), is("modified")); + assertThat(rs.getString("business_key"), is(instance.businessKey)); + } + }); + } + + @Test + public void updateNotRunningWorkflowInstanceUpdatesBusinessKeyForNotRunningInstance() { + final WorkflowInstance instance = constructWorkflowInstanceBuilder().build(); + long id = dao.insertWorkflowInstance(instance); + WorkflowInstance modifiedInstance = new WorkflowInstance.Builder(instance).setId(id).setBusinessKey("modifiedKey") + .setStateText("modified").build(); + boolean updated = dao.updateNotRunningWorkflowInstance(modifiedInstance); + assertThat(updated, is(true)); + jdbc.query("select * from nflow_workflow where id = " + id, new RowCallbackHandler() { + @Override + public void processRow(ResultSet rs) throws SQLException { + assertThat(rs.getString("status"), is(instance.status.name())); + assertThat(rs.getString("state"), is(instance.state)); + assertThat(rs.getTimestamp("next_activation").getTime(), is(instance.nextActivation.getMillis())); + assertThat(rs.getString("state_text"), is("modified")); + assertThat(rs.getString("business_key"), is("modifiedKey")); } }); } @@ -565,19 +586,17 @@ public void fakePostgreSQLinsertWorkflowInstance() { DateTime started = now(); WorkflowInstance wf = new WorkflowInstance.Builder().setStatus(inProgress).setState("updateState").setStateText("update text") - .setParentWorkflowId(110L).setParentActionId(421L).setNextActivation(started.plusSeconds(1)) - .setRetries(3).setId(43).putStateVariable("A", "B").putStateVariable("C", "D").setSignal(Optional.of(1)) - .setStartedIfNotSet(started).setPriority((short) 10).build(); + .setParentWorkflowId(110L).setParentActionId(421L).setNextActivation(started.plusSeconds(1)).setRetries(3).setId(43) + .putStateVariable("A", "B").putStateVariable("C", "D").setSignal(Optional.of(1)).setStartedIfNotSet(started) + .setPriority((short) 10).build(); d.insertWorkflowInstance(wf); - assertEquals( - "with wf as (insert into nflow_workflow(type, priority, parent_workflow_id, parent_action_id, business_key, " - + "external_id, executor_group, status, state, state_text, next_activation, workflow_signal) values " - + "(?, ?, ?, ?, ?, ?, ?, ?::workflow_status, ?, ?, ?, ?) returning id), ins12 as " - + "(insert into nflow_workflow_state(workflow_id, action_id, state_key, state_value) select wf.id,0,?,? from wf), " - + "ins14 as (insert into nflow_workflow_state(workflow_id, action_id, state_key, state_value) " - + "select wf.id,0,?,? from wf) select wf.id from wf", - sql.getValue()); + assertEquals("with wf as (insert into nflow_workflow(type, priority, parent_workflow_id, parent_action_id, business_key, " + + "external_id, executor_group, status, state, state_text, next_activation, workflow_signal) values " + + "(?, ?, ?, ?, ?, ?, ?, ?::workflow_status, ?, ?, ?, ?) returning id), ins12 as " + + "(insert into nflow_workflow_state(workflow_id, action_id, state_key, state_value) select wf.id,0,?,? from wf), " + + "ins14 as (insert into nflow_workflow_state(workflow_id, action_id, state_key, state_value) " + + "select wf.id,0,?,? from wf) select wf.id from wf", sql.getValue()); assertThat(args.getAllValues().size(), is(countMatches(sql.getValue(), "?"))); int i = 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..33edd6630 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 @@ -123,6 +123,12 @@ public boolean updateWorkflowInstance(final long id, msg += "API updated state variables. "; } } + if (!isEmpty(req.businessKey)) { + builder.setBusinessKey(req.businessKey); + if (isBlank(req.actionDescription)) { + msg = "API changed business key to " + req.businessKey + ". "; + } + } if (msg.isEmpty()) { return true; } diff --git a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/UpdateWorkflowInstanceRequest.java b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/UpdateWorkflowInstanceRequest.java index 4d089f573..8d2f3632a 100644 --- a/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/UpdateWorkflowInstanceRequest.java +++ b/nflow-rest-api-common/src/main/java/io/nflow/rest/v1/msg/UpdateWorkflowInstanceRequest.java @@ -26,4 +26,6 @@ public class UpdateWorkflowInstanceRequest extends ModelObject { @ApiModelProperty("State variables to be added or updated.") public Map stateVariables = new HashMap<>(); + @ApiModelProperty("Business key related to the workflow instance.") + public String businessKey; } 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 be67f2f1e..3b9b72554 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 @@ -185,6 +185,30 @@ public void whenUpdatingStateVariablesUpdateWorkflowInstanceWorks() { assertThat(instance.getStateVariable("textNode"), is("\"text\"")); } + @Test + public void whenUpdatingBusinessKeyUpdateWorkflowInstanceWorks() { + UpdateWorkflowInstanceRequest req = new UpdateWorkflowInstanceRequest(); + req.businessKey = "modifiedKey"; + makeRequest(() -> resource.updateWorkflowInstance(3, req)); + verify(workflowInstances).updateWorkflowInstance( + (WorkflowInstance) argThat(hasField("businessKey", equalTo("modifiedKey"))), + (WorkflowInstanceAction) argThat( + allOf(hasField("stateText", equalTo("API changed business key to " + req.businessKey + ".")), + hasField("type", equalTo(externalChange))))); + } + + @Test + public void whenUpdatingBusinessKeyWithDescriptionUpdateWorkflowInstanceWorks() { + UpdateWorkflowInstanceRequest req = new UpdateWorkflowInstanceRequest(); + req.businessKey = "modifiedKey"; + req.actionDescription = "description"; + makeRequest(() -> resource.updateWorkflowInstance(3, req)); + verify(workflowInstances).updateWorkflowInstance( + (WorkflowInstance) argThat(hasField("businessKey", equalTo("modifiedKey"))), + (WorkflowInstanceAction) argThat( + allOf(hasField("stateText", equalTo("description")), hasField("type", equalTo(externalChange))))); + } + @Test public void listWorkflowInstancesWorks() { makeRequest(() -> resource.listWorkflowInstances(asList(42L), asList("type"), 99L, 88L, asList("state"),