Skip to content

Commit

Permalink
scenario thresholds (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
ludeknovy authored Mar 2, 2021
1 parent 6b9b291 commit 0ccae37
Show file tree
Hide file tree
Showing 12 changed files with 241 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import { ItemControlsComponent } from './scenario/item-controls/item-controls.co
import { ShareComponent } from './item-detail/share/share.component';
import { CreateNewShareLinkComponent } from './item-detail/share/create-new-share-link/create-new-share-link.component';
import { DeleteShareLinkComponent } from './item-detail/share/delete-share-link/delete-share-link.component';
import { ThresholdComponent } from './scenario/threshold/threshold.component';

const appRoutes: Routes = [
{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
Expand Down Expand Up @@ -138,6 +139,7 @@ const appRoutes: Routes = [
ShareComponent,
CreateNewShareLinkComponent,
DeleteShareLinkComponent,
ThresholdComponent,
],
imports: [
RouterModule.forRoot(
Expand Down
43 changes: 40 additions & 3 deletions src/app/item-detail/item-detail.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
<div class="btn-group mr-3">
<div display="dynamic" [placement]="['bottom-right', 'bottom-left']" class="btn-group" ngbDropdown role="group"
aria-label="Button group with nested dropdown">
<button class="btn btn-sm jtl-no-glow jtl-control-menu hamburger-menu" ngbDropdownToggle><i class="fas fa-bars"></i></button>
<button class="btn btn-sm jtl-no-glow jtl-control-menu hamburger-menu" ngbDropdownToggle><i
class="fas fa-bars"></i></button>
<div class="dropdown-menu jtl-dropdown-control-menu" ngbDropdownMenu>
<app-edit-item *ngIf="itemData.environment"
[itemDetailData]="{note: itemData.note, environment: itemData.environment, hostname: itemData.hostname, isBase: itemData.isBase, params: itemParams}"
Expand Down Expand Up @@ -48,8 +49,44 @@
</div>

<div class="items-overview content-container" *ngIf="itemData.overview !== null && itemData.reportStatus === 'ready'">


<div class="container-fluid">

<div class="overview-info">

<div class="row">
<div class="col">
<ng-template #tipContent>
<p>90 percentile decrease toleration: {{itemData.thresholds.thresholds.percentile}} %
</p>
<p>
Througput decrease toleration: {{itemData.thresholds.thresholds.throughput}}%
</p>
<p>
Error rate increase toleration: {{itemData.thresholds.thresholds.errorRate}}%
</p>
</ng-template>
<div *ngIf="itemData.thresholds && !itemData.thresholds?.passed" class="card perf-issue">
<div class="card-body perf-issue">
<h6 class="card-title text-alizarin">Performance regression issue detected! <i class="ttp text-secondary"
placement="bottom"
[ngbTooltip]="tipContent"><i
class="far fa-question-circle icon"></i></i></h6>
<ul>
<li *ngIf="!itemData.thresholds.result.percentile.passed"> 90 percentile response time is about {{
Math.round(itemData.thresholds.result.percentile.diffValue - 100) }}% slower than the average.
</li>
<li *ngIf="!itemData.thresholds.result.errorRate.passed">Error rate is about {{
Math.round(100 - itemData.thresholds.result.errorRate.diffValue) }}% higher than the average.</li>
<li *ngIf="!itemData.thresholds.result.throughput.passed">Throughput is about {{
Math.round(100 - itemData.thresholds.result.throughput.diffValue ) }}% lower than the average.</li>
</ul>
</div>
</div>
</div>
</div>

<div class="row">
<div class="col-sm">
<div class="card">
Expand Down Expand Up @@ -158,7 +195,6 @@ <h2 class="card-title text-alizarin">{{itemData.overview.errorRate}} <span

</div>


<div class="overview-additional">
<div class="row info">
<div class="col-sm">
Expand Down Expand Up @@ -364,7 +400,8 @@ <h6 class="card-header bg-transparent">Request Statistics <span class="compare">
[labelInput]="{ labelName: _.label, params: itemParams }"></app-label-error>
</td>
<td>
<app-label-trend *ngIf="!isAnonymous" [trendInput]="{ environment: itemData.environment, labelName: _.label }">
<app-label-trend *ngIf="!isAnonymous"
[trendInput]="{ environment: itemData.environment, labelName: _.label }">
</app-label-trend>
</td>
</tr>
Expand Down
8 changes: 8 additions & 0 deletions src/app/item-detail/item-detail.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,11 @@ thead .hd {
.charts .card-header {
border-bottom: 0px;
}

.perf-issue {
border-left: 2px solid #e74c3c;
}

.perf-issue .card-body h6 {
font-size: 1rem !important;
}
8 changes: 8 additions & 0 deletions src/app/items.service.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ export interface ItemDetail {
};
statistics: any;
attachements: [];
thresholds?: {
passed: boolean,
diff: {
errorRateDiff: number,
percentileRateDiff: number,
throughputRateDiff: number
}
};
}

interface MonitoringData {
Expand Down
6 changes: 5 additions & 1 deletion src/app/notification/notification-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Injectable } from '@angular/core';
export class NotificationMessage {

newTestItemNotificationMessage(response) {
return this.statusCodeMessage(response, 'File updoaded and processing just started');
return this.statusCodeMessage(response, 'File uploaded and processing just started');
}

newProjectNotificationMessage(response) {
Expand Down Expand Up @@ -78,6 +78,10 @@ export class NotificationMessage {
return this.statusCodeMessage(response, 'Link was deleted');
}

scenarioThresholdUpdate(response) {
return this.statusCodeMessage(response, 'Thresholds were updated');
}

private statusCodeMessage(response, succesMessgae) {
let message = { success: false, message: `Something went wrong` };
if (response.status >= 200 && response.status < 300) {
Expand Down
7 changes: 7 additions & 0 deletions src/app/scenario-api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,14 @@ export class ScenarioApiService {

createNewScenarioNotification(projectName, scenarioName, body): Observable<{}> {
return this.http.post(`projects/${projectName}/scenarios/${scenarioName}/notifications`, body, { observe: 'response' });
}

fetchThresholds(projectName, scenarioName) {
return this.http.get(`projects/${projectName}/scenarios/${scenarioName}/thresholds`);
}

updateThresholds(projectName, scenarioName, body) {
return this.http.put(`projects/${projectName}/scenarios/${scenarioName}/thresholds`, body, { observe: 'response'});
}

setData(data) {
Expand Down
4 changes: 4 additions & 0 deletions src/app/scenario/scenario.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<div class="dropdown-menu jtl-dropdown-control-menu" ngbDropdownMenu>
<app-edit-scenario [scenarioData]="items$"></app-edit-scenario>
<app-external-notification></app-external-notification>
<app-threshold [params]=params></app-threshold>
<app-delete-scenario [scenarioData]="params"></app-delete-scenario>
</div>
</div>
Expand Down Expand Up @@ -132,6 +133,9 @@ <h6 class="card-header bg-transparent">Test Runs
class="fas fa-minus-circle text-danger status-icon"></i></span>
<span *ngIf="_.status === '10'" title="Status not set"><i
class="far fa-circle text-secondary status-icon"></i></span>

<i *ngIf="_.thresholdPassed === false" class="fas fa-exclamation-circle text-alizarin performance-issue"></i>

</td>
<td class="text-right" >
<app-item-controls [item]="{note: _.note, environment: _.environment, hostname: _.hostname, isBase: _.base, params: { id: _.id, scenarioName: params.scenarioName, projectName: params.projectName }}"></app-item-controls>
Expand Down
4 changes: 4 additions & 0 deletions src/app/scenario/scenario.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,7 @@ tbody tr:hover {
font-size: 19px;
}

.performance-issue {
margin-left: 10px;
font-size: 19px;
}
Empty file.
39 changes: 39 additions & 0 deletions src/app/scenario/threshold/threshold.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<ng-template #content let-modal>
<div class="modal-header">
<h5 class="modal-title" id="modal-basic-title">Scenario thresholds</h5>
<button type="button" style="outline: none;" style="outline: none;" class="close" aria-label="Close" (click)="modal.dismiss('Cross click')">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form [formGroup]="thresholdForm" (ngSubmit)="onSubmit()">
<div class="modal-body">
<div class="alert alert-primary" role="alert">
<i class="fas fa-info-circle"> </i>
Scenario thresholds will raise an alert in a report detail in case the given metrics diverge from the previous
reports average by more than specified threshold percentage.
</div>
<div class="form-group">
<label for="exampleInputEmail1">90% percentil</label>
<input t type="input" class="form-control" formControlName="percentile" >
</div>
<div class="form-group">
<label for="exampleInputEmail1">Throughput</label>
<input type="text" class="form-control" formControlName="throughput">
</div>
<div class="form-group">
<label for="exampleInputEmail1">Error rate</label>
<input type="text" class="form-control" formControlName="errorRate">
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="exampleCheck1" formControlName="enabled">
<label class="form-check-label" for="exampleCheck1">Enabled</label>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>

</ng-template>

<button class="notification btn btn-sm btn-primary" (click)="open(content)" ngbDropdownItem> Thresholds</button>
30 changes: 30 additions & 0 deletions src/app/scenario/threshold/threshold.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { HttpClientModule } from '@angular/common/http';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';

import { ThresholdComponent } from './threshold.component';

describe('ThresholdComponent', () => {
let component: ThresholdComponent;
let fixture: ComponentFixture<ThresholdComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ThresholdComponent],
imports: [ReactiveFormsModule, HttpClientModule],

})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(ThresholdComponent);
component = fixture.componentInstance;
component.params = {};
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
94 changes: 94 additions & 0 deletions src/app/scenario/threshold/threshold.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Component, Input, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { of } from 'rxjs';
import { catchError } from 'rxjs/internal/operators/catchError';
import { NotificationMessage } from 'src/app/notification/notification-messages';
import { ScenarioApiService } from 'src/app/scenario-api.service';

@Component({
selector: 'app-threshold',
templateUrl: './threshold.component.html',
styleUrls: ['./threshold.component.css']
})
export class ThresholdComponent implements OnInit {

thresholdForm: FormGroup;
percentile;
errorRate;
throughput;
enabled;

@Input() params;

constructor(
private modalService: NgbModal,
private scenarioApiService: ScenarioApiService,
private notification: NotificationMessage
) {}

ngOnInit(): void {
this.scenarioApiService.fetchThresholds(this.params.projectName, this.params.scenarioName).subscribe(_ => {
this.createFormControls(_);
this.createForm();
});
}

createFormControls(thresholds) {
this.percentile = new FormControl(thresholds.percentile, [
Validators.min(0),
Validators.max(100),
Validators.required,
]);
this.throughput = new FormControl(thresholds.throughput, [
Validators.min(0),
Validators.max(100),
Validators.required,
]);
this.errorRate = new FormControl(thresholds.errorRate, [
Validators.min(0),
Validators.max(100),
Validators.required
]);
this.enabled = new FormControl(thresholds.enabled, [
Validators.required,
]);
}

createForm() {
this.thresholdForm = new FormGroup({
percentile: this.percentile,
throughput: this.throughput,
errorRate: this.errorRate,
enabled: this.enabled
});
}

open(content) {
this.modalService.open(content, { ariaLabelledBy: 'modal-basic-title', size: 'lg' });
}

onSubmit() {
if (this.thresholdForm.valid) {
const { projectName, scenarioName } = this.params;
const body = {
errorRate: parseFloat(this.thresholdForm.value.errorRate),
throughput: parseFloat(this.thresholdForm.value.throughput),
percentile: parseFloat(this.thresholdForm.value.percentile),
enabled: this.thresholdForm.value.enabled
};

this.scenarioApiService.updateThresholds(projectName, scenarioName, body)
.pipe(catchError(r => of(r)))
.subscribe(_ => {
const message = this.notification.scenarioThresholdUpdate(_);
this.scenarioApiService.setData(message);
});
this.modalService.dismissAll();
}
}


}


0 comments on commit 0ccae37

Please sign in to comment.