diff --git a/src/app/administration/administration.css b/src/app/administration/administration.css index e2b3bf6e..30922844 100644 --- a/src/app/administration/administration.css +++ b/src/app/administration/administration.css @@ -22,15 +22,6 @@ a { font-size: 12px; } -.head { - font-size: 12px; - padding-bottom: 5px; - border-top: 0px; - width: 30rem; - height: 43px; - color: #343a40; -} - .card-body { padding: 0px; } @@ -65,7 +56,6 @@ a { th { padding-left: 20px; - color: #888888c7; } td { diff --git a/src/app/administration/api-token/api-keys.component.html b/src/app/administration/api-token/api-keys.component.html index 641d2618..ed8527a8 100644 --- a/src/app/administration/api-token/api-keys.component.html +++ b/src/app/administration/api-token/api-keys.component.html @@ -15,11 +15,11 @@ - - - - - + + + + + diff --git a/src/app/administration/projects/administration.component.html b/src/app/administration/projects/administration.component.html index 6b4f8235..4d4f717b 100644 --- a/src/app/administration/projects/administration.component.html +++ b/src/app/administration/projects/administration.component.html @@ -15,11 +15,11 @@
tokendescriptioncreated atcreated bytokendescriptioncreated atcreated by
- - - - - + + + + + diff --git a/src/app/administration/users/users.component.html b/src/app/administration/users/users.component.html index 5f5d4ebf..68842600 100644 --- a/src/app/administration/users/users.component.html +++ b/src/app/administration/users/users.component.html @@ -15,10 +15,10 @@
project namenumber of launchesnumber of scenarioslast runproject namenumber of launchesnumber of scenarioslast run
- - - - + + + + diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ceb6e19b..e544379c 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -50,6 +50,11 @@ import { MyProfileComponent } from './administration/my-profile/my-profile.compo import { UsersComponent } from './administration/users/users.component'; import { AddUserComponent } from './administration/users/add-user/add-user.component'; import { DeleteUserComponent } from './administration/users/delete-user/delete-user.component'; +import { ExternalNotificationComponent } from './scenario/external-notification/external-notification.component'; +// tslint:disable-next-line:max-line-length +import { AddNewExternalNotificationComponent } from './scenario/external-notification/add-new-external-notification/add-new-external-notification.component'; +// tslint:disable-next-line:max-line-length +import { DeleteExternalNotificationComponent } from './scenario/external-notification/delete-external-notification/delete-external-notification.component'; const appRoutes: Routes = [ { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] }, @@ -122,6 +127,9 @@ const appRoutes: Routes = [ UsersComponent, AddUserComponent, DeleteUserComponent, + ExternalNotificationComponent, + AddNewExternalNotificationComponent, + DeleteExternalNotificationComponent, ], imports: [ RouterModule.forRoot( diff --git a/src/app/dashboard/dashboard.component.css b/src/app/dashboard/dashboard.component.css index 6c4296e2..bf73a994 100644 --- a/src/app/dashboard/dashboard.component.css +++ b/src/app/dashboard/dashboard.component.css @@ -22,13 +22,6 @@ a { font-size: 12px; } -.head { - font-size: 12px; - color: #888888c7; - border-top: 0px; - min-width: 8rem; -} - .recent-runs .card-body { padding-top: 0.2rem; } diff --git a/src/app/dashboard/dashboard.component.html b/src/app/dashboard/dashboard.component.html index ed0f551d..d1c166f7 100644 --- a/src/app/dashboard/dashboard.component.html +++ b/src/app/dashboard/dashboard.component.html @@ -72,11 +72,11 @@
Recent Test Runs
usernamecreate date# of tokensusernamecreate date# of tokens
- - - - - + + + + + diff --git a/src/app/items.service.model.ts b/src/app/items.service.model.ts index 962b8348..f58c67ac 100644 --- a/src/app/items.service.model.ts +++ b/src/app/items.service.model.ts @@ -157,3 +157,10 @@ export interface LabelMaxVu { count: number; }]; } + +export interface ScenarioNotifications { + id: string; + url: string; + type: string; +} + diff --git a/src/app/notification/notification-messages.ts b/src/app/notification/notification-messages.ts index d5542b73..821c7090 100644 --- a/src/app/notification/notification-messages.ts +++ b/src/app/notification/notification-messages.ts @@ -62,6 +62,14 @@ export class NotificationMessage { return this.statusCodeMessage(response, 'User has been deleted'); } + deleteScenarioNotification(response) { + return this.statusCodeMessage(response, 'Notification has been deleted'); + } + + createScenarioNotification(response) { + return this.statusCodeMessage(response, 'Notification has been created'); + } + private statusCodeMessage(response, succesMessgae) { let message = { success: false, message: `Something went wrong` }; if (response.status >= 200 && response.status < 300) { diff --git a/src/app/scenario-api.service.ts b/src/app/scenario-api.service.ts index 2aaf348c..120e1ae5 100644 --- a/src/app/scenario-api.service.ts +++ b/src/app/scenario-api.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { BehaviorSubject, Observable } from 'rxjs'; -import { IScenarios } from './items.service.model'; +import {IScenarios, ScenarioNotifications} from './items.service.model'; @Injectable({ providedIn: 'root' @@ -11,8 +11,7 @@ export class ScenarioApiService { private response = new BehaviorSubject({}); public response$ = this.response.asObservable(); - constructor(private http: HttpClient) { - } + constructor(private http: HttpClient) {} updateScenario(projectName, scenarioName, body): Observable<{}> { return this.http.put(`projects/${projectName}/scenarios/${scenarioName}`, body, { observe: 'response'}); @@ -35,6 +34,19 @@ export class ScenarioApiService { return this.http.post(`projects/${projectName}/scenarios`, body, { observe: 'response'}); } + fetchScenarioNotification(projectName, scenarioName): Observable { + return this.http.get(`projects/${projectName}/scenarios/${scenarioName}/notifications`); + } + + deleteScenarioNotification(projectName, scenarioName, id): Observable<{}> { + return this.http.delete<{}>(`projects/${projectName}/scenarios/${scenarioName}/notifications/${id}`, { observe: 'response'}); + } + + createNewScenarioNotification(projectName, scenarioName, body): Observable<{}> { + return this.http.post(`projects/${projectName}/scenarios/${scenarioName}/notifications`, body, { observe: 'response' }); + + } + setData(data) { this.response.next(data); } diff --git a/src/app/scenario.service.ts b/src/app/scenario.service.ts index 5ddeb6e1..c4ddf34f 100644 --- a/src/app/scenario.service.ts +++ b/src/app/scenario.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; import { BehaviorSubject, interval } from 'rxjs'; import { scenarioHistoryGraphs } from './graphs/scenario-trends'; +import { ScenarioNotifications } from './items.service.model'; import { ScenarioApiService } from './scenario-api.service'; @Injectable({ @@ -12,6 +13,9 @@ export class ScenarioService { private trends = new BehaviorSubject<{}>({}); public trends$ = this.trends.asObservable(); + private notifications = new BehaviorSubject([]); + public notifications$ = this.notifications.asObservable(); + constructor( private scenarioApiService: ScenarioApiService ) { } @@ -22,4 +26,9 @@ export class ScenarioService { .subscribe(_ => this.trends.next(scenarioHistoryGraphs(_, projectName, scenarioName))); } + fetchScenarioNotifications(projectName, scenarioName) { + this.scenarioApiService.fetchScenarioNotification(projectName, scenarioName) + .subscribe(_ => this.notifications.next(_)); + } + } diff --git a/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.css b/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.css new file mode 100644 index 00000000..e69de29b diff --git a/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.html b/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.html new file mode 100644 index 00000000..b7725801 --- /dev/null +++ b/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.html @@ -0,0 +1,44 @@ + + +
+ + + + + +
+ + diff --git a/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.spec.ts b/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.spec.ts new file mode 100644 index 00000000..d205206a --- /dev/null +++ b/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.spec.ts @@ -0,0 +1,29 @@ +import { HttpClientModule } from '@angular/common/http'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { AddNewExternalNotificationComponent } from './add-new-external-notification.component'; + +describe('AddNewExternalNotificationComponent', () => { + let component: AddNewExternalNotificationComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AddNewExternalNotificationComponent ], + imports: [RouterTestingModule, ReactiveFormsModule, HttpClientModule], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AddNewExternalNotificationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.ts b/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.ts new file mode 100644 index 00000000..ec563007 --- /dev/null +++ b/src/app/scenario/external-notification/add-new-external-notification/add-new-external-notification.component.ts @@ -0,0 +1,77 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { FormControl, Validators, FormGroup } from '@angular/forms'; +import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { of } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { NotificationMessage } from 'src/app/notification/notification-messages'; +import { ScenarioApiService } from 'src/app/scenario-api.service'; +import { ScenarioService } from 'src/app/scenario.service'; + +@Component({ + selector: 'app-add-new-external-notification', + templateUrl: './add-new-external-notification.component.html', + styleUrls: ['./add-new-external-notification.component.css'] +}) +export class AddNewExternalNotificationComponent implements OnInit { + + myform: FormGroup; + url; + name; + modal: NgbActiveModal; + @Input() params; + + constructor( + private modalService: NgbModal, + private notification: NotificationMessage, + private scenarioApiService: ScenarioApiService, + private scenarioService: ScenarioService, + ) { } + + ngOnInit() { + this.createFormControls(); + this.createForm(); + } + + open(content) { + this.modal = this.modalService.open(content, { ariaLabelledBy: 'modal-basic-title' }); + } + + createFormControls() { + this.url = new FormControl('', [ + Validators.maxLength(400), + Validators.required + ]); + this.name = new FormControl('', [ + Validators.maxLength(100), + Validators.required + ]); + } + + createForm() { + this.myform = new FormGroup({ + url: this.url, + name: this.name + }); + } + + onSubmit() { + if (this.myform.valid) { + const { projectName, scenarioName } = this.params; + const body = { + ...this.myform.value, + type: 'ms-teams' + }; + + this.scenarioApiService.createNewScenarioNotification(projectName, scenarioName, body) + .pipe(catchError(r => of(r))) + .subscribe(_ => { + const message = this.notification.createScenarioNotification(_); + this.scenarioApiService.setData(message); + this.scenarioService.fetchScenarioNotifications(projectName, scenarioName); + }); + this.myform.reset(); + this.modal.close(); + } + } + +} diff --git a/src/app/scenario/external-notification/delete-external-notification/delete-external-notification.component.css b/src/app/scenario/external-notification/delete-external-notification/delete-external-notification.component.css new file mode 100644 index 00000000..e69de29b diff --git a/src/app/scenario/external-notification/delete-external-notification/delete-external-notification.component.html b/src/app/scenario/external-notification/delete-external-notification/delete-external-notification.component.html new file mode 100644 index 00000000..c233f3ab --- /dev/null +++ b/src/app/scenario/external-notification/delete-external-notification/delete-external-notification.component.html @@ -0,0 +1,25 @@ + + +
+ + + + +
+ + diff --git a/src/app/scenario/external-notification/delete-external-notification/delete-external-notification.component.spec.ts b/src/app/scenario/external-notification/delete-external-notification/delete-external-notification.component.spec.ts new file mode 100644 index 00000000..7c08f6d9 --- /dev/null +++ b/src/app/scenario/external-notification/delete-external-notification/delete-external-notification.component.spec.ts @@ -0,0 +1,29 @@ +import { HttpClientModule } from '@angular/common/http'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { DeleteExternalNotificationComponent } from './delete-external-notification.component'; + +describe('DeleteExternalNotificationComponent', () => { + let component: DeleteExternalNotificationComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DeleteExternalNotificationComponent ], + imports: [RouterTestingModule, ReactiveFormsModule, HttpClientModule], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DeleteExternalNotificationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/scenario/external-notification/delete-external-notification/delete-external-notification.component.ts b/src/app/scenario/external-notification/delete-external-notification/delete-external-notification.component.ts new file mode 100644 index 00000000..789087ee --- /dev/null +++ b/src/app/scenario/external-notification/delete-external-notification/delete-external-notification.component.ts @@ -0,0 +1,58 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { of } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { NotificationMessage } from 'src/app/notification/notification-messages'; +import { ScenarioApiService } from 'src/app/scenario-api.service'; +import { ScenarioService } from 'src/app/scenario.service'; + +@Component({ + selector: 'app-delete-external-notification', + templateUrl: './delete-external-notification.component.html', + styleUrls: ['./delete-external-notification.component.css'] +}) +export class DeleteExternalNotificationComponent implements OnInit { + + myform: FormGroup; + modal: NgbActiveModal; + @Input() notificationInput: { id: string, name: string }; + @Input() params: { projectName: string, scenarioName: string }; + + constructor( + private modalService: NgbModal, + private scenarioApiService: ScenarioApiService, + private notification: NotificationMessage, + private scenarioService: ScenarioService + ) { } + + ngOnInit(): void { + this.createForm(); + } + + + createForm() { + this.myform = new FormGroup({}); + } + + open(content) { + this.modal = this.modalService.open(content, { ariaLabelledBy: 'modal-basic-title' }); + } + + onSubmit() { + if (this.myform.valid) { + const { projectName, scenarioName } = this.params; + this.scenarioApiService.deleteScenarioNotification(projectName, scenarioName, this.notificationInput.id) + .pipe(catchError(r => of(r))) + .subscribe(_ => { + const message = this.notification.deleteScenarioNotification(_); + this.scenarioApiService.setData(message); + this.scenarioService.fetchScenarioNotifications(projectName, scenarioName); + }); + this.myform.reset(); + this.modal.close(); + } + } + + +} diff --git a/src/app/scenario/external-notification/external-notification.component.css b/src/app/scenario/external-notification/external-notification.component.css new file mode 100644 index 00000000..1364fa13 --- /dev/null +++ b/src/app/scenario/external-notification/external-notification.component.css @@ -0,0 +1,13 @@ +.btn { + margin-right: 15px; +} + +.notif-control-panel { + margin-bottom: 10px; +} +.ellipsis { + max-width: 15rem; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} diff --git a/src/app/scenario/external-notification/external-notification.component.html b/src/app/scenario/external-notification/external-notification.component.html new file mode 100644 index 00000000..029bacd9 --- /dev/null +++ b/src/app/scenario/external-notification/external-notification.component.html @@ -0,0 +1,63 @@ + + +
+
scenarioproject nameenvironmentstart timestatusscenarioproject nameenvironmentstart timestatus
+ + + + + + + + + + + + + + + + + + + + + + + +
+ Name + + Type + + URL + +
+
No data found.
+
+ {{_.name}} + + {{_.type}} + + {{_.url}} +
+ + + + + + + + + diff --git a/src/app/scenario/external-notification/external-notification.component.spec.ts b/src/app/scenario/external-notification/external-notification.component.spec.ts new file mode 100644 index 00000000..7210b00a --- /dev/null +++ b/src/app/scenario/external-notification/external-notification.component.spec.ts @@ -0,0 +1,35 @@ +import { HttpClientModule } from '@angular/common/http'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterTestingModule } from '@angular/router/testing'; +import { DataTableModule } from '@rushvora/ng-datatable'; +import { AddNewExternalNotificationComponent } from './add-new-external-notification/add-new-external-notification.component'; +import { DeleteExternalNotificationComponent } from './delete-external-notification/delete-external-notification.component'; + +import { ExternalNotificationComponent } from './external-notification.component'; + +describe('ExternalNotificationComponent', () => { + let component: ExternalNotificationComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ExternalNotificationComponent, + AddNewExternalNotificationComponent, + DeleteExternalNotificationComponent], + imports: [RouterTestingModule, ReactiveFormsModule, HttpClientModule, DataTableModule], + + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ExternalNotificationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/scenario/external-notification/external-notification.component.ts b/src/app/scenario/external-notification/external-notification.component.ts new file mode 100644 index 00000000..99fbaf2b --- /dev/null +++ b/src/app/scenario/external-notification/external-notification.component.ts @@ -0,0 +1,47 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {FormControl, FormGroup, Validators} from '@angular/forms'; +import {ActivatedRoute} from '@angular/router'; +import {NgbModal} from '@ng-bootstrap/ng-bootstrap'; +import {ScenarioApiService} from '../../scenario-api.service'; +import {ScenarioNotifications} from '../../items.service.model'; +import { ScenarioService } from 'src/app/scenario.service'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'app-external-notification', + templateUrl: './external-notification.component.html', + styleUrls: ['./external-notification.component.css'] +}) +export class ExternalNotificationComponent implements OnInit { + + myform: FormGroup; + params; + notifications$: Observable; + + + + constructor( + private route: ActivatedRoute, + private modalService: NgbModal, + private scenarioApiService: ScenarioApiService, + private scenarioService: ScenarioService + ) { + this.notifications$ = this.scenarioService.notifications$; + + } + + ngOnInit(): void { + this.route.params.subscribe(_ => this.params = _); + this.createForm(); + this.scenarioService.fetchScenarioNotifications(this.params.projectName, this.params.scenarioName); + } + + createForm() { + this.myform = new FormGroup({}); + } + + open(content) { + this.modalService.open(content, { ariaLabelledBy: 'modal-basic-title', size: 'lg' }); + } + +} diff --git a/src/app/scenario/scenario.component.html b/src/app/scenario/scenario.component.html index b363bafb..88eabff4 100644 --- a/src/app/scenario/scenario.component.html +++ b/src/app/scenario/scenario.component.html @@ -8,6 +8,7 @@
+
@@ -60,28 +61,28 @@
Test Runs - - - - - - - - diff --git a/src/app/scenario/scenario.component.scss b/src/app/scenario/scenario.component.scss index 40237a4b..cce3b1e7 100644 --- a/src/app/scenario/scenario.component.scss +++ b/src/app/scenario/scenario.component.scss @@ -26,13 +26,6 @@ a { font-size: 12px; } -.head { - font-size: 12px; - color: #888888c7; - border-top: 0px; - border-bottom: 2px solid #a5a2a247; -} - .vu, .duration, .status, .base { max-width: 5rem; } diff --git a/src/app/scenario/scenario.component.spec.ts b/src/app/scenario/scenario.component.spec.ts index 99bce0c2..fdf2d0a4 100644 --- a/src/app/scenario/scenario.component.spec.ts +++ b/src/app/scenario/scenario.component.spec.ts @@ -13,6 +13,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { ReactiveFormsModule, FormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { DataTableModule } from '@rushvora/ng-datatable'; +import { ExternalNotificationComponent } from './external-notification/external-notification.component'; describe('ScenarioComponent', () => { let component: ScenarioComponent; @@ -27,7 +28,8 @@ describe('ScenarioComponent', () => { EditScenarioComponent, DeleteScenarioComponent, ControlPanelComponent, - ScenarioGraphComponent + ScenarioGraphComponent, + ExternalNotificationComponent, ], imports: [ NgxSpinnerModule, diff --git a/src/app/scenario/scenario.component.ts b/src/app/scenario/scenario.component.ts index f88675c7..39683ec0 100644 --- a/src/app/scenario/scenario.component.ts +++ b/src/app/scenario/scenario.component.ts @@ -3,7 +3,6 @@ import { ActivatedRoute, Router } from '@angular/router'; import { of, Observable, Subscription } from 'rxjs'; import { switchMap, catchError } from 'rxjs/operators'; import { ProjectOverview, Items } from '../items.service.model'; -import { ProjectService } from '../project.service'; import { ItemsService } from '../items.service'; import { SharedMainBarService } from '../shared-main-bar.service'; import { ScenarioService } from '../scenario.service'; diff --git a/src/styles.scss b/src/styles.scss index f721deb5..666203c9 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -93,3 +93,12 @@ a:hover { .icon-primary:hover { color: #105196 } + +.jtl-head { + font-size: 12px; + padding-bottom: 5px; + border-top: 0 !important; + width: 30rem; + height: 43px; + color: #54545491; +}
+ virtual users + duration + environment + note + start time + hostname + status +