Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): Add ability to select multiple workflows from list and perform actions on them. Fixes #3185 #3234

Merged
merged 69 commits into from
Jun 25, 2020

Conversation

rbreeze
Copy link
Member

@rbreeze rbreeze commented Jun 15, 2020

Checklist:

  • Either (a) I've created an enhancement proposal and discussed it with the community, (b) this is a bug fix, or (c) this is a chore.
  • The title of the PR is (a) conventional, (b) states what changed, and (c) suffixes the related issues number. E.g. "fix(controller): Updates such and such. Fixes #1234".
  • I've signed the CLA.
  • I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged.
  • My builds are green. Try syncing with master if they are not.
  • My organization is added to USERS.md.

rbreeze added 30 commits June 1, 2020 10:34
…mponent to keep track of show/hide state. Reformat column widths for readability and change format of labels
…-functional delete selected button, must revise to deal with async correctly later
@rbreeze rbreeze requested review from simster7 and alexec June 16, 2020 22:00
@alexec alexec changed the title enhancement(ui): Add ability to select multiple workflows from list and perform actions on them. Fix #3185 feat(ui): Add ability to select multiple workflows from list and perform actions on them. Fixes #3185 Jun 16, 2020
@@ -31,7 +31,8 @@ export class WorkflowsService {
'items.metadata.labels',
'items.status.phase',
'items.status.finishedAt',
'items.status.startedAt'
'items.status.startedAt',
'items.spec'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a lot of data - can we avoid it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll check if it can be further specified. I believe I added that for the Util functions for checking to see if an action can be performed on a wf. I'll also see if that can be determined without using spec at all.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only needed item.spec.suspended for the check. Just fixed

Copy link
Member

@simster7 simster7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First round of comments. Code only -- haven't built it yet since I'm working on another build atm


export interface WorkflowAction {
title: string;
action: () => Promise<any>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this Promise return? Does it have to be any?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this any because of the WorkflowDeleteResponse type used here in workflows-service.ts:

public delete(name: string, namespace: string): Promise<WorkflowDeleteResponse> {...

For some reason, the delete function is the only one with its own Response type. I'll look into making a generic WorkflowActionResponse type to get rid of that any

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. If we don't use the result of the promise anywhere then this should be fine

Comment on lines 180 to 185
private confirmAction(title: string): void {
if (!confirm(`Are you sure you want to ${title} this workflow?`)) {
return;
}
return;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this function correct? It doesn't seem the if statement has any effect here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is correct, I tried it without the if and it didn't work (the confirm dialogue didn't pop up)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant more that it doesn't matter if confirm returns true or false, the result doesn't chagne

}
];
const actions: any = Actions.WorkflowActions;
const items = Object.keys(actions).map(actionName => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure that this returns the items in the same order every time? We don't want to have the buttons shuffle around.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fairly confident that as of ES2015 the order of object props can be guaranteed, and as of ES6 Object.keys relies on the underlying method that they use to guarantee the order. Here's the relevant portion of the spec:
https://tc39.es/ecma262/#sec-ordinaryownpropertykeys

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! Thanks

const items = Object.keys(actions).map(actionName => {
const action = actions[actionName];
return {
title: action.title.charAt(0).toUpperCase() + action.title.slice(1),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a tad dirty. Is there a reason as to why action.action is lower case? Could it be capital case instead and if it needs to be lower case then we can do action.action.lower() or whatever the TS equivalent is

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I wasn't wild about that either. Capital case is a good solution and *.lower() is certainly cleaner, I'll do that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem like this was changed?

Comment on lines 187 to 194
private getHandleErrorFunction(title: string): () => void {
return () => {
this.appContext.apis.notifications.show({
content: `Unable to ${title} workflow`,
type: NotificationType.Error
});
};
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can probably just be inlined

Comment on lines 50 to 52
private noneSelected(): boolean {
return Object.keys(this.props.selectedWorkflows).length < 1;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary? Couldn't we just do getNumberSelected() == 0?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good point. Changed

}

private getNumberSelected(): number {
return Object.keys(this.props.selectedWorkflows).length;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could be wrong, but doesn't

Suggested change
return Object.keys(this.props.selectedWorkflows).length;
return this.props.selectedWorkflows.length;

work?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried it, it doesn't - since selectedWorkflows is an object rather than an array it doesn't have a length property

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great

Comment on lines 75 to 80
private confirmAction(title: string): void {
if (!confirm(`Are you sure you want to ${title} all selected workflows?`)) {
return;
}
return;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here regarding the if statement

Comment on lines 82 to 90
private getHandleErrorFunction(title: string): () => void {
return () => {
this.props.loadWorkflows();
this.appContext.apis.notifications.show({
content: `Unable to ${title} workflow`,
type: NotificationType.Error
});
};
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could also be inlined


private renderActions(ctx: any): JSX.Element[] {
const actionButtons = [];
for (const action of this.getActions(ctx)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we don't need getActions and can do the whole thing in one loop

Copy link
Member

@simster7 simster7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I click the checkbox on a Workflow, it treats it as a link: https://cl.ly/13ecf2ed9f9c I'm on Firefox if that matters

On another note, can we hide the toolbar unless there is one or more Workflows selected?

Also, I don't think we need the "Selected" in $ACTION Selected on the buttons. If we only show the toolbar when there is >= 1 Workflows selected, then it should be clear what we mean

@rbreeze
Copy link
Member Author

rbreeze commented Jun 18, 2020

Ah interesting, will test on Firefox and fix. Might be a z-index issue.

I was thinking along the same lines but was undecided, that makes the decision easy... will change both

@sonarcloud
Copy link

sonarcloud bot commented Jun 18, 2020

Kudos, SonarCloud Quality Gate passed!

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities (and Security Hotspot 0 Security Hotspots to review)
Code Smell A 0 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

alexec
alexec previously requested changes Jun 18, 2020
Copy link
Contributor

@alexec alexec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good work!

[action in WorkflowActionName]: boolean;
};

export type WorkflowActionName = 'RETRY' | 'RESUBMIT' | 'SUSPEND' | 'RESUME' | 'STOP' | 'TERMINATE' | 'DELETE';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the actions map to API which use lowercase names?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also - I think you should use the term operation rather than action

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had them as lowercase but changed to upper for labeling purposes. Simon pointed out that the way I was initially converting lowercase to a label (string.charAt(0).toUpperCase() + string.slice(1);) was clunky and just having them as uppercase would be easier. Thinking about it now though, it wouldn't be clunky to have them lowercase and do string.toUpperCase() to convert. The union type is never used to actually query the API here, so would the change to lowercase be for consistency?

Operation sounds good, I'll change that.

disabled: (wf: Workflow) => boolean;
}

export const WorkflowActions = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

disabled: (wf: Workflow) => !Utils.isWorkflowSuspended(wf),
action: services.workflows.terminate
},
DELETE: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make sure that we get an "are you sure" confirmation for delete?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the workflows toolbar and the workflows details, there's a confirmation before deleting - in fact for the workflows toolbar, there's a confirmation before performing any batch action. Would it be better to have the confirmation happen in workflow-actions.ts?

Copy link
Member

@simster7 simster7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Excellent work!

@@ -69,7 +70,8 @@ export class WorkflowsService {
'result.object.status.phase',
'result.object.status.startedAt',
'result.type',
'result.object.metadata.labels'
'result.object.metadata.labels',
'result.object.spec'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want the whole spec here? could be a lot of data


export type WorkflowOperationName = 'RETRY' | 'RESUBMIT' | 'SUSPEND' | 'RESUME' | 'STOP' | 'TERMINATE' | 'DELETE';

export interface WorkflowAction {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WorkflowOperation?

@@ -14,7 +14,7 @@ import {CostOptimisationNudge} from '../../../shared/components/cost-optimisatio
import {Loading} from '../../../shared/components/loading';
import {hasWarningConditionBadge} from '../../../shared/conditions-panel';
import {Consumer, ContextApis} from '../../../shared/context';
import {Utils} from '../../../shared/utils';
import * as Actions from '../../../shared/workflow-operations';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Operations?

Copy link
Contributor

@alexec alexec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add some screenshots please?

@rbreeze
Copy link
Member Author

rbreeze commented Jun 22, 2020

of course, here are a couple:
Screen Shot 2020-06-22 at 12 36 31 PM
Screen Shot 2020-06-22 at 12 43 43 PM

@rbreeze rbreeze merged commit baad42e into argoproj:master Jun 25, 2020
@hadim
Copy link

hadim commented Jul 4, 2020

I can't see this feature in 2.9.1. Is that planned for 2.10 or do we need to do something to enable it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants