Skip to content

Commit ff040fe

Browse files
olibaimjeffp1Ayush ThengneJeffP07Doe1111
authored
Workflow bulk deletion API and action module options (#110)
* Initial commit to add bulk workflow deletion * Added an API endpoint for bulk deletion * Add archiveworkflow param * Added bulk delete action to BulkActionModule * Added archive option to bulk delete * Addition of terminateRemove() Functions (#9) * Added an API endpoint for bulk deletion * Fixed WorkflowBulkServiceTest, added test cases for deleteWorkflow an… (#10) * Fixed WorkflowBulkServiceTest, added test cases for deleteWorkflow and terminateRemove, and added WorkflowResourceTest test for single terminateRemove * pass spotlessjava --------- Co-authored-by: JeffP <jeffp@jeffpham.com> --------- Co-authored-by: jeffp1 <jeffp@jeffpham.com> Co-authored-by: Ayush Thengne <ayushthengne@ayushs-macbook-pro.local> Co-authored-by: Jeff P <28172529+JeffP07@users.noreply.github.com> Co-authored-by: Doe1111 <106997600+Doe1111@users.noreply.github.com>
1 parent 7e7b88b commit ff040fe

10 files changed

Lines changed: 236 additions & 4 deletions

File tree

core/src/main/java/com/netflix/conductor/service/WorkflowBulkService.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,23 @@ BulkResponse terminate(
6767
"Cannot process more than {max} workflows. Please use multiple requests.")
6868
List<String> workflowIds,
6969
String reason);
70+
71+
BulkResponse deleteWorkflow(
72+
@NotEmpty(message = "WorkflowIds list cannot be null.")
73+
@Size(
74+
max = MAX_REQUEST_ITEMS,
75+
message =
76+
"Cannot process more than {max} workflows. Please use multiple requests.")
77+
List<String> workflowIds,
78+
boolean archiveWorkflow);
79+
80+
BulkResponse terminateRemove(
81+
@NotEmpty(message = "WorkflowIds list cannot be null.")
82+
@Size(
83+
max = MAX_REQUEST_ITEMS,
84+
message =
85+
"Cannot process more than {max} workflows. Please use multiple requests.")
86+
List<String> workflowIds,
87+
String reason,
88+
boolean archiveWorkflow);
7089
}

core/src/main/java/com/netflix/conductor/service/WorkflowBulkServiceImpl.java

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,12 @@ public class WorkflowBulkServiceImpl implements WorkflowBulkService {
3030

3131
private static final Logger LOGGER = LoggerFactory.getLogger(WorkflowBulkService.class);
3232
private final WorkflowExecutor workflowExecutor;
33+
private final WorkflowService workflowService;
3334

34-
public WorkflowBulkServiceImpl(WorkflowExecutor workflowExecutor) {
35+
public WorkflowBulkServiceImpl(
36+
WorkflowExecutor workflowExecutor, WorkflowService workflowService) {
3537
this.workflowExecutor = workflowExecutor;
38+
this.workflowService = workflowService;
3639
}
3740

3841
/**
@@ -164,4 +167,70 @@ public BulkResponse terminate(List<String> workflowIds, String reason) {
164167
}
165168
return bulkResponse;
166169
}
170+
171+
/**
172+
* Removes a list of workflows from the system.
173+
*
174+
* @param workflowIds List of WorkflowIDs of the workflows you want to remove from system.
175+
* @param archiveWorkflow Archives the workflow and associated tasks instead of removing them.
176+
*/
177+
public BulkResponse deleteWorkflow(List<String> workflowIds, boolean archiveWorkflow) {
178+
BulkResponse bulkResponse = new BulkResponse();
179+
for (String workflowId : workflowIds) {
180+
try {
181+
workflowService.deleteWorkflow(
182+
workflowId,
183+
archiveWorkflow); // TODO: change this to method that cancels then deletes
184+
bulkResponse.appendSuccessResponse(workflowId);
185+
} catch (Exception e) {
186+
LOGGER.error(
187+
"bulk delete exception, workflowId {}, message: {} ",
188+
workflowId,
189+
e.getMessage(),
190+
e);
191+
bulkResponse.appendFailedResponse(workflowId, e.getMessage());
192+
}
193+
}
194+
return bulkResponse;
195+
}
196+
197+
/**
198+
* Terminates execution for workflows in a list, then removes each workflow.
199+
*
200+
* @param workflowIds List of workflow IDs to terminate and delete.
201+
* @param reason Reason for terminating the workflow.
202+
* @param archiveWorkflow Archives the workflow and associated tasks instead of removing them.
203+
* @return bulk response object containing a list of succeeded workflows and a list of failed
204+
* ones with errors
205+
*/
206+
public BulkResponse terminateRemove(
207+
List<String> workflowIds, String reason, boolean archiveWorkflow) {
208+
BulkResponse bulkResponse = new BulkResponse();
209+
for (String workflowId : workflowIds) {
210+
try {
211+
workflowExecutor.terminateWorkflow(workflowId, reason);
212+
bulkResponse.appendSuccessResponse(workflowId);
213+
} catch (Exception e) {
214+
LOGGER.error(
215+
"bulk terminate exception, workflowId {}, message: {} ",
216+
workflowId,
217+
e.getMessage(),
218+
e);
219+
bulkResponse.appendFailedResponse(workflowId, e.getMessage());
220+
}
221+
222+
try {
223+
workflowService.deleteWorkflow(workflowId, archiveWorkflow);
224+
bulkResponse.appendSuccessResponse(workflowId);
225+
} catch (Exception e) {
226+
LOGGER.error(
227+
"bulk delete exception, workflowId {}, message: {} ",
228+
workflowId,
229+
e.getMessage(),
230+
e);
231+
bulkResponse.appendFailedResponse(workflowId, e.getMessage());
232+
}
233+
}
234+
return bulkResponse;
235+
}
167236
}

core/src/main/java/com/netflix/conductor/service/WorkflowService.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,19 @@ void terminateWorkflow(
242242
@NotEmpty(message = "WorkflowId cannot be null or empty.") String workflowId,
243243
String reason);
244244

245+
/**
246+
* Terminate workflow execution, and then remove it from the system. Acts as terminate and
247+
* remove combined.
248+
*
249+
* @param workflowId WorkflowId of the workflow
250+
* @param reason Reason for terminating the workflow.
251+
* @param archiveWorkflow Archives the workflow and associated tasks instead of removing them.
252+
*/
253+
void terminateRemove(
254+
@NotEmpty(message = "WorkflowId cannot be null or empty.") String workflowId,
255+
String reason,
256+
boolean archiveWorkflow);
257+
245258
/**
246259
* Search for workflows based on payload and given parameters. Use sort options as sort ASCor
247260
* DESC e.g. sort=name or sort=workflowId:DESC. If order is not specified, defaults to ASC.

core/src/main/java/com/netflix/conductor/service/WorkflowServiceImpl.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,19 @@ public void deleteWorkflow(String workflowId, boolean archiveWorkflow) {
196196
executionService.removeWorkflow(workflowId, archiveWorkflow);
197197
}
198198

199+
/**
200+
* Terminate workflow execution, and then remove it from the system. Acts as terminate and
201+
* remove combined.
202+
*
203+
* @param workflowId WorkflowId of the workflow
204+
* @param reason Reason for terminating the workflow.
205+
* @param archiveWorkflow Archives the workflow and associated tasks instead of removing them.
206+
*/
207+
public void terminateRemove(String workflowId, String reason, boolean archiveWorkflow) {
208+
workflowExecutor.terminateWorkflow(workflowId, reason);
209+
executionService.removeWorkflow(workflowId, archiveWorkflow);
210+
}
211+
199212
/**
200213
* Retrieves all the running workflows.
201214
*

core/src/test/java/com/netflix/conductor/service/WorkflowBulkServiceTest.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,14 @@ WorkflowExecutor workflowExecutor() {
5050
}
5151

5252
@Bean
53-
public WorkflowBulkService workflowBulkService(WorkflowExecutor workflowExecutor) {
54-
return new WorkflowBulkServiceImpl(workflowExecutor);
53+
WorkflowService workflowService() {
54+
return mock(WorkflowService.class);
55+
}
56+
57+
@Bean
58+
public WorkflowBulkService workflowBulkService(
59+
WorkflowExecutor workflowExecutor, WorkflowService workflowService) {
60+
return new WorkflowBulkServiceImpl(workflowExecutor, workflowService);
5561
}
5662
}
5763

@@ -144,4 +150,28 @@ public void testTerminateNull() {
144150
throw ex;
145151
}
146152
}
153+
154+
@Test(expected = ConstraintViolationException.class)
155+
public void testDeleteWorkflowNull() {
156+
try {
157+
workflowBulkService.deleteWorkflow(null, false);
158+
} catch (ConstraintViolationException ex) {
159+
assertEquals(1, ex.getConstraintViolations().size());
160+
Set<String> messages = getConstraintViolationMessages(ex.getConstraintViolations());
161+
assertTrue(messages.contains("WorkflowIds list cannot be null."));
162+
throw ex;
163+
}
164+
}
165+
166+
@Test(expected = ConstraintViolationException.class)
167+
public void testTerminateRemoveNull() {
168+
try {
169+
workflowBulkService.terminateRemove(null, null, false);
170+
} catch (ConstraintViolationException ex) {
171+
assertEquals(1, ex.getConstraintViolations().size());
172+
Set<String> messages = getConstraintViolationMessages(ex.getConstraintViolations());
173+
assertTrue(messages.contains("WorkflowIds list cannot be null."));
174+
throw ex;
175+
}
176+
}
147177
}

rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowBulkResource.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import java.util.List;
1616

17+
import org.springframework.web.bind.annotation.DeleteMapping;
1718
import org.springframework.web.bind.annotation.PostMapping;
1819
import org.springframework.web.bind.annotation.PutMapping;
1920
import org.springframework.web.bind.annotation.RequestBody;
@@ -111,4 +112,33 @@ public BulkResponse terminate(
111112
@RequestParam(value = "reason", required = false) String reason) {
112113
return workflowBulkService.terminate(workflowIds, reason);
113114
}
115+
116+
/**
117+
* Delete the list of workflows.
118+
*
119+
* @param workflowIds - list of workflow Ids to be deleted
120+
* @return bulk reponse object containing a list of successfully deleted workflows
121+
*/
122+
@DeleteMapping("/remove")
123+
public BulkResponse deleteWorkflow(
124+
@RequestBody List<String> workflowIds,
125+
@RequestParam(value = "archiveWorkflow", defaultValue = "true", required = false)
126+
boolean archiveWorkflow) {
127+
return workflowBulkService.deleteWorkflow(workflowIds, archiveWorkflow);
128+
}
129+
130+
/**
131+
* Terminate then delete the list of workflows.
132+
*
133+
* @param workflowIds - list of workflow Ids to be deleted
134+
* @return bulk response object containing a list of successfully deleted workflows
135+
*/
136+
@DeleteMapping("/terminate-remove")
137+
public BulkResponse terminateRemove(
138+
@RequestBody List<String> workflowIds,
139+
@RequestParam(value = "archiveWorkflow", defaultValue = "true", required = false)
140+
boolean archiveWorkflow,
141+
@RequestParam(value = "reason", required = false) String reason) {
142+
return workflowBulkService.terminateRemove(workflowIds, reason, archiveWorkflow);
143+
}
114144
}

rest/src/main/java/com/netflix/conductor/rest/controllers/WorkflowResource.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,16 @@ public void terminate(
207207
workflowService.terminateWorkflow(workflowId, reason);
208208
}
209209

210+
@DeleteMapping("/{workflowId}/terminate-remove")
211+
@Operation(summary = "Terminate workflow execution and remove the workflow from the system")
212+
public void terminateRemove(
213+
@PathVariable("workflowId") String workflowId,
214+
@RequestParam(value = "reason", required = false) String reason,
215+
@RequestParam(value = "archiveWorkflow", defaultValue = "true", required = false)
216+
boolean archiveWorkflow) {
217+
workflowService.terminateRemove(workflowId, reason, archiveWorkflow);
218+
}
219+
210220
@Operation(
211221
summary = "Search for workflows based on payload and other parameters",
212222
description =

rest/src/test/java/com/netflix/conductor/rest/controllers/WorkflowResourceTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,13 @@ public void testTerminate() {
210210
verify(mockWorkflowService, times(1)).terminateWorkflow(anyString(), anyString());
211211
}
212212

213+
@Test
214+
public void testTerminateRemove() {
215+
workflowResource.terminateRemove("w123", "test", false);
216+
verify(mockWorkflowService, times(1))
217+
.terminateRemove(anyString(), anyString(), anyBoolean());
218+
}
219+
213220
@Test
214221
public void testSearch() {
215222
workflowResource.search(0, 100, "asc", "*", "*");

ui/src/data/bulkactions.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,20 @@ export const useBulkTerminateWithReasonAction = (callbacks) => {
4848
});
4949
}, callbacks);
5050
};
51+
52+
export const useBulkDeleteAction = (callbacks) => {
53+
const fetchContext = useFetchContext();
54+
55+
return useMutation((mutateParams) => {
56+
const path = new Path("/workflow/bulk/remove");
57+
path.search.append("archiveWorkflow", mutateParams.archiveWorkflow);
58+
59+
return fetchWithContext(path, fetchContext, {
60+
method: "delete",
61+
headers: {
62+
"Content-Type": "application/json",
63+
},
64+
body: _.get(mutateParams, "body"),
65+
});
66+
}, callbacks);
67+
};

ui/src/pages/executions/BulkActionModule.jsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
useBulkPauseAction,
2222
useBulkRetryAction,
2323
useBulkTerminateWithReasonAction,
24+
useBulkDeleteAction,
2425
} from "../../data/bulkactions";
2526

2627
const useStyles = makeStyles({
@@ -59,6 +60,8 @@ export default function BulkActionModule({ selectedRows }) {
5960
mutate: terminateWithReasonAction,
6061
isLoading: terminateWithReasonLoading,
6162
} = useBulkTerminateWithReasonAction({ onSuccess });
63+
const { mutate: deleteAction, isLoading: deleteLoading } =
64+
useBulkDeleteAction({ onSuccess });
6265

6366
const isLoading =
6467
pauseLoading ||
@@ -67,7 +70,8 @@ export default function BulkActionModule({ selectedRows }) {
6770
restartLatestLoading ||
6871
retryLoading ||
6972
terminateLoading ||
70-
terminateWithReasonLoading;
73+
terminateWithReasonLoading ||
74+
deleteLoading;
7175

7276
function onSuccess(data, variables, context) {
7377
const retval = {
@@ -133,6 +137,26 @@ export default function BulkActionModule({ selectedRows }) {
133137
}
134138
},
135139
},
140+
{
141+
label: "Archive",
142+
handler: () => {
143+
const archiveWorkflow = "true";
144+
deleteAction({
145+
body: JSON.stringify(selectedIds),
146+
archiveWorkflow,
147+
});
148+
},
149+
},
150+
{
151+
label: "Delete",
152+
handler: () => {
153+
const archiveWorkflow = "false";
154+
deleteAction({
155+
body: JSON.stringify(selectedIds),
156+
archiveWorkflow,
157+
});
158+
},
159+
},
136160
]}
137161
>
138162
Bulk Action

0 commit comments

Comments
 (0)