Skip to content

Commit

Permalink
Issue argoproj#46: Override notification settings (argoproj#345)
Browse files Browse the repository at this point in the history
* argoproj#46 Added "Set Up Job Notifications"

* argoproj#46 Added "Selected job events" and "Argo Users and Groups".

* argoproj#46 Added 'Slack Channels' side panel, style adjustment.

* argoproj#46 Added 'Email' input to be able to add any user by email, added validation.

* argoproj#46 Added default notification. Displaying correct data at side panels.

* argoproj#46 Connected created step to multiple service panel. Added validations.

* argoproj#46 Fix types issue. Added email next to user name - this is required if we have two recipients with the same name and surname.

* argoproj#46 Job notifications connected to multiple service launch panel.

* argoproj#46 Open side panels if checkbox changed to true.

* argoproj#46 Remove unused line.
  • Loading branch information
wokeGit committed Nov 20, 2017
1 parent 4c67099 commit 10023e3
Show file tree
Hide file tree
Showing 12 changed files with 548 additions and 114 deletions.
2 changes: 2 additions & 0 deletions saas/axops/src/ui/src/app/common/components.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { CommitPanelComponent } from './commit-panel/commit-panel.component';
import { CommitAuthorComponent } from './commit-author/commit-author.component';
import { CommitDescriptionComponent } from './commit-description/commit-description.component';
import { MultipleServiceLaunchPanelComponent } from './multiple-service-launch-panel/multiple-service-launch-panel.component';
import { SetupJobNotificationsComponent } from './multiple-service-launch-panel/setup-job-notifications/setup-job-notifications.component';
import { LaunchPanelService } from './multiple-service-launch-panel/launch-panel.service';
import { BranchesPanelComponent } from './branches-panel/branches-panel.component';
import { BranchSelectorPanelComponent } from './branch-selector-panel/branch-selector-panel.component';
Expand Down Expand Up @@ -87,6 +88,7 @@ let components: any[] = [
CommitAuthorComponent,
CommitDescriptionComponent,
MultipleServiceLaunchPanelComponent,
SetupJobNotificationsComponent,
BranchesPanelComponent,
BranchSelectorPanelComponent,
CommitSelectorPanelComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import * as _ from 'lodash';
import { Component, Output, EventEmitter } from '@angular/core';
import { Component, Output, EventEmitter, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { Subscription } from 'rxjs/Subscription';
import { Observable } from 'rxjs/Observable';

import {Artifact, Commit, Template, Task, Project, ProjectAction, Branch } from '../../model';
import { Artifact, Commit, Template, Task, Project, ProjectAction, Branch } from '../../model';
import { TemplateService, TaskService, CommitsService } from '../../services';
import { NotificationsService } from 'argo-ui-lib/src/components';
import { Session, HtmlForm, MULTIPLE_SERVICE_LAUNCH_PANEL_TABS } from './multiple-service-launch-panel.view-models';
import { SetupJobNotificationsComponent } from './setup-job-notifications/setup-job-notifications.component';

@Component({
selector: 'ax-multiple-service-launch-panel',
Expand All @@ -18,14 +19,17 @@ import { Session, HtmlForm, MULTIPLE_SERVICE_LAUNCH_PANEL_TABS } from './multipl
export class MultipleServiceLaunchPanelComponent {
@Output() submitted: EventEmitter<any> = new EventEmitter();

@ViewChild(SetupJobNotificationsComponent)
private setupJobNotifications: SetupJobNotificationsComponent;

public templates: Template[] = [];
public templatesToSubmit: Template[] = [];
public allSelected: boolean = false;
public selectedItems: number = 0;
public templateLoader: boolean = false;
public commit: Commit = new Commit();
public isVisibleSelectServiceTemplatesPanel: boolean = false;
public selectingScreen: boolean = true;
public stepNumber: 1 | 2 | 3 = 1;
public activeElementId: number = 0;
public isSubmitClicked: boolean = false;
public summaryErrorMessage: boolean = false;
Expand Down Expand Up @@ -82,7 +86,7 @@ export class MultipleServiceLaunchPanelComponent {
this.task = null;
this.projectInfo = null;
this.resubmit = resubmit;
this.selectingScreen = true;
this.stepNumber = 1;

this.withCommitOnly = withCommitOnly;
if (options) {
Expand All @@ -95,11 +99,11 @@ export class MultipleServiceLaunchPanelComponent {
this.isVisibleSelectServiceTemplatesPanel = true;
this.next();
}
this.selectingScreen = false;
this.stepNumber = 2;
} else {
this.task = null;
this.projectInfo = null;
this.selectingScreen = true;
this.stepNumber = 1;
}

if (commit) {
Expand Down Expand Up @@ -133,7 +137,7 @@ export class MultipleServiceLaunchPanelComponent {
this.session = new Session();
this.templates = [];
this.templatesToSubmit = [];
this.selectingScreen = true;
this.stepNumber = 1;
this.activeElementId = 0;
this.allSelected = false;
this.allForms.reset();
Expand Down Expand Up @@ -250,18 +254,23 @@ export class MultipleServiceLaunchPanelComponent {
}) !== undefined;
}

goToNextStep() {
this.stepNumber += 1;
this.next();
}

next() {
this.selectingScreen = false;
this.templatesToSubmit = this.templates.filter(item => item.selected);

this.prepareForms(this.templatesToSubmit);
}

submit() {
let notifications = this.setupJobNotifications.getNotifications();
this.isSubmitClicked = true;
this.summaryErrorMessage = this.allForms.invalid;

if (this.allForms.valid) {
if (this.allForms.valid && notifications) {
if (this.resubmit) {
this.resubmitTask(this.task, this.resubmit);
} else {
Expand All @@ -271,6 +280,7 @@ export class MultipleServiceLaunchPanelComponent {
observableList.push(this.taskService.launchTask({
template_id: this.templatesToSubmit[index].id,
arguments: this.listParameters(this.allForms.controls[index.toString()]['controls']),
notifications: notifications
}));
});

Expand Down Expand Up @@ -323,7 +333,6 @@ export class MultipleServiceLaunchPanelComponent {
this.getTemplates(branch.repo);
}


private prepareForms(templates: Template[], resubmitFailedParameters?: any) {
templates.forEach((template, index) => {
let newForm = new FormGroup({});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<ax-sliding-panel [show]="isVisibleSelectServiceTemplatesPanel" [hasCloseButton]="false" [hasNoPadding]="true" (closePanel)="closePanel()">
<sliding-panel-header>
<button *ngIf="selectingScreen" class="ax-button ax-button--base" [ngClass]="{'disabled': !isAnyTemplateSelected()}"
type="button" (click)="next()" [disabled]="!isAnyTemplateSelected()" axButtonWave>Next</button>
<button *ngIf="!selectingScreen" class="ax-button ax-button--base" [ngClass]="{'disabled': summaryErrorMessage}"
<button *ngIf="stepNumber === 1" class="ax-button ax-button--base" [ngClass]="{'disabled': !isAnyTemplateSelected()}"
type="button" (click)="goToNextStep()" [disabled]="!isAnyTemplateSelected()" axButtonWave>Next</button>
<button *ngIf="stepNumber === 2" class="ax-button ax-button--base"
type="button" (click)="goToNextStep()" [disabled]="false" axButtonWave>Next</button>
<button *ngIf="stepNumber === 3" class="ax-button ax-button--base" [ngClass]="{'disabled': summaryErrorMessage}"
type="button" (click)="submit()" [disabled]="summaryErrorMessage || templateLoader"
axButtonWave>SUBMIT<span *ngIf="templatesToSubmit?.length > 1"> ALL</span></button>
<button class="ax-button" type="button" (click)="closePanel()" axButtonWave>Cancel</button>
Expand All @@ -14,7 +16,7 @@
</ul>
</sliding-panel-header>
<sliding-panel-body>
<div class="panel-box" *ngIf="selectingScreen">
<div class="panel-box" *ngIf="stepNumber === 1">
<div class="panel-box__content">
<div class="panel-box__title">
Select templates to create jobs
Expand Down Expand Up @@ -105,7 +107,7 @@ <h3>Templates</h3>
</div>
</div>

<div class="panel-box" *ngIf="!selectingScreen">
<div class="panel-box" *ngIf="stepNumber === 2">
<div class="panel-box__summary-error" *ngIf="summaryErrorMessage">
Please enter the required parameters and submit again.
</div>
Expand Down Expand Up @@ -195,6 +197,9 @@ <h3 *ngIf="!templateLoader && isActiveFormEmpty">
</div>
</div>
</div>
<div class="panel-box" *ngIf="stepNumber === 3">
<ax-setup-job-notifications></ax-setup-job-notifications>
</div>
</sliding-panel-body>
</ax-sliding-panel>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import * as _ from 'lodash';
import { Component, OnInit } from '@angular/core';

import { FilterMultiSelect } from 'argo-ui-lib/src/components';

import { PolicyNotification } from '../../../model';
import { CustomRegex } from '../../customValidators/CustomRegex';
import { AuthenticationService } from '../../../services';

interface NotificationObject {
jobEvents: FilterMultiSelect;
isArgoUsersAndGroupsVisible: boolean;
isSlackChannelsVisible: boolean;
isEmailVisible: boolean;
rules: PolicyNotification;
outsideUsers: string[];
filteredOutsideUsers: string[];
validationMessages: any;
}

@Component({
selector: 'ax-setup-job-notifications',
templateUrl: './setup-job-notifications.html',
styles: [ require('./setup-job-notifications.scss') ],
})
export class SetupJobNotificationsComponent implements OnInit {

public notification: NotificationObject = {
jobEvents: {
items: [
{name: 'on_change', value: 'on_change', checked: false},
{name: 'on_cron', value: 'on_cron', checked: false},
{name: 'on_failure', value: 'on_failure', checked: false},
{name: 'on_pull_request', value: 'on_pull_request', checked: false},
{name: 'on_pull_request_merge', value: 'on_pull_request_merge', checked: false},
{name: 'on_push', value: 'on_push', checked: false},
{name: 'on_start', value: 'on_start', checked: false},
{name: 'on_success', value: 'on_success', checked: false},
],
selectedItems: [],
messages: {
name: 'JOB EVENTS',
},
isVisible: false,
isStaticList: true,
isDisplayedInline: true,
},

isArgoUsersAndGroupsVisible: false,
isSlackChannelsVisible: false,
isEmailVisible: false,
rules: {
whom: [],
when: []
},
outsideUsers: [],
filteredOutsideUsers: [],
validationMessages: {
jobEvents: { show: false, text: 'You have to choose at least one Job Event' },
wrongFormatRecipients: { show: false, text: 'Recipients have to be an email format' },
missingRecipients: { show: false, text: 'You have to choose at least one recipient from any of the groups' },
}
};

public rules: PolicyNotification[] = [];
public notificationsList: any[] = [];
public isVisibleUserSelectorPanel: boolean = false;
public isVisibleSlackPanel: boolean = false;
public selectedId: number = 0;

constructor(private authenticationService: AuthenticationService) {
}

ngOnInit() {
if (!this.rules.length) {
this.addDefaultNotificationRule();
}
}

public onRuleChange(when: string[], index) {
this.notificationsList[index].rules.when = when;
this.notificationsList[index].validationMessages.jobEvents.show = false;
}

public addNotificationRule() {
this.notificationsList.push(JSON.parse(JSON.stringify(this.notification)));
}

public removeNotificationRule(index) {
this.notificationsList.splice(index, 1);
}

public openUserSelectorPanel(index) {
this.isVisibleUserSelectorPanel = true;
this.selectedId = index;
this.notificationsList[index].validationMessages.missingRecipients.show = false;
}

public closeUserSelectorPanel() {
this.isVisibleUserSelectorPanel = false;
}

public openSlackChannelPanel(index) {
this.isVisibleSlackPanel = true;
this.selectedId = index;
this.notificationsList[index].validationMessages.missingRecipients.show = false;
}

public closeSlackChannelPalen() {
this.isVisibleSlackPanel = false;
}

public getOutsideUsers(index) {
this.notificationsList[index].filteredOutsideUsers =
this.notificationsList[index].rules.whom.recipients.filter(recipient => recipient.indexOf('@user') !== -1).sort();
this.notificationsList[index].outsideUsers = this.notificationsList[index].filteredOutsideUsers.map(user => user.substring(0, user.indexOf('@user')));
return this.notificationsList[index].rules.whom.filter(recipient => recipient.indexOf('@user') !== -1).sort();
}

public getOnlyUsersAndGroups(index) {
return this.notificationsList.length ?
this.notificationsList[index].rules.whom.filter(recipient => recipient.indexOf('@slack') === -1 && recipient.indexOf('@user') === -1).sort() : [];
}

public getOnlySlackChannels(index) {
return this.notificationsList.length ?
this.notificationsList[index].rules.whom.filter(recipient => recipient.indexOf('@slack') !== -1).sort() : [];
}

public updateOnlyUsersAndGroupsList(users: string[]) {
this.notificationsList[this.selectedId].rules.whom =
this.notificationsList[this.selectedId].rules.whom.filter(recipient => (recipient.indexOf('@slack') !== -1 || recipient.indexOf('@user') !== -1)).sort();
this.updateNotificationWhomList(users);
}

public updateSlackChannelsList(channels: string[]) {
this.notificationsList[this.selectedId].rules.whom =
this.notificationsList[this.selectedId].rules.whom.filter(recipient => (recipient.indexOf('@slack') === -1)).sort();
let axSlackChannelsList = channels.map(channel => `${channel}@slack`);
this.updateNotificationWhomList(axSlackChannelsList);
}

public updateNotificationWhomList(list: string[]) {
this.notificationsList[this.selectedId].rules.whom =
this.notificationsList[this.selectedId].rules.whom.concat(list).filter((value, index, self) => self.indexOf(value) === index );
}

public updateOutsideUsers(users: string, index) {
this.selectedId = index;
this.notificationsList[index].outsideUsers = users.split(',');
this.notificationsList[index].filteredOutsideUsers =
this.notificationsList[index].outsideUsers.filter(user => CustomRegex.emailPattern.test(user.trim())).map(user => `${user}@user`);
this.notificationsList[index].validationMessages.wrongFormatRecipients.show = false;
this.notificationsList[index].validationMessages.missingRecipients.show = false;
}

public argoUsersAndGroupsCheckboxChange(notification, index) {
notification.isArgoUsersAndGroupsVisible = !notification.isArgoUsersAndGroupsVisible;
if (!notification.isArgoUsersAndGroupsVisible) {
notification.rules.whom = notification.rules.whom.filter(recipient => (recipient.indexOf('@slack') !== -1 || recipient.indexOf('@user') !== -1)).sort();
} else {
this.openUserSelectorPanel(index);
}
}

public emailCheckboxChange(notification) {
notification.isEmailVisible = !notification.isEmailVisible;
if (!notification.isEmailVisible) {
notification.outsideUsers = [];
notification.filteredOutsideUsers = [];
notification.rules.whom = notification.rules.whom.filter(recipient => (recipient.indexOf('@user') === -1)).sort();
}
}

public slackChannelsCheckboxChange(notification, index) {
notification.isSlackChannelsVisible = !notification.isSlackChannelsVisible;
if (!notification.isSlackChannelsVisible) {
notification.rules.whom = notification.rules.whom.filter(recipient => (recipient.indexOf('@slack') === -1)).sort();
} else {
this.openSlackChannelPanel(index);
}
}

public getNotifications(): PolicyNotification[] {
let isAnyError = false;
this.notificationsList.forEach(notification => {
if (!notification.rules.when.length) {
notification.validationMessages.jobEvents.show = true;
isAnyError = true;
}

if (notification.outsideUsers.length !== notification.filteredOutsideUsers.length && notification.outsideUsers.toString().length) {
notification.validationMessages.wrongFormatRecipients.show = true;
isAnyError = true;
} else {
// remove all '@user' elements from rules.whom
notification.rules.whom = notification.rules.whom.filter(item => item.indexOf('@user') === -1);
this.updateNotificationWhomList(notification.filteredOutsideUsers);
}

if (!notification.rules.whom.length) {
notification.validationMessages.missingRecipients.show = true;
isAnyError = true;
}
});

// if there is error in any notification rule, don't allow to submit
if (isAnyError) {
return;
}

this.rules = this.notificationsList.map(notification => {
return notification.rules;
});

return _.uniqBy(this.rules, v => [v.whom, v.when].join());
}

private addDefaultNotificationRule() {
let notification = JSON.parse(JSON.stringify(this.notification));
notification.rules = {
whom: [this.authenticationService.getUsername()],
when: ['on_failure', 'on_success']
};
notification.isArgoUsersAndGroupsVisible = true;

this.notificationsList.push(notification);
}
}
Loading

0 comments on commit 10023e3

Please sign in to comment.