From 6a0953e6baf51d329e9bba0e94e6d87546d4fe33 Mon Sep 17 00:00:00 2001 From: Ross Edfort Date: Mon, 5 Dec 2022 10:30:59 -0700 Subject: [PATCH] DT-143 - batch cancel workflows on Recent Workflows page (#959) * DT-143 - batch cancel * fix cy tests * use old batch api endpoints for versoins !> 2.9.0 * fix cy tests * fix some layout issues * change cancel single workflow to opt in * remove extra th and fix filter indicator animation Co-authored-by: Alex Tideman --- .../integration/workflow-bulk-actions.spec.js | 60 ++++--- cypress/support/commands.js | 6 +- src/api.d.ts | 16 +- src/lib/components/dropdown-menu.svelte | 6 +- src/lib/components/workflow-actions.svelte | 2 +- .../batch-operation-confirmation-modal.svelte | 80 +++++++++ ...orkflows-summary-table-with-filters.svelte | 33 +++- .../holocene/table/bulk-action-button.svelte | 15 +- src/lib/layouts/workflow-header.svelte | 2 +- src/lib/layouts/workflow-run-layout.svelte | 2 +- .../pages/workflows-with-new-search.svelte | 170 ++++++++++-------- ...{terminate-service.ts => batch-service.ts} | 111 ++++++++---- src/lib/services/workflow-service.ts | 30 +++- src/lib/utilities/route-for-api.ts | 8 + .../[workflow]/[run]/__layout@root.svelte | 2 +- .../[namespace]/workflows/index@root.svelte | 5 +- 16 files changed, 385 insertions(+), 163 deletions(-) create mode 100644 src/lib/components/workflow/batch-operation-confirmation-modal.svelte rename src/lib/services/{terminate-service.ts => batch-service.ts} (55%) diff --git a/cypress/integration/workflow-bulk-actions.spec.js b/cypress/integration/workflow-bulk-actions.spec.js index a6df3b795..e7c46a00b 100644 --- a/cypress/integration/workflow-bulk-actions.spec.js +++ b/cypress/integration/workflow-bulk-actions.spec.js @@ -1,20 +1,23 @@ /// -describe('Bulk Terminate', () => { - it("disallows bulk actions for cluster that doesn't have elasticsearch enabled", () => { - cy.interceptApi(); +describe('Batch and Bulk Workflow Actions', () => { + describe('when advanced visibility is disabled', () => { + it('disallows bulk and batch actions', () => { + cy.interceptApi(); - cy.visit('/namespaces/default/workflows'); + cy.visit('/namespaces/default/workflows'); - cy.wait('@cluster-api'); - cy.wait('@workflows-api'); + cy.wait('@cluster-api'); + cy.wait('@workflows-api'); - cy.get('#workflows-table-with-bulk-actions').should('not.exist'); + cy.get('#workflows-table-with-bulk-actions').should('not.exist'); + }); }); - describe('for cluster that does have elasticsearch enabled', () => { - it('allows running workflows to be terminated by ID', () => { + describe('when advanced visibility is enabled', () => { + beforeEach(() => { cy.interceptApi(); + cy.intercept(Cypress.env('VITE_API_HOST') + '/api/v1/cluster*', { fixture: 'cluster-with-elasticsearch.json', }).as('cluster-api-elasticsearch'); @@ -23,39 +26,56 @@ describe('Bulk Terminate', () => { cy.wait('@cluster-api-elasticsearch'); cy.wait('@workflows-api'); + }); + it('allows running workflows to be terminated by ID', () => { cy.get('#workflows-table-with-bulk-actions').should('exist'); cy.get('#select-visible-workflows').click({ force: true }); cy.get('[data-cy="bulk-terminate-button"]').click(); - cy.get('#bulk-terminate-reason').type('Sarah Connor'); + cy.get('#bulk-action-reason').type('Sarah Connor'); cy.get('div.modal button.destructive').click(); cy.get('#batch-terminate-success-toast'); }); it('allows running workflows to be terminated by a query', () => { - cy.interceptApi(); - cy.intercept(Cypress.env('VITE_API_HOST') + '/api/v1/cluster*', { - fixture: 'cluster-with-elasticsearch.json', - }).as('cluster-api-elasticsearch'); + cy.get('#workflows-table-with-bulk-actions').should('exist'); - cy.visit('/namespaces/default/workflows'); + cy.get('#select-visible-workflows').click({ force: true }); + cy.get('[data-cy="select-all-workflows"]').click(); + cy.get('[data-cy="bulk-terminate-button"]').click(); + cy.get('[data-cy="batch-action-workflows-query"]').should( + 'have.text', + 'ExecutionStatus="Running"', + ); + cy.get('#bulk-action-reason').type('Sarah Connor'); + cy.get('div.modal button.destructive').click(); + cy.get('#batch-terminate-success-toast'); + }); - cy.wait('@cluster-api-elasticsearch'); - cy.wait('@workflows-api'); + it('allows running workflows to be cancelled by ID', () => { + cy.get('#workflows-table-with-bulk-actions').should('exist'); + + cy.get('#select-visible-workflows').click({ force: true }); + cy.get('[data-cy="bulk-cancel-button"]').click(); + cy.get('#bulk-action-reason').type('Sarah Connor'); + cy.get('div.modal button.destructive').click(); + cy.get('#batch-cancel-success-toast'); + }); + it('allows running workflows to be cancelled by a query', () => { cy.get('#workflows-table-with-bulk-actions').should('exist'); cy.get('#select-visible-workflows').click({ force: true }); cy.get('[data-cy="select-all-workflows"]').click(); - cy.get('[data-cy="bulk-terminate-button"]').click(); + cy.get('[data-cy="bulk-cancel-button"]').click(); cy.get('[data-cy="batch-action-workflows-query"]').should( 'have.text', 'ExecutionStatus="Running"', ); - cy.get('#bulk-terminate-reason').type('Sarah Connor'); + cy.get('#bulk-action-reason').type('Sarah Connor'); cy.get('div.modal button.destructive').click(); - cy.get('#batch-terminate-success-toast'); + cy.get('#batch-cancel-success-toast'); }); }); }); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index c6efff1e6..cf7194eb0 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -133,12 +133,12 @@ Cypress.Commands.add('interceptScheduleApi', () => { ).as('schedule-api'); }); -Cypress.Commands.add('interceptBatchTerminateApi', () => { +Cypress.Commands.add('interceptCreateBatchOperationApi', () => { cy.intercept( Cypress.env('VITE_API_HOST') + `/api/v1/namespaces/*/workflows/batch/terminate?`, { statusCode: 200, body: {} }, - ).as('batch-terminate-api'); + ).as('create-batch-operation-api'); }); Cypress.Commands.add('interceptDescribeBatchOperationApi', () => { @@ -181,7 +181,7 @@ Cypress.Commands.add( cy.interceptSearchAttributesApi(); cy.interceptSchedulesApi(); cy.interceptScheduleApi(); - cy.interceptBatchTerminateApi(); + cy.interceptCreateBatchOperationApi(); cy.interceptDescribeBatchOperationApi(); cy.interceptTerminateWorkflowApi(); cy.interceptCancelWorkflowApi(); diff --git a/src/api.d.ts b/src/api.d.ts index 4134266a1..d1c42789b 100644 --- a/src/api.d.ts +++ b/src/api.d.ts @@ -1,9 +1,7 @@ type WorkflowsAPIRoutePath = | 'workflows' | 'workflows.archived' - | 'workflows.count' - | 'workflows.batch.terminate' - | 'workflows.batch.describe'; + | 'workflows.count'; type WorkflowAPIRoutePath = | 'workflow' @@ -13,6 +11,13 @@ type WorkflowAPIRoutePath = | 'events.descending' | 'query'; +type BatchAPIRoutePath = + | 'batch-operations' + | 'batch-operation.describe' + // TODO: Remove when new batch APIs are deployed + | 'workflows.batch.terminate' + | 'workflows.batch.describe'; + type NamespaceAPIRoutePath = 'namespace'; type TaskQueueAPIRoutePath = 'task-queue'; @@ -29,7 +34,8 @@ type APIRoutePath = | TaskQueueAPIRoutePath | WorkflowAPIRoutePath | WorkflowsAPIRoutePath - | NamespaceAPIRoutePath; + | NamespaceAPIRoutePath + | BatchAPIRoutePath; type APIRouteParameters = { namespace: string; @@ -49,6 +55,8 @@ type WorkflowRouteParameters = Pick< 'namespace' | 'workflowId' | 'runId' >; +type BatchRouteParameters = Pick; + type TaskQueueRouteParameters = Pick; type ValidWorkflowEndpoints = WorkflowsAPIRoutePath; diff --git a/src/lib/components/dropdown-menu.svelte b/src/lib/components/dropdown-menu.svelte index b5586b3bf..6cc31efe5 100644 --- a/src/lib/components/dropdown-menu.svelte +++ b/src/lib/components/dropdown-menu.svelte @@ -86,11 +86,7 @@ {/if} {#if value} - + {/if} diff --git a/src/lib/components/workflow-actions.svelte b/src/lib/components/workflow-actions.svelte index e0e4da362..0d2147fa2 100644 --- a/src/lib/components/workflow-actions.svelte +++ b/src/lib/components/workflow-actions.svelte @@ -2,7 +2,7 @@ import { tick } from 'svelte'; import { refresh } from '$lib/stores/workflow-run'; - import { terminateWorkflow } from '$lib/services/terminate-service'; + import { terminateWorkflow } from '$lib/services/workflow-service'; import { settings } from '$lib/stores/settings'; import { writeActionsAreAllowed } from '$lib/utilities/write-actions-are-allowed'; diff --git a/src/lib/components/workflow/batch-operation-confirmation-modal.svelte b/src/lib/components/workflow/batch-operation-confirmation-modal.svelte new file mode 100644 index 000000000..50b00718a --- /dev/null +++ b/src/lib/components/workflow/batch-operation-confirmation-modal.svelte @@ -0,0 +1,80 @@ + + + +

{action} Workflows

+ +
+ {#if allSelected} +

+ Are you sure you want to {action.toLowerCase()} all worklfows matching + the following query? This action cannot be undone. +

+
+ + {query} + +
+ Note: The actual count of workflows that will be affected is the + total number of running workflows matching this query at the time of + clicking "{confirmText}". + {:else} +

+ Are you sure you want to {action.toLowerCase()} + {actionableWorkflowsLength} running {pluralize( + 'workflow', + actionableWorkflowsLength, + )}? +

+ {/if} +
+ +
+
diff --git a/src/lib/components/workflow/workflows-summary-table-with-filters.svelte b/src/lib/components/workflow/workflows-summary-table-with-filters.svelte index 846145b8b..9e79a43a4 100644 --- a/src/lib/components/workflow/workflows-summary-table-with-filters.svelte +++ b/src/lib/components/workflow/workflows-summary-table-with-filters.svelte @@ -16,11 +16,13 @@ const dispatch = createEventDispatcher<{ terminateWorkflows: undefined; + cancelWorkflows: undefined; toggleAll: { checked: boolean }; togglePage: { checked: boolean; visibleWorkflows: WorkflowExecution[] }; }>(); export let bulkActionsEnabled: boolean = false; + export let cancelEnabled: boolean = false; export let updating: boolean = false; export let visibleWorkflows: WorkflowExecution[]; export let selectedWorkflowsCount: number; @@ -35,6 +37,10 @@ dispatch('terminateWorkflows'); }; + const handleBulkCancel = () => { + dispatch('cancelWorkflows'); + }; + const handleSelectAll = (event: MouseEvent | KeyboardEvent) => { if ( event instanceof MouseEvent || @@ -55,7 +61,7 @@ let coreUser = coreUserStore(); - $: terminateDisabled = $coreUser.namespaceWriteDisabled( + $: namespaceWriteDisabled = $coreUser.namespaceWriteDisabled( $page.params.namespace, ); @@ -107,18 +113,27 @@ >)
{/if} - Terminate +
+ {#if cancelEnabled} + Request Cancellation + {/if} + Terminate +
- + - {:else}
diff --git a/src/lib/holocene/table/bulk-action-button.svelte b/src/lib/holocene/table/bulk-action-button.svelte index d6fd7cde2..ac296d2cc 100644 --- a/src/lib/holocene/table/bulk-action-button.svelte +++ b/src/lib/holocene/table/bulk-action-button.svelte @@ -1,20 +1,29 @@